Commits

Anonymous committed 0c29f6a

- sync to 1.8.0

Comments (0)

Files changed (8)

 #an explicit path for it here, or install manually.
 INSTALL=install
 
-#If you don't have FreeType and/or Xpm installed, including the
+#If you don't have FreeType, libjpeg and/or Xpm installed, including the
 #header files, uncomment this (default).
-CFLAGS=-O
-#If you do have FreeType and/or Xpm fully installed, uncomment a
+#CFLAGS=-O
+#If you do have FreeType, libjpeg and/or Xpm fully installed, uncomment a
 #variation of this and comment out the line above. See also LIBS below.
-#CFLAGS=-O -DHAVE_XPM -DHAVE_LIBTTF
+CFLAGS=-O -DHAVE_XPM -DHAVE_JPEG -DHAVE_LIBTTF
 
 #If you don't have FreeType and/or Xpm fully installed, uncomment this
 #(default).
-LIBS=-lm -lgd -lpng -lz
+#LIBS=-lm -lgd -lpng -lz
 
-#If you do have FreeType and/or Xpm fully installed, uncomment a 
+#If you do have FreeType, JPEG and/or Xpm fully installed, uncomment a 
 #variation of this and comment out the line above. Note that
 #Xpm requires X11. See also CFLAGS above.
-#LIBS=-lm -lgd -lpng -lz -lttf -lXpm -lX11
+LIBS=-lm -lgd -lpng -lz -ljpeg -lttf -lXpm -lX11
 
-#Typical install locations for freetype, zlib, xpm and libpng header files.
+#Typical install locations for freetype, zlib, xpm, libjpeg and libpng header files.
 #If yours are somewhere else, change this. 
 INCLUDEDIRS=-I/usr/local/include -I/usr/include/X11 -I/usr/X11R6/include/X11
 
 	$(CC) gdtestttf.o -o gdtestttf	$(LIBDIRS) $(LIBS)
 
 libgd.a: gd.o gd_gd.o gd_gd2.o gd_io.o gd_io_dp.o gd_io_file.o gd_ss.o \
-	gd_io_ss.o gd_png.o gdxpm.o gdfontt.o gdfonts.o gdfontmb.o gdfontl.o \
+	gd_io_ss.o gd_png.o gd_jpeg.o gdxpm.o gdfontt.o gdfonts.o gdfontmb.o gdfontl.o \
 	gdfontg.o gdtables.o gdttf.o gdcache.o gdkanji.o \
 	gd.h gdfontt.h gdfonts.h gdfontmb.h gdfontl.h gdfontg.h
 	rm -f libgd.a
 	$(AR) rc libgd.a gd.o gd_gd.o gd_gd2.o gd_io.o gd_io_dp.o \
-		gd_io_file.o gd_ss.o gd_io_ss.o gd_png.o gdxpm.o \
+		gd_io_file.o gd_ss.o gd_io_ss.o gd_png.o gd_jpeg.o gdxpm.o \
 		gdfontt.o gdfonts.o gdfontmb.o gdfontl.o gdfontg.o \
 		gdtables.o gdttf.o gdcache.o gdkanji.o
 	-ranlib libgd.a
 	return ct;
 }
 
+/* This code is taken from http://www.acm.org/jgt/papers/SmithLyons96/hwb_rgb.html, an article
+ * on colour conversion to/from RBG and HWB colour systems. 
+ * It has been modified to return the converted value as a * parameter. 
+ */
+
+#define RETURN_HWB(h, w, b) {HWB->H = h; HWB->W = w; HWB->B = b; return HWB;} 
+#define RETURN_RGB(r, g, b) {RGB->R = r; RGB->G = g; RGB->B = b; return RGB;} 
+#define HWB_UNDEFINED -1
+#define SETUP_RGB(s, r, g, b) {s.R = r/255.0; s.G = g/255.0; s.B = b/255.0;}
+
+#define MIN(a,b) ((a)<(b)?(a):(b))
+#define MIN3(a,b,c) ((a)<(b)?(MIN(a,c)):(MIN(b,c)))
+#define MAX(a,b) ((a)<(b)?(b):(a))
+#define MAX3(a,b,c) ((a)<(b)?(MAX(b,c)):(MAX(a,c)))
+
+
+/*
+ * Theoretically, hue 0 (pure red) is identical to hue 6 in these transforms. Pure 
+ * red always maps to 6 in this implementation. Therefore UNDEFINED can be 
+ * defined as 0 in situations where only unsigned numbers are desired.
+ */
+typedef struct {float R, G, B;} RGBType; 
+typedef struct {float H, W, B;} HWBType;
+
+static HWBType* RGB_to_HWB( RGBType RGB, HWBType* HWB) {
+
+	/*
+	 * RGB are each on [0, 1]. W and B are returned on [0, 1] and H is  
+	 * returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B.  
+	 */
+
+ 	float R = RGB.R, G = RGB.G, B = RGB.B, w, v, b, f;  
+	int i;  
+  
+ 	w = MIN3(R, G, B);  
+ 	v = MAX3(R, G, B);  
+ 	b = 1 - v;  
+	if (v == w) RETURN_HWB(HWB_UNDEFINED, w, b);  
+ 	f = (R == w) ? G - B : ((G == w) ? B - R : R - G);  
+ 	i = (R == w) ? 3 : ((G == w) ? 5 : 1);  
+	RETURN_HWB(i - f /(v - w), w, b);  
+
+}
+
+static float HWB_Diff(int r1, int g1, int b1, int r2, int g2, int b2) {
+	RGBType		RGB1, RGB2;
+	HWBType		HWB1, HWB2;
+	float		diff;
+
+	SETUP_RGB(RGB1, r1, g1, b1);
+	SETUP_RGB(RGB2, r2, g2, b2);	
+
+	RGB_to_HWB(RGB1, &HWB1);
+	RGB_to_HWB(RGB2, &HWB2);
+
+	/*
+	 * I made this bit up; it seems to produce OK results, and it is certainly
+	 * more visually correct than the current RGB metric. (PJW)
+	 */
+
+	if ( (HWB1.H == HWB_UNDEFINED) || (HWB2.H == HWB_UNDEFINED) ) {
+		diff = 0; /* Undefined hues always match... */
+	} else {
+		diff = abs(HWB1.H - HWB2.H);
+		if (diff > 3) {
+			diff = 6 - diff; /* Remember, it's a colour circle */
+		}
+	}
+
+	diff = diff*diff + (HWB1.W - HWB2.W)*(HWB1.W - HWB2.W) + (HWB1.B - HWB2.B)*(HWB1.B - HWB2.B);
+
+	return diff;
+}
+
+
+/*
+ * This is not actually used, but is here for completeness, in case someone wants to
+ * use the HWB stuff for anything else...
+ */
+static RGBType* HWB_to_RGB( HWBType HWB, RGBType* RGB ) {
+
+	/* 
+	 * H is given on [0, 6] or UNDEFINED. W and B are given on [0, 1].  
+	 * RGB are each returned on [0, 1]. 
+	 */
+ 
+	float h = HWB.H, w = HWB.W, b = HWB.B, v, n, f;  
+	int i;  
+  
+	v = 1 - b;  
+	if (h == HWB_UNDEFINED) RETURN_RGB(v, v, v);  
+	i = floor(h);  
+	f = h - i;  
+	if (i & 1) f = 1 - f; /* if i is odd */ 
+	n = w + f * (v - w); /* linear interpolation between w and v */ 
+	switch (i) {  
+		case 6:  
+		case 0: RETURN_RGB(v, n, w);  
+		case 1: RETURN_RGB(n, v, w);  
+		case 2: RETURN_RGB(w, v, n);  
+		case 3: RETURN_RGB(w, n, v);  
+		case 4: RETURN_RGB(n, w, v);  
+		case 5: RETURN_RGB(v, w, n);  
+	}  
+
+}
+
+int gdImageColorClosestHWB(gdImagePtr im, int r, int g, int b)
+{
+	int i;
+	long rd, gd, bd;
+	int ct = (-1);
+	int first = 1;
+	float mindist = 0;
+	for (i=0; (i<(im->colorsTotal)); i++) {
+		float dist;
+		if (im->open[i]) {
+			continue;
+		}
+		dist = HWB_Diff(im->red[i], im->green[i], im->blue[i], r, g, b); 
+		if (first || (dist < mindist)) {
+			mindist = dist;	
+			ct = i;
+			first = 0;
+		}
+	}
+	return ct;
+}
+
+
+
 int gdImageColorExact(gdImagePtr im, int r, int g, int b)
 {
 	int i;
 		for (y=0 ; y < (to->sy) ; y++) {
 			p = gdImageGetPixel(to, x, y);
 			if (xlate[p] == -1) {
-				xlate[p] = gdImageColorClosest(from, to->red[p], to->green[p], to->blue[p]);
+				xlate[p] = gdImageColorClosestHWB(from, to->red[p], to->green[p], to->blue[p]);
 				/*printf("Mapping %d (%d, %d, %d) to %d (%d, %d, %d)\n", */
 				/*	p,  to->red[p], to->green[p], to->blue[p], */
 				/*	xlate[p], from->red[xlate[p]], from->green[xlate[p]], from->blue[xlate[p]]); */
 void gdImagePaletteCopy(gdImagePtr dst, gdImagePtr src);
 void gdImagePng(gdImagePtr im, FILE *out);
 void gdImagePngCtx(gdImagePtr im, gdIOCtx *out);
+void gdImageWBMP(gdImagePtr image, int fg, FILE *out);
+void gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out);
 
-/* A custom data sink. */
+void gdImageJpeg(gdImagePtr im, FILE *out, int quality);
+void gdImageJpegCtx(gdImagePtr im, gdIOCtx *out, int quality);
+void *gdImageJpegPtr(gdImagePtr im, int *size, int quality);
+gdImagePtr gdImageCreateFromJpeg(FILE *infile);
+gdImagePtr gdImageCreateFromJpegCtx(gdIOCtx *infile);
+
+/* A custom data sink. For backwards compatibility. Use
+	gdIOCtx instead. */
 /* The sink function must return -1 on error, otherwise the number
         of bytes written, which must be equal to len. */
 /* context will be passed to your sink function. */
+/*
+ * gd_jpeg.c: Read and write JPEG (JFIF) format image files using the
+ * gd graphics library (http://www.boutell.com/gd/).
+ * 
+ * This software is based in part on the work of the Independent JPEG
+ * Group.  For more information on the IJG JPEG software (and JPEG
+ * documentation, etc.), see ftp://ftp.uu.net/graphics/jpeg/.
+ *
+ * NOTE: IJG 12-bit JSAMPLE (BITS_IN_JSAMPLE == 12) mode, although
+ * theoretically supported in this code, has not really been tested.
+ * Caveat emptor.
+ *
+ * Copyright 2000 Doug Becker, mailto:thebeckers@home.com
+ */
+
+#ifdef HAVE_JPEG
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <setjmp.h>
+#include <limits.h>
+#include <string.h>
+#include "jinclude.h"
+#include "jpeglib.h"
+#include "jerror.h"
+#include "gd.h"
+
+static const char * const GD_JPEG_VERSION = "1.0";
+
+typedef struct _jmpbuf_wrapper {
+    jmp_buf jmpbuf;
+} jmpbuf_wrapper;
+
+/* Called by the IJG JPEG library upon encountering a fatal error */
+static void
+fatal_jpeg_error(j_common_ptr cinfo)
+{
+    jmpbuf_wrapper *jmpbufw;
+
+    fprintf(stderr, "gd-jpeg: JPEG library reports unrecoverable error: ");
+    (*cinfo->err->output_message)(cinfo);
+    fflush(stderr);
+
+    jmpbufw = (jmpbuf_wrapper *)cinfo->client_data;
+    jpeg_destroy(cinfo);
+
+    if (jmpbufw != 0) {
+	longjmp(jmpbufw->jmpbuf, 1);
+	fprintf(stderr, "gd-jpeg: EXTREMELY fatal error: longjmp"
+		" returned control; terminating\n");
+    } else {
+	fprintf(stderr, "gd-jpeg: EXTREMELY fatal error: jmpbuf"
+		" unrecoverable; terminating\n");
+    }
+
+    fflush(stderr);
+    exit(99);
+}
+
+/*
+ * Write IM to OUTFILE as a JFIF-formatted JPEG image, using quality
+ * QUALITY.  If QUALITY is in the range 0-100, increasing values
+ * represent higher quality but also larger image size.  If QUALITY is
+ * negative, the IJG JPEG library's default quality is used (which
+ * should be near optimal for many applications).  See the IJG JPEG
+ * library documentation for more details.  */
+
+void gdImageJpeg(gdImagePtr im, FILE *outFile, int quality)
+{
+        gdIOCtx *out = gdNewFileCtx(outFile);
+        gdImageJpegCtx(im, out, quality);
+        out->free(out);
+}
+
+void* gdImageJpegPtr(gdImagePtr im, int *size, int quality)
+{
+        void *rv;
+        gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
+        gdImageJpegCtx(im, out, quality);
+        rv = gdDPExtractData(out, size);
+        out->free(out);
+        return rv;
+}
+
+void jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile);
+
+void
+gdImageJpegCtx(gdImagePtr im, gdIOCtx *outfile, int quality)
+{
+    struct jpeg_compress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+    int i, j, jidx;
+    /* volatile so we can free it on return from longjmp */
+    volatile JSAMPROW row = 0;
+    JSAMPROW rowptr[1];
+    jmpbuf_wrapper jmpbufw;
+    JDIMENSION nlines;
+    char comment[255];
+
+#ifdef DEBUG
+    printf("gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION);
+    printf("gd-jpeg: JPEG library version %d, %d-bit sample values\n",
+	   JPEG_LIB_VERSION, BITS_IN_JSAMPLE);
+
+    for (i = 0; i < im->colorsTotal; i++) {
+	if (!im->open[i])
+	    printf("gd-jpeg: gd colormap index %d: (%d, %d, %d)\n", i,
+		   im->red[i], im->green[i], im->blue[i]);
+    }
+#endif /* DEBUG */
+
+    memset(&cinfo, 0, sizeof(cinfo));
+    memset(&jerr, 0, sizeof(jerr));
+
+    cinfo.err = jpeg_std_error(&jerr);
+    cinfo.client_data = &jmpbufw;
+    if (setjmp(jmpbufw.jmpbuf) != 0) {
+	/* we're here courtesy of longjmp */
+	if (row)
+	    free(row);
+	return;
+    }
+
+    cinfo.err->error_exit = fatal_jpeg_error;
+
+    jpeg_create_compress(&cinfo);
+
+    cinfo.image_width = im->sx;
+    cinfo.image_height = im->sy;
+    cinfo.input_components = 3;	/* # of color components per pixel */
+    cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
+    jpeg_set_defaults(&cinfo);
+    if (quality >= 0)
+	jpeg_set_quality(&cinfo, quality, TRUE);
+
+    /* If user requests interlace, translate that to progressive JPEG */
+    if (gdImageGetInterlaced(im)) {
+#ifdef DEBUG
+	printf("gd-jpeg: interlace set, outputting progressive"
+	       " JPEG image\n"); 
+#endif	
+	jpeg_simple_progression(&cinfo);
+    }
+
+    jpeg_gdIOCtx_dest(&cinfo, outfile);
+
+    row = (JSAMPROW)calloc(1, cinfo.image_width * cinfo.input_components
+			   * sizeof(JSAMPLE));
+    if (row == 0) {
+	fprintf(stderr, "gd-jpeg: error: unable to allocate JPEG row "
+		"structure: calloc returns NULL\n");
+	jpeg_destroy_compress(&cinfo);
+	return;
+    }
+
+    rowptr[0] = row;
+
+    jpeg_start_compress(&cinfo, TRUE);
+
+    sprintf(comment, "CREATOR: gd-jpeg v%s (using IJG JPEG v%d),",
+	    GD_JPEG_VERSION, JPEG_LIB_VERSION);
+    if (quality >= 0)
+	sprintf(comment + strlen(comment), " quality = %d\n",
+		quality);
+    else
+	strcat(comment + strlen(comment), " default quality\n");
+    jpeg_write_marker(&cinfo, JPEG_COM, (JOCTET *)comment,
+		      (unsigned int)strlen(comment));
+
+    for (i = 0; i < im->sy; i++) {
+	for (jidx = 0, j = 0; j < im->sx; j++) {
+	    int idx = im->pixels[i][j];
+
+	    /*
+	     * NB: Although gd RGB values are ints, their max value is
+	     * 255 (see the documentation for gdImageColorAllocate())
+	     * -- perfect for 8-bit JPEG encoding (which is the norm)
+	     */
+#if BITS_IN_JSAMPLE == 8
+	    row[jidx++] = im->red[idx];
+	    row[jidx++] = im->green[idx];
+	    row[jidx++] = im->blue[idx];
+#elif BITS_IN_JSAMPLE == 12
+	    row[jidx++] = im->red[idx] << 4;
+	    row[jidx++] = im->green[idx] << 4;
+	    row[jidx++] = im->blue[idx] << 4;
+#else
+#error IJG JPEG library BITS_IN_JSAMPLE value must be 8 or 12
+#endif
+	}
+
+	nlines = jpeg_write_scanlines(&cinfo, rowptr, 1);
+	if (nlines != 1)
+	    fprintf(stderr, "gd_jpeg: warning: jpeg_write_scanlines"
+		    " returns %u -- expected 1\n", nlines);
+    }
+
+    jpeg_finish_compress(&cinfo);
+    jpeg_destroy_compress(&cinfo);
+    free(row);
+}
+
+gdImagePtr gdImageCreateFromJpeg(FILE *inFile)
+{
+        gdImagePtr im;
+        gdIOCtx *in = gdNewFileCtx(inFile);
+        im = gdImageCreateFromJpegCtx(in);
+        in->free(in);
+        return im;
+}
+
+void
+jpeg_gdIOCtx_src (j_decompress_ptr cinfo,
+	gdIOCtx *infile);
+
+/* 
+ * Create a gd-format image from the JPEG-format INFILE.  Returns the
+ * image, or NULL upon error.
+*/
+gdImagePtr
+gdImageCreateFromJpegCtx(gdIOCtx *infile)
+{
+    struct jpeg_decompress_struct cinfo;
+    struct jpeg_error_mgr jerr;
+    jmpbuf_wrapper jmpbufw;
+    /* volatile so we can free them after longjmp */
+    volatile JSAMPROW row = 0;
+    volatile gdImagePtr im = 0;
+    JSAMPROW rowptr[1];
+    int i, j, retval;
+    JDIMENSION nrows;
+
+#ifdef DEBUG
+    printf("gd-jpeg: gd JPEG version %s\n", GD_JPEG_VERSION);
+    printf("gd-jpeg: JPEG library version %d, %d-bit sample values\n",
+	   JPEG_LIB_VERSION, BITS_IN_JSAMPLE);
+#endif
+
+    memset(&cinfo, 0, sizeof(cinfo));
+    memset(&jerr, 0, sizeof(jerr));
+
+    cinfo.err = jpeg_std_error(&jerr);
+    cinfo.client_data = &jmpbufw;
+    if (setjmp(jmpbufw.jmpbuf) != 0) {
+	/* we're here courtesy of longjmp */
+	if (row)
+	    free(row);
+	if (im)
+	    gdImageDestroy(im);
+	return 0;
+    }
+
+    cinfo.err->error_exit = fatal_jpeg_error;
+
+    jpeg_create_decompress(&cinfo);
+
+    jpeg_gdIOCtx_src(&cinfo, infile);
+
+    retval = jpeg_read_header(&cinfo, TRUE);
+    if (retval != JPEG_HEADER_OK)
+	fprintf(stderr, "gd-jpeg: warning: jpeg_read_header returns"
+		" %d, expected %d\n", retval, JPEG_HEADER_OK);
+
+    if (cinfo.image_height > INT_MAX)
+	fprintf(stderr, "gd-jpeg: warning: JPEG image height (%u) is"
+		" greater than INT_MAX (%d) (and thus greater than"
+		" gd can handle)", cinfo.image_height,
+		INT_MAX);
+
+    if (cinfo.image_width > INT_MAX)
+	fprintf(stderr, "gd-jpeg: warning: JPEG image width (%u) is"
+		" greater than INT_MAX (%d) (and thus greater than"
+		" gd can handle)\n", cinfo.image_width, INT_MAX);
+
+    im = gdImageCreate((int)cinfo.image_width,
+		       (int)cinfo.image_height);
+    if (im == 0) {
+        fprintf(stderr, "gd-jpeg error: cannot allocate gdImage"
+		" struct\n");
+	goto error;
+    }
+
+    /*
+     * Have the JPEG library quantize the number of image colors to
+     * 256 maximum; force into RGB colorspace
+     */
+    cinfo.out_color_space = JCS_RGB;
+    cinfo.quantize_colors = TRUE;
+    cinfo.desired_number_of_colors = gdMaxColors;
+
+    if (jpeg_start_decompress(&cinfo) != TRUE)
+	fprintf(stderr, "gd-jpeg: warning: jpeg_start_decompress"
+		" reports suspended data source\n");
+
+#ifdef DEBUG
+    printf("gd-jpeg: JPEG image information:");
+    if (cinfo.saw_JFIF_marker)
+	printf(" JFIF version %d.%.2d",
+	       (int)cinfo.JFIF_major_version,
+	       (int)cinfo.JFIF_minor_version);
+    else if (cinfo.saw_Adobe_marker)
+	printf(" Adobe format");
+    else
+	printf(" UNKNOWN format");
+
+    printf(" %ux%u (raw) / %ux%u (scaled) %d-bit", cinfo.image_width,
+	   cinfo.image_height, cinfo.output_width,
+	   cinfo.output_height, cinfo.data_precision);
+    printf(" %s", (cinfo.progressive_mode ? "progressive" :
+		   "baseline"));
+    printf(" image, %d quantized colors, ",
+	   cinfo.actual_number_of_colors);
+
+    switch (cinfo.jpeg_color_space) {
+    case JCS_GRAYSCALE:
+	printf("grayscale");
+	break;
+
+    case JCS_RGB:
+	printf("RGB");
+	break;
+
+    case JCS_YCbCr:
+	printf("YCbCr (a.k.a. YUV)");
+	break;
+
+    case JCS_CMYK:
+	printf("CMYK");
+	break;
+
+    case JCS_YCCK:
+	printf("YCbCrK");
+	break;
+
+    default:
+	printf("UNKNOWN (value: %d)", (int)cinfo.jpeg_color_space);
+	break;
+    }
+    printf(" colorspace\n");
+    fflush(stdout);
+#endif /* DEBUG */
+
+    gdImageInterlace(im, cinfo.progressive_mode != 0);
+
+    im->colorsTotal = cinfo.actual_number_of_colors;
+    if (cinfo.output_components != 1) {
+	fprintf(stderr, "gd-jpeg: error: JPEG color quantization"
+		" request resulted in output_components == %d"
+		" (expected 1)\n",  cinfo.output_components);
+	goto error;
+    }
+
+    for (i = 0; i < im->colorsTotal; i++) {
+#if BITS_IN_JSAMPLE == 8
+	im->red[i] = cinfo.colormap[0][i];
+	im->green[i] = cinfo.colormap[1][i];
+	im->blue[i] = cinfo.colormap[2][i];
+#elif BITS_IN_JSAMPLE == 12
+	im->red[i] = (cinfo.colormap[0][i] >> 4) & 0xff;
+	im->green[i] = (cinfo.colormap[1][i] >> 4) & 0xff;
+	im->blue[i] = (cinfo.colormap[2][i] >> 4) & 0xff;
+#else
+#error IJG JPEG library BITS_IN_JSAMPLE value must be 8 or 12
+#endif
+	im->open[i] = 0;
+#ifdef DEBUG
+	printf("gd-jpeg: gd colormap index %d set to (%d, %d, %d)\n", i,
+	       im->red[i], im->green[i], im->blue[i]);
+#endif
+    }
+
+    row = calloc(cinfo.output_width, sizeof(JSAMPLE));
+    if (row == 0) {
+	fprintf(stderr, "gd-jpeg: error: unable to allocate row for"
+		" JPEG scanline: calloc returns NULL\n");
+	goto error;
+    }
+    rowptr[0] = row;
+
+    for (i = 0; i < cinfo.output_height; i++) {
+	nrows = jpeg_read_scanlines(&cinfo, rowptr, 1);
+	if (nrows != 1) {
+	    fprintf(stderr, "gd-jpeg: error: jpeg_read_scanlines"
+		    " returns %u, expected 1\n", nrows);
+	    goto error;
+	}
+
+	for (j = 0; j < cinfo.output_width; j++)
+	    im->pixels[i][j] = row[j];
+    }
+
+    if (jpeg_finish_decompress(&cinfo) != TRUE)
+	fprintf(stderr, "gd-jpeg: warning: jpeg_finish_decompress"
+		" reports suspended data source\n");
+
+
+    jpeg_destroy_decompress(&cinfo);
+    free(row);
+    return im;
+
+error:
+    jpeg_destroy_decompress(&cinfo);
+    if (row)
+	free(row);
+    if (im)
+	gdImageDestroy(im);
+    return 0;
+}
+
+/*
+ *
+ * gdIOCtx JPEG data sources and sinks, T. Boutell
+ * almost a simple global replace from T. Lane's stdio versions.
+ *
+ */
+
+/* Different versions of libjpeg use either 'jboolean' or 'boolean', and
+	some platforms define 'boolean', and so forth. Deal with this
+	madness by typedeffing 'safeboolean' to 'boolean' if HAVE_BOOLEAN
+	is already set, because this is the test that libjpeg uses.
+	Otherwise, typedef it to int, because that's what libjpeg does
+	if HAVE_BOOLEAN is not defined. -TBB */
+
+#ifdef HAVE_BOOLEAN
+typedef boolean safeboolean;
+#else
+typedef int safeboolean;
+#endif /* HAVE_BOOLEAN */
+
+/* Expanded data source object for gdIOCtx input */
+
+typedef struct {
+  struct jpeg_source_mgr pub;	/* public fields */
+
+  gdIOCtx *infile;		/* source stream */
+  JOCTET * buffer;		/* start of buffer */
+  safeboolean start_of_file;	/* have we gotten any data yet? */
+} my_source_mgr;
+
+typedef my_source_mgr * my_src_ptr;
+
+#define INPUT_BUF_SIZE  4096	/* choose an efficiently fread'able size */
+
+/*
+ * Initialize source --- called by jpeg_read_header
+ * before any data is actually read.
+ */
+
+void
+init_source (j_decompress_ptr cinfo)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+
+  /* We reset the empty-input-file flag for each image,
+   * but we don't clear the input buffer.
+   * This is correct behavior for reading a series of images from one source.
+   */
+  src->start_of_file = TRUE;
+}
+
+
+/*
+ * Fill the input buffer --- called whenever buffer is emptied.
+ *
+ * In typical applications, this should read fresh data into the buffer
+ * (ignoring the current state of next_input_byte & bytes_in_buffer),
+ * reset the pointer & count to the start of the buffer, and return TRUE
+ * indicating that the buffer has been reloaded.  It is not necessary to
+ * fill the buffer entirely, only to obtain at least one more byte.
+ *
+ * There is no such thing as an EOF return.  If the end of the file has been
+ * reached, the routine has a choice of ERREXIT() or inserting fake data into
+ * the buffer.  In most cases, generating a warning message and inserting a
+ * fake EOI marker is the best course of action --- this will allow the
+ * decompressor to output however much of the image is there.  However,
+ * the resulting error message is misleading if the real problem is an empty
+ * input file, so we handle that case specially.
+ *
+ * In applications that need to be able to suspend compression due to input
+ * not being available yet, a FALSE return indicates that no more data can be
+ * obtained right now, but more may be forthcoming later.  In this situation,
+ * the decompressor will return to its caller (with an indication of the
+ * number of scanlines it has read, if any).  The application should resume
+ * decompression after it has loaded more data into the input buffer.  Note
+ * that there are substantial restrictions on the use of suspension --- see
+ * the documentation.
+ *
+ * When suspending, the decompressor will back up to a convenient restart point
+ * (typically the start of the current MCU). next_input_byte & bytes_in_buffer
+ * indicate where the restart point will be if the current call returns FALSE.
+ * Data beyond this point must be rescanned after resumption, so move it to
+ * the front of the buffer rather than discarding it.
+ */
+
+#define END_JPEG_SEQUENCE "\r\n[*]--:END JPEG:--[*]\r\n"
+
+safeboolean
+fill_input_buffer (j_decompress_ptr cinfo)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+  size_t nbytes = 0;
+  size_t got;
+  char *s;
+  memset(src->buffer, 0, INPUT_BUF_SIZE);
+  while (nbytes < INPUT_BUF_SIZE) {
+	  int got = gdGetBuf(src->buffer + nbytes, 
+		  INPUT_BUF_SIZE - nbytes,
+		  src->infile);
+      if ((got == EOF) || (got == 0)) {
+		  /* EOF or error. If we got any data, don't worry about it.
+			If we didn't, then this is unexpected. */
+		  if (!nbytes) {
+			nbytes = -1;
+		  }
+		  break;
+      }
+	  nbytes += got;
+  }
+  if (nbytes <= 0) {
+    if (src->start_of_file)	/* Treat empty input file as fatal error */
+      ERREXIT(cinfo, JERR_INPUT_EMPTY);
+    WARNMS(cinfo, JWRN_JPEG_EOF);
+    /* Insert a fake EOI marker */
+    src->buffer[0] = (JOCTET) 0xFF;
+    src->buffer[1] = (JOCTET) JPEG_EOI;
+    nbytes = 2;
+  }
+
+  src->pub.next_input_byte = src->buffer;
+  src->pub.bytes_in_buffer = nbytes;
+  src->start_of_file = FALSE;
+
+  return TRUE;
+}
+
+
+/*
+ * Skip data --- used to skip over a potentially large amount of
+ * uninteresting data (such as an APPn marker).
+ *
+ * Writers of suspendable-input applications must note that skip_input_data
+ * is not granted the right to give a suspension return.  If the skip extends
+ * beyond the data currently in the buffer, the buffer can be marked empty so
+ * that the next read will cause a fill_input_buffer call that can suspend.
+ * Arranging for additional bytes to be discarded before reloading the input
+ * buffer is the application writer's problem.
+ */
+
+void
+skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+{
+  my_src_ptr src = (my_src_ptr) cinfo->src;
+
+  /* Just a dumb implementation for now. Not clear that being smart is worth
+   * any trouble anyway --- large skips are infrequent.
+   */
+  if (num_bytes > 0) {
+    while (num_bytes > (long) src->pub.bytes_in_buffer) {
+      num_bytes -= (long) src->pub.bytes_in_buffer;
+      (void) fill_input_buffer(cinfo);
+      /* note we assume that fill_input_buffer will never return FALSE,
+       * so suspension need not be handled.
+       */
+    }
+    src->pub.next_input_byte += (size_t) num_bytes;
+    src->pub.bytes_in_buffer -= (size_t) num_bytes;
+  }
+}
+
+
+/*
+ * An additional method that can be provided by data source modules is the
+ * resync_to_restart method for error recovery in the presence of RST markers.
+ * For the moment, this source module just uses the default resync method
+ * provided by the JPEG library.  That method assumes that no backtracking
+ * is possible.
+ */
+
+
+/*
+ * Terminate source --- called by jpeg_finish_decompress
+ * after all data has been read.  Often a no-op.
+ *
+ * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+ * application must deal with any cleanup that should happen even
+ * for error exit.
+ */
+
+void
+term_source (j_decompress_ptr cinfo)
+{
+	my_src_ptr src = (my_src_ptr) cinfo->src;
+}
+
+
+/*
+ * Prepare for input from a gdIOCtx stream.
+ * The caller must have already opened the stream, and is responsible
+ * for closing it after finishing decompression.
+ */
+
+void
+jpeg_gdIOCtx_src (j_decompress_ptr cinfo,
+	gdIOCtx *infile)
+{
+  my_src_ptr src;
+
+  /* The source object and input buffer are made permanent so that a series
+   * of JPEG images can be read from the same file by calling jpeg_gdIOCtx_src
+   * only before the first one.  (If we discarded the buffer at the end of
+   * one image, we'd likely lose the start of the next one.)
+   * This makes it unsafe to use this manager and a different source
+   * manager serially with the same JPEG object.  Caveat programmer.
+   */
+  if (cinfo->src == NULL) {	/* first time for this JPEG object? */
+    cinfo->src = (struct jpeg_source_mgr *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				  SIZEOF(my_source_mgr));
+    src = (my_src_ptr) cinfo->src;
+    src->buffer = (JOCTET *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				  INPUT_BUF_SIZE * SIZEOF(JOCTET));
+  }
+
+  src = (my_src_ptr) cinfo->src;
+  src->pub.init_source = init_source;
+  src->pub.fill_input_buffer = fill_input_buffer;
+  src->pub.skip_input_data = skip_input_data;
+  src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
+  src->pub.term_source = term_source;
+  src->infile = infile;
+  src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
+  src->pub.next_input_byte = NULL; /* until buffer loaded */
+}
+
+/* Expanded data destination object for stdio output */
+
+typedef struct {
+  struct jpeg_destination_mgr pub; /* public fields */
+  gdIOCtx * outfile;		/* target stream */
+  JOCTET * buffer;		/* start of buffer */
+} my_destination_mgr;
+
+typedef my_destination_mgr * my_dest_ptr;
+
+#define OUTPUT_BUF_SIZE  4096	/* choose an efficiently fwrite'able size */
+
+/*
+ * Initialize destination --- called by jpeg_start_compress
+ * before any data is actually written.
+ */
+
+void init_destination (j_compress_ptr cinfo)
+{
+  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+
+  /* Allocate the output buffer --- it will be released when done with image */
+  dest->buffer = (JOCTET *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
+				  OUTPUT_BUF_SIZE * SIZEOF(JOCTET));
+
+  dest->pub.next_output_byte = dest->buffer;
+  dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+}
+
+
+/*
+ * Empty the output buffer --- called whenever buffer fills up.
+ *
+ * In typical applications, this should write the entire output buffer
+ * (ignoring the current state of next_output_byte & free_in_buffer),
+ * reset the pointer & count to the start of the buffer, and return TRUE
+ * indicating that the buffer has been dumped.
+ *
+ * In applications that need to be able to suspend compression due to output
+ * overrun, a FALSE return indicates that the buffer cannot be emptied now.
+ * In this situation, the compressor will return to its caller (possibly with
+ * an indication that it has not accepted all the supplied scanlines).  The
+ * application should resume compression after it has made more room in the
+ * output buffer.  Note that there are substantial restrictions on the use of
+ * suspension --- see the documentation.
+ *
+ * When suspending, the compressor will back up to a convenient restart point
+ * (typically the start of the current MCU). next_output_byte & free_in_buffer
+ * indicate where the restart point will be if the current call returns FALSE.
+ * Data beyond this point will be regenerated after resumption, so do not
+ * write it out when emptying the buffer externally.
+ */
+
+safeboolean empty_output_buffer (j_compress_ptr cinfo)
+{
+  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+
+  if (gdPutBuf(dest->buffer, OUTPUT_BUF_SIZE, dest->outfile) !=
+      (size_t) OUTPUT_BUF_SIZE)
+    ERREXIT(cinfo, JERR_FILE_WRITE);
+
+  dest->pub.next_output_byte = dest->buffer;
+  dest->pub.free_in_buffer = OUTPUT_BUF_SIZE;
+
+  return TRUE;
+}
+
+
+/*
+ * Terminate destination --- called by jpeg_finish_compress
+ * after all data has been written.  Usually needs to flush buffer.
+ *
+ * NB: *not* called by jpeg_abort or jpeg_destroy; surrounding
+ * application must deal with any cleanup that should happen even
+ * for error exit.
+ */
+
+void term_destination (j_compress_ptr cinfo)
+{
+  my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
+  size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
+
+  /* Write any data remaining in the buffer */
+  if (datacount > 0) {
+    if (gdPutBuf(dest->buffer, datacount, dest->outfile) != datacount)
+      ERREXIT(cinfo, JERR_FILE_WRITE);
+  }
+}
+
+
+/*
+ * Prepare for output to a stdio stream.
+ * The caller must have already opened the stream, and is responsible
+ * for closing it after finishing compression.
+ */
+
+void jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile)
+{
+  my_dest_ptr dest;
+
+  /* The destination object is made permanent so that multiple JPEG images
+   * can be written to the same file without re-executing jpeg_stdio_dest.
+   * This makes it dangerous to use this manager and a different destination
+   * manager serially with the same JPEG object, because their private object
+   * sizes may be different.  Caveat programmer.
+   */
+  if (cinfo->dest == NULL) {	/* first time for this JPEG object? */
+    cinfo->dest = (struct jpeg_destination_mgr *)
+      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
+				  SIZEOF(my_destination_mgr));
+  }
+
+  dest = (my_dest_ptr) cinfo->dest;
+  dest->pub.init_destination = init_destination;
+  dest->pub.empty_output_buffer = empty_output_buffer;
+  dest->pub.term_destination = term_destination;
+  dest->outfile = outfile;
+}
+
+#endif /* HAVE_JPEG */
+
+/* 
+   Creation of wbmp image files with gd library
+   gd_wbmp.c
+
+   Copyright (C) Maurice Szmurlo --- T-SIT --- January 2000
+   (Maurice.Szmurlo@info.unicaen.fr)
+
+   Permission to use, copy, modify, and distribute this software and its
+   documentation for any purpose and without fee is hereby granted, provided
+   that the above copyright notice appear in all copies and that both that
+   copyright notice and this permission notice appear in supporting
+   documentation.  This software is provided "as is" without express or
+   implied warranty.
+
+   -----------------------------------------------------------------------------------------
+   Parts od this code are inspired by  'pbmtowbmp.c' and 'wbmptopbm.c' by 
+   Terje Sannum <terje@looplab.com>.
+   **
+   ** Permission to use, copy, modify, and distribute this software and its
+   ** documentation for any purpose and without fee is hereby granted, provided
+   ** that the above copyright notice appear in all copies and that both that
+   ** copyright notice and this permission notice appear in supporting
+   ** documentation.  This software is provided "as is" without express or
+   ** implied warranty.
+   **
+   -----------------------------------------------------------------------------------------
+
+   Todo:
+
+   gdCreateFromWBMP function for reading WBMP files
+
+   -----------------------------------------------------------------------------------------
+
+   Compilation:
+   
+   * testing the mbi data structure:
+   gcc -Wall -W -D__MBI_DEBUG__ gd_wbmp.c -o mbi -L/usr/local/lib -lgd
+   
+   * testing generation of wbmp images
+   gcc -Wall -W -D__GD_WBMP_DEBUG__ gd_wbmp.c -o gd_wbmp -L/usr/local/lib -lgd -lpng
+   
+   * simply making the object file
+   gcc -c -Wall -W gd_wbmp.c -o gd_wbmp.o
+*/
+
+#include <gd.h>
+#include <gdfonts.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+
+
+/* *************************** Multi Byte Integer Funcionnalities  *************************** */
+
+/* **************************************************
+  An mbi is an array of bytes.
+  For 32 bit integers, 5 bytes are sufficient.
+*/
+typedef unsigned char mbi_t[6];
+
+
+/* **************************************************
+  initialization of an mbi in order to be used by the int2mbi function
+*/
+static void mbiInit(mbi_t  mbi) {
+  int i;
+  mbi[0] = mbi[5] = (unsigned char)0;
+  for(i=1; i<5; mbi[i++] = 0x80);
+}
+
+
+/*  **************************************************
+   Displays the content of the mbi as a serie of hexa bytes.
+   For debuging purpose only
+*/
+#ifdef __MBI_DEBUG__
+static void mbiDisplay(mbi_t mbi, FILE *out) {
+  unsigned int i;
+  fprintf(out, "MBI:%d [", (unsigned int)mbi[0]);
+  if(mbi[0] > 0)
+	fprintf(out, "%X", (unsigned int)mbi[1]);
+  for(i=2; i<=mbi[0]; i++)
+	fprintf(out, ":%02X", (unsigned int)mbi[i]);
+  fprintf(out, "]\n");
+}
+#endif /* __MBI_DEBUG__ */
+
+
+/* **************************************************
+  writes the bytes constituing the mbi on the stream 'out'
+*/
+static void mbiPrint(mbi_t mbi, gdIOCtx *out) {
+  unsigned int i;
+  for(i=1; i<=(unsigned int)mbi[0]; gdPutC(mbi[i++], out));
+}
+
+
+/* **************************************************
+   Conversion routines:
+   unsigned int        ->  multi byte integer
+   multi byte integer  ->  unsigned int
+*/
+static void int2mbi(mbi_t mbi, unsigned int val) {
+  unsigned int i, j;
+  mbiInit(mbi);
+  for(i=5; i>0; i--) {
+	mbi[i] = mbi[i] | (val & 0x7F);
+	val = val >> 7;
+  }
+  for(j=1; (mbi[j] & 0x7F) == 0 && j<=5; j++);
+  mbi[0] = (unsigned char)(5-j+1);
+  for(i=1; i<=mbi[0]; mbi[i++] = mbi[j++]);
+}
+
+static unsigned int mbi2int(mbi_t mbi) {
+  unsigned int res=0,  i;
+  for(i=0; i<(unsigned int)mbi[0]; i++)
+	res = (res << 7) | (unsigned int) ( mbi[i+1] & 0x7F );
+  return res;
+}
+
+#ifdef __MBI_DEBUG__
+int main(void) {
+  mbi_t mbi;
+  fprintf(stdout, "testing Multi Byte Integers:\n");
+
+  int2mbi(mbi, 0);
+  mbiDisplay(mbi, stdout);
+  fprintf(stdout, ">> = %u\n", mbi2int(mbi));
+
+  /* 0xA0 and 0x60 are the two examples from the wbmp specs */
+  int2mbi(mbi, 0xA0);
+  mbiDisplay(mbi, stdout);
+  fprintf(stdout, ">> = %u\n", mbi2int(mbi));
+  
+  int2mbi(mbi, 0x60);
+  mbiDisplay(mbi, stdout);
+  fprintf(stdout, ">> = %u\n", mbi2int(mbi));
+
+  int2mbi(mbi, 1024+512);
+  mbiDisplay(mbi, stdout);
+  fprintf(stdout, ">> = %u\n", mbi2int(mbi));
+
+  int2mbi(mbi, 1024+512+1);
+  mbiDisplay(mbi, stdout);
+  fprintf(stdout, ">> = %u\n", mbi2int(mbi));
+
+  int2mbi(mbi, 775432);
+  mbiDisplay(mbi, stdout);
+  fprintf(stdout, ">> = %u\n", mbi2int(mbi));
+
+  int2mbi(mbi, 405589432);
+  mbiDisplay(mbi, stdout);
+  fprintf(stdout, ">> = %u\n", mbi2int(mbi));
+
+  int2mbi(mbi, UINT_MAX);
+  mbiDisplay(mbi, stdout);
+  fprintf(stdout, ">> = %u\n", mbi2int(mbi));
+
+  fprintf(stdout, "\n");
+  return 0;
+}
+#endif
+
+/* *************************** GD interface *************************** */
+
+/*
+  Write the image as a wbmp file
+  Parameters are:
+  image:  gd image structure;
+  fg:     the index of the foreground color. any other value will be considered 
+          as background and will not be written
+  out:    the stream where to write
+*/
+void gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out) {
+  
+  int x, y;
+  mbi_t mbi;
+
+  /* wbmp images are type 0: B&W; no compression; empty FixHeaderFlield */
+  gdPutC(0, out);					/* header */
+  gdPutC(0, out);					/* FixHeaderFlield */
+
+  /* width and height as mbi */
+  int2mbi(mbi, gdImageSX(image));    /* width */
+  mbiPrint(mbi, out);
+  int2mbi(mbi, gdImageSY(image));    /* height */
+  mbiPrint(mbi, out);
+
+  /* 
+	 now come the pixels as strings of bits padded with 0's at the end of the
+	 lines, if necessary
+  */
+  for(y=0; y<gdImageSY(image); y++) {
+	unsigned char nbPixs = 0;		/* how many pixels already written */
+	unsigned char pixels = 0;	    /* the 8 pixels string */
+	for(x=0; x<gdImageSX(image); x++) {
+	  if(gdImageGetPixel(image, x, y) == fg) {
+		pixels = pixels | (1 << (7-nbPixs)); 
+	  }
+	  nbPixs ++;
+	  if(nbPixs == 8) { /* 8 pixels written -> write to file */
+		gdPutC(pixels, out);
+		pixels = 0;
+		nbPixs = 0;
+	  }
+	}
+	/* if there are pixels left to write, write them */
+	if(nbPixs != 0)
+	  gdPutC(pixels, out);
+  }
+}
+
+/*
+  Write the image as a wbmp file
+  Parameters are:
+  image:  gd image structure;
+  fg:     the index of the foreground color. any other value will be considered 
+          as background and will not be written
+  out:    the stream where to write
+*/
+void gdImageWBMP(gdImagePtr image, int fg, FILE *out)
+{
+	gdIOCtx *out = gdNewFileCtx(outFile);
+	gdImageWBMPCtx(im, out);
+	out->free(out);
+}
+
+void* gdImageWBMPPtr(gdImagePtr im, int *size)
+{
+        void *rv;
+        gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
+        gdImageWBMPCtx(im, out);
+        rv = gdDPExtractData(out, size);
+        out->free(out);
+        return rv;
+}
+ 
+#ifdef __GD_WBMP_DEBUG__
+int  main(int argc, char **argv) {
+
+  gdImagePtr image;
+  int bg, fg;
+  FILE *out;
+
+  if(argc != 2) {
+	fprintf(stderr, "usage %s <wbmp file>\n", argv[0]);
+	exit(1);
+  }
+  
+  fprintf(stdout, "testing gd generation of WBMP files\n");
+
+  fprintf(stdout, "Opening WBMP file '%s'\n", argv[1]);
+  if(!(out = fopen(argv[1], "w"))) {
+	fprintf(stderr, "Cannot open out file '%s'\n", argv[1]);
+	exit(1);
+  }
+
+  fprintf(stdout, "Creating GD image and drawing...y\n");
+  if(!(image = gdImageCreate(60, 30))) {
+	fprintf(stderr, "Cannot create image 30x60\n");
+	exit(1);
+  }
+
+  /* don't really care about rgb values */
+  bg = gdImageColorAllocate(image, 0, 0, 0);
+  fg = gdImageColorAllocate(image, 255, 255, 255);
+
+  /* draw something : */
+  gdImageRectangle(image, 1, 1, gdImageSX(image)-2, gdImageSY(image)-2, fg);
+  
+  /* write something interesting */
+  gdImageString(image, gdFontSmall, 3, 0, "Hello", fg);
+
+  /* write the wbmp file */
+  fprintf(stdout, "Writing WBMP file '%s':\n", argv[1]);
+  gdImageWBMP(image, fg, out);
+  fclose(out);
+  fprintf(stdout, "Written.\nTry now to load the file '%s' with a WAP browser.\n", argv[1]);
+  
+
+
+
+
+  fprintf(stdout, "\n");
+  return 0;
+}
+#endif /* __GD_WBMP_DEBUG__ */
+
+
+
+  
 	printf("[Merged Image has %d colours]\n",im2->colorsTotal);
 	CompareImages("Merged (gdtest.png, gdtest_merge.png)", im2, im3);
 
-        /*out = fopen("gdtest_merge.png", "wb"); */
-        /*gdImageLzw(im2, out); */
-        /*fclose(out); */
-
 	gdImageDestroy(im2);
         gdImageDestroy(im3);
 
+	out = fopen("test/gdtest.jpg", "wb");
+	if (!out) {
+		fprintf(stderr, "Can't create file test/gdtest.jpg.\n");
+		exit(1);
+	}
+	gdImageJpeg(im, out, -1);	
+	fclose(out);
+	in = fopen("test/gdtest.jpg", "rb");
+	if (!in) {
+		fprintf(stderr, "Can't open file test/gdtest.jpg.\n");
+		exit(1);
+	}
+	im2 = gdImageCreateFromJpeg(in);
+	fclose(in);
+	if (!im2) {
+		fprintf(stderr, "gdImageCreateFromJpeg failed.\n");
+		exit(1);
+	}
+	gdImageDestroy(im2);	
+	printf("Created test/gdtest.jpg successfully. Compare this image\n"
+		"to the input image manually. Some difference must be\n"
+		"expected as JPEG is a lossy file format.\n");
 	gdImageDestroy(im);
 	gdImageDestroy(ref);
 
 <HTML>
 <HEAD>
-<TITLE>gd 1.7.3</TITLE>
+<TITLE>gd 1.8</TITLE>
 </HEAD>
 <BODY>
 <!-- BANNER HERE -->
-<H1>gd 1.7.3</H1>
+<H1>gd 1.8</H1>
 <H2>A graphics library for fast image creation</H2>
 <H2>Follow this link to the
 <A HREF="http://www.boutell.com/gd/">latest version
 of this document</A>.</H2>
 <blockquote>
 <strong>HEY! READ THIS!</strong>
-gd 1.7.3 creates PNG images, not GIF images. This is a good thing.
-PNG is a more compact format, and full compression is available.
-Existing code will need modification to call gdImagePng instead
+gd 1.8 creates PNG, JPEG and WBMP images, not GIF images. This is a good
+thing.  PNG is a more compact format, and full compression is
+available.  JPEG works well with photographic images, and is still
+more compatible with the major Web browsers than even PNG is. WBMP is
+intended for wireless devices (not regular web browsers). Existing
+code will need modification to call gdImagePng or gdImageJpeg instead
 of gdImageGif. <strong>Please do not ask us to send you the old GIF
 version of GD.</strong> Unisys holds a patent on the LZW compression
-algorithm, which is used in fully compressed GIF images. We are
-still investigating the legal issues surrounding various
-alternative means of producing a valid GIF file.
+algorithm, which is used in fully compressed GIF images. The best
+solution is to move to legally unencumbered, well-compressed,
+modern image formats such as PNG and JPEG as soon as possible.
 <p>
-gd 1.7.3 <strong>requires</strong> that the following libraries 
+gd 1.8 <strong>requires</strong> that the following libraries 
 also be installed:
 <p>
 libpng
 <p>
+jpeg-6b or later
+<p>
 zlib
 <p>
 If you want to use the TrueType font support, you must also
 <H3>Table of Contents</H3>
 <UL>
 <LI><A HREF="#notice">Credits and license terms</A>
+<LI><A HREF="#whatsnew1.8">What's new in version 1.8?</A>
 <LI><A HREF="#whatsnew1.7.3">What's new in version 1.7.3?</A>
 <LI><A HREF="#whatsnew1.7.2">What's new in version 1.7.2?</A>
 <LI><A HREF="#whatsnew1.7.1">What's new in version 1.7.1?</A>
 </pre>
 <blockquote>
 
-Portions copyright 1994, 1995, 1996, 1997, 1998, 1999, by Cold Spring
+Portions copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000 by Cold Spring
 Harbor Laboratory. Funded under Grant P41-RR02188 by the National
 Institutes of Health.
 <P>
-Portions copyright 1996, 1997, 1998, 1999, by Boutell.Com, Inc.
+Portions copyright 1996, 1997, 1998, 1999, 2000 by Boutell.Com, Inc.
+<p>
+Portions relating to GD2 format copyright 1999, 2000 Philip Warner.
 <p>
-Portions relating to GD2 format copyright 1999 Philip Warner.
+Portions relating to PNG copyright 1999, 2000 Greg Roelofs.
 <p>
-Portions relating to PNG copyright 1999, Greg Roelofs.
+Portions relating to libttf copyright 1999, 2000 John Ellson (ellson@lucent.com).
 <p>
-Portions relating to libttf copyright 1999, John Ellson (ellson@lucent.com).
+Portions relating to JPEG copyright 2000, Doug Becker and copyright (C)
+1994-1998, Thomas G. Lane.  This software is based in part on the work
+of the Independent JPEG Group.
 <p>
-<strong>Permission has been granted to copy and distribute gd in any
+Portions relating to WBMP copyright 2000 Maurice Szmurlo.
+<p>
+<strong>Permission has been granted to copy, distribute and modify gd in any
 context without fee, including a commercial application, provided that this notice
 is present in user-accessible supporting documentation.</strong>
 <p>
 fitness for a particular purpose, with respect to this code and accompanying
 documentation.
 <p>
-Although their code does not appear in gd 1.7.3, the authors wish to 
+Although their code does not appear in gd 1.8, the authors wish to 
 thank David Koblas, David Rowley, and Hutchison Avenue Software 
 Corporation for their prior contributions.
 </blockquote>
 gd is a graphics library. It allows your code to quickly
 draw images complete with lines, arcs, text, multiple
 colors, cut and paste from other images, and flood fills, and
-write out the result as a .PNG file. This is particularly
-useful in World Wide Web applications, where .PNG is one
+write out the result as a PNG or JPEG file. This is particularly
+useful in World Wide Web applications, where PNG and JPEG are two
 of the formats accepted for inline images by most browsers.
 <P>
 gd is not a paint program.
 operation. It is not necessary or desirable for gd to become
 a kitchen-sink graphics package, but version 1.7.3 incorporates
 most of the commonly requested features for an 8-bit 2D package.
-Support for truecolor images, JPEG and 
-truecolor PNG is planned for version 2.0.
+Support for truecolor images, including truecolor JPEG and PNG,
+is planned for version 2.0.
 <P>
 <A NAME="gdother"><H3>What if I want to use another programming
 language?</h3></A>
 <a href=http://www.tcltk.com/ftp/ellson/>Gdtclft</a>
 dynamically loaded extension package.
 (Gdtclft2.0 or later is needed for gd-1.6 and up with PNG output.)
+<h4>Pascal</h4>
+Pascal enthusiasts should look into Michael Bradbury's
+<a href="http://www.elj.com/dev/free-pascal/gdfp/">gdfp</a> package.
 <h4>Any Language</h4>
 There are, at the moment, at least three simple interpreters that
 perform gd operations. You can output the desired commands to a simple
 <li><a href="http://s27w007.pswfs.gov/tgd/">tgd</a>, by Bradley K. Sherman
 <li><a href="http://www.unimelb.edu.au/fly/fly.html">fly</a>, by Martin Gleeson
 </ul>
+<P><A NAME="whatsnew1.8"><H3>What's new in version 1.8?</H3></A>
+<ul>
+<li>Support for JPEG output, courtesy of Doug Becker
+<li>A link to Michael Bradbery's Pascal wrapper
+<li>Support for WBMP output, courtesy of Maurice Szmurlo
+<li>gdImageColorClosestHWB function based on hue, whiteness, blackness,
+superior to the regular gdImageColorClosest function, courtesy 
+of Philip Warner
+<li>License clarification: yes, you can modify gd
+</ul>
+<h4>Additional JPEG Information</h4>
+Support for reading and writing JPEG-format images is courtesy
+of Doug Becker and the Independent JPEG Group / Thomas G. Lane.  You
+can get the latest version of the IJG JPEG software from <A
+HREF="ftp://ftp.uu.net/graphics/jpeg/">ftp://ftp.uu.net/graphics/jpeg/</A>
+(e.g., the <A
+HREF="ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz">jpegsrc.v6b.tar.gz</A>
+file).  You <strong>must</strong> use
+version 6b or later of the IJG JPEG software.  You might also consult
+the <A HREF="http://www.faqs.org/faqs/jpeg-faq/">JPEG FAQ</A> at
+<A HREF="http://www.faqs.org/faqs/jpeg-faq/">http://www.faqs.org/faqs/jpeg-faq/</A>.
 <P><A NAME="whatsnew1.7.3"><H3>What's new in version 1.7.3?</H3></A>
 Another attempt at Makefile fixes to permit
 linking with all libraries required on platforms with order-
 <br>gdImageGd2, gdImageCreateFromGd2 - Support for new format
 <br>gdImageCopyMerge - Merges two images (useful to highlight part of an image)
 <br>gdImageCopyMergeGray - Similar to gdImageCopyMerge, but tries to preserve source image hue.
-<br>gdImagePngPtr, gdImageGdPtr, gdImageGd2Ptr - return memort blocks for each type of image.
+<br>gdImagePngPtr, gdImageJpegPtr, gdImageWBMPPtr, gdImageGdPtr, gdImageGd2Ptr - return memort blocks for each type of image.
 <br>gdImageCreateFromPngCtx, gdImageCreateFromGdCtx, gdImageCreateFromGd2Ctx, gdImageCreateFromGd2PartCtx - Support for new I/O context.
 
 </dl>
 <A NAME="getgd"><H3>How do I get gd?</H3></A>
 <h4>By HTTP</h4>
 <ul>
-<li><a href="http://www.boutell.com/gd/http/gd-1.7.3.tar.gz">Gzipped Tar File (Unix)</a>
-<li><a href="http://www.boutell.com/gd/http/gd-1.7.3.zip">.ZIP File (Windows)</a>
+<li><a href="http://www.boutell.com/gd/http/gd-1.8.tar.gz">Gzipped Tar File (Unix)</a>
+<li><a href="http://www.boutell.com/gd/http/gd-1.8.zip">.ZIP File (Windows)</a>
 </ul>
 <h4>By FTP</h4>
 <ul>
-<li><a href="ftp://ftp.boutell.com/pub/boutell/gd/gd-1.7.3.tar.gz">Gzipped Tar File (Unix)</a>
-<li><a href="ftp://ftp.boutell.com/pub/boutell/gd/gd-1.7.3.zip">.ZIP File (Windows)</a>
+<li><a href="ftp://ftp.boutell.com/pub/boutell/gd/gd-1.8.tar.gz">Gzipped Tar File (Unix)</a>
+<li><a href="ftp://ftp.boutell.com/pub/boutell/gd/gd-1.8.zip">.ZIP File (Windows)</a>
 </ul>
 <P>
 <A NAME="buildgd"><H3>How do I build gd?</H3></A>
 consult with an experienced user of your system. Sorry, we cannot
 answer questions about basic Internet skills.
 <p>
-Unpacking the archive will produce a directory called "gd-1.7.3".
+Unpacking the archive will produce a directory called "gd-1.8".
 <p>
 <h4>For Unix</h4>
-<code>cd</code> to the 1.7.3 directory. Edit the Makefile with
+<code>cd</code> to the 1.8 directory. Edit the Makefile with
 your preferred text editor and make any necessary changes to the
 settings at the top, especially if you want Xpm or TrueType support.
 Next, type "make". If you are the system administrator, and you
 <p>
 If you get errors, edit the Makefile again, paying special attention
 to the INCLUDEDIRS and LIBDIRS settings.
+<p>
+IF YOU GET LINKER ERRORS, TRY JUGGLING THE ORDER OF THE -l DIRECTIVES
+IN THE MAKEFILE. Some platforms may prefer that the libraries be listed
+in the opposite order.
 <h4>For Windows, Mac, Et Cetera</h4>
 Create a project using your favorite programming environment.
 Copy all of the gd files to the project directory. Add <code>gd.c</code>
 image which was scaled and copied into the output image.
 <P>
 <A NAME="basics"><H3>gd basics: using gd in your program</H3></A>
-gd lets you create PNG images on the fly. To use gd in your
+gd lets you create PNG or JPEG images on the fly. To use gd in your
 program, include the file gd.h, and link with the libgd.a
 library produced by "make libgd.a", under Unix. Under other
 operating systems you will add gd.c to your own project.
 int main() {
 	/* Declare the image */
 	<A HREF="#gdImagePtr">gdImagePtr</A> im;
-	/* Declare an output file */
-	FILE *out;
+	/* Declare output files */
+	FILE *pngout, *jpegout;
 	/* Declare color indexes */
 	int black;
 	int white;
 
 	/* Open a file for writing. "wb" means "write binary", important
 		under MSDOS, harmless under Unix. */
-	out = fopen("test.png", "wb");
+	pngout = fopen("test.png", "wb");
+
+	/* Do the same for a JPEG-format file. */
+	jpegout = fopen("test.jpg", "wb");
+
+	/* Output the image to the disk file in PNG format. */
+	<A HREF="#gdImagePng">gdImagePng</A>(im, pngout);
 
-	/* Output the image to the disk file. */
-	<A HREF="#gdImagePng">gdImagePng</A>(im, out);	
+	/* Output the same image in JPEG format, using the default
+		JPEG quality setting. */
+	<A HREF="#gdImageJpeg">gdImageJpeg</A>(im, jpegout, -1);
 
-	/* Close the file. */
-	fclose(out);
+	/* Close the files. */
+	fclose(pngout);
+	fclose(jpegout);
 
 	/* Destroy the image in memory. */
 	<A HREF="#gdImageDestroy">gdImageDestroy</A>(im);
 When executed, this program creates an image, allocates
 two colors (the first color allocated becomes the background
 color), draws a diagonal line (note that 0, 0 is the upper
-left corner), writes the image to a PNG file, and
+left corner), writes the image to PNG and JPEG files, and
 destroys the image.
 <P>
 The above example program should
 /* ... Use the image ... */
 <A HREF="#gdImageDestroy">gdImageDestroy</A>(im);
 </PRE>
+<DT><A NAME="gdImageCreateFromJpeg">gdImageCreateFromJpeg(FILE *in)</A>
+<strong>(FUNCTION)</strong>
+<br>
+<A NAME="gdImageCreateFromJpegCtx">gdImageCreateFromJpegCtx(FILE *in)</A>
+<strong>(FUNCTION)</strong>
+<p>
+<DD>
+gdImageCreateFromJpeg is called to load images from JPEG format files.
+Invoke gdImageCreateFromJpeg with an already opened pointer to a file
+containing the desired image.
+gdImageCreateFromJpeg
+returns a <A HREF="#gdImagePtr">gdImagePtr</A> to the new image, or NULL
+if unable to load the image (most often because the file is corrupt or
+does not contain a JPEG image). gdImageCreateFromPng does <em>not</em>
+close the file. You can inspect the sx and sy members of the
+image to determine its size. The image must eventually be destroyed
+using <A HREF="#gdImageDestroy">gdImageDestroy()</A>.
+<PRE>
+<A HREF="#gdImagePtr">gdImagePtr</A> im;
+... inside a function ...
+FILE *in;
+in = fopen("myjpeg.jpg", "rb");
+im = gdImageCreateFromJpeg(in);
+fclose(in);
+/* ... Use the image ... */
+<A HREF="#gdImageDestroy">gdImageDestroy</A>(im);
+</PRE>
 <DT><A NAME="gdImageCreateFromPng">gdImageCreateFromPng(FILE *in)</A>
 <strong>(FUNCTION)</strong>
 <BR><A NAME="gdImageCreateFromPngCtx">gdImageCreateFromPngCtx(<a href=#gdioctx>gdIOCtx</a> *in)</A>
 with an already opened pointer to a file containing the desired image
 in the <A HREF="#gdformat">gd file format</A>, which is specific to
 gd and intended for very fast loading. (It is <em>not</em> intended for
-compression; for compression, use PNG.)
+compression; for compression, use PNG or JPEG.)
 gdImageCreateFromGd
 returns a <A HREF="#gdImagePtr">gdImagePtr</A> to the new image, or NULL
 if unable to load the image (most often because the file is corrupt or
 /* Now destroy it */
 <A HREF="#gdImageDestroy">gdImageDestroy</A>(im);
 </PRE>
+<DT><A NAME="gdImageJpeg">
+void gdImageJpeg(gdImagePtr im, FILE *out, int quality)</A>
+<STRONG>(FUNCTION)</STRONG><BR>
+void gdImageJpegCtx(gdImagePtr im, gdIOCtx *out, int quality)</A>
+<STRONG>(FUNCTION)</STRONG><BR>
+<DD>
+gdImageJpeg outputs the specified image to the specified
+file in JPEG format. The file must be open for writing. Under MSDOS
+and all versions of Windows, it is important to use "wb" as opposed
+to simply "w" as the mode when opening the file, and under Unix there
+is no penalty for doing so. gdImageJpeg does <em>not</em>
+close the file; your code must do so.
+<P>
+If quality is negative, the default IJG JPEG quality value (which
+should yield a good general quality / size tradeoff for most
+situations) is used.  Otherwise, for practical purposes, quality
+should be a value in the range 0-95, higher quality values usually
+implying both higher quality and larger image sizes.
+<P>
+If you have set image interlacing using
+<A HREF="#gdImageInterlace">gdImageInterlace</A>, this function will
+interpret that to mean you wish to output a progressive JPEG.  Some
+programs (e.g., Web browsers) can display progressive JPEGs
+incrementally; this can be useful when browsing over a relatively slow
+communications link, for example.  Progressive JPEGs can also be
+slightly smaller than sequential (non-progressive) JPEGs.
+<PRE>
+... inside a function ...
+<A HREF="#gdImagePtr">gdImagePtr</A> im;
+int black, white;
+FILE *out;
+/* Create the image */
+im = <A HREF="#gdImageCreate">gdImageCreate</A>(100, 100);
+/* Allocate background */
+white = <A HREF="#gdImageColorAllocate">gdImageColorAllocate</A>(im, 255, 255, 255);
+/* Allocate drawing color */
+black = <A HREF="#gdImageColorAllocate">gdImageColorAllocate</A>(im, 0, 0, 0);
+/* Draw rectangle */
+<A HREF="#gdImageRectangle">gdImageRectangle</A>(im, 0, 0, 99, 99, black);
+/* Open output file in binary mode */
+out = fopen("rect.jpg", "wb");
+/* Write JPEG using default quality */
+gdImageJpeg(im, out, -1);
+/* Close file */
+fclose(out);
+/* Destroy image */
+<A HREF="#gdImageDestroy">gdImageDestroy</A>(im);
+</PRE>
+<DT><A NAME="gdImageJpegPtr">
+void* gdImageJpegPtr(gdImagePtr im, int *size)</A>
+<STRONG>(FUNCTION)</STRONG>
+<DD>Identical to gdImageJpeg except that it returns a pointer to a memory
+area with the JPEG data. This memory must be freed by the caller when it is
+no longer needed. The 'size' parameter receives the total size of the block
+of memory.
 <DT><A NAME="gdImagePng">
 void gdImagePng(gdImagePtr im, FILE *out)</A>
 <STRONG>(FUNCTION)</STRONG>
 <STRONG>(FUNCTION)</STRONG>
 <DD>Identical to gdImagePng except that it returns a pointer to a memory
 area with the PNG data. This memory must be freed by the caller when it is
-no longer needed. The 'size' parameter received the total size of the block
+no longer needed. The 'size' parameter receives the total size of the block
 of memory.
 
 <DT><A NAME="gdImagePngToSink">gdImagePngToSink(gdImagePtr im, gdSinkPtr out)</A>
 	gdImagePngToSink(im, &mySink);
 }
 </pre>
+<DT><A NAME="gdImageWBMP">
+void gdImageWBMP(gdImagePtr im, int fg, FILE *out)</A>
+<BR><A NAME="gdImageWBMPCtx">gdImageWBMPCtx(<a href=#gdioctx>gdIOCtx</a> *out)</A>
+<strong>(FUNCTION)</strong><STRONG>(FUNCTION)</STRONG>
+<DD>
+gdImageWBMP outputs the specified image to the specified
+file in WBMP format. The file must be open for writing. Under MSDOS
+and all versions of Windows, it is important to use "wb" as opposed
+to simply "w" as the mode when opening the file, and under Unix there
+is no penalty for doing so. gdImageWBMP does <em>not</em>
+close the file; your code must do so.
+<p>
+<strong>WBMP file support is black and white only. The color index
+specified by the fg argument is the "foreground," and only pixels
+of this color will be set in the WBMP file.</strong> All other pixels
+will be considered "background."
+<PRE>
+... inside a function ...
+<A HREF="#gdImagePtr">gdImagePtr</A> im;
+int black, white;
+FILE *out;
+/* Create the image */
+im = <A HREF="#gdImageCreate">gdImageCreate</A>(100, 100);
+/* Allocate background */
+white = <A HREF="#gdImageColorAllocate">gdImageColorAllocate</A>(im, 255, 255, 255);
+/* Allocate drawing color */
+black = <A HREF="#gdImageColorAllocate">gdImageColorAllocate</A>(im, 0, 0, 0);
+/* Draw rectangle */
+<A HREF="#gdImageRectangle">gdImageRectangle</A>(im, 0, 0, 99, 99, black);
+/* Open output file in binary mode */
+out = fopen("rect.wbmp", "wb");
+/* Write WBMP, with black as foreground */
+gdImageWBMP(im, black, out);
+/* Close file */
+fclose(out);
+/* Destroy image */
+<A HREF="#gdImageDestroy">gdImageDestroy</A>(im);
+</PRE>
+<DT><A NAME="gdImageWBMPPtr">
+void* gdImageWBMPPtr(gdImagePtr im, int *size)</A>
+<STRONG>(FUNCTION)</STRONG>
+<DD>Identical to gdImageWBMP except that it returns a pointer to a memory
+area with the WBMP data. This memory must be freed by the caller when it is
+no longer needed. The 'size' parameter receives the total size of the block
+of memory.
 <DT><A NAME="gdImageGd">
 void gdImageGd(gdImagePtr im, FILE *out)</A>
 <STRONG>(FUNCTION)</STRONG>
 <STRONG>(FUNCTION)</STRONG>
 <DD>Identical to gdImageGd except that it returns a pointer to a memory
 area with the GD data. This memory must be freed by the caller when it is
-no longer needed. The 'size' parameter received the total size of the block
+no longer needed. The 'size' parameter receives the total size of the block
 of memory.
 
 <DT><A NAME="gdImageGd2">
 <STRONG>(FUNCTION)</STRONG>
 <DD>Identical to gdImageGd2 except that it returns a pointer to a memory
 area with the GD2 data. This memory must be freed by the caller when it is
-no longer needed. The 'size' parameter received the total size of the block
+no longer needed. The 'size' parameter receives the total size of the block
 of memory.
 
 </DL>
 working with existing PNG files that already use 256 colors.)
 Note that gdImageColorAllocate
 does not check for existing colors that match your request;
-see <A HREF="#gdImageColorExact">gdImageColorExact</A>
-and <A HREF="#gdImageColorClosest">gdImageColorClosest</A>
+see <A HREF="#gdImageColorExact">gdImageColorExact</A>,
+<A HREF="#gdImageColorClosest">gdImageColorClosest</A> and
+<A HREF="#gdImageColorClosest">gdImageColorClosestHWB</A>
 for ways to locate existing colors that approximate the
 color desired in situations where a new color is not available.
 Also see <A HREF="#gdImageColorResolve">gdImageColorResolve</A>,
 /* Destroy it */
 <A HREF="#gdImageDestroy">gdImageDestroy</A>(im);
 </PRE>
-<DT><A NAME="gdImageColorExact">
+<DT><A NAME="gdImageColorClosestHWB">
+int gdImageColorClosestHWB(gdImagePtr im, int r, int g, int b)</A>
+<STRONG>(FUNCTION)</STRONG>
+<DD>
+gdImageColorClosestHWB searches the colors which have been
+defined thus far in the image specified and returns the
+index of the color with hue, whiteness and blackness closest to the
+requested color. This scheme is typically superior to the 
+Euclidian distance scheme used by 
+<a href="#gdImageColorClosest">gdImageColorClosest</a>.
+<P>
+If no colors have yet been allocated in the image,
+gdImageColorClosestHWB returns -1.
+<P>
+This function is most useful as a backup method for choosing
+a drawing color when an image already contains
+<A HREF="#gdMaxColors">gdMaxColors</A> (256) colors and
+no more can be allocated. (This is not uncommon when
+working with existing PNG files that already use many colors.)
+See <A HREF="#gdImageColorExact">gdImageColorExact</A>
+for a method of locating exact matches only.
+<PRE>
+... inside a function ...
+<A HREF="#gdImagePtr">gdImagePtr</A> im;
+FILE *in;
+int red;
+/* Let's suppose that photo.png is a scanned photograph with
+	many colors. */
+in = fopen("photo.png", "rb");
+im = <A HREF="#gdImageCreateFromPng">gdImageCreateFromPng</A>(in);
+fclose(in);
+/* Try to allocate red directly */
+red = <A HREF="#gdImageColorAllocate">gdImageColorAllocate</A>(im, 255, 0, 0);	
+/* If we fail to allocate red... */
+if (red == (-1)) {
+	/* Find the <em>closest</em> color instead. */
+	red = gdImageColorClosestHWB(im, 255, 0, 0);
+}
+/* Draw a dashed line from the upper left corner to the lower right corner */
+gdImageDashedLine(im, 0, 0, 99, 99, red);
+/* ... Do something with the image, such as saving it to a file... */
+/* Destroy it */
+<A HREF="#gdImageDestroy">gdImageDestroy</A>(im);
+</PRE><DT><A NAME="gdImageColorExact">
 int gdImageColorExact(gdImagePtr im, int r, int g, int b)</A>
 <STRONG>(FUNCTION)</STRONG>
 <DD>
 if the image is interlaced, false (0) if not.
 Use this macro to obtain this information; do not
 access the structure directly.
-See <A NAME="gdImageInterlace">gdImageInterlace</A> for
+See <A HREF="#gdImageInterlace">gdImageInterlace</A> for
 a means of interlacing images.
 <DT><A NAME="gdImageGetTransparent">
 int gdImageGetTransparent(gdImagePtr im)</A>
 gdImageColorTransparent sets the transparent color index
 for the specified image to the specified index. To indicate
 that there should be <em>no</em> transparent color, invoke
-gdImageColorTransparent with a color index of -1.
+gdImageColorTransparent with a color index of -1.  Note that
+JPEG images do not support transparency, so this setting has no effect
+when writing JPEG images.
 <P>
 The color index used should be an index
 allocated by <A HREF="#gdImageColorAllocate">gdImageColorAllocate</A>,
 invoked by loading an image.
 In order to ensure that your image has a reasonable appearance
 when viewed by users who do not have transparent background
-capabilities, be sure to give reasonable RGB values to the
+capabilities (or when you are writing a JPEG-format file, which does
+not support transparency), be sure to give reasonable RGB values to the
 color you allocate for use as a transparent color,
 <em>even though it will be transparent on systems
-that support transparency</em>.
+that support PNG transparency</em>.
 <PRE>
 ... inside a function ...
 <A HREF="#gdImagePtr">gdImagePtr</A> im;
 <DT><A NAME="gdImagePaletteCopy">void gdImagePaletteCopy(gdImagePtr dst, gdImagePtr src)
 <STRONG> (FUNCTION)</STRONG>
 <DD>
-Copies a palette from one image to another, doing it's best to match the colors in the target image
+Copies a palette from one image to another, attempting to match the colors in the target image
 to the colors
 in the source palette.
 </DL>
 in a linear fashion, in which lines will appear on the display from
 first to last, or in an interlaced fashion, in which the image
 will "fade in" over several passes. By default, images are not
-interlaced.
+interlaced.  (When writing JPEG images, interlacing implies generating
+progressive JPEG files, which are represented as a series of scans of
+increasing quality.  Noninterlaced gd images result in regular
+[sequential] JPEG data streams.)
 <P>
 A nonzero value for the interlace argument turns on interlace;
 a zero value turns it off. Note that interlace has no effect
 on other functions, and has no meaning unless you save the
-image in PNG format; the gd and xbm formats do not support
+image in PNG or JPEG format; the gd and xbm formats do not support
 interlace.
 <P>
-When a PNG is loaded with <A HREF="#gdImageCreateFromPng">gdImageCreateFromPng
-</A>, interlace will be set according to the setting in the PNG file.
+When a PNG is loaded with
+<A HREF="#gdImageCreateFromPng">gdImageCreateFromPng</A> or a JPEG is
+loaded with
+<A HREF="#gdImageCreateFromJpeg">gdImageCreateFromJpeg</A>, interlace
+will be set according to the setting in the PNG or JPEG file.
 <P>
-Note that many PNG viewers and web browsers do <em>not</em> support
-interlace. However, the interlaced PNG should still display; it
-will simply appear all at once, just as other images do.
+Note that many PNG and JPEG viewers and web browsers do <em>not</em>
+support interlace or the incremental display of progressive
+JPEGs. However, the interlaced PNG or progressive JPEG should still
+display; it will simply appear all at once, just as other images do.
 <PRE>
 gdImagePtr im;
 FILE *out;
 gdImageInterlace(im, 1);
 /* And open an output file */
 out = fopen("test.png", "wb");
-/* And save the image */
+/* And save the image  -- could also use <A HREF="#gdImageJpeg">gdImageJpeg</A> */
 <A HREF="#gdImagePng">gdImagePng</A>(im, out);
 fclose(out);
 <A HREF="#gdImageDestroy">gdImageDestroy</A>(im);
 </DL>
 
 <A NAME="gdformat"><H3>About the additional .gd image file format</H3></A>
-In addition to reading and writing the PNG format and reading the
+In addition to reading and writing the PNG and JPEG formats and reading the
 X Bitmap format, gd has the capability to read and write its
 own ".gd" format. This format is <em>not</em> intended for
 general purpose use and should never be used to distribute
 to large image files. Its purpose is solely to
 allow very fast loading of <strong>parts</strong> of images
 If you are experiencing
-performance problems when loading large, fixed PNG images your
+performance problems when loading large, fixed PNG or JPEG images your
 program needs to produce its output images, you may wish
 to examine the functions <A HREF="#gdImageCreateFromGd2">
 gdImageCreateFromGd2</A>, <A HREF="#gdImageCreateFromGd2Part">
 <A HREF="#gdImageCreateFromGd">gdImageCreateFromGd</A> |
 <A HREF="#gdImageCreateFromGd2">gdImageCreateFromGd2</A> |
 <A HREF="#gdImageCreateFromGd2Part">gdImageCreateFromGd2Part</A> |
+<A HREF="#gdImageCreateFromJpeg">gdImageCreateFromJpeg</A> |
 <A HREF="#gdImageCreateFromPng">gdImageCreateFromPng</A> |
 <A HREF="#gdImageCreateFromPngSource">gdImageCreateFromPngSource</A> |
 <A HREF="#gdImageCreateFromXbm">gdImageCreateFromXbm</A> |
 <A HREF="#gdImageGetTransparent">gdImageGetTransparent</A> |
 <A HREF="#gdImageGreen">gdImageGreen</A> |
 <A HREF="#gdImageInterlace">gdImageInterlace</A> |
+<A HREF="#gdImageJpeg">gdImageJpeg</A> |
 <A HREF="#gdImageLine">gdImageLine</A> |
 <A HREF="#gdImageFilledPolygon">gdImageFilledPolygon</A> |
 <A HREF="#gdImagePaletteCopy">gdImagePaletteCopy</A> |
 <A HREF="#gdImagePngToSink">gdImagePngToSink</A> |
 <A HREF="#gdImagePolygon">gdImagePolygon</A> |
 <A HREF="#gdImagePtr">gdImagePtr</A> |
+<A HREF="#gdImageWBMP">gdImageWBMP</A> |
 <A HREF="#gdImageRectangle">gdImageRectangle</A> |
 <A HREF="#gdImageRed">gdImageRed</A> |
 <A HREF="#gdImageSetBrush">gdImageSetBrush</A> |
 <A HREF="#gdImageStringTTF">gdImageStringTTF</A> |
 <A HREF="#gdImageStringUp">gdImageStringUp</A> |
 <A HREF="#gdImageStringUp">gdImageStringUp16</A> |
+<A HREF="#gdImageWBMP">gdImageWBMP</A> |
 <A HREF="#gdMaxColors">gdMaxColors</A> |
 <A HREF="#gdPoint">gdPoint</A> |
 <A HREF="#gdStyled">gdStyled</A> |
 
-                                   gd 1.7.3
+                                    gd 1.8
                                        
 A graphics library for fast image creation
 
 Follow this link to the latest version of this document.
 
-     _HEY! READ THIS!_ gd 1.7.3 creates PNG images, not GIF images. This
-     is a good thing. PNG is a more compact format, and full compression
-     is available. Existing code will need modification to call
-     gdImagePng instead of gdImageGif. _Please do not ask us to send you
-     the old GIF version of GD._ Unisys holds a patent on the LZW
-     compression algorithm, which is used in fully compressed GIF
-     images. We are still investigating the legal issues surrounding
-     various alternative means of producing a valid GIF file.
+     _HEY! READ THIS!_ gd 1.8 creates PNG, JPEG and WBMP images, not GIF
+     images. This is a good thing. PNG is a more compact format, and
+     full compression is available. JPEG works well with photographic
+     images, and is still more compatible with the major Web browsers
+     than even PNG is. WBMP is intended for wireless devices (not
+     regular web browsers). Existing code will need modification to call
+     gdImagePng or gdImageJpeg instead of gdImageGif. _Please do not ask
+     us to send you the old GIF version of GD._ Unisys holds a patent on
+     the LZW compression algorithm, which is used in fully compressed
+     GIF images. The best solution is to move to legally unencumbered,
+     well-compressed, modern image formats such as PNG and JPEG as soon
+     as possible.
      
-     gd 1.7.3 _requires_ that the following libraries also be installed:
+     gd 1.8 _requires_ that the following libraries also be installed:
      
      libpng
      
+     jpeg-6b or later
+     
      zlib
      
      If you want to use the TrueType font support, you must also install
   Table of Contents
   
      * Credits and license terms
+     * What's new in version 1.8?
      * What's new in version 1.7.3?
      * What's new in version 1.7.2?
      * What's new in version 1.7.1?
 
 COPYRIGHT STATEMENT FOLLOWS THIS LINE
 
-     Portions copyright 1994, 1995, 1996, 1997, 1998, 1999, by Cold
+     Portions copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000 by Cold
      Spring Harbor Laboratory. Funded under Grant P41-RR02188 by the
      National Institutes of Health.
      
-     Portions copyright 1996, 1997, 1998, 1999, by Boutell.Com, Inc.
+     Portions copyright 1996, 1997, 1998, 1999, 2000 by Boutell.Com,
+     Inc.
      
-     Portions relating to GD2 format copyright 1999 Philip Warner.
+     Portions relating to GD2 format copyright 1999, 2000 Philip Warner.
      
-     Portions relating to PNG copyright 1999, Greg Roelofs.
+     Portions relating to PNG copyright 1999, 2000 Greg Roelofs.
      
-     Portions relating to libttf copyright 1999, John Ellson
+     Portions relating to libttf copyright 1999, 2000 John Ellson
      (ellson@lucent.com).
      
-     _Permission has been granted to copy and distribute gd in any
-     context without fee, including a commercial application, provided
-     that this notice is present in user-accessible supporting
+     Portions relating to JPEG copyright 2000, Doug Becker and copyright
+     (C) 1994-1998, Thomas G. Lane. This software is based in part on
+     the work of the Independent JPEG Group.
+     
+     Portions relating to WBMP copyright 2000 Maurice Szmurlo.
+     
+     _Permission has been granted to copy, distribute and modify gd in
+     any context without fee, including a commercial application,
+     provided that this notice is present in user-accessible supporting
      documentation._
      
      This does not affect your ownership of the derived work itself, and
      particular purpose, with respect to this code and accompanying
      documentation.
      
-     Although their code does not appear in gd 1.7.3, the authors wish
-     to thank David Koblas, David Rowley, and Hutchison Avenue Software
+     Although their code does not appear in gd 1.8, the authors wish to
+     thank David Koblas, David Rowley, and Hutchison Avenue Software
      Corporation for their prior contributions.
      
 END OF COPYRIGHT STATEMENT
   
    gd is a graphics library. It allows your code to quickly draw images
    complete with lines, arcs, text, multiple colors, cut and paste from
-   other images, and flood fills, and write out the result as a .PNG
-   file. This is particularly useful in World Wide Web applications,
-   where .PNG is one of the formats accepted for inline images by most
-   browsers.
+   other images, and flood fills, and write out the result as a PNG or
+   JPEG file. This is particularly useful in World Wide Web applications,
+   where PNG and JPEG are two of the formats accepted for inline images
+   by most browsers.
    
    gd is not a paint program. If you are looking for a paint program, you
    are looking in the wrong place. If you are not a programmer, you are
    It is not necessary or desirable for gd to become a kitchen-sink
    graphics package, but version 1.7.3 incorporates most of the commonly
    requested features for an 8-bit 2D package. Support for truecolor
-   images, JPEG and truecolor PNG is planned for version 2.0.
+   images, including truecolor JPEG and PNG, is planned for version 2.0.
    
   What if I want to use another programming language?
   
    extension package. (Gdtclft2.0 or later is needed for gd-1.6 and up
    with PNG output.)
    
+    Pascal
+    
+   Pascal enthusiasts should look into Michael Bradbury's gdfp package.
+   
     Any Language
     
    There are, at the moment, at least three simple interpreters that
      * tgd, by Bradley K. Sherman
      * fly, by Martin Gleeson
        
+  What's new in version 1.8?
+  
+     * Support for JPEG output, courtesy of Doug Becker
+     * A link to Michael Bradbery's Pascal wrapper
+     * Support for WBMP output, courtesy of Maurice Szmurlo
+     * gdImageColorClosestHWB function based on hue, whiteness,
+       blackness, superior to the regular gdImageColorClosest function,
+       courtesy of Philip Warner
+     * License clarification: yes, you can modify gd
+       
+    Additional JPEG Information
+    
+   Support for reading and writing JPEG-format images is courtesy of Doug
+   Becker and the Independent JPEG Group / Thomas G. Lane. You can get
+   the latest version of the IJG JPEG software from
+   ftp://ftp.uu.net/graphics/jpeg/ (e.g., the jpegsrc.v6b.tar.gz file).
+   You _must_ use version 6b or later of the IJG JPEG software. You might
+   also consult the JPEG FAQ at http://www.faqs.org/faqs/jpeg-faq/.
+   
   What's new in version 1.7.3?
   
    Another attempt at Makefile fixes to permit linking with all libraries
           of an image)
           gdImageCopyMergeGray - Similar to gdImageCopyMerge, but tries
           to preserve source image hue.
-          gdImagePngPtr, gdImageGdPtr, gdImageGd2Ptr - return memort
-          blocks for each type of image.
+          gdImagePngPtr, gdImageJpegPtr, gdImageWBMPPtr, gdImageGdPtr,
+          gdImageGd2Ptr - return memort blocks for each type of image.
           gdImageCreateFromPngCtx, gdImageCreateFromGdCtx,
           gdImageCreateFromGd2Ctx, gdImageCreateFromGd2PartCtx - Support
           for new I/O context.
    (Windows), please consult with an experienced user of your system.
    Sorry, we cannot answer questions about basic Internet skills.
    
-   Unpacking the archive will produce a directory called "gd-1.7.3".
+   Unpacking the archive will produce a directory called "gd-1.8".
    
     For Unix
     
-   cd to the 1.7.3 directory. Edit the Makefile with your preferred text
+   cd to the 1.8 directory. Edit the Makefile with your preferred text
    editor and make any necessary changes to the settings at the top,
    especially if you want Xpm or TrueType support. Next, type "make". If
    you are the system administrator, and you wish to make the gd library
    If you get errors, edit the Makefile again, paying special attention
    to the INCLUDEDIRS and LIBDIRS settings.
    
+   IF YOU GET LINKER ERRORS, TRY JUGGLING THE ORDER OF THE -l DIRECTIVES
+   IN THE MAKEFILE. Some platforms may prefer that the libraries be
+   listed in the opposite order.
+   
     For Windows, Mac, Et Cetera
     
    Create a project using your favorite programming environment. Copy all
    
   gd basics: using gd in your program
   
-   gd lets you create PNG images on the fly. To use gd in your program,
-   include the file gd.h, and link with the libgd.a library produced by
-   "make libgd.a", under Unix. Under other operating systems you will add
-   gd.c to your own project.
+   gd lets you create PNG or JPEG images on the fly. To use gd in your
+   program, include the file gd.h, and link with the libgd.a library
+   produced by "make libgd.a", under Unix. Under other operating systems
+   you will add gd.c to your own project.
    
    If you want to use the provided fonts, include gdfontt.h, gdfonts.h,
    gdfontmb.h, gdfontl.h and/or gdfontg.h. For more impressive results,
 int main() {
         /* Declare the image */
         gdImagePtr im;
-        /* Declare an output file */
-        FILE *out;
+        /* Declare output files */
+        FILE *pngout, *jpegout;
         /* Declare color indexes */
         int black;
         int white;
 
         /* Open a file for writing. "wb" means "write binary", important
                 under MSDOS, harmless under Unix. */
-        out = fopen("test.png", "wb");
+        pngout = fopen("test.png", "wb");
 
-        /* Output the image to the disk file. */
-        gdImagePng(im, out);
+        /* Do the same for a JPEG-format file. */
+        jpegout = fopen("test.jpg", "wb");
 
-        /* Close the file. */
-        fclose(out);
+        /* Output the image to the disk file in PNG format. */
+        gdImagePng(im, pngout);
+
+        /* Output the same image in JPEG format, using the default
+                JPEG quality setting. */
+        gdImageJpeg(im, jpegout, -1);
+
+        /* Close the files. */
+        fclose(pngout);
+        fclose(jpegout);
 
         /* Destroy the image in memory. */
         gdImageDestroy(im);
    When executed, this program creates an image, allocates two colors
    (the first color allocated becomes the background color), draws a
    diagonal line (note that 0, 0 is the upper left corner), writes the
-   image to a PNG file, and destroys the image.
+   image to PNG and JPEG files, and destroys the image.
    
    The above example program should give you an idea of how the package
    works. gd provides many additional functions, which are listed in the
 /* ... Use the image ... */
 gdImageDestroy(im);
 
+   gdImageCreateFromJpeg(FILE *in) _(FUNCTION)_
+          gdImageCreateFromJpegCtx(FILE *in) _(FUNCTION)_
+          
+          
+          gdImageCreateFromJpeg is called to load images from JPEG format
+          files. Invoke gdImageCreateFromJpeg with an already opened
+          pointer to a file containing the desired image.
+          gdImageCreateFromJpeg returns a gdImagePtr to the new image, or
+          NULL if unable to load the image (most often because the file
+          is corrupt or does not contain a JPEG image).
+          gdImageCreateFromPng does _not_ close the file. You can inspect
+          the sx and sy members of the image to determine its size. The
+          image must eventually be destroyed using gdImageDestroy().
+          
+
+gdImagePtr im;
+... inside a function ...
+FILE *in;
+in = fopen("myjpeg.jpg", "rb");
+im = gdImageCreateFromJpeg(in);
+fclose(in);
+/* ... Use the image ... */
+gdImageDestroy(im);
+
    gdImageCreateFromPng(FILE *in) _(FUNCTION)_
           gdImageCreateFromPngCtx(gdIOCtx *in) _(FUNCTION)_
           
           pointer to a file containing the desired image in the gd file
           format, which is specific to gd and intended for very fast
           loading. (It is _not_ intended for compression; for
-          compression, use PNG.) gdImageCreateFromGd returns a gdImagePtr
-          to the new image, or NULL if unable to load the image (most
-          often because the file is corrupt or does not contain a gd
-          format image). gdImageCreateFromGd does _not_ close the file.
-          You can inspect the sx and sy members of the image to determine
-          its size. The image must eventually be destroyed using
-          gdImageDestroy().
+          compression, use PNG or JPEG.) gdImageCreateFromGd returns a
+          gdImagePtr to the new image, or NULL if unable to load the
+          image (most often because the file is corrupt or does not
+          contain a gd format image). gdImageCreateFromGd does _not_
+          close the file. You can inspect the sx and sy members of the
+          image to determine its size. The image must eventually be
+          destroyed using gdImageDestroy().
           
 
 ... inside a function ...
 /* Now destroy it */
 gdImageDestroy(im);
 
-   void gdImagePng(gdImagePtr im, FILE *out) _(FUNCTION)_
-          gdImagePng outputs the specified image to the specified file in
-          PNG format. The file must be open for writing. Under MSDOS and
-          all versions of Windows, it is important to use "wb" as opposed
-          to simply "w" as the mode when opening the file, and under Unix
-          there is no penalty for doing so. gdImagePng does _not_ close
-          the file; your code must do so.
+   void gdImageJpeg(gdImagePtr im, FILE *out, int quality) _(FUNCTION)_
+          void gdImageJpegCtx(gdImagePtr im, gdIOCtx *out, int quality)
           
+   _(FUNCTION)_
+   
+   gdImageJpeg outputs the specified image to the specified file in JPEG
+   format. The file must be open for writing. Under MSDOS and all
+   versions of Windows, it is important to use "wb" as opposed to simply
+   "w" as the mode when opening the file, and under Unix there is no
+   penalty for doing so. gdImageJpeg does _not_ close the file; your code
+   must do so.
+   
+   If quality is negative, the default IJG JPEG quality value (which
+   should yield a good general quality / size tradeoff for most