Commits

Anonymous committed 4990220

- sync to 1.3.0

Comments (0)

Files changed (13)

+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+#include <stdlib.h>
+#include "gd.h"
+#include "mtables.c"
+
+static void gdImageBrushApply(gdImagePtr im, int x, int y);
+static void gdImageTileApply(gdImagePtr im, int x, int y);
+
+gdImagePtr gdImageCreate(int sx, int sy)
+{
+	int i;
+	gdImagePtr im;
+	im = (gdImage *) malloc(sizeof(gdImage));
+	/* NOW ROW-MAJOR IN GD 1.3 */
+	im->pixels = (unsigned char **) malloc(sizeof(unsigned char *) * sy);
+	im->polyInts = 0;
+	im->polyAllocated = 0;
+	im->brush = 0;
+	im->tile = 0;
+	im->style = 0;
+	for (i=0; (i<sy); i++) {
+		/* NOW ROW-MAJOR IN GD 1.3 */
+		im->pixels[i] = (unsigned char *) calloc(
+			sx, sizeof(unsigned char));
+	}	
+	im->sx = sx;
+	im->sy = sy;
+	im->colorsTotal = 0;
+	im->transparent = (-1);
+	im->interlace = 0;
+	return im;
+}
+
+void gdImageDestroy(gdImagePtr im)
+{
+	int i;
+	for (i=0; (i<im->sy); i++) {
+		free(im->pixels[i]);
+	}	
+	free(im->pixels);
+	if (im->polyInts) {
+			free(im->polyInts);
+	}
+	if (im->style) {
+		free(im->style);
+	}
+	free(im);
+}
+
+int gdImageColorClosest(gdImagePtr im, int r, int g, int b)
+{
+	int i;
+	long rd, gd, bd;
+	int ct = (-1);
+	long mindist = 0;
+	for (i=0; (i<(im->colorsTotal)); i++) {
+		long dist;
+		if (im->open[i]) {
+			continue;
+		}
+		rd = (im->red[i] - r);	
+		gd = (im->green[i] - g);
+		bd = (im->blue[i] - b);
+		dist = rd * rd + gd * gd + bd * bd;
+		if ((i == 0) || (dist < mindist)) {
+			mindist = dist;	
+			ct = i;
+		}
+	}
+	return ct;
+}
+
+int gdImageColorExact(gdImagePtr im, int r, int g, int b)
+{
+	int i;
+	for (i=0; (i<(im->colorsTotal)); i++) {
+		if (im->open[i]) {
+			continue;
+		}
+		if ((im->red[i] == r) && 
+			(im->green[i] == g) &&
+			(im->blue[i] == b)) {
+			return i;
+		}
+	}
+	return -1;
+}
+
+int gdImageColorAllocate(gdImagePtr im, int r, int g, int b)
+{
+	int i;
+	int ct = (-1);
+	for (i=0; (i<(im->colorsTotal)); i++) {
+		if (im->open[i]) {
+			ct = i;
+			break;
+		}
+	}	
+	if (ct == (-1)) {
+		ct = im->colorsTotal;
+		if (ct == gdMaxColors) {
+			return -1;
+		}
+		im->colorsTotal++;
+	}
+	im->red[ct] = r;
+	im->green[ct] = g;
+	im->blue[ct] = b;
+	im->open[ct] = 0;
+	return ct;
+}
+
+void gdImageColorDeallocate(gdImagePtr im, int color)
+{
+	/* Mark it open. */
+	im->open[color] = 1;
+}
+
+void gdImageColorTransparent(gdImagePtr im, int color)
+{
+	im->transparent = color;
+}
+
+void gdImageSetPixel(gdImagePtr im, int x, int y, int color)
+{
+	int p;
+	switch(color) {
+		case gdStyled:
+		if (!im->style) {
+			/* Refuse to draw if no style is set. */
+			return;
+		} else {
+			p = im->style[im->stylePos++];
+		}
+		if (p != (gdTransparent)) {
+			gdImageSetPixel(im, x, y, p);
+		}
+		im->stylePos = im->stylePos %  im->styleLength;
+		break;
+		case gdStyledBrushed:
+		if (!im->style) {
+			/* Refuse to draw if no style is set. */
+			return;
+		}
+		p = im->style[im->stylePos++];
+		if ((p != gdTransparent) && (p != 0)) {
+			gdImageSetPixel(im, x, y, gdBrushed);
+		}
+		im->stylePos = im->stylePos %  im->styleLength;
+		break;
+		case gdBrushed:
+		gdImageBrushApply(im, x, y);
+		break;
+		case gdTiled:
+		gdImageTileApply(im, x, y);
+		break;
+		default:
+		if (gdImageBoundsSafe(im, x, y)) {
+			/* NOW ROW-MAJOR IN GD 1.3 */
+			im->pixels[y][x] = color;
+		}
+		break;
+	}
+}
+
+static void gdImageBrushApply(gdImagePtr im, int x, int y)
+{
+	int lx, ly;
+	int hy;
+	int hx;
+	int x1, y1, x2, y2;
+	int srcx, srcy;
+	if (!im->brush) {
+		return;
+	}
+	hy = gdImageSY(im->brush)/2;
+	y1 = y - hy;
+	y2 = y1 + gdImageSY(im->brush);	
+	hx = gdImageSX(im->brush)/2;
+	x1 = x - hx;
+	x2 = x1 + gdImageSX(im->brush);
+	srcy = 0;
+	for (ly = y1; (ly < y2); ly++) {
+		srcx = 0;
+		for (lx = x1; (lx < x2); lx++) {
+			int p;
+			p = gdImageGetPixel(im->brush, srcx, srcy);
+			/* Allow for non-square brushes! */
+			if (p != gdImageGetTransparent(im->brush)) {
+				gdImageSetPixel(im, lx, ly,
+					im->brushColorMap[p]);
+			}
+			srcx++;
+		}
+		srcy++;
+	}	
+}		
+
+static void gdImageTileApply(gdImagePtr im, int x, int y)
+{
+	int srcx, srcy;
+	int p;
+	if (!im->tile) {
+		return;
+	}
+	srcx = x % gdImageSX(im->tile);
+	srcy = y % gdImageSY(im->tile);
+	p = gdImageGetPixel(im->tile, srcx, srcy);
+	/* Allow for transparency */
+	if (p != gdImageGetTransparent(im->tile)) {
+		gdImageSetPixel(im, x, y,
+			im->tileColorMap[p]);
+	}
+}		
+
+int gdImageGetPixel(gdImagePtr im, int x, int y)
+{
+	if (gdImageBoundsSafe(im, x, y)) {
+		/* NOW ROW-MAJOR IN GD 1.3 */
+		return im->pixels[y][x];
+	} else {
+		return 0;
+	}
+}
+
+/* Bresenham as presented in Foley & Van Dam */
+
+void gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
+{
+	int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
+	dx = abs(x2-x1);
+	dy = abs(y2-y1);
+	if (dy <= dx) {
+		d = 2*dy - dx;
+		incr1 = 2*dy;
+		incr2 = 2 * (dy - dx);
+		if (x1 > x2) {
+			x = x2;
+			y = y2;
+			ydirflag = (-1);
+			xend = x1;
+		} else {
+			x = x1;
+			y = y1;
+			ydirflag = 1;
+			xend = x2;
+		}
+		gdImageSetPixel(im, x, y, color);
+		if (((y2 - y1) * ydirflag) > 0) {
+			while (x < xend) {
+				x++;
+				if (d <0) {
+					d+=incr1;
+				} else {
+					y++;
+					d+=incr2;
+				}
+				gdImageSetPixel(im, x, y, color);
+			}
+		} else {
+			while (x < xend) {
+				x++;
+				if (d <0) {
+					d+=incr1;
+				} else {
+					y--;
+					d+=incr2;
+				}
+				gdImageSetPixel(im, x, y, color);
+			}
+		}		
+	} else {
+		d = 2*dx - dy;
+		incr1 = 2*dx;
+		incr2 = 2 * (dx - dy);
+		if (y1 > y2) {
+			y = y2;
+			x = x2;
+			yend = y1;
+			xdirflag = (-1);
+		} else {
+			y = y1;
+			x = x1;
+			yend = y2;
+			xdirflag = 1;
+		}
+		gdImageSetPixel(im, x, y, color);
+		if (((x2 - x1) * xdirflag) > 0) {
+			while (y < yend) {
+				y++;
+				if (d <0) {
+					d+=incr1;
+				} else {
+					x++;
+					d+=incr2;
+				}
+				gdImageSetPixel(im, x, y, color);
+			}
+		} else {
+			while (y < yend) {
+				y++;
+				if (d <0) {
+					d+=incr1;
+				} else {
+					x--;
+					d+=incr2;
+				}
+				gdImageSetPixel(im, x, y, color);
+			}
+		}
+	}
+}
+
+static void dashedSet(gdImagePtr im, int x, int y, int color,
+	int *onP, int *dashStepP);
+
+void gdImageDashedLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
+{
+	int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
+	int dashStep = 0;
+	int on = 1;
+	dx = abs(x2-x1);
+	dy = abs(y2-y1);
+	if (dy <= dx) {
+		d = 2*dy - dx;
+		incr1 = 2*dy;
+		incr2 = 2 * (dy - dx);
+		if (x1 > x2) {
+			x = x2;
+			y = y2;
+			ydirflag = (-1);
+			xend = x1;
+		} else {
+			x = x1;
+			y = y1;
+			ydirflag = 1;
+			xend = x2;
+		}
+		dashedSet(im, x, y, color, &on, &dashStep);
+		if (((y2 - y1) * ydirflag) > 0) {
+			while (x < xend) {
+				x++;
+				if (d <0) {
+					d+=incr1;
+				} else {
+					y++;
+					d+=incr2;
+				}
+				dashedSet(im, x, y, color, &on, &dashStep);
+			}
+		} else {
+			while (x < xend) {
+				x++;
+				if (d <0) {
+					d+=incr1;
+				} else {
+					y--;
+					d+=incr2;
+				}
+				dashedSet(im, x, y, color, &on, &dashStep);
+			}
+		}		
+	} else {
+		d = 2*dx - dy;
+		incr1 = 2*dx;
+		incr2 = 2 * (dx - dy);
+		if (y1 > y2) {
+			y = y2;
+			x = x2;
+			yend = y1;
+			xdirflag = (-1);
+		} else {
+			y = y1;
+			x = x1;
+			yend = y2;
+			xdirflag = 1;
+		}
+		dashedSet(im, x, y, color, &on, &dashStep);
+		if (((x2 - x1) * xdirflag) > 0) {
+			while (y < yend) {
+				y++;
+				if (d <0) {
+					d+=incr1;
+				} else {
+					x++;
+					d+=incr2;
+				}
+				dashedSet(im, x, y, color, &on, &dashStep);
+			}
+		} else {
+			while (y < yend) {
+				y++;
+				if (d <0) {
+					d+=incr1;
+				} else {
+					x--;
+					d+=incr2;
+				}
+				dashedSet(im, x, y, color, &on, &dashStep);
+			}
+		}
+	}
+}
+
+static void dashedSet(gdImagePtr im, int x, int y, int color,
+	int *onP, int *dashStepP)
+{
+	int dashStep = *dashStepP;
+	int on = *onP;
+	dashStep++;
+	if (dashStep == gdDashSize) {
+		dashStep = 0;
+		on = !on;
+	}
+	if (on) {
+		gdImageSetPixel(im, x, y, color);
+	}
+	*dashStepP = dashStep;
+	*onP = on;
+}
+	
+
+int gdImageBoundsSafe(gdImagePtr im, int x, int y)
+{
+	return (!(((y < 0) || (y >= im->sy)) ||
+		((x < 0) || (x >= im->sx))));
+}
+
+void gdImageChar(gdImagePtr im, gdFontPtr f, int x, int y, 
+	int c, int color)
+{
+	int cx, cy;
+	int px, py;
+	int fline;
+	cx = 0;
+	cy = 0;
+	if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
+		return;
+	}
+	fline = (c - f->offset) * f->h * f->w;
+	for (py = y; (py < (y + f->h)); py++) {
+		for (px = x; (px < (x + f->w)); px++) {
+			if (f->data[fline + cy * f->w + cx]) {
+				gdImageSetPixel(im, px, py, color);	
+			}
+			cx++;
+		}
+		cx = 0;
+		cy++;
+	}
+}
+
+void gdImageCharUp(gdImagePtr im, gdFontPtr f, 
+	int x, int y, int c, int color)
+{
+	int cx, cy;
+	int px, py;
+	int fline;
+	cx = 0;
+	cy = 0;
+	if ((c < f->offset) || (c >= (f->offset + f->nchars))) {
+		return;
+	}
+	fline = (c - f->offset) * f->h * f->w;
+	for (py = y; (py > (y - f->w)); py--) {
+		for (px = x; (px < (x + f->h)); px++) {
+			if (f->data[fline + cy * f->w + cx]) {
+				gdImageSetPixel(im, px, py, color);	
+			}
+			cy++;
+		}
+		cy = 0;
+		cx++;
+	}
+}
+
+void gdImageString(gdImagePtr im, gdFontPtr f, 
+	int x, int y, unsigned char *s, int color)
+{
+	int i;
+	int l;
+	l = strlen(s);
+	for (i=0; (i<l); i++) {
+		gdImageChar(im, f, x, y, s[i], color);
+		x += f->w;
+	}
+}
+
+void gdImageStringUp(gdImagePtr im, gdFontPtr f, 
+	int x, int y, unsigned char *s, int color)
+{
+	int i;
+	int l;
+	l = strlen(s);
+	for (i=0; (i<l); i++) {
+		gdImageCharUp(im, f, x, y, s[i], color);
+		y -= f->w;
+	}
+}
+
+static int strlen16(unsigned short *s);
+
+void gdImageString16(gdImagePtr im, gdFontPtr f, 
+	int x, int y, unsigned short *s, int color)
+{
+	int i;
+	int l;
+	l = strlen16(s);
+	for (i=0; (i<l); i++) {
+		gdImageChar(im, f, x, y, s[i], color);
+		x += f->w;
+	}
+}
+
+void gdImageStringUp16(gdImagePtr im, gdFontPtr f, 
+	int x, int y, unsigned short *s, int color)
+{
+	int i;
+	int l;
+	l = strlen16(s);
+	for (i=0; (i<l); i++) {
+		gdImageCharUp(im, f, x, y, s[i], color);
+		y -= f->w;
+	}
+}
+
+static int strlen16(unsigned short *s)
+{
+	int len = 0;
+	while (*s) {
+		s++;
+		len++;
+	}
+	return len;
+}
+
+/* s and e are integers modulo 360 (degrees), with 0 degrees
+  being the rightmost extreme and degrees changing clockwise.
+  cx and cy are the center in pixels; w and h are the horizontal 
+  and vertical diameter in pixels. Nice interface, but slow, since
+  I don't yet use Bresenham (I'm using an inefficient but
+  simple solution with too much work going on in it; generalizing
+  Bresenham to ellipses and partial arcs of ellipses is non-trivial,
+  at least for me) and there are other inefficiencies (small circles
+  do far too much work). */
+
+void gdImageArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color)
+{
+	int i;
+	int lx = 0, ly = 0;
+	int w2, h2;
+	w2 = w/2;
+	h2 = h/2;
+	while (e < s) {
+		e += 360;
+	}
+	for (i=s; (i <= e); i++) {
+		int x, y;
+		x = ((long)cost[i % 360] * (long)w2 / costScale) + cx; 
+		y = ((long)sint[i % 360] * (long)h2 / sintScale) + cy;
+		if (i != s) {
+			gdImageLine(im, lx, ly, x, y, color);	
+		}
+		lx = x;
+		ly = y;
+	}
+}
+
+
+#if 0
+	/* Bresenham octant code, which I should use eventually */
+	int x, y, d;
+	x = 0;
+	y = w;
+	d = 3-2*w;
+	while (x < y) {
+		gdImageSetPixel(im, cx+x, cy+y, color);
+		if (d < 0) {
+			d += 4 * x + 6;
+		} else {
+			d += 4 * (x - y) + 10;
+			y--;
+		}
+		x++;
+	}
+	if (x == y) {
+		gdImageSetPixel(im, cx+x, cy+y, color);
+	}
+#endif
+
+void gdImageFillToBorder(gdImagePtr im, int x, int y, int border, int color)
+{
+	int lastBorder;
+	/* Seek left */
+	int leftLimit, rightLimit;
+	int i;
+	leftLimit = (-1);
+	if (border < 0) {
+		/* Refuse to fill to a non-solid border */
+		return;
+	}
+	for (i = x; (i >= 0); i--) {
+		if (gdImageGetPixel(im, i, y) == border) {
+			break;
+		}
+		gdImageSetPixel(im, i, y, color);
+		leftLimit = i;
+	}
+	if (leftLimit == (-1)) {
+		return;
+	}
+	/* Seek right */
+	rightLimit = x;
+	for (i = (x+1); (i < im->sx); i++) {	
+		if (gdImageGetPixel(im, i, y) == border) {
+			break;
+		}
+		gdImageSetPixel(im, i, y, color);
+		rightLimit = i;
+	}
+	/* Look at lines above and below and start paints */
+	/* Above */
+	if (y > 0) {
+		lastBorder = 1;
+		for (i = leftLimit; (i <= rightLimit); i++) {
+			int c;
+			c = gdImageGetPixel(im, i, y-1);
+			if (lastBorder) {
+				if ((c != border) && (c != color)) {	
+					gdImageFillToBorder(im, i, y-1, 
+						border, color);		
+					lastBorder = 0;
+				}
+			} else if ((c == border) || (c == color)) {
+				lastBorder = 1;
+			}
+		}
+	}
+	/* Below */
+	if (y < ((im->sy) - 1)) {
+		lastBorder = 1;
+		for (i = leftLimit; (i <= rightLimit); i++) {
+			int c;
+			c = gdImageGetPixel(im, i, y+1);
+			if (lastBorder) {
+				if ((c != border) && (c != color)) {	
+					gdImageFillToBorder(im, i, y+1, 
+						border, color);		
+					lastBorder = 0;
+				}
+			} else if ((c == border) || (c == color)) {
+				lastBorder = 1;
+			}
+		}
+	}
+}
+
+void gdImageFill(gdImagePtr im, int x, int y, int color)
+{
+	int lastBorder;
+	int old;
+	int leftLimit, rightLimit;
+	int i;
+	old = gdImageGetPixel(im, x, y);
+	if (color == gdTiled) {
+		/* Tile fill -- got to watch out! */
+		int p, tileColor;	
+		int srcx, srcy;
+		if (!im->tile) {
+			return;
+		}
+		/* Refuse to flood-fill with a transparent pattern --
+			I can't do it without allocating another image */
+		if (gdImageGetTransparent(im->tile) != (-1)) {
+			return;
+		}	
+		srcx = x % gdImageSX(im->tile);
+		srcy = y % gdImageSY(im->tile);
+		p = gdImageGetPixel(im->tile, srcx, srcy);
+		tileColor = im->tileColorMap[p];
+		if (old == tileColor) {
+			/* Nothing to be done */
+			return;
+		}
+	} else {
+		if (old == color) {
+			/* Nothing to be done */
+			return;
+		}
+	}
+	/* Seek left */
+	leftLimit = (-1);
+	for (i = x; (i >= 0); i--) {
+		if (gdImageGetPixel(im, i, y) != old) {
+			break;
+		}
+		gdImageSetPixel(im, i, y, color);
+		leftLimit = i;
+	}
+	if (leftLimit == (-1)) {
+		return;
+	}
+	/* Seek right */
+	rightLimit = x;
+	for (i = (x+1); (i < im->sx); i++) {	
+		if (gdImageGetPixel(im, i, y) != old) {
+			break;
+		}
+		gdImageSetPixel(im, i, y, color);
+		rightLimit = i;
+	}
+	/* Look at lines above and below and start paints */
+	/* Above */
+	if (y > 0) {
+		lastBorder = 1;
+		for (i = leftLimit; (i <= rightLimit); i++) {
+			int c;
+			c = gdImageGetPixel(im, i, y-1);
+			if (lastBorder) {
+				if (c == old) {	
+					gdImageFill(im, i, y-1, color);		
+					lastBorder = 0;
+				}
+			} else if (c != old) {
+				lastBorder = 1;
+			}
+		}
+	}
+	/* Below */
+	if (y < ((im->sy) - 1)) {
+		lastBorder = 1;
+		for (i = leftLimit; (i <= rightLimit); i++) {
+			int c;
+			c = gdImageGetPixel(im, i, y+1);
+			if (lastBorder) {
+				if (c == old) {
+					gdImageFill(im, i, y+1, color);		
+					lastBorder = 0;
+				}
+			} else if (c != old) {
+				lastBorder = 1;
+			}
+		}
+	}
+}
+	
+/* Code drawn from ppmtogif.c, from the pbmplus package
+**
+** Based on GIFENCOD by David Rowley <mgardi@watdscu.waterloo.edu>. A
+** Lempel-Zim compression based on "compress".
+**
+** Modified by Marcel Wijkstra <wijkstra@fwi.uva.nl>
+**
+** Copyright (C) 1989 by Jef Poskanzer.
+**
+** 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.
+**
+** The Graphics Interchange Format(c) is the Copyright property of
+** CompuServe Incorporated.  GIF(sm) is a Service Mark property of
+** CompuServe Incorporated.
+*
+*  Heavily modified by Mouse, 1998-02-12.  
+*  Remove LZW compression.
+*  Added miGIF run length compression.
+*
+*/
+
+/*
+ * a code_int must be able to hold 2**GIFBITS values of type int, and also -1
+ */
+typedef int code_int;
+
+static int colorstobpp(int colors);
+static void BumpPixel (void);
+static int GIFNextPixel (gdImagePtr im);
+static void GIFEncode (FILE *fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im);
+static void Putword (int w, FILE *fp);
+static void compress (int, FILE *, gdImagePtr, int);
+static void output (code_int code);
+static void char_init (void);
+static void char_out (int c);
+/* Allows for reuse */
+static void init_statics(void);
+
+void gdImageGif(gdImagePtr im, FILE *out)
+{
+	int interlace, transparent, BitsPerPixel;
+	interlace = im->interlace;
+	transparent = im->transparent;
+
+	BitsPerPixel = colorstobpp(im->colorsTotal);
+	/* Clear any old values in statics strewn through the GIF code */
+	init_statics();
+	/* All set, let's do it. */
+	GIFEncode(
+		out, im->sx, im->sy, interlace, 0, transparent, BitsPerPixel,
+		im->red, im->green, im->blue, im);
+}
+
+static int
+colorstobpp(int colors)
+{
+    int bpp = 0;
+
+    if ( colors <= 2 )
+        bpp = 1;
+    else if ( colors <= 4 )
+        bpp = 2;
+    else if ( colors <= 8 )
+        bpp = 3;
+    else if ( colors <= 16 )
+        bpp = 4;
+    else if ( colors <= 32 )
+        bpp = 5;
+    else if ( colors <= 64 )
+        bpp = 6;
+    else if ( colors <= 128 )
+        bpp = 7;
+    else if ( colors <= 256 )
+        bpp = 8;
+    return bpp;
+    }
+
+/*****************************************************************************
+ *
+ * GIFENCODE.C    - GIF Image compression interface
+ *
+ * GIFEncode( FName, GHeight, GWidth, GInterlace, Background, Transparent,
+ *            BitsPerPixel, Red, Green, Blue, gdImagePtr )
+ *
+ *****************************************************************************/
+
+#define TRUE 1
+#define FALSE 0
+
+static int Width, Height;
+static int curx, cury;
+static long CountDown;
+static int Pass = 0;
+static int Interlace;
+
+/*
+ * Bump the 'curx' and 'cury' to point to the next pixel
+ */
+static void
+BumpPixel(void)
+{
+        /*
+         * Bump the current X position
+         */
+        ++curx;
+
+        /*
+         * If we are at the end of a scan line, set curx back to the beginning
+         * If we are interlaced, bump the cury to the appropriate spot,
+         * otherwise, just increment it.
+         */
+        if( curx == Width ) {
+                curx = 0;
+
+                if( !Interlace )
+                        ++cury;
+                else {
+                     switch( Pass ) {
+
+                       case 0:
+                          cury += 8;
+                          if( cury >= Height ) {
+                                ++Pass;
+                                cury = 4;
+                          }
+                          break;
+
+                       case 1:
+                          cury += 8;
+                          if( cury >= Height ) {
+                                ++Pass;
+                                cury = 2;
+                          }
+                          break;
+
+                       case 2:
+                          cury += 4;
+                          if( cury >= Height ) {
+                             ++Pass;
+                             cury = 1;
+                          }
+                          break;
+
+                       case 3:
+                          cury += 2;
+                          break;
+                        }
+                }
+        }
+}
+
+/*
+ * Return the next pixel from the image
+ */
+static int
+GIFNextPixel(gdImagePtr im)
+{
+        int r;
+
+        if( CountDown == 0 )
+                return EOF;
+
+        --CountDown;
+
+        r = gdImageGetPixel(im, curx, cury);
+
+        BumpPixel();
+
+        return r;
+}
+
+/* public */
+
+static void
+GIFEncode(FILE *fp, int GWidth, int GHeight, int GInterlace, int Background, int Transparent, int BitsPerPixel, int *Red, int *Green, int *Blue, gdImagePtr im)
+{
+        int B;
+        int RWidth, RHeight;
+        int LeftOfs, TopOfs;
+        int Resolution;
+        int ColorMapSize;
+        int InitCodeSize;
+        int i;
+
+        Interlace = GInterlace;
+
+        ColorMapSize = 1 << BitsPerPixel;
+
+        RWidth = Width = GWidth;
+        RHeight = Height = GHeight;
+        LeftOfs = TopOfs = 0;
+
+        Resolution = BitsPerPixel;
+
+        /*
+         * Calculate number of bits we are expecting
+         */
+        CountDown = (long)Width * (long)Height;
+
+        /*
+         * Indicate which pass we are on (if interlace)
+         */
+        Pass = 0;
+
+        /*
+         * The initial code size
+         */
+        if( BitsPerPixel <= 1 )
+                InitCodeSize = 2;
+        else
+                InitCodeSize = BitsPerPixel;
+
+        /*
+         * Set up the current x and y position
+         */
+        curx = cury = 0;
+
+        /*
+         * Write the Magic header
+         */
+        fwrite( Transparent < 0 ? "GIF87a" : "GIF89a", 1, 6, fp );
+
+        /*
+         * Write out the screen width and height
+         */
+        Putword( RWidth, fp );
+        Putword( RHeight, fp );
+
+        /*
+         * Indicate that there is a global colour map
+         */
+        B = 0x80;       /* Yes, there is a color map */
+
+        /*
+         * OR in the resolution
+         */
+        B |= (Resolution - 1) << 4;
+
+        /*
+         * OR in the Bits per Pixel
+         */
+        B |= (BitsPerPixel - 1);
+
+        /*
+         * Write it out
+         */
+        fputc( B, fp );
+
+        /*
+         * Write out the Background colour
+         */
+        fputc( Background, fp );
+
+        /*
+         * Byte of 0's (future expansion)
+         */
+        fputc( 0, fp );
+
+        /*
+         * Write out the Global Colour Map
+         */
+        for( i=0; i<ColorMapSize; ++i ) {
+                fputc( Red[i], fp );
+                fputc( Green[i], fp );
+                fputc( Blue[i], fp );
+        }
+
+	/*
+	 * Write out extension for transparent colour index, if necessary.
+	 */
+	if ( Transparent >= 0 ) {
+	    fputc( '!', fp );
+	    fputc( 0xf9, fp );
+	    fputc( 4, fp );
+	    fputc( 1, fp );
+	    fputc( 0, fp );
+	    fputc( 0, fp );
+	    fputc( (unsigned char) Transparent, fp );
+	    fputc( 0, fp );
+	}
+
+        /*
+         * Write an Image separator
+         */
+        fputc( ',', fp );
+
+        /*
+         * Write the Image header
+         */
+
+        Putword( LeftOfs, fp );
+        Putword( TopOfs, fp );
+        Putword( Width, fp );
+        Putword( Height, fp );
+
+        /*
+         * Write out whether or not the image is interlaced
+         */
+        if( Interlace )
+                fputc( 0x40, fp );
+        else
+                fputc( 0x00, fp );
+
+        /*
+         * Write out the initial code size
+         */
+        fputc( InitCodeSize, fp );
+
+        /*
+         * Go and actually compress the data
+         */
+        compress( InitCodeSize+1, fp, im, Background );
+
+        /*
+         * Write out a Zero-length packet (to end the series)
+         */
+        fputc( 0, fp );
+
+        /*
+         * Write the GIF file terminator
+         */
+        fputc( ';', fp );
+}
+
+/*
+ * Write out a word to the GIF file
+ */
+static void
+Putword(int w, FILE *fp)
+{
+        fputc( w & 0xff, fp );
+        fputc( (w / 256) & 0xff, fp );
+}
+
+#define GIFBITS 12
+
+/*-----------------------------------------------------------------------
+ *
+ * miGIF Compression - mouse and ivo's GIF-compatible compression
+ *
+ *          -run length encoding compression routines-
+ *
+ * Copyright (C) 1998 Hutchison Avenue Software Corporation
+ *               http://www.hasc.com
+ *               info@hasc.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." The Hutchison Avenue 
+ * Software Corporation disclaims all warranties, either express or implied, 
+ * including but not limited to implied warranties of merchantability and 
+ * fitness for a particular purpose, with respect to this code and accompanying
+ * documentation. 
+ * 
+ * The miGIF compression routines do not, strictly speaking, generate files 
+ * conforming to the GIF spec, since the image data is not LZW-compressed 
+ * (this is the point: in order to avoid transgression of the Unisys patent 
+ * on the LZW algorithm.)  However, miGIF generates data streams that any 
+ * reasonably sane LZW decompresser will decompress to what we want.
+ *
+ * miGIF compression uses run length encoding. It compresses horizontal runs 
+ * of pixels of the same color. This type of compression gives good results
+ * on images with many runs, for example images with lines, text and solid 
+ * shapes on a solid-colored background. It gives little or no compression 
+ * on images with few runs, for example digital or scanned photos.
+ *
+ *                               der Mouse
+ *                      mouse@rodents.montreal.qc.ca
+ *            7D C8 61 52 5D E7 2D 39  4E F1 31 3E E8 B3 27 4B
+ *
+ *                             ivo@hasc.com
+ *
+ * The Graphics Interchange Format(c) is the Copyright property of
+ * CompuServe Incorporated.  GIF(sm) is a Service Mark property of
+ * CompuServe Incorporated.
+ *
+ */
+
+static int rl_pixel;
+static int rl_basecode;
+static int rl_count;
+static int rl_table_pixel;
+static int rl_table_max;
+static int just_cleared;
+static int out_bits;
+static int out_bits_init;
+static int out_count;
+static int out_bump;
+static int out_bump_init;
+static int out_clear;
+static int out_clear_init;
+static int max_ocodes;
+static int code_clear;
+static int code_eof;
+static unsigned int obuf;
+static int obits;
+static FILE *ofile;
+static unsigned char oblock[256];
+static int oblen;
+
+/* Used only when debugging GIF compression code */
+/* #define DEBUGGING_ENVARS */
+
+#ifdef DEBUGGING_ENVARS
+
+static int verbose_set = 0;
+static int verbose;
+#define VERBOSE (verbose_set?verbose:set_verbose())
+
+static int set_verbose(void)
+{
+ verbose = !!getenv("GIF_VERBOSE");
+ verbose_set = 1;
+ return(verbose);
+}
+
+#else
+
+#define VERBOSE 0
+
+#endif
+
+
+static const char *binformat(unsigned int v, int nbits)
+{
+ static char bufs[8][64];
+ static int bhand = 0;
+ unsigned int bit;
+ int bno;
+ char *bp;
+
+ bhand --;
+ if (bhand < 0) bhand = (sizeof(bufs)/sizeof(bufs[0]))-1;
+ bp = &bufs[bhand][0];
+ for (bno=nbits-1,bit=1U<<bno;bno>=0;bno--,bit>>=1)
+  { *bp++ = (v & bit) ? '1' : '0';
+    if (((bno&3) == 0) && (bno != 0)) *bp++ = '.';
+  }
+ *bp = '\0';
+ return(&bufs[bhand][0]);
+}
+
+static void write_block(void)
+{
+ int i;
+
+ if (VERBOSE)
+  { printf("write_block %d:",oblen);
+    for (i=0;i<oblen;i++) printf(" %02x",oblock[i]);
+    printf("\n");
+  }
+ fputc(oblen,ofile);
+ fwrite(&oblock[0],1,oblen,ofile);
+ oblen = 0;
+}
+
+static void block_out(unsigned char c)
+{
+ if (VERBOSE) printf("block_out %s\n",binformat(c,8));
+ oblock[oblen++] = c;
+ if (oblen >= 255) write_block();
+}
+
+static void block_flush(void)
+{
+ if (VERBOSE) printf("block_flush\n");
+ if (oblen > 0) write_block();
+}
+
+static void output(int val)
+{
+ if (VERBOSE) printf("output %s [%s %d %d]\n",binformat(val,out_bits),binformat(obuf,obits),obits,out_bits);
+ obuf |= val << obits;
+ obits += out_bits;
+ while (obits >= 8)
+  { block_out(obuf&0xff);
+    obuf >>= 8;
+    obits -= 8;
+  }
+ if (VERBOSE) printf("output leaving [%s %d]\n",binformat(obuf,obits),obits);
+}
+
+static void output_flush(void)
+{
+ if (VERBOSE) printf("output_flush\n");
+ if (obits > 0) block_out(obuf);
+ block_flush();
+}
+
+static void did_clear(void)
+{
+ if (VERBOSE) printf("did_clear\n");
+ out_bits = out_bits_init;
+ out_bump = out_bump_init;
+ out_clear = out_clear_init;
+ out_count = 0;
+ rl_table_max = 0;
+ just_cleared = 1;
+}
+
+static void output_plain(int c)
+{
+ if (VERBOSE) printf("output_plain %s\n",binformat(c,out_bits));
+ just_cleared = 0;
+ output(c);
+ out_count ++;
+ if (out_count >= out_bump)
+  { out_bits ++;
+    out_bump += 1 << (out_bits - 1);
+  }
+ if (out_count >= out_clear)
+  { output(code_clear);
+    did_clear();
+  }
+}
+
+static unsigned int isqrt(unsigned int x)
+{
+ unsigned int r;
+ unsigned int v;
+
+ if (x < 2) return(x);
+ for (v=x,r=1;v;v>>=2,r<<=1) ;
+ while (1)
+  { v = ((x / r) + r) / 2;
+    if ((v == r) || (v == r+1)) return(r);
+    r = v;
+  }
+}
+
+static unsigned int compute_triangle_count(unsigned int count, unsigned int nrepcodes)
+{
+ unsigned int perrep;
+ unsigned int cost;
+
+ cost = 0;
+ perrep = (nrepcodes * (nrepcodes+1)) / 2;
+ while (count >= perrep)
+  { cost += nrepcodes;
+    count -= perrep;
+  }
+ if (count > 0)
+  { unsigned int n;
+    n = isqrt(count);
+    while ((n*(n+1)) >= 2*count) n --;
+    while ((n*(n+1)) < 2*count) n ++;
+    cost += n;
+  }
+ return(cost);
+}
+
+static void max_out_clear(void)
+{
+ out_clear = max_ocodes;
+}
+
+static void reset_out_clear(void)
+{
+ out_clear = out_clear_init;
+ if (out_count >= out_clear)
+  { output(code_clear);
+    did_clear();
+  }
+}
+
+static void rl_flush_fromclear(int count)
+{
+ int n;
+
+ if (VERBOSE) printf("rl_flush_fromclear %d\n",count);
+ max_out_clear();
+ rl_table_pixel = rl_pixel;
+ n = 1;
+ while (count > 0)
+  { if (n == 1)
+     { rl_table_max = 1;
+       output_plain(rl_pixel);
+       count --;
+     }
+    else if (count >= n)
+     { rl_table_max = n;
+       output_plain(rl_basecode+n-2);
+       count -= n;
+     }
+    else if (count == 1)
+     { rl_table_max ++;
+       output_plain(rl_pixel);
+       count = 0;
+     }
+    else
+     { rl_table_max ++;
+       output_plain(rl_basecode+count-2);
+       count = 0;
+     }
+    if (out_count == 0) n = 1; else n ++;
+  }
+ reset_out_clear();
+ if (VERBOSE) printf("rl_flush_fromclear leaving table_max=%d\n",rl_table_max);
+}
+
+static void rl_flush_clearorrep(int count)
+{
+ int withclr;
+
+ if (VERBOSE) printf("rl_flush_clearorrep %d\n",count);
+ withclr = 1 + compute_triangle_count(count,max_ocodes);
+ if (withclr < count)
+  { output(code_clear);
+    did_clear();
+    rl_flush_fromclear(count);
+  }
+ else
+  { for (;count>0;count--) output_plain(rl_pixel);
+  }
+}
+
+static void rl_flush_withtable(int count)
+{
+ int repmax;
+ int repleft;
+ int leftover;
+
+ if (VERBOSE) printf("rl_flush_withtable %d\n",count);
+ repmax = count / rl_table_max;
+ leftover = count % rl_table_max;
+ repleft = (leftover ? 1 : 0);
+ if (out_count+repmax+repleft > max_ocodes)
+  { repmax = max_ocodes - out_count;
+    leftover = count - (repmax * rl_table_max);
+    repleft = 1 + compute_triangle_count(leftover,max_ocodes);
+  }
+ if (VERBOSE) printf("rl_flush_withtable repmax=%d leftover=%d repleft=%d\n",repmax,leftover,repleft);
+ if (1+compute_triangle_count(count,max_ocodes) < repmax+repleft)
+  { output(code_clear);
+    did_clear();
+    rl_flush_fromclear(count);
+    return;
+  }
+ max_out_clear();
+ for (;repmax>0;repmax--) output_plain(rl_basecode+rl_table_max-2);
+ if (leftover)
+  { if (just_cleared)
+     { rl_flush_fromclear(leftover);
+     }
+    else if (leftover == 1)
+     { output_plain(rl_pixel);
+     }
+    else
+     { output_plain(rl_basecode+leftover-2);
+     }
+  }
+ reset_out_clear();
+}
+
+static void rl_flush(void)
+{
+ int table_reps;
+ int table_extra;
+
+ if (VERBOSE) printf("rl_flush [ %d %d\n",rl_count,rl_pixel);
+ if (rl_count == 1)
+  { output_plain(rl_pixel);
+    rl_count = 0;
+    if (VERBOSE) printf("rl_flush ]\n");
+    return;
+  }
+ if (just_cleared)
+  { rl_flush_fromclear(rl_count);
+  }
+ else if ((rl_table_max < 2) || (rl_table_pixel != rl_pixel))
+  { rl_flush_clearorrep(rl_count);
+  }
+ else
+  { rl_flush_withtable(rl_count);
+  }
+ if (VERBOSE) printf("rl_flush ]\n");
+ rl_count = 0;
+}
+
+static void compress(int init_bits, FILE *outfile, gdImagePtr im, int background)
+{
+ int c;
+
+ ofile = outfile;
+ obuf = 0;
+ obits = 0;
+ oblen = 0;
+ code_clear = 1 << (init_bits - 1);
+ code_eof = code_clear + 1;
+ rl_basecode = code_eof + 1;
+ out_bump_init = (1 << (init_bits - 1)) - 1;
+ /* for images with a lot of runs, making out_clear_init larger will
+    give better compression. */ 
+ out_clear_init = (init_bits <= 3) ? 9 : (out_bump_init-1);
+#ifdef DEBUGGING_ENVARS
+  { const char *ocienv;
+    ocienv = getenv("GIF_OUT_CLEAR_INIT");
+    if (ocienv)
+     { out_clear_init = atoi(ocienv);
+       if (VERBOSE) printf("[overriding out_clear_init to %d]\n",out_clear_init);
+     }
+  }
+#endif
+ out_bits_init = init_bits;
+ max_ocodes = (1 << GIFBITS) - ((1 << (out_bits_init - 1)) + 3);
+ did_clear();
+ output(code_clear);
+ rl_count = 0;
+ while (1)
+  { c = GIFNextPixel(im);
+    if ((rl_count > 0) && (c != rl_pixel)) rl_flush();
+    if (c == EOF) break;
+    if (rl_pixel == c)
+     { rl_count ++;
+     }
+    else
+     { rl_pixel = c;
+       rl_count = 1;
+     }
+  }
+ output(code_eof);
+ output_flush();
+}
+
+/*-----------------------------------------------------------------------
+ *
+ * End of miGIF section  - See copyright notice at start of section.
+ *
+/*-----------------------------------------------------------------------
+
+
+/******************************************************************************
+ *
+ * GIF Specific routines
+ *
+ ******************************************************************************/
+
+/*
+ * Number of characters so far in this 'packet'
+ */
+static int a_count;
+
+/*
+ * Set up the 'byte output' routine
+ */
+static void
+char_init(void)
+{
+        a_count = 0;
+}
+
+/*
+ * Define the storage for the packet accumulator
+ */
+static char accum[ 256 ];
+
+static void init_statics(void) {
+	/* Some of these are properly initialized later. What I'm doing
+		here is making sure code that depends on C's initialization
+		of statics doesn't break when the code gets called more
+		than once. */
+	Width = 0;
+	Height = 0;
+	curx = 0;
+	cury = 0;
+	CountDown = 0;
+	Pass = 0;
+	Interlace = 0;
+	a_count = 0;
+}
+
+
+/* +-------------------------------------------------------------------+ */
+/* | Copyright 1990, 1991, 1993, David Koblas.  (koblas@netcom.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.           | */
+/* +-------------------------------------------------------------------+ */
+
+
+#define        MAXCOLORMAPSIZE         256
+
+#define        TRUE    1
+#define        FALSE   0
+
+#define CM_RED         0
+#define CM_GREEN       1
+#define CM_BLUE                2
+
+#define        MAX_LWZ_BITS            12
+
+#define INTERLACE              0x40
+#define LOCALCOLORMAP  0x80
+#define BitSet(byte, bit)      (((byte) & (bit)) == (bit))
+
+#define        ReadOK(file,buffer,len) (fread(buffer, len, 1, file) != 0)
+
+#define LM_to_uint(a,b)                        (((b)<<8)|(a))
+
+/* We may eventually want to use this information, but def it out for now */
+#if 0
+static struct {
+       unsigned int    Width;
+       unsigned int    Height;
+       unsigned char   ColorMap[3][MAXCOLORMAPSIZE];
+       unsigned int    BitPixel;
+       unsigned int    ColorResolution;
+       unsigned int    Background;
+       unsigned int    AspectRatio;
+} GifScreen;
+#endif
+
+static struct {
+       int     transparent;
+       int     delayTime;
+       int     inputFlag;
+       int     disposal;
+} Gif89 = { -1, -1, -1, 0 };
+
+static int ReadColorMap (FILE *fd, int number, unsigned char (*buffer)[256]);
+static int DoExtension (FILE *fd, int label, int *Transparent);
+static int GetDataBlock (FILE *fd, unsigned char *buf);
+static int GetCode (FILE *fd, int code_size, int flag);
+static int LWZReadByte (FILE *fd, int flag, int input_code_size);
+static void ReadImage (gdImagePtr im, FILE *fd, int len, int height, unsigned char (*cmap)[256], int interlace, int ignore);
+
+int ZeroDataBlock;
+
+gdImagePtr
+gdImageCreateFromGif(FILE *fd)
+{
+       int imageNumber;
+       int BitPixel;
+       int ColorResolution;
+       int Background;
+       int AspectRatio;
+       int Transparent = (-1);
+       unsigned char   buf[16];
+       unsigned char   c;
+       unsigned char   ColorMap[3][MAXCOLORMAPSIZE];
+       unsigned char   localColorMap[3][MAXCOLORMAPSIZE];
+       int             imw, imh;
+       int             useGlobalColormap;
+       int             bitPixel;
+       int             imageCount = 0;
+       char            version[4];
+       gdImagePtr im = 0;
+       ZeroDataBlock = FALSE;
+
+       imageNumber = 1;
+       if (! ReadOK(fd,buf,6)) {
+		return 0;
+	}
+       if (strncmp((char *)buf,"GIF",3) != 0) {
+		return 0;
+	}
+       strncpy(version, (char *)buf + 3, 3);
+       version[3] = '\0';
+
+       if ((strcmp(version, "87a") != 0) && (strcmp(version, "89a") != 0)) {
+		return 0;
+	}
+       if (! ReadOK(fd,buf,7)) {
+		return 0;
+	}
+       BitPixel        = 2<<(buf[4]&0x07);
+       ColorResolution = (int) (((buf[4]&0x70)>>3)+1);
+       Background      = buf[5];
+       AspectRatio     = buf[6];
+
+       if (BitSet(buf[4], LOCALCOLORMAP)) {    /* Global Colormap */
+               if (ReadColorMap(fd, BitPixel, ColorMap)) {
+			return 0;
+		}
+       }
+       for (;;) {
+               if (! ReadOK(fd,&c,1)) {
+                       return 0;
+               }
+               if (c == ';') {         /* GIF terminator */
+                       int i;
+                       if (imageCount < imageNumber) {
+                               return 0;
+                       }
+                       /* Terminator before any image was declared! */
+                       if (!im) {
+                              return 0;
+                       }
+		       /* Check for open colors at the end, so
+                          we can reduce colorsTotal and ultimately
+                          BitsPerPixel */
+                       for (i=((im->colorsTotal-1)); (i>=0); i--) {
+                               if (im->open[i]) {
+                                       im->colorsTotal--;
+                               } else {
+                                       break;
+                               }
+                       } 
+                       return im;
+               }
+
+               if (c == '!') {         /* Extension */
+                       if (! ReadOK(fd,&c,1)) {
+                               return 0;
+                       }
+                       DoExtension(fd, c, &Transparent);
+                       continue;
+               }
+
+               if (c != ',') {         /* Not a valid start character */
+                       continue;
+               }
+
+               ++imageCount;
+
+               if (! ReadOK(fd,buf,9)) {
+	               return 0;
+               }
+
+               useGlobalColormap = ! BitSet(buf[8], LOCALCOLORMAP);
+
+               bitPixel = 1<<((buf[8]&0x07)+1);
+
+               imw = LM_to_uint(buf[4],buf[5]);
+               imh = LM_to_uint(buf[6],buf[7]);
+	       if (!(im = gdImageCreate(imw, imh))) {
+			 return 0;
+	       }
+               im->interlace = BitSet(buf[8], INTERLACE);
+               if (! useGlobalColormap) {
+                       if (ReadColorMap(fd, bitPixel, localColorMap)) { 
+                                 return 0;
+                       }
+                       ReadImage(im, fd, imw, imh, localColorMap, 
+                                 BitSet(buf[8], INTERLACE), 
+                                 imageCount != imageNumber);
+               } else {
+                       ReadImage(im, fd, imw, imh,
+                                 ColorMap, 
+                                 BitSet(buf[8], INTERLACE), 
+                                 imageCount != imageNumber);
+               }
+               if (Transparent != (-1)) {
+                       gdImageColorTransparent(im, Transparent);
+               }	   
+       }
+}
+
+static int
+ReadColorMap(FILE *fd, int number, unsigned char (*buffer)[256])
+{
+       int             i;
+       unsigned char   rgb[3];
+
+
+       for (i = 0; i < number; ++i) {
+               if (! ReadOK(fd, rgb, sizeof(rgb))) {
+                       return TRUE;
+               }
+               buffer[CM_RED][i] = rgb[0] ;
+               buffer[CM_GREEN][i] = rgb[1] ;
+               buffer[CM_BLUE][i] = rgb[2] ;
+       }
+
+
+       return FALSE;
+}
+
+static int
+DoExtension(FILE *fd, int label, int *Transparent)
+{
+       static unsigned char     buf[256];
+
+       switch (label) {
+       case 0xf9:              /* Graphic Control Extension */
+               (void) GetDataBlock(fd, (unsigned char*) buf);
+               Gif89.disposal    = (buf[0] >> 2) & 0x7;
+               Gif89.inputFlag   = (buf[0] >> 1) & 0x1;
+               Gif89.delayTime   = LM_to_uint(buf[1],buf[2]);
+               if ((buf[0] & 0x1) != 0)
+                       *Transparent = buf[3];
+
+               while (GetDataBlock(fd, (unsigned char*) buf) != 0)
+                       ;
+               return FALSE;
+       default:
+               break;
+       }
+       while (GetDataBlock(fd, (unsigned char*) buf) != 0)
+               ;
+
+       return FALSE;
+}
+
+static int
+GetDataBlock_(FILE *fd, unsigned char *buf)
+{
+       unsigned char   count;
+
+       if (! ReadOK(fd,&count,1)) {
+               return -1;
+       }
+
+       ZeroDataBlock = count == 0;
+
+       if ((count != 0) && (! ReadOK(fd, buf, count))) {
+               return -1;
+       }
+
+       return count;
+}
+
+static int
+GetDataBlock(FILE *fd, unsigned char *buf)
+{
+ int rv;
+ int i;
+
+ rv = GetDataBlock_(fd,buf);
+ if (VERBOSE)
+  { printf("[GetDataBlock returning %d",rv);
+    if (rv > 0)
+     { printf(":");
+       for (i=0;i<rv;i++) printf(" %02x",buf[i]);
+     }
+    printf("]\n");
+  }
+ return(rv);
+}
+
+static int
+GetCode_(FILE *fd, int code_size, int flag)
+{
+       static unsigned char    buf[280];
+       static int              curbit, lastbit, done, last_byte;
+       int                     i, j, ret;
+       unsigned char           count;
+
+       if (flag) {
+               curbit = 0;
+               lastbit = 0;
+               done = FALSE;
+               return 0;
+       }
+
+       if ( (curbit+code_size) >= lastbit) {
+               if (done) {
+                       if (curbit >= lastbit) {
+                                /* Oh well */
+                       }                        
+                       return -1;
+               }
+               buf[0] = buf[last_byte-2];
+               buf[1] = buf[last_byte-1];
+
+               if ((count = GetDataBlock(fd, &buf[2])) == 0)
+                       done = TRUE;
+
+               last_byte = 2 + count;
+               curbit = (curbit - lastbit) + 16;
+               lastbit = (2+count)*8 ;
+       }
+
+       ret = 0;
+       for (i = curbit, j = 0; j < code_size; ++i, ++j)
+               ret |= ((buf[ i / 8 ] & (1 << (i % 8))) != 0) << j;
+
+       curbit += code_size;
+       return ret;
+}
+
+static int
+GetCode(FILE *fd, int code_size, int flag)
+{
+ int rv;
+
+ rv = GetCode_(fd,code_size,flag);
+ if (VERBOSE) printf("[GetCode(,%d,%d) returning %d]\n",code_size,flag,rv);
+ return(rv);
+}
+
+static int
+LWZReadByte_(FILE *fd, int flag, int input_code_size)
+{
+       static int      fresh = FALSE;
+       int             code, incode;
+       static int      code_size, set_code_size;
+       static int      max_code, max_code_size;
+       static int      firstcode, oldcode;
+       static int      clear_code, end_code;
+       static int      table[2][(1<< MAX_LWZ_BITS)];
+       static int      stack[(1<<(MAX_LWZ_BITS))*2], *sp;
+       register int    i;
+
+       if (flag) {
+               set_code_size = input_code_size;
+               code_size = set_code_size+1;
+               clear_code = 1 << set_code_size ;
+               end_code = clear_code + 1;
+               max_code_size = 2*clear_code;
+               max_code = clear_code+2;
+
+               GetCode(fd, 0, TRUE);
+               
+               fresh = TRUE;
+
+               for (i = 0; i < clear_code; ++i) {
+                       table[0][i] = 0;
+                       table[1][i] = i;
+               }
+               for (; i < (1<<MAX_LWZ_BITS); ++i)
+                       table[0][i] = table[1][0] = 0;
+
+               sp = stack;
+
+               return 0;
+       } else if (fresh) {
+               fresh = FALSE;
+               do {
+                       firstcode = oldcode =
+                               GetCode(fd, code_size, FALSE);
+               } while (firstcode == clear_code);
+               return firstcode;
+       }
+
+       if (sp > stack)
+               return *--sp;
+
+       while ((code = GetCode(fd, code_size, FALSE)) >= 0) {
+               if (code == clear_code) {
+                       for (i = 0; i < clear_code; ++i) {
+                               table[0][i] = 0;
+                               table[1][i] = i;
+                       }
+                       for (; i < (1<<MAX_LWZ_BITS); ++i)
+                               table[0][i] = table[1][i] = 0;
+                       code_size = set_code_size+1;
+                       max_code_size = 2*clear_code;
+                       max_code = clear_code+2;
+                       sp = stack;
+                       firstcode = oldcode =
+                                       GetCode(fd, code_size, FALSE);
+                       return firstcode;
+               } else if (code == end_code) {
+                       int             count;
+                       unsigned char   buf[260];
+
+                       if (ZeroDataBlock)
+                               return -2;
+
+                       while ((count = GetDataBlock(fd, buf)) > 0)
+                               ;
+
+                       if (count != 0)
+                       return -2;
+               }
+
+               incode = code;
+
+               if (code >= max_code) {
+                       *sp++ = firstcode;
+                       code = oldcode;
+               }
+
+               while (code >= clear_code) {
+                       *sp++ = table[1][code];
+                       if (code == table[0][code]) {
+                               /* Oh well */
+                       }
+                       code = table[0][code];
+               }
+
+               *sp++ = firstcode = table[1][code];
+
+               if ((code = max_code) <(1<<MAX_LWZ_BITS)) {
+                       table[0][code] = oldcode;
+                       table[1][code] = firstcode;
+                       ++max_code;
+                       if ((max_code >= max_code_size) &&
+                               (max_code_size < (1<<MAX_LWZ_BITS))) {
+                               max_code_size *= 2;
+                               ++code_size;
+                       }
+               }
+
+               oldcode = incode;
+
+               if (sp > stack)
+                       return *--sp;
+       }
+       return code;
+}
+
+static int
+LWZReadByte(FILE *fd, int flag, int input_code_size)
+{
+ int rv;
+
+ rv = LWZReadByte_(fd,flag,input_code_size);
+ if (VERBOSE) printf("[LWZReadByte(,%d,%d) returning %d]\n",flag,input_code_size,rv);
+ return(rv);
+}
+
+static void
+ReadImage(gdImagePtr im, FILE *fd, int len, int height, unsigned char (*cmap)[256], int interlace, int ignore)
+{
+       unsigned char   c;      
+       int             v;
+       int             xpos = 0, ypos = 0, pass = 0;
+       int i;
+       /* Stash the color map into the image */
+       for (i=0; (i<gdMaxColors); i++) {
+               im->red[i] = cmap[CM_RED][i];	
+               im->green[i] = cmap[CM_GREEN][i];	
+               im->blue[i] = cmap[CM_BLUE][i];	
+               im->open[i] = 1;
+       }
+       /* Many (perhaps most) of these colors will remain marked open. */
+       im->colorsTotal = gdMaxColors;
+       /*
+       **  Initialize the Compression routines
+       */
+       if (! ReadOK(fd,&c,1)) {
+               return; 
+       }
+       if (LWZReadByte(fd, TRUE, c) < 0) {
+               return;
+       }
+
+       /*
+       **  If this is an "uninteresting picture" ignore it.
+       */
+       if (ignore) {
+               while (LWZReadByte(fd, FALSE, c) >= 0)
+                       ;
+               return;
+       }
+
+       while ((v = LWZReadByte(fd,FALSE,c)) >= 0 ) {
+               /* This how we recognize which colors are actually used. */
+               if (im->open[v]) {
+                       im->open[v] = 0;
+               }
+               gdImageSetPixel(im, xpos, ypos, v);
+               ++xpos;
+               if (xpos == len) {
+                       xpos = 0;
+                       if (interlace) {
+                               switch (pass) {
+                               case 0:
+                               case 1:
+                                       ypos += 8; break;
+                               case 2:
+                                       ypos += 4; break;
+                               case 3:
+                                       ypos += 2; break;
+                               }
+
+                               if (ypos >= height) {
+                                       ++pass;
+                                       switch (pass) {
+                                       case 1:
+                                               ypos = 4; break;
+                                       case 2:
+                                               ypos = 2; break;
+                                       case 3:
+                                               ypos = 1; break;
+                                       default:
+                                               goto fini;
+                                       }
+                               }
+                       } else {
+                               ++ypos;
+                       }
+               }
+               if (ypos >= height)
+                       break;
+       }
+
+fini:
+       if (LWZReadByte(fd,FALSE,c)>=0) {
+               /* Ignore extra */
+       }
+}
+
+void gdImageRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
+{
+	gdImageLine(im, x1, y1, x2, y1, color);		
+	gdImageLine(im, x1, y2, x2, y2, color);		
+	gdImageLine(im, x1, y1, x1, y2, color);
+	gdImageLine(im, x2, y1, x2, y2, color);
+}
+
+void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
+{
+	int x, y;
+	for (y=y1; (y<=y2); y++) {
+		for (x=x1; (x<=x2); x++) {
+			gdImageSetPixel(im, x, y, color);
+		}
+	}
+}
+
+void gdImageCopy(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
+{
+	int c;
+	int x, y;
+	int tox, toy;
+	int i;
+	int colorMap[gdMaxColors];
+	for (i=0; (i<gdMaxColors); i++) {
+		colorMap[i] = (-1);
+	}
+	toy = dstY;
+	for (y=srcY; (y < (srcY + h)); y++) {
+		tox = dstX;
+		for (x=srcX; (x < (srcX + w)); x++) {
+			int nc;
+			c = gdImageGetPixel(src, x, y);
+			/* Added 7/24/95: support transparent copies */
+			if (gdImageGetTransparent(src) == c) {
+				tox++;
+				continue;
+			}
+			/* Have we established a mapping for this color? */
+			if (colorMap[c] == (-1)) {
+				/* If it's the same image, mapping is trivial */
+				if (dst == src) {
+					nc = c;
+				} else { 
+					/* First look for an exact match */
+					nc = gdImageColorExact(dst,
+						src->red[c], src->green[c],
+						src->blue[c]);
+				}	
+				if (nc == (-1)) {
+					/* No, so try to allocate it */
+					nc = gdImageColorAllocate(dst,
+						src->red[c], src->green[c],
+						src->blue[c]);
+					/* If we're out of colors, go for the
+						closest color */
+					if (nc == (-1)) {
+						nc = gdImageColorClosest(dst,
+							src->red[c], src->green[c],
+							src->blue[c]);
+					}
+				}
+				colorMap[c] = nc;
+			}
+			gdImageSetPixel(dst, tox, toy, colorMap[c]);
+			tox++;
+		}
+		toy++;
+	}
+}			
+
+void gdImageCopyResized(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
+{
+	int c;
+	int x, y;
+	int tox, toy;
+	int ydest;
+	int i;
+	int colorMap[gdMaxColors];
+	/* Stretch vectors */
+	int *stx;
+	int *sty;
+	/* We only need to use floating point to determine the correct
+		stretch vector for one line's worth. */
+	double accum;
+	stx = (int *) malloc(sizeof(int) * srcW);
+	sty = (int *) malloc(sizeof(int) * srcH);
+	accum = 0;
+	for (i=0; (i < srcW); i++) {
+		int got;
+		accum += (double)dstW/(double)srcW;
+		got = floor(accum);
+		stx[i] = got;
+		accum -= got;
+	}
+	accum = 0;
+	for (i=0; (i < srcH); i++) {
+		int got;
+		accum += (double)dstH/(double)srcH;
+		got = floor(accum);
+		sty[i] = got;
+		accum -= got;
+	}
+	for (i=0; (i<gdMaxColors); i++) {
+		colorMap[i] = (-1);
+	}
+	toy = dstY;
+	for (y=srcY; (y < (srcY + srcH)); y++) {
+		for (ydest=0; (ydest < sty[y-srcY]); ydest++) {
+			tox = dstX;
+			for (x=srcX; (x < (srcX + srcW)); x++) {
+				int nc;
+				if (!stx[x - srcX]) {
+					continue;
+				}
+				c = gdImageGetPixel(src, x, y);
+				/* Added 7/24/95: support transparent copies */
+				if (gdImageGetTransparent(src) == c) {
+					tox += stx[x-srcX];
+					continue;
+				}
+				/* Have we established a mapping for this color? */
+				if (colorMap[c] == (-1)) {
+					/* If it's the same image, mapping is trivial */
+					if (dst == src) {
+						nc = c;
+					} else { 
+						/* First look for an exact match */
+						nc = gdImageColorExact(dst,
+							src->red[c], src->green[c],
+							src->blue[c]);
+					}	
+					if (nc == (-1)) {
+						/* No, so try to allocate it */
+						nc = gdImageColorAllocate(dst,
+							src->red[c], src->green[c],
+							src->blue[c]);
+						/* If we're out of colors, go for the
+							closest color */
+						if (nc == (-1)) {
+							nc = gdImageColorClosest(dst,
+								src->red[c], src->green[c],
+								src->blue[c]);
+						}
+					}
+					colorMap[c] = nc;
+				}
+				for (i=0; (i < stx[x - srcX]); i++) {
+					gdImageSetPixel(dst, tox, toy, colorMap[c]);
+					tox++;
+				}
+			}
+			toy++;
+		}
+	}
+	free(stx);
+	free(sty);
+}
+
+int gdGetWord(int *result, FILE *in)
+{
+	int r;
+	r = getc(in);
+	if (r == EOF) {
+		return 0;
+	}
+	*result = r << 8;
+	r = getc(in);	
+	if (r == EOF) {
+		return 0;
+	}
+	*result += r;
+	return 1;
+}
+
+void gdPutWord(int w, FILE *out)
+{
+	putc((unsigned char)(w >> 8), out);
+	putc((unsigned char)(w & 0xFF), out);
+}
+
+int gdGetByte(int *result, FILE *in)
+{
+	int r;
+	r = getc(in);
+	if (r == EOF) {
+		return 0;
+	}
+	*result = r;
+	return 1;
+}
+
+gdImagePtr gdImageCreateFromGd(FILE *in)
+{
+	int sx, sy;
+	int x, y;
+	int i;
+	gdImagePtr im;
+	if (!gdGetWord(&sx, in)) {
+		goto fail1;
+	}
+	if (!gdGetWord(&sy, in)) {
+		goto fail1;
+	}
+	im = gdImageCreate(sx, sy);
+	if (!gdGetByte(&im->colorsTotal, in)) {
+		goto fail2;
+	}
+	if (!gdGetWord(&im->transparent, in)) {
+		goto fail2;
+	}
+	if (im->transparent == 257) {
+		im->transparent = (-1);
+	}
+	for (i=0; (i<gdMaxColors); i++) {
+		if (!gdGetByte(&im->red[i], in)) {
+			goto fail2;
+		}
+		if (!gdGetByte(&im->green[i], in)) {
+			goto fail2;
+		}
+		if (!gdGetByte(&im->blue[i], in)) {
+			goto fail2;
+		}
+	}	
+	for (y=0; (y<sy); y++) {
+		for (x=0; (x<sx); x++) {	
+			int ch;
+			ch = getc(in);
+			if (ch == EOF) {
+				gdImageDestroy(im);
+				return 0;
+			}
+			/* ROW-MAJOR IN GD 1.3 */
+			im->pixels[y][x] = ch;
+		}
+	}
+	return im;
+fail2:
+	gdImageDestroy(im);
+fail1:
+	return 0;
+}
+	
+void gdImageGd(gdImagePtr im, FILE *out)
+{
+	int x, y;
+	int i;
+	int trans;
+	gdPutWord(im->sx, out);
+	gdPutWord(im->sy, out);
+	putc((unsigned char)im->colorsTotal, out);
+	trans = im->transparent;
+	if (trans == (-1)) {
+		trans = 257;
+	}	
+	gdPutWord(trans, out);
+	for (i=0; (i<gdMaxColors); i++) {
+		putc((unsigned char)im->red[i], out);
+		putc((unsigned char)im->green[i], out);	
+		putc((unsigned char)im->blue[i], out);	
+	}
+	for (y=0; (y < im->sy); y++) {	
+		for (x=0; (x < im->sx); x++) {	
+			/* ROW-MAJOR IN GD 1.3 */
+			putc((unsigned char)im->pixels[y][x], out);
+		}
+	}
+}
+
+gdImagePtr
+gdImageCreateFromXbm(FILE *fd)
+{
+	gdImagePtr im;	
+	int bit;
+	int w, h;
+	int bytes;
+	int ch;
+	int i, x, y;
+	char *sp;
+	char s[161];
+	if (!fgets(s, 160, fd)) {
+		return 0;
+	}
+	sp = &s[0];
+	/* Skip #define */
+	sp = strchr(sp, ' ');
+	if (!sp) {
+		return 0;
+	}
+	/* Skip width label */
+	sp++;
+	sp = strchr(sp, ' ');
+	if (!sp) {
+		return 0;
+	}
+	/* Get width */
+	w = atoi(sp + 1);
+	if (!w) {
+		return 0;
+	}
+	if (!fgets(s, 160, fd)) {
+		return 0;
+	}
+	sp = s;
+	/* Skip #define */
+	sp = strchr(sp, ' ');
+	if (!sp) {
+		return 0;
+	}
+	/* Skip height label */
+	sp++;
+	sp = strchr(sp, ' ');
+	if (!sp) {
+		return 0;
+	}
+	/* Get height */
+	h = atoi(sp + 1);
+	if (!h) {
+		return 0;
+	}
+	/* Skip declaration line */
+	if (!fgets(s, 160, fd)) {
+		return 0;
+	}
+	bytes = (w * h / 8) + 1;
+	im = gdImageCreate(w, h);
+	gdImageColorAllocate(im, 255, 255, 255);
+	gdImageColorAllocate(im, 0, 0, 0);
+	x = 0;
+	y = 0;
+	for (i=0; (i < bytes); i++) {
+		char h[3];
+		int b;
+		/* Skip spaces, commas, CRs, 0x */
+		while(1) {
+			ch = getc(fd);
+			if (ch == EOF) {
+				goto fail;
+			}
+			if (ch == 'x') {
+				break;
+			}	
+		}
+		/* Get hex value */
+		ch = getc(fd);
+		if (ch == EOF) {
+			goto fail;
+		}
+		h[0] = ch;
+		ch = getc(fd);
+		if (ch == EOF) {
+			goto fail;
+		}
+		h[1] = ch;
+		h[2] = '\0';
+		sscanf(h, "%x", &b);		
+		for (bit = 1; (bit <= 128); (bit = bit << 1)) {
+			gdImageSetPixel(im, x++, y, (b & bit) ? 1 : 0);	
+			if (x == im->sx) {
+				x = 0;
+				y++;
+				if (y == im->sy) {
+					return im;
+				}
+				/* Fix 8/8/95 */
+				break;
+			}
+		}
+	}
+	/* Shouldn't happen */
+	fprintf(stderr, "Error: bug in gdImageCreateFromXbm!\n");
+	return 0;
+fail:
+	gdImageDestroy(im);
+	return 0;
+}
+
+void gdImagePolygon(gdImagePtr im, gdPointPtr p, int n, int c)
+{
+	int i;
+	int lx, ly;
+	if (!n) {
+		return;
+	}
+	lx = p->x;
+	ly = p->y;
+	gdImageLine(im, lx, ly, p[n-1].x, p[n-1].y, c);
+	for (i=1; (i < n); i++) {
+		p++;
+		gdImageLine(im, lx, ly, p->x, p->y, c);
+		lx = p->x;
+		ly = p->y;
+	}
+}	
+	
+int gdCompareInt(const void *a, const void *b);
+	
+void gdImageFilledPolygon(gdImagePtr im, gdPointPtr p, int n, int c)
+{
+	int i;
+	int y;
+	int y1, y2;