Commits

Anonymous committed 0b6804e

- sync to 1.5.0

Comments (0)

Files changed (26)

+990628 Thomas Boutell (TBB)
+
+Removed all C++ style comments for broader compatibility.
+
+990310 Philip Warner (pjw)
+--------------------------
+
+Broke gd.c into multiple files; gd.c now has graphics operations, and gd_<zzz> has specific
+support for different formats (eg. GD, GD2, GIF).
+
+Restructured I/O so that reading from files/streams/anything is now possible. All handled
+through the IOCtx record which must define some basic I/O services. All services are defined
+for files, but only some are defined for in-memory pointers. The in-memory pointer 
+implementation is primarily for Perl compatibility. Changed basically involved writing some 
+low level IO routines, and relpacing most FILE* variables with IOCtx*.
+
+Added gdImageGdPtr, gdImageGd2Ptr, gdImageGifPtr, gdImageLzwPtr for Perl compatibility.
+
+Broke up the GIF handling routines; gd_gif_in.c does all GIF input (this did not change when
+support for LZW compression was removed). gd_gif_out.c not handles the miGif compression that
+was introduced in version 1.3
+
+Reinstated LZW support through the gdImageLzw function (defined in gd_lzw_out.c). 
+This is NOT built by default, and the header file must be modified to include it in the build.
+
+Added 'GD2' support. This is a zlib-compressed internal format useful for extracting portions
+of images without having to read entire image files. Informal tests suggest it is only 30% slower
+than the old GD format, and file space requirements are similar to GOF. The GD2 header fields 
+now contain version information, so the format can probably be considered stable across versions.
+
+Various other functions also added (see index.html).
+
+Informal Tests
+--------------
+
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t064.gd2 100 3000 2000 400 300
+Extracting 100 times from (3000, 2000), size is 400x300
+23 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t128.gd2 100 3000 2000 400 300
+Extracting 100 times from (3000, 2000), size is 400x300
+19 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t256.gd2 100 3000 2000 400 300
+Extracting 100 times from (3000, 2000), size is 400x300
+33 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t512.gd2 100 3000 2000 400 300
+Extracting 100 times from (3000, 2000), size is 400x300
+65 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t064.gd2 100 3000 2000 200 100
+Extracting 100 times from (3000, 2000), size is 200x100
+8 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t128.gd2 100 3000 2000 200 100
+Extracting 100 times from (3000, 2000), size is 200x100
+8 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t256.gd2 100 3000 2000 200 100
+Extracting 100 times from (3000, 2000), size is 200x100
+16 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t512.gd2 100 3000 2000 200 100
+Extracting 100 times from (3000, 2000), size is 200x100
+59 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t064.gd2 100 3000 2000 800 600
+Extracting 100 times from (3000, 2000), size is 800x600
+71 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t128.gd2 100 3000 2000 800 600
+Extracting 100 times from (3000, 2000), size is 800x600
+70 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t256.gd2 100 3000 2000 800 600
+Extracting 100 times from (3000, 2000), size is 800x600
+86 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t512.gd2 100 3000 2000 800 600
+Extracting 100 times from (3000, 2000), size is 800x600
+160 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ls -lrt t*.gd2
+-rw-r--r--   1 pjw      users     1363301 Mar 10 12:03 t512.gd2
+-rw-r--r--   1 pjw      users     1339003 Mar 10 12:06 t256.gd2
+-rw-r--r--   1 pjw      users     1323925 Mar 10 12:09 t128.gd2
+-rw-r--r--   1 pjw      users     1492096 Mar 10 12:12 t064.gd2
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t064.gd2 100 3000 2000 50 25
+Extracting 100 times from (3000, 2000), size is 50x25
+4 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t128.gd2 100 3000 2000 50 25
+Extracting 100 times from (3000, 2000), size is 50x25
+2 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t256.gd2 100 3000 2000 50 25
+Extracting 100 times from (3000, 2000), size is 50x25
+4 seconds to extract (& destroy) 100 times
+pjw@Acheron:/home/pjw/work/gd1.3pjw > ./gd2time t512.gd2 100 3000 2000 50 25
+Extracting 100 times from (3000, 2000), size is 50x25
+15 seconds to extract (& destroy) 100 times
+
 
 CC=gcc 
 AR=ar
-CFLAGS=-O
-LIBS=-L./ -lgd -lm
+#CFLAGS= -ggdb
+CFLAGS= -O 
+LIBS=-L. -lgd -lz -lm
 
-all: libgd.a gddemo giftogd webgif
+all: libgd.a gdtest gddemo giftolzw giftogd giftogd2 gdtogif gd2togif gd2tolzw gd2copypal gdparttogif gd2time webgif
+
+gdtest:	gdtest.o libgd.a gd.h
+	$(CC) gdtest.o -o gdtest        $(LIBS)
 
 gddemo: gddemo.o libgd.a gd.h gdfonts.h gdfontl.h
 	$(CC) gddemo.o -o gddemo	$(LIBS)
 giftogd: giftogd.o libgd.a gd.h
 	$(CC) giftogd.o -o giftogd	$(LIBS) 
 
-libgd.a: gd.o gdfontt.o gdfonts.o gdfontmb.o gdfontl.o gdfontg.o \
+giftogd2: giftogd2.o libgd.a gd.h
+	$(CC) giftogd2.o -o giftogd2      $(LIBS)
+
+gdtogif: gdtogif.o libgd.a gd.h
+	$(CC) gdtogif.o -o gdtogif      $(LIBS)
+
+giftolzw: giftolzw.o libgd.a gd.h
+	$(CC) giftolzw.o -o giftolzw      $(LIBS)
+
+gd2togif: gd2togif.o libgd.a gd.h
+	$(CC) gd2togif.o -o gd2togif      $(LIBS)
+
+gd2tolzw: gd2tolzw.o libgd.a gd.h
+	$(CC) gd2tolzw.o -o gd2tolzw      $(LIBS)
+
+gd2copypal: gd2copypal.o libgd.a gd.h
+	$(CC) gd2copypal.o -o gd2copypal      $(LIBS)
+
+gdparttogif: gdparttogif.o libgd.a gd.h
+	$(CC) gdparttogif.o -o gdparttogif      $(LIBS)
+
+gd2time: gd2time.o libgd.a gd.h
+	$(CC) gd2time.o -o gd2time      $(LIBS)
+
+libgd.a: gd.o io.o io_file.o io_dp.o io_ss.o gd_gif_in.o gd_gif_out.o gd_lzw_out.o gd_gd.o gd_gd2.o gdfontt.o gdfonts.o gdfontmb.o gdfontl.o gdfontg.o \
 	gd.h gdfontt.h gdfonts.h gdfontmb.h gdfontl.h gdfontg.h
 	rm -f libgd.a
-	$(AR) rc libgd.a gd.o gdfontt.o gdfonts.o gdfontmb.o \
+	$(AR) rc libgd.a gd.o io.o io_file.o io_dp.o gd_gif_in.o gd_gif_out.o gd_lzw_out.o gd_gd.o gd_gd2.o gdfontt.o gdfonts.o gdfontmb.o \
 		gdfontl.o gdfontg.o
 
 webgif: webgif.o libgd.a gd.h
 	$(CC) webgif.o -o webgif	$(LIBS)
 
 clean:
-	rm -f *.o *.a gddemo giftogd 
+	rm -f *.o *.a gddemo gdtest giftogd giftogd2 giftolzw gdtogif gd2togif gd2tolzw gdparttogif gd2time gd2copypal webgif 
 
 char ${gdname}Data[] = {
 EOF
 
-my($i);
+my ($i);
 for $i (0 .. 255)
 	{
 	$data[$i] = '' unless defined $data[$i];
 	$data[$i] = '0' x ($width * $height - length $data[$i]) . $data[$i];
 	
 	print FILEC "/* Char $i */\n";
-	my($line);
+	my ($line);
 	for $line (0 .. $height - 1)
 		{ print FILEC join ',', split(//, substr($data[$i], $line * $width, $width)), "\n"; }
 
 	print FILEC "\n";
 
 	next;
-	my($line);	
-	for $line (0 .. $height - 1)
+	
+	for my $line (0 .. $height - 1)
 		{ print substr($data[$i], $line * $width, $width), "\n"; }
 	}
 
-#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 (gdSinkPtr 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, gdSinkPtr fp);
-static void compress (int, gdSinkPtr, 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);
-
-static int stdioSink(void *context, char *buffer, int len)
-{
-	return fwrite(buffer, 1, len, (FILE *) context);
-}
-
-void gdImageGif(gdImagePtr im, FILE *out)
-{
-	gdSink mySink;
-	mySink.context = (void *) out;
-	mySink.sink = stdioSink;
-	gdImageGifToSink(im, &mySink);
-}
-
-void gdImageGifToSink(gdImagePtr im, gdSinkPtr 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;
-}
-
-static void sinkPutC(int B, gdSinkPtr fp)
-{
-	unsigned char b = B;
-	fp->sink(fp->context, (char *) &b, 1);
-}
-
-/* public */
-
-static void
-GIFEncode(gdSinkPtr 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
-         */
-        fp->sink(fp->context, Transparent < 0 ? "GIF87a" : "GIF89a", 6);
-
-        /*
-         * 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
-         */
-        sinkPutC( B, fp );
-
-        /*
-         * Write out the Background colour
-         */
-        sinkPutC( Background, fp );
-
-        /*
-         * Byte of 0's (future expansion)
-         */
-        sinkPutC( 0, fp );
-
-        /*
-         * Write out the Global Colour Map
-         */
-        for( i=0; i<ColorMapSize; ++i ) {
-                sinkPutC( Red[i], fp );
-                sinkPutC( Green[i], fp );
-                sinkPutC( Blue[i], fp );
-        }
-
-	/*
-	 * Write out extension for transparent colour index, if necessary.
-	 */
-	if ( Transparent >= 0 ) {
-	    sinkPutC( '!', fp );
-	    sinkPutC( 0xf9, fp );
-	    sinkPutC( 4, fp );
-	    sinkPutC( 1, fp );
-	    sinkPutC( 0, fp );
-	    sinkPutC( 0, fp );
-	    sinkPutC( (unsigned char) Transparent, fp );
-	    sinkPutC( 0, fp );
-	}
-
-        /*
-         * Write an Image separator
-         */
-        sinkPutC( ',', 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 )
-                sinkPutC( 0x40, fp );
-        else
-                sinkPutC( 0x00, fp );
-
-        /*
-         * Write out the initial code size
-         */
-        sinkPutC( InitCodeSize, fp );
-
-        /*
-         * Go and actually compress the data
-         */
-        compress( InitCodeSize+1, fp, im, Background );
-
-        /*
-         * Write out a Zero-length packet (to end the series)
-         */
-        sinkPutC( 0, fp );
-
-        /*
-         * Write the GIF file terminator
-         */
-        sinkPutC( ';', fp );
-}
-
-/*
- * Write out a word to the GIF file
- */
-static void
-Putword(int w, gdSinkPtr fp)
-{
-	unsigned char buf[2];
-	buf[0] = w & 0xff;
-	buf[1] = (w / 256) & 0xff;
-        fp->sink(fp->context, (char *) buf, 2);
-}
-
-#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 gdSinkPtr 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");
-  }
- sinkPutC(oblen,ofile);
- ofile->sink(ofile->context, &oblock[0], oblen);
- 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, gdSinkPtr 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(from, buffer, len) \
-	(from->source(from->context, buffer, len) > 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 (gdSourcePtr fd, int number, unsigned char (*buffer)[256]);
-static int DoExtension (gdSourcePtr fd, int label, int *Transparent);
-static int GetDataBlock (gdSourcePtr fd, unsigned char *buf);
-static int GetCode (gdSourcePtr fd, int code_size, int flag);
-static int LWZReadByte (gdSourcePtr fd, int flag, int input_code_size);
-static void ReadImage (gdImagePtr im, gdSourcePtr fd, int len, int height, unsigned char (*cmap)[256], int interlace);
-
-int ZeroDataBlock;
-
-static int freadWrapper(void *context, char *buf, int len);
-
-gdImagePtr
-gdImageCreateFromGif(FILE *in)
-{
-	gdSource s;
-	s.source = freadWrapper;
-	s.context = in;
-	return gdImageCreateFromGifSource(&s);
-}
-
-static int freadWrapper(void *context, char *buf, int len)
-{
-	int got = fread(buf, 1, len, (FILE *) context);
-	return got;
-}
-
-gdImagePtr
-gdImageCreateFromGifSource(gdSourcePtr fd)
-{
-       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 i;
-       char            version[4];
-       gdImagePtr im = 0;
-       ZeroDataBlock = FALSE;
-
-       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 */
-				   goto terminated;
-               }
-
-               if (c == '!') {         /* Extension */
-                       if (! ReadOK(fd,&c,1)) {
-                               return 0;
-                       }
-                       DoExtension(fd, c, &Transparent);
-                       continue;
-               }
-
-               if (c != ',') {         /* Not a valid start character */
-                       continue;
-               }
-
-               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));
-               } else {
-                       ReadImage(im, fd, imw, imh,
-                                 ColorMap,
-                             BitSet(buf[8], INTERLACE));
-               }
-               if (Transparent != (-1)) {
-                       gdImageColorTransparent(im, Transparent);
-               }
-				/* We have an image now. Let's get out of Dodge. */
-			   goto terminated;
-   }
-terminated:
-   /* 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;
-}
-
-static int
-ReadColorMap(gdSourcePtr 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(gdSourcePtr 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_(gdSourcePtr 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(gdSourcePtr 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_(gdSourcePtr 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(gdSourcePtr 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);
-}
-
-#define STACK_SIZE ((1<<(MAX_LWZ_BITS))*2)
-static int
-LWZReadByte_(gdSourcePtr 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[STACK_SIZE], *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 (sp == (stack + STACK_SIZE)) {
-					/* Bad compressed data stream */
-					return -1;
-			   }
-               if (code >= max_code) {
-                       *sp++ = firstcode;
-                       code = oldcode;
-               }
-
-               while (code >= clear_code) {
-					   if (sp == (stack + STACK_SIZE)) {
-							/* Bad compressed data stream */
-							return -1;
-					   }
-                       *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(gdSourcePtr 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, gdSourcePtr fd, int len, int height, unsigned char (*cmap)[256], int interlace)
-{
-       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;
-       }
-
-       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 = (int) floor(accum);
-		stx[i] = got;
-		accum -= got;
-	}
-	accum = 0;
-	for (i=0; (i < srcH); i++) {
-		int got;
-		accum += (double)dstH/(double)srcH;
-		got = (int) 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;
-				}