diff options
-rw-r--r-- | docs/naturaldocs/project/Menu.txt | 1 | ||||
-rw-r--r-- | src/Makefile.am | 2 | ||||
-rw-r--r-- | src/gd.h | 5 | ||||
-rw-r--r-- | src/gd_filename.c | 254 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/gdimagefile/gdnametest.c | 153 | ||||
-rw-r--r-- | tests/gdimagefile/img-ref.tga | bin | 0 -> 9018 bytes | |||
-rw-r--r-- | tests/gdimagefile/img-ref.xbm | 37 | ||||
-rw-r--r-- | tests/gdimagefile/img-ref.xpm | 58 |
9 files changed, 511 insertions, 2 deletions
diff --git a/docs/naturaldocs/project/Menu.txt b/docs/naturaldocs/project/Menu.txt index ce0c702..2db47b0 100644 --- a/docs/naturaldocs/project/Menu.txt +++ b/docs/naturaldocs/project/Menu.txt @@ -48,6 +48,7 @@ Format: 1.51 File: About LibGD 2.1.1-dev (preamble.txt) File: gd.h (gd.h) +File: gd_filename.c (gd_filename.c) File: gd_filter.c (gd_filter.c) File: gd_interpolation.c (gd_interpolation.c) File: gdImageCreate (gd.c) diff --git a/src/Makefile.am b/src/Makefile.am index 3e62065..ac66c79 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -48,7 +48,7 @@ libgd_la_SOURCES = gd.c gd_color.c gd_color_map.c gd_transform.c gdfx.c gd_secur gdfontmb.c gdfonts.c gdfontt.c gdft.c gdhelpers.c gdhelpers.h gdkanji.c gdtables.c gdxpm.c jisx0208.h wbmp.c wbmp.h \ gd_filter.c gd_nnquant.c gd_rotate.c gd_matrix.c gd_interpolation.c gd_crop.c webpimg.c webpimg.h gd_webp.c gd_tiff.c \ gd_tga.c gd_tga.h gd_bmp.c bmp.h gd_color.h gd_nnquant.h gd_tga.h gd_intern.h gd_io_stream.h gd_xbm.c \ - gd_color_match.c gd_version.c + gd_color_match.c gd_version.c gd_filename.c libgd_la_LDFLAGS = -version-info $(GDLIB_CURRENT):$(GDLIB_REVISION):$(GDLIB_AGE) -no-undefined @@ -457,6 +457,7 @@ BGD_DECLARE(gdImagePtr) gdImageCreateFromTgaPtr(int size, void *data); BGD_DECLARE(gdImagePtr) gdImageCreateFromBmp (FILE * inFile); BGD_DECLARE(gdImagePtr) gdImageCreateFromBmpPtr (int size, void *data); BGD_DECLARE(gdImagePtr) gdImageCreateFromBmpCtx (gdIOCtxPtr infile); +BGD_DECLARE(gdImagePtr) gdImageCreateFromFile(const char *filename); /* A custom data source. */ /* The source function must return -1 on error, otherwise the number @@ -790,6 +791,10 @@ BGD_DECLARE(void) gdImagePngCtxEx (gdImagePtr im, gdIOCtx * out, int level); BGD_DECLARE(void) gdImageWBMP (gdImagePtr image, int fg, FILE * out); BGD_DECLARE(void) gdImageWBMPCtx (gdImagePtr image, int fg, gdIOCtx * out); +BGD_DECLARE(int) gdImageFile(gdImagePtr im, const char *filename); +BGD_DECLARE(int) gdSupportsFileType(const char *filename, int writing); + + /* Guaranteed to correctly free memory returned by the gdImage*Ptr functions */ BGD_DECLARE(void) gdFree (void *m); diff --git a/src/gd_filename.c b/src/gd_filename.c new file mode 100644 index 0000000..068c4ad --- /dev/null +++ b/src/gd_filename.c @@ -0,0 +1,254 @@ +/* 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 (*ReadFn)(FILE *in); +typedef void (*WriteFn)(gdImagePtr im, FILE *out); +typedef gdImagePtr (*LoadFn)(char *filename); + +#ifdef HAVE_LIBZ +static void writegd2(gdImagePtr im, FILE *out) { + gdImageGd2(im, out, 0, GD2_FMT_COMPRESSED); +}/* writegd*/ +#endif + +#ifdef HAVE_LIBJPEG +static void writejpeg(gdImagePtr im, FILE *out) { + gdImageJpeg(im, out, -1); +}/* writejpeg*/ +#endif + +static void writewbmp(gdImagePtr im, FILE *out) { + int fg = gdImageColorClosest(im, 0, 0, 0); + + gdImageWBMP(im, fg, out); +}/* writejpeg*/ + +static void writebmp(gdImagePtr im, FILE *out) { + gdImageBmp(im, out, GD_TRUE); +}/* writejpeg*/ + + +enum FType {UNKNOWN, PNG, JPG, GIF, TIFF, GD, GD2, WEBP}; +static 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_LIBPNG + {".png", gdImageCreateFromPng, gdImagePng, NULL}, +#endif + +#ifdef HAVE_LIBJPEG + {".jpg", gdImageCreateFromJpeg, writejpeg, NULL}, + {".jpeg", gdImageCreateFromJpeg, writejpeg, NULL}, +#endif + +#ifdef HAVE_LIBTIFF + {".tiff", gdImageCreateFromTiff, gdImageTiff, NULL}, + {".tif" , gdImageCreateFromTiff, gdImageTiff, NULL}, +#endif + +#ifdef HAVE_LIBZ + {".gd2", gdImageCreateFromGd2, writegd2, NULL}, +#endif + +#ifdef HAVE_LIBVPX + {".webp", gdImageCreateFromWebp, gdImageWebp, NULL}, +#endif + +#ifdef HAVE_LIBXPM + {".xpm", NULL, NULL, gdImageCreateFromXpm}, +#endif + + {NULL, NULL, NULL} +}; + + +struct FileType * +ftype(const char *filename) { + int n; + char *ext; + + /* Find the file extension (i.e. the last period in the string. */ + ext = rindex(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 + - .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) { + 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) { + 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) { + 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*/ + diff --git a/tests/Makefile.am b/tests/Makefile.am index c0c6cf7..59a04a1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -32,7 +32,8 @@ check_PROGRAMS = \ gdinterpolatedscale/gdTrivialResize \ gdinterpolatedscale/gdModesAndPalettes \ gd/gd_versiontest \ - gdimagefilter/gdCopyBlurred + gdimagefilter/gdCopyBlurred \ + gdimagefile/gdnametest EXTRA_PROGRAMS = \ gdimagestringft/gdimagestringft_bbox \ diff --git a/tests/gdimagefile/gdnametest.c b/tests/gdimagefile/gdnametest.c new file mode 100644 index 0000000..07791ec --- /dev/null +++ b/tests/gdimagefile/gdnametest.c @@ -0,0 +1,153 @@ +#include <stdio.h> +#include <stdlib.h> + +#include "gd.h" +#include "gdtest.h" + +#define WIDTH 60 +#define HEIGHT 50 +#define LX (WIDTH/2) // Line X +#define LY (HEIGHT/2) // Line Y +#define HT 2 // Half of line-thickness + + +gdImagePtr mkwhite(int x, int y) +{ + gdImagePtr im; + + im = gdImageCreateTrueColor(x, y); + gdImageFilledRectangle(im, 0, 0, x-1, y-1, + gdImageColorExactAlpha(im, 255, 255, 255, 0)); + + gdTestAssert(im != NULL); + + gdImageSetInterpolationMethod(im, GD_BICUBIC); // FP interp'n + + return im; +}/* mkwhite*/ + + +gdImagePtr mkcross() { + gdImagePtr im; + int fg, n; + + im = mkwhite(WIDTH, HEIGHT); + fg = gdImageColorAllocate(im, 0, 0, 0); + + for (n = -HT; n < HT; n++) { + gdImageLine(im, LX-n, 0, LX-n, HEIGHT-1, fg); + gdImageLine(im, 0, LY-n, WIDTH-1, LY-n, fg); + }/* for */ + + return im; +}/* mkcross*/ + + + + +void +do_test() { + int n; + struct { + const char *nm; // Filename + unsigned maxdiff; // Maximum total pixel diff + int required; // 1 -> image type always supported, -1 -> skip it + int readonly; // 1 -> gd can only read this type + } names[] = { + {"img.png", 0, 0, 0}, + {"img.gif", 5, 1, 0}, // This seems to come from tc<->palette + {"img.GIF", 5, 1, 0}, // Test for case insensitivity + {"img.gd", 0, 1, 0}, + {"img.gd2", 0, 0, 0}, + {"img.jpg", 25, 0, 0}, + {"img.jpeg", 25, 0, 0}, + {"img.wbmp", 0, 1, 0}, + {"img.bmp", 0, 1, 0}, + {"img-ref.xpm", 0, 0, 1}, + + // These break the test so I'm skipping them since the point + // of this test is not those loaders. + {"img-ref.xbm", 0, -1, 1}, + {"img-ref.tga", 0, -1, 1}, + {"img.webp", 0, -1, 0}, + + {NULL, 0} + }; + + for (n = 0; names[n].nm; n++) { + gdImagePtr orig, copy; + int status; + char full_filename[255]; + + /* Some image readers are buggy and crash the program so we + * skip them. Bug fixers should remove these from the list of + * skipped items as bugs are fixed. */ + if (names[n].required < 0) { + printf("Skipping test for '%s'. FIX THIS!\n", names[n].nm); + continue; + }/* if */ + + /* Skip this file if the current library build doesn't support + * it. (If it's one of the built-in types, *that* a different + * problem; we assert that here.) */ + if (!gdSupportsFileType(names[n].nm, 0)) { + gdTestAssert(!names[n].required); + continue; + }/* if */ + + orig = mkcross(); + + /* Prepend the test directory; this is expected to be run in + * the parent dir. */ + snprintf(full_filename, sizeof(full_filename), "gdimagefile/%s", + names[n].nm); + + /* Write the image unless writing is not supported. */ + if (!names[n].readonly) { + gdImageFile(orig, full_filename); + }/* if */ + + copy = gdImageCreateFromFile(full_filename); + gdTestAssert(!!copy); + if (!copy) continue; + + /* Debug printf. */ + //printf("%s -> %d\n", full_filename, gdMaxPixelDiff(orig, copy)); + + gdTestAssert(gdMaxPixelDiff(orig, copy) <= names[n].maxdiff); + + if (!names[n].readonly) { + status = remove(full_filename); + gdTestAssert(status == 0); + }/* if */ + + gdImageDestroy(orig); + gdImageDestroy(copy); + }/* for */ + +}/* do_test*/ + + +void +do_errortest() { + gdImagePtr im; + + im = mkcross(); + + gdTestAssert(!gdImageFile(im, "img.xpng")); + gdTestAssert(!gdImageFile(im, "bobo")); + gdTestAssert(!gdImageFile(im, "png")); + gdTestAssert(!gdImageFile(im, "")); + + gdImageDestroy(im); +}/* do_errortest*/ + + +int main(int argc, char **argv) +{ + + do_test(); + do_errortest(); + + return gdNumFailures(); +} diff --git a/tests/gdimagefile/img-ref.tga b/tests/gdimagefile/img-ref.tga Binary files differnew file mode 100644 index 0000000..201e145 --- /dev/null +++ b/tests/gdimagefile/img-ref.tga diff --git a/tests/gdimagefile/img-ref.xbm b/tests/gdimagefile/img-ref.xbm new file mode 100644 index 0000000..bb7ebb7 --- /dev/null +++ b/tests/gdimagefile/img-ref.xbm @@ -0,0 +1,37 @@ +#define foo-ref_width 60 +#define foo-ref_height 50 +static char foo-ref_bits[] = { + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xE0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, + 0x01, 0x00, 0x00, 0x00, }; diff --git a/tests/gdimagefile/img-ref.xpm b/tests/gdimagefile/img-ref.xpm new file mode 100644 index 0000000..574d3d9 --- /dev/null +++ b/tests/gdimagefile/img-ref.xpm @@ -0,0 +1,58 @@ +/* XPM */ +static char *img_ref[] = { +/* columns rows colors chars-per-pixel */ +"60 50 2 1 ", +" c gray100", +". c black", +/* pixels */ +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +"............................................................", +"............................................................", +"............................................................", +"............................................................", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... ", +" .... " +}; |