summaryrefslogtreecommitdiff
path: root/src/gd_filename.c
blob: 7ce4817f93e41cbe7116003ca50b29625b848b41 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
/* Convenience functions to read or write images from or to disk,
 * determining file type from the filename extension. */

#ifdef HAVE_CONFIG_H
#	include "config.h"
#endif

#include <stdio.h>
#include <string.h>

#include "gd.h"

typedef gdImagePtr (BGD_STDCALL *ReadFn)(FILE *in);
typedef void (BGD_STDCALL *WriteFn)(gdImagePtr im, FILE *out);
typedef gdImagePtr (BGD_STDCALL *LoadFn)(char *filename);

#ifdef HAVE_LIBZ
static void BGD_STDCALL writegd2(gdImagePtr im, FILE *out) {
    gdImageGd2(im, out, 0, GD2_FMT_COMPRESSED);
}/* writegd*/
#endif

#ifdef HAVE_LIBJPEG
static void BGD_STDCALL writejpeg(gdImagePtr im, FILE *out) {
    gdImageJpeg(im, out, -1);
}/* writejpeg*/
#endif

static void BGD_STDCALL writewbmp(gdImagePtr im, FILE *out) {
    int fg = gdImageColorClosest(im, 0, 0, 0);

    gdImageWBMP(im, fg, out);
}/* writejpeg*/

static void BGD_STDCALL writebmp(gdImagePtr im, FILE *out) {
    gdImageBmp(im, out, GD_TRUE);
}/* writejpeg*/


static const struct FileType {
    const char *ext;
    ReadFn reader;
    WriteFn writer;
    LoadFn loader;
} Types[] = {
    {".gif",    gdImageCreateFromGif,   gdImageGif,     NULL},
    {".gd",     gdImageCreateFromGd,    gdImageGd,      NULL},
    {".wbmp",   gdImageCreateFromWBMP,  writewbmp,      NULL},
    {".bmp",    gdImageCreateFromBmp,   writebmp,       NULL},

    {".xbm",    gdImageCreateFromXbm,   NULL,           NULL},
    {".tga",    gdImageCreateFromTga,   NULL,           NULL},

#ifdef HAVE_LIBAVIF
    {".avif",   gdImageCreateFromAvif,  gdImageAvif,    NULL},
#endif

#ifdef HAVE_LIBPNG
    {".png",    gdImageCreateFromPng,   gdImagePng,     NULL},
#endif

#ifdef HAVE_LIBJPEG
    {".jpg",    gdImageCreateFromJpeg,  writejpeg,      NULL},
    {".jpeg",   gdImageCreateFromJpeg,  writejpeg,      NULL},
#endif

#ifdef HAVE_LIBHEIF
    {".heic",   gdImageCreateFromHeif,  gdImageHeif,    NULL},
    {".heix",   gdImageCreateFromHeif,  NULL,           NULL},
#endif

#ifdef HAVE_LIBTIFF
    {".tiff",   gdImageCreateFromTiff,  gdImageTiff,    NULL},
    {".tif" ,   gdImageCreateFromTiff,  gdImageTiff,    NULL},
#endif

#ifdef HAVE_LIBZ
    {".gd2",    gdImageCreateFromGd2,   writegd2,       NULL},
#endif

#ifdef HAVE_LIBWEBP
    {".webp",   gdImageCreateFromWebp,  gdImageWebp,    NULL},
#endif

#ifdef HAVE_LIBXPM
    {".xpm",    NULL,                   NULL,           gdImageCreateFromXpm},
#endif

    {NULL, NULL, NULL, NULL}
};


static const struct FileType *
ftype(const char *filename) {
    int n;
    char *ext;

    /* Find the file extension (i.e. the last period in the string. */
    ext = strrchr(filename, '.');
    if (!ext) return NULL;

    for (n = 0; Types[n].ext; n++) {
        if (strcasecmp(ext, Types[n].ext) == 0) {
            return &Types[n];
        }/* if */
    }/* for */

    return NULL;
}/* ftype*/


/*
  Function: gdSupportsFileType

    Tests if a given file type is supported by GD.

    Given the name of an image file (which does not have to exist),
    returns 1 (i.e. TRUE) if <gdImageCreateFromFile> can read a file
    of that type.  This is useful if you do not know which image types
    were enabled at compile time.

    If _writing_ is true, the result will be true only if
    <gdImageFile> can write a file of this type.

    Note that filename parsing is done exactly the same as is done by
    <gdImageCreateFromFile> and <gdImageFile> and is subject to the
    same limitations.

    Assuming LibGD is compiled with support for these image types, the
    following extensions are supported:

        - .gif
        - .gd, .gd2
        - .wbmp
        - .bmp
        - .xbm
        - .tga
        - .png
        - .jpg, .jpeg
        - .heif, .heix
        - .avif
        - .tiff, .tif
        - .webp
        - .xpm

    Names are parsed case-insenstively.

  Parameters:

    filename    - Filename with tested extension.
    writing     - Flag: true tests if writing works

  Returns:

    GD_TRUE (1) if the file type is supported, GD_FALSE (0) if not.

*/
BGD_DECLARE(int)
gdSupportsFileType(const char *filename, int writing) {
    const struct FileType *entry = ftype(filename);
    return !!entry && (!writing || !!entry->writer);
}/* gdSupportsFileType*/


/*
  Function: gdImageCreateFromFile

    Read an image file of any supported.

    Given the path to a file, <gdImageCreateFromFile> will open the
    file, read its contents with the appropriate _gdImageCreateFrom*_
    function and return it.

    File type is determined by the filename extension, so having an
    incorrect extension will probably not work.  For example, renaming
    PNG image "foo.png" to "foo.gif" and then attempting to load it
    will fail even if GD supports both formats.  See
    <gdSupportsFiletype> for more details.

    NULL is returned on error.

  Parameters:

    filename    - the input file name

  Returns:

    A pointer to the new image or NULL if an error occurred.

*/

BGD_DECLARE(gdImagePtr)
gdImageCreateFromFile(const char *filename) {
    const struct FileType *entry = ftype(filename);
    FILE *fh;
    gdImagePtr result;

    if (!entry) return NULL;
    if (entry->loader) return entry->loader((char *)filename);
    if (!entry->reader) return NULL;

    fh = fopen(filename, "rb");
    if (!fh) return NULL;

    result = entry->reader(fh);

    fclose(fh);

    return result;
}/* gdImageCreateFromFile*/



/*
  Function: gdImageFile

    Writes an image to a file in the format indicated by the filename.

    File type is determined by the extension of the file name.  See
    <gdSupportsFiletype> for an overview of the parsing.

    For file types that require extra arguments, <gdImageFile>
    attempts to use sane defaults:

    <gdImageGd2>    - chunk size = 0, compression is enabled.
    <gdImageJpeg>   - quality = -1 (i.e. the reasonable default)
    <gdImageWBMP>   - foreground is the darkest available color

    Everything else is called with the two-argument function and so
    will use the default values.

    <gdImageFile> has some rudimentary error detection and will return
    GD_FALSE (0) if a detectable error occurred.  However, the image
    loaders do not normally return their error status so a result of
    GD_TRUE (1) does **not** mean the file was saved successfully.

  Parameters:

    im          - The image to save.
    filename    - The path to the file to which the image is saved.

  Returns:

    GD_FALSE (0) if an error was detected, GD_TRUE (1) if not.

*/

BGD_DECLARE(int)
gdImageFile(gdImagePtr im, const char *filename) {
    const struct FileType *entry = ftype(filename);
    FILE *fh;

    if (!entry || !entry->writer) return GD_FALSE;

    fh = fopen(filename, "wb");
    if (!fh) return GD_FALSE;

    entry->writer(im, fh);

    fclose(fh);

    return GD_TRUE;
}/* gdImageFile*/