summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/naturaldocs/project/Menu.txt1
-rw-r--r--src/Makefile.am2
-rw-r--r--src/gd.h5
-rw-r--r--src/gd_filename.c254
-rw-r--r--tests/Makefile.am3
-rw-r--r--tests/gdimagefile/gdnametest.c153
-rw-r--r--tests/gdimagefile/img-ref.tgabin0 -> 9018 bytes
-rw-r--r--tests/gdimagefile/img-ref.xbm37
-rw-r--r--tests/gdimagefile/img-ref.xpm58
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
diff --git a/src/gd.h b/src/gd.h
index ac64cac..2a619bf 100644
--- a/src/gd.h
+++ b/src/gd.h
@@ -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
new file mode 100644
index 0000000..201e145
--- /dev/null
+++ b/tests/gdimagefile/img-ref.tga
Binary files differ
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 */
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+"............................................................",
+"............................................................",
+"............................................................",
+"............................................................",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... ",
+" .... "
+};