Commits

Anonymous committed fceb93d

- sync to 2.0.0

Comments (0)

Files changed (22)

src/alphachanneltest.html

+<head>
+<title>gd Alpha Channel Test</title>
+<head>
+<body bgcolor="#c0c0FF">
+<h1>gd Alpha Channel Test</h1>
+If your browser fully supports PNG alpha channels, the
+"blended" and "alpha channel" images should appear identical
+to the naked eye. Miniscule mathematical differences may
+exist due to the 7-bit alpha channel resolution of gd. 
+<p>
+Palette and truecolor images will look slightly different,
+as the palette versions are limited to 256 palette entries,
+which are used as well as possible to approach the effect
+of the original truecolor version.
+<table>
+<tr>
+<th colspan=2>Truecolor Images</th>
+<tr>
+<th>Blending against light blue, Half Size<td><img src="blending-halfsize-truecolor.png">
+<tr>
+<th>Alpha channel, Half Size<td><img src="noblending-halfsize-truecolor.png">
+<tr>
+<th>Blending against light blue, Full Size<td><img src="blending-fullsize-truecolor.png">
+<tr>
+<th>Alpha channel, Full Size<td><img src="noblending-fullsize-truecolor.png">
+<tr>
+<th>Blending against light blue, Double Size<td><img src="blending-doublesize-truecolor.png">
+<tr>
+<th>Alpha channel, Double Size<td><img src="noblending-doublesize-truecolor.png">
+<tr>
+<th colspan=2>Palette Images</th>
+<tr>
+<th>Blending against light blue, Half Size<td><img src="blending-halfsize-palette.png">
+<tr>
+<th>Alpha channel, Half Size<td><img src="noblending-halfsize-palette.png">
+<tr>
+<th>Blending against light blue, Full Size<td><img src="blending-fullsize-palette.png">
+<tr>
+<th>Alpha channel, Full Size<td><img src="noblending-fullsize-palette.png">
+<tr>
+<th>Blending against light blue, Double Size<td><img src="blending-doublesize-palette.png">
+<tr>
+<th>Alpha channel, Double Size<td><img src="noblending-doublesize-palette.png"></table>
+</body>
+
Added
New image
+gcc -I. -I/usr/include/freetype2 -I/usr/include/X11 -I/usr/X11R6/include/X11 -I/usr/local/include  -O -DHAVE_LIBXPM -DHAVE_LIBPNG -DHAVE_LIBJPEG -DHAVE_LIBFREETYPE   -c gd_gd2.c -o gd_gd2.o
 	int i;
 	gdImagePtr im;
 	im = (gdImage *) gdMalloc(sizeof(gdImage));
-	/* NOW ROW-MAJOR IN GD 1.3 */
+	memset(im, 0, sizeof(gdImage));
+	/* Row-major ever since gd 1.3 */
 	im->pixels = (unsigned char **) gdMalloc(sizeof(unsigned char *) * sy);
 	im->polyInts = 0;
 	im->polyAllocated = 0;
 	im->tile = 0;
 	im->style = 0;
 	for (i=0; (i<sy); i++) {
-		/* NOW ROW-MAJOR IN GD 1.3 */
+		/* Row-major ever since gd 1.3 */
 		im->pixels[i] = (unsigned char *) gdCalloc(
 			sx, sizeof(unsigned char));
 	}	
 	im->colorsTotal = 0;
 	im->transparent = (-1);
 	im->interlace = 0;
-
+	im->thick = 1;
         for (i=0; (i < gdMaxColors); i++) {
            im->open[i] = 1;
 	   im->red[i] = 0;
            im->green[i] = 0;
            im->blue[i] = 0;
 	};
+	im->trueColor = 0;
+	im->tpixels = 0;
+	return im;
+}
 
+gdImagePtr gdImageCreateTrueColor(int sx, int sy)
+{
+	int i;
+	gdImagePtr im;
+	im = (gdImage *) gdMalloc(sizeof(gdImage));
+	memset(im, 0, sizeof(gdImage));
+	im->tpixels = (int **) gdMalloc(sizeof(int *) * sy);
+	im->polyInts = 0;
+	im->polyAllocated = 0;
+	im->brush = 0;
+	im->tile = 0;
+	im->style = 0;
+	for (i=0; (i<sy); i++) {
+		im->tpixels[i] = (int *) gdCalloc(
+			sx, sizeof(int));
+	}	
+	im->sx = sx;
+	im->sy = sy;
+	im->transparent = (-1);
+	im->interlace = 0;
+	im->trueColor = 1;
+	im->saveAlphaFlag = 1;
+	im->alphaBlendingFlag = 0;
+	im->thick = 1;
 	return im;
 }
 
 void gdImageDestroy(gdImagePtr im)
 {
 	int i;
-	for (i=0; (i<im->sy); i++) {
-		gdFree(im->pixels[i]);
-	}	
-	gdFree(im->pixels);
+	if (im->pixels) {
+		for (i=0; (i<im->sy); i++) {
+			gdFree(im->pixels[i]);
+		}	
+		gdFree(im->pixels);
+	}
+	if (im->tpixels) {
+		for (i=0; (i<im->sy); i++) {
+			gdFree(im->tpixels[i]);
+		}	
+		gdFree(im->tpixels);
+	}
 	if (im->polyInts) {
 			gdFree(im->polyInts);
 	}
 
 int gdImageColorClosest(gdImagePtr im, int r, int g, int b)
 {
+	return gdImageColorClosestAlpha(im, r, g, b, gdAlphaOpaque);
+}
+
+int gdImageColorClosestAlpha(gdImagePtr im, int r, int g, int b, int a)
+{
 	int i;
-	long rd, gd, bd;
+	long rd, gd, bd, ad;
 	int ct = (-1);
 	int first = 1;
 	long mindist = 0;
+	if (im->trueColor) {
+		return gdTrueColorAlpha(r, g, b, a);
+	}
 	for (i=0; (i<(im->colorsTotal)); i++) {
 		long dist;
 		if (im->open[i]) {
 		rd = (im->red[i] - r);	
 		gd = (im->green[i] - g);
 		bd = (im->blue[i] - b);
-		dist = rd * rd + gd * gd + bd * bd;
+		ad = (im->blue[i] - b);
+		dist = rd * rd + gd * gd + bd * bd + ad * ad;
 		if (first || (dist < mindist)) {
 			mindist = dist;	
 			ct = i;
 	int ct = (-1);
 	int first = 1;
 	float mindist = 0;
+	if (im->trueColor) {
+		return gdTrueColor(r, g, b);
+	}
 	for (i=0; (i<(im->colorsTotal)); i++) {
 		float dist;
 		if (im->open[i]) {
 	return ct;
 }
 
-
-
 int gdImageColorExact(gdImagePtr im, int r, int g, int b)
 {
+	return gdImageColorExactAlpha(im, r, g, b, gdAlphaOpaque);
+}
+
+int gdImageColorExactAlpha(gdImagePtr im, int r, int g, int b, int a)
+{
 	int i;
+	if (im->trueColor) {
+		return gdTrueColorAlpha(r, g, b, a);
+	}
 	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)) {
+			(im->blue[i] == b) &&
+			(im->alpha[i] == a))
+		{
 			return i;
 		}
 	}
 
 int gdImageColorAllocate(gdImagePtr im, int r, int g, int b)
 {
+	return gdImageColorAllocateAlpha(im, r, g, b, gdAlphaOpaque);
+}
+
+int gdImageColorAllocateAlpha(gdImagePtr im, int r, int g, int b, int a)
+{
 	int i;
 	int ct = (-1);
+	if (im->trueColor) {
+		return gdTrueColorAlpha(r, g, b, a);
+	}
 	for (i=0; (i<(im->colorsTotal)); i++) {
 		if (im->open[i]) {
 			ct = i;
 	im->red[ct] = r;
 	im->green[ct] = g;
 	im->blue[ct] = b;
+	im->alpha[ct] = a;
 	im->open[ct] = 0;
 	return ct;
 }
  * in a single function.    Its advantage is that it is guaranteed to
  * return a color index in one search over the color table.
  */
+
 int gdImageColorResolve(gdImagePtr im, int r, int g, int b)
 {
+	return gdImageColorResolveAlpha(im, r, g, b, gdAlphaOpaque);
+}
+
+int gdImageColorResolveAlpha(gdImagePtr im, int r, int g, int b, int a)
+{
         int c;
         int ct = -1;
         int op = -1;
-        long rd, gd, bd, dist;
-        long mindist = 3*255*255;  /* init to max poss dist */
+        long rd, gd, bd, ad, dist;
+        long mindist = 4*255*255;  /* init to max poss dist */
+	if (im->trueColor) {
+		return gdTrueColorAlpha(r, g, b, a);
+	}
 
         for (c = 0; c < im->colorsTotal; c++) {
                 if (im->open[c]) {
                 rd = (long)(im->red  [c] - r);
                 gd = (long)(im->green[c] - g);
                 bd = (long)(im->blue [c] - b);
-                dist = rd * rd + gd * gd + bd * bd;
+                ad = (long)(im->alpha [c] - a);
+                dist = rd * rd + gd * gd + bd * bd + ad * ad;
                 if (dist < mindist) {
                         if (dist == 0) {
                                 return c;               /* Return exact match color */
         im->red  [op] = r;
         im->green[op] = g;
         im->blue [op] = b;
+        im->alpha [op] = a;
         im->open [op] = 0;
         return op;                                      /* Return newly allocated color */
 }
 
 void gdImageColorDeallocate(gdImagePtr im, int color)
 {
+	if (im->trueColor) {
+		return;
+	}
 	/* Mark it open. */
 	im->open[color] = 1;
 }
 
 void gdImageColorTransparent(gdImagePtr im, int color)
 {
+	if (!im->trueColor) {
+		if (im->transparent != -1) {
+			im->alpha[im->transparent] = gdAlphaOpaque;
+		}
+		if (color != -1) {
+			im->alpha[color] = gdAlphaTransparent;
+		} 
+	} 
 	im->transparent = color;
 }
 
 void gdImagePaletteCopy(gdImagePtr to, gdImagePtr from)
 {
-        int i;
+		int i;
 	int x, y, p;
 	int xlate[256];
+	if (to->trueColor) {
+		return;
+	}
+	if (from->trueColor) {
+		return;
+	}
 
 	for (i=0; i < 256 ; i++) {
 		xlate[i] = -1;
 		for (y=0 ; y < (to->sy) ; y++) {
 			p = gdImageGetPixel(to, x, y);
 			if (xlate[p] == -1) {
-				xlate[p] = gdImageColorClosestHWB(from, to->red[p], to->green[p], to->blue[p]);
-				/*printf("Mapping %d (%d, %d, %d) to %d (%d, %d, %d)\n", */
-				/*	p,  to->red[p], to->green[p], to->blue[p], */
-				/*	xlate[p], from->red[xlate[p]], from->green[xlate[p]], from->blue[xlate[p]]); */
+				/* This ought to use HWB, but we don't have an alpha-aware
+					version of that yet. */
+				xlate[p] = gdImageColorClosestAlpha(from, to->red[p], to->green[p], to->blue[p], to->alpha[p]);
+				/*printf("Mapping %d (%d, %d, %d, %d) to %d (%d, %d, %d, %d)\n", */
+				/*	p,  to->red[p], to->green[p], to->blue[p], to->alpha[p], */
+				/*	xlate[p], from->red[xlate[p]], from->green[xlate[p]], from->blue[xlate[p]], from->alpha[xlate[p]]); */
 			};
 			gdImageSetPixel(to, x, y, xlate[p]);
 		};
 	};
 
         for (i=0; (i < (from->colorsTotal) ) ; i++) {
-		/*printf("Copying color %d (%d, %d, %d)\n", i, from->red[i], from->blue[i], from->green[i]); */
+		/*printf("Copying color %d (%d, %d, %d, %d)\n", i, from->red[i], from->blue[i], from->green[i], from->alpha[i]); */
 		to->red[i] = from->red[i];
                 to->blue[i] = from->blue[i];
                 to->green[i] = from->green[i];
+                to->alpha[i] = from->alpha[i];
 		to->open[i] = 0;
         };
 
 		break;
 		default:
 		if (gdImageBoundsSafe(im, x, y)) {
-			/* NOW ROW-MAJOR IN GD 1.3 */
-			im->pixels[y][x] = color;
+			if (im->trueColor) {
+				if (im->alphaBlendingFlag) {
+					im->tpixels[y][x] = 
+						gdAlphaBlend(im->tpixels[y][x],
+							color);
+				} else {
+					im->tpixels[y][x] = color;
+				}
+			} else {
+				im->pixels[y][x] = color;
+			}
 		}
 		break;
 	}
 	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)) {
+	if (im->trueColor) {
+		for (ly = y1; (ly < y2); ly++) {
+			srcx = 0;
+			for (lx = x1; (lx < x2); lx++) {
+				int p;
+				p = gdImageGetTrueColorPixel(
+					im->brush, srcx, srcy);
 				gdImageSetPixel(im, lx, ly,
-					im->brushColorMap[p]);
+					p);
+				srcx++;
 			}
-			srcx++;
-		}
-		srcy++;
-	}	
+			srcy++;
+		}	
+	} else {
+		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)) {
+					/* Truecolor brush. Very slow
+						on a palette destination. */
+					if (im->brush->trueColor) {
+						gdImageSetPixel(im, lx, ly,
+							gdImageColorResolveAlpha(
+								im, 
+								gdTrueColorGetRed(p),
+								gdTrueColorGetGreen(p),
+								gdTrueColorGetBlue(p),
+								gdTrueColorGetAlpha(p)));
+					} else {
+						gdImageSetPixel(im, lx, ly,
+							im->brushColorMap[p]);
+					}
+				}
+				srcx++;
+			}
+			srcy++;
+		}	
+	}
 }		
 
 static void gdImageTileApply(gdImagePtr im, int x, int y)
 	}
 	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]);
+	if (im->trueColor) {
+		p = gdImageGetTrueColorPixel(im->tile, srcx, srcy);
+		gdImageSetPixel(im, x, y, p);
+	} else {
+		/* Allow for transparency */
+		if (p != gdImageGetTransparent(im->tile)) {
+			if (im->tile->trueColor) {
+				/* Truecolor tile. Very slow
+					on a palette destination. */
+				gdImageSetPixel(im, x, y,
+					gdImageColorResolveAlpha(
+						im, 
+						gdTrueColorGetRed(p),
+						gdTrueColorGetGreen(p),
+						gdTrueColorGetBlue(p),
+						gdTrueColorGetAlpha(p)));
+			} else {
+				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];
+		if (im->trueColor) {
+			return im->tpixels[y][x];
+		} else {
+			return im->pixels[y][x];
+		}
 	} else {
 		return 0;
 	}
 }
 
-/* Bresenham as presented in Foley & Van Dam */
+int gdImageGetTrueColorPixel(gdImagePtr im, int x, int y)
+{
+	int p = gdImageGetPixel(im, x, y);
+	if (!im->trueColor) {
+		return gdTrueColorAlpha(im->red[p], im->green[p], im->blue[p],
+			(im->transparent == p) ? gdAlphaTransparent :
+			gdAlphaOpaque);
+	} else {
+		return p;
+	}
+}
 
+/* 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;
+	int wid;
+	int w, wstart;
+	int thick = im->thick;
 	dx = abs(x2-x1);
 	dy = abs(y2-y1);
 	if (dy <= dx) {
+		/* More-or-less horizontal. use wid for vertical stroke */
+		wid = thick * cos ( atan2(dy, dx) );
+		if (wid==0) wid=1;
+
 		d = 2*dy - dx;
 		incr1 = 2*dy;
 		incr2 = 2 * (dy - dx);
 			ydirflag = 1;
 			xend = x2;
 		}
-		gdImageSetPixel(im, x, y, color);
+
+		/* Set up line thickness */
+		wstart = y - wid/2;
+		for ( w = wstart; w < wstart + wid; w++ )
+		  gdImageSetPixel(im, x, w, color);
+		  
 		if (((y2 - y1) * ydirflag) > 0) {
 			while (x < xend) {
 				x++;
 					y++;
 					d+=incr2;
 				}
-				gdImageSetPixel(im, x, y, color);
+				wstart = y - wid/2;
+				for ( w = wstart; w < wstart + wid; w++)
+				  gdImageSetPixel(im, x, w, color);
 			}
 		} else {
-			while (x < xend) {
+ 			while (x < xend) {
 				x++;
 				if (d <0) {
 					d+=incr1;
 					y--;
 					d+=incr2;
 				}
-				gdImageSetPixel(im, x, y, color);
+				wstart = y - wid / 2;
+				for ( w=wstart; w < wstart+wid; w++)
+				  gdImageSetPixel(im, x, w, color);
 			}
 		}		
 	} else {
+  		/* More-or-less vertical. use wid for horizontal stroke */
+		wid = thick * sin ( atan2(dy, dx) );
+		if (wid == 0 ) wid=1;
+
 		d = 2*dx - dy;
 		incr1 = 2*dx;
 		incr2 = 2 * (dx - dy);
 			yend = y2;
 			xdirflag = 1;
 		}
-		gdImageSetPixel(im, x, y, color);
+
+		/* Set up line thickness */
+		wstart = x - wid/2;
+		for ( w = wstart; w < wstart + wid; w++ )
+		  gdImageSetPixel(im, w, y, color);
+
 		if (((x2 - x1) * xdirflag) > 0) {
 			while (y < yend) {
 				y++;
 					x++;
 					d+=incr2;
 				}
-				gdImageSetPixel(im, x, y, color);
+				wstart = x - wid/2;
+				for ( w=wstart; w < wstart + wid; w++)
+				  gdImageSetPixel(im, w, y, color);
 			}
 		} else {
 			while (y < yend) {
 					x--;
 					d+=incr2;
 				}
-				gdImageSetPixel(im, x, y, color);
+				wstart = x - wid/2;
+				for ( w = wstart; w < wstart+wid; w++)
+				  gdImageSetPixel(im, w, y, color);
 			}
 		}
 	}
 }
-
 static void dashedSet(gdImagePtr im, int x, int y, int color,
-	int *onP, int *dashStepP);
+	int *onP, int *dashStepP, int wid, int vert);
 
 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;
+        int wid;
+        int w, wstart, vert;
+	int thick = im->thick;
+
 	dx = abs(x2-x1);
 	dy = abs(y2-y1);
 	if (dy <= dx) {
+		/* More-or-less horizontal. use wid for vertical stroke */
+		wid = thick * sin ( atan2(dy, dx) );
+		vert=1;
+
 		d = 2*dy - dx;
 		incr1 = 2*dy;
 		incr2 = 2 * (dy - dx);
 			ydirflag = 1;
 			xend = x2;
 		}
-		dashedSet(im, x, y, color, &on, &dashStep);
+		dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
 		if (((y2 - y1) * ydirflag) > 0) {
 			while (x < xend) {
 				x++;
 					y++;
 					d+=incr2;
 				}
-				dashedSet(im, x, y, color, &on, &dashStep);
+				dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
 			}
 		} else {
 			while (x < xend) {
 					y--;
 					d+=incr2;
 				}
-				dashedSet(im, x, y, color, &on, &dashStep);
+		                dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
 			}
 		}		
 	} else {
+		/* More-or-less vertical. use wid for horizontal stroke */
+		wid = thick * sin ( atan2(dy, dx) );
+		vert = 0;
+
 		d = 2*dx - dy;
 		incr1 = 2*dx;
 		incr2 = 2 * (dx - dy);
 			yend = y2;
 			xdirflag = 1;
 		}
-		dashedSet(im, x, y, color, &on, &dashStep);
+                dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
 		if (((x2 - x1) * xdirflag) > 0) {
 			while (y < yend) {
 				y++;
 					x++;
 					d+=incr2;
 				}
-				dashedSet(im, x, y, color, &on, &dashStep);
+                                dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
 			}
 		} else {
 			while (y < yend) {
 					x--;
 					d+=incr2;
 				}
-				dashedSet(im, x, y, color, &on, &dashStep);
+                                dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
 			}
 		}
 	}
 }
 
 static void dashedSet(gdImagePtr im, int x, int y, int color,
-	int *onP, int *dashStepP)
+	int *onP, int *dashStepP, int wid, int vert)
 {
 	int dashStep = *dashStepP;
 	int on = *onP;
+	int w, wstart;
+
 	dashStep++;
 	if (dashStep == gdDashSize) {
 		dashStep = 0;
 		on = !on;
 	}
 	if (on) {
-		gdImageSetPixel(im, x, y, color);
+          if (vert) { 
+	    wstart = y - wid / 2; 
+	    for (w=wstart; w < wstart+wid;w++) 
+	      gdImageSetPixel(im, x, w, color); 
+	  }
+	  else 
+	  { 
+	    wstart = x - wid / 2; 
+	    for (w=wstart; w < wstart+wid;w++) 
+	      gdImageSetPixel(im, w, y, color); 
+	  }
 	}
 	*dashStepP = dashStep;
 	*onP = on;
 }
 	
-
 int gdImageBoundsSafe(gdImagePtr im, int x, int y)
 {
 	return (!(((y < 0) || (y >= im->sy)) ||
 	return len;
 }
 
+#ifndef HAVE_LSQRT
+/* If you don't have a nice square root function for longs, you can use
+** this hack
+*/
+long lsqrt(long n)
+{
+  long result = (long)sqrt((double)n);
+  return result;
+}
+#endif
+
+void gdImageEllipse (gdImagePtr im, int cx, int cy, int w, int h, int color)
+{
+  long d, b_sq, b_sq_4, b_sq_6;
+  long a_sq, a_sq_4, a_sq_6;
+  int x, y, switchem;
+  long lsqrt(long);
+  int pix, half, pstart;
+  int thick = im->thick;
+
+  half = thick/2;
+  w /= 2; /* ImageArc uses diameter, not radius */
+  h /= 2;
+      
+  d = 2 * (long) h * h + (long) w * w - 2 * (long)w * w * h;
+  b_sq = (long)h * h;
+  b_sq_4 = 4 * (long) h * h;
+  b_sq_6 = 6 * (long) h * h;
+  a_sq = (long) w * w;
+  a_sq_4 = 4 * (long)w * w;
+  a_sq_6 = 6 * (long)w * w;
+
+  x = 0;
+  y = -h;
+  switchem = a_sq / lsqrt( a_sq + b_sq );
+
+  while ( x <= switchem )
+  {
+    pstart = y - half;
+    for (pix = pstart; pix < pstart + thick; pix++)
+    {
+      gdImageSetPixel(im, cx+x, cy+pix, color);
+      gdImageSetPixel(im, cx-x, cy+pix, color);
+      gdImageSetPixel(im, cx+x, cy-pix, color);
+      gdImageSetPixel(im, cx-x, cy-pix, color);
+    }
+    if ( d < 0 )
+      d += b_sq_4 * x++ + b_sq_6;
+    else
+      d += b_sq_4*x++ + b_sq_6 + a_sq_4*(++y);
+  }
+
+  /* Middlesplat!
+  ** Go a little further if the thickness is not nominal...
+  */
+  if ( thick > 1 )
+  {
+    int xp = x;
+    int yp = y;
+    int dp = d;
+    int thick2 = thick + 2;
+    int half2 = half + 1;
+
+    while ( xp <= switchem + half )
+    {
+      pstart = yp - half2;
+      for (pix = pstart; pix < pstart + thick2; pix++)
+      {
+        gdImageSetPixel(im, cx+xp, cy+pix, color);
+        gdImageSetPixel(im, cx-xp, cy+pix, color);
+        gdImageSetPixel(im, cx+xp, cy-pix, color);
+        gdImageSetPixel(im, cx-xp, cy-pix, color);
+      }
+      if ( dp < 0 )
+        dp += b_sq_4 * xp++ + b_sq_6;
+      else
+        dp += b_sq_4*xp++ + b_sq_6 + a_sq_4*(++yp);
+    }
+  }
+  
+  d += -2*(long)b_sq + 2*(long)a_sq - 2*(long)b_sq*(x-1) + 2*(long)a_sq*(y-1);
+
+  while ( y <= 0 )
+  {
+    pstart = x - half;
+    for (pix = pstart; pix < pstart + thick; pix++)
+    {
+      gdImageSetPixel(im, cx+pix, cy+y, color);
+      gdImageSetPixel(im, cx-pix, cy+y, color);
+      gdImageSetPixel(im, cx+pix, cy-y, color);
+      gdImageSetPixel(im, cx-pix, cy-y, color);
+    }
+    
+    if (d < 0)
+      d += a_sq_4*y++ + a_sq_6 + b_sq_4*(++x);
+    else
+      d += a_sq_4*y++ + a_sq_6;
+  }
+}
+
 /* 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 
   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)
+static int lineside ( int x, int y, int dx, int dy, int x0, int y0 )
 {
-	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)gdCosT[i % 360] * (long)w2 / 1024) + cx; 
-		y = ((long)gdSinT[i % 360] * (long)h2 / 1024) + cy;
-		if (i != s) {
-			gdImageLine(im, lx, ly, x, y, color);	
-		}
-		lx = x;
-		ly = y;
-	}
+  return ( y - y0) * dx - dy * ( x - x0 ) < 0;
 }
 
+void gdImageArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color)
+{
+	int i;
+	double w2, h2;
+	int xr0, xr1, yr0, yr1; /* starting and ending coordinates */
+	int dx, dy;             /* slope of the resulting line */
+	int end;
+        long d, b_sq, b_sq_4, b_sq_6;
+        long a_sq, a_sq_4, a_sq_6;
+        int x, y, switchem;
+        long lsqrt(long);
+        int pix, half, pstart;
+	int thick = im->thick;
+
+        half = thick/2;
+
+	w2 = w / 2.0;
+	h2 = h / 2.0;
+	while (s > 360) s -= 360;
+	while (s < 0 ) s += 360;
+	while (e < s ) e += 360;
+
+	if ( e - s >= 359 )
+	{ 
+          gdImageEllipse (im, cx, cy, w, h, color);
+	  return;
+	}
+
+	xr0 = xr1 = cx;
+	yr0 = yr1 = cy;
+	xr0 += w2 * cos ( (s * 2.0 * 3.14159265) / 360.0 );
+	yr0 += h2 * sin ( (s * 2.0 * 3.14159265) / 360.0 );
+	xr1 += w2 * cos ( (e * 2.0 * 3.14159265 ) / 360.0 );
+	yr1 += h2 * sin ( (e * 2.0 * 3.14159265 ) / 360.0 );
+        dx = xr1 - xr0;
+        dy = yr1 - yr0;
+
+	/* From here it should be just like the ellipse, but qualified to be on 
+	** ONE side of the chord from xr0,yr0 to xr1, yr1 (positive)
+	*/
+        d = 2 * (long) h2 * h2 + (long) w2 * w2 - 2 * (long)w2 * w2 * h2;
+        b_sq = (long)h2 * h2;
+        b_sq_4 = 4 * (long) h2 * h2;
+        b_sq_6 = 6 * (long) h2 * h2;
+        a_sq = (long) w2 * w2;
+        a_sq_4 = 4 * (long)w2 * w2;
+        a_sq_6 = 6 * (long)w2 * w2;
+
+        x = 0;
+        y = -h2;
+        switchem = a_sq / lsqrt( a_sq + b_sq );
+
+        while ( x <= switchem )
+        {
+          pstart = y - half;
+          for (pix = pstart; pix < pstart + thick; pix++)
+          {
+            int xx, yy;
+
+            xx = cx+x; yy = cy+pix;
+            if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+              gdImageSetPixel(im, xx, yy, color);
+
+            xx = cx - x;
+            if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+              gdImageSetPixel(im, xx, yy, color);
+
+            xx = cx+x; yy = cy-pix;
+            if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+              gdImageSetPixel(im, xx, yy, color);
+
+            xx = cx-x;
+            if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+              gdImageSetPixel(im, xx, yy, color);
+          }
+          if ( d < 0 )
+            d += b_sq_4 * x++ + b_sq_6;
+          else
+            d += b_sq_4*x++ + b_sq_6 + a_sq_4*(++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
+        /* Middlesplat!
+        ** Go a little further if the thickness is not nominal...
+        */
+        if ( thick > 1 )
+        {
+          int xp = x;
+          int yp = y;
+          int dp = d;
+          int thick2 = thick + 2;
+          int half2 = half + 1;
+      
+          while ( xp <= switchem + half )
+          {
+            pstart = yp - half2;
+            for (pix = pstart; pix < pstart + thick2; pix++)
+            {
+              int xx, yy;
+
+              xx = cx+xp; yy = cy+pix;
+              if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+                gdImageSetPixel(im, xx, yy, color);
+
+              xx = cx - xp;
+              if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+                gdImageSetPixel(im, xx, yy, color);
+
+              xx = cx+xp; yy = cy-pix;
+              if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+                gdImageSetPixel(im, xx, yy, color);
+
+              xx = cx-xp;
+              if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+                gdImageSetPixel(im, xx, yy, color);
+            }
+            if ( dp < 0 )
+              dp += b_sq_4 * xp++ + b_sq_6;
+            else
+              dp += b_sq_4*xp++ + b_sq_6 + a_sq_4*(++yp);
+          }
+        }
+  
+        d += -2*(long)b_sq + 2*(long)a_sq - 2*(long)b_sq*(x-1) + 2*(long)a_sq*(y-1);
+
+        while ( y <= 0 )
+        {
+          pstart = x - half;
+          for (pix = pstart; pix < pstart + thick; pix++)
+          {
+            int xx, yy;
+
+            xx = cx+pix; yy = cy+y;
+            if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+              gdImageSetPixel(im, xx, yy, color);
+
+            xx = cx - pix;
+            if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+              gdImageSetPixel(im, xx, yy, color);
+
+            xx = cx+pix; yy = cy-y;
+            if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+              gdImageSetPixel(im, xx, yy, color);
+
+            xx = cx-pix;
+            if ( lineside ( xx, yy, dx, dy, xr0, yr0 ) )
+              gdImageSetPixel(im, xx, yy, color);
+          }
+    
+          if (d < 0)
+            d += a_sq_4*y++ + a_sq_6 + b_sq_4*(++x);
+          else
+            d += a_sq_4*y++ + a_sq_6;
+        }
+}
 
 void gdImageFillToBorder(gdImagePtr im, int x, int y, int border, int color)
 {
 		srcx = x % gdImageSX(im->tile);
 		srcy = y % gdImageSY(im->tile);
 		p = gdImageGetPixel(im->tile, srcx, srcy);
-		tileColor = im->tileColorMap[p];
+		if (im->trueColor) {	
+			tileColor = p;
+		} else {
+			if (im->tile->trueColor) {
+				tileColor = gdImageColorResolveAlpha(im,
+					gdTrueColorGetRed(p), 
+					gdTrueColorGetGreen(p), 
+					gdTrueColorGetBlue(p),
+					gdTrueColorGetAlpha(p));
+			} else {
+				tileColor = im->tileColorMap[p];
+			}
+		}
 		if (old == tileColor) {
 			/* Nothing to be done */
 			return;
 
 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);
+	int x1h=x1, x1v=x1, y1h=y1, y1v=y1, x2h=x2, x2v=x2, y2h=y2, y2v=y2;
+	int thick = im->thick;
+	if (thick > 1)
+	{
+	  int half = thick/2;
+	  int half1 = thick - half;
+
+	  if ( y1 < y2 )
+	  {
+	    y1v = y1h - half;
+            y2v = y2h + half1 - 1;
+	  }
+	  else
+	  {
+	    y1v = y1h + half1 - 1;
+            y2v = y2h - half;
+	  }
+	}
+
+	gdImageLine(im, x1h, y1h, x2h, y1h, color);		
+	gdImageLine(im, x1h, y2h, x2h, y2h, color);		
+	gdImageLine(im, x1v, y1v, x1v, y2v, color);
+	gdImageLine(im, x2v, y1v, x2v, y2v, color);
 }
 
 void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color)
 	int tox, toy;
 	int i;
 	int colorMap[gdMaxColors];
+	if (dst->trueColor) {
+		/* 2.0: much easier when the destination is truecolor. */
+		for (y = 0; (y < h); y++) {
+			for (x = 0; (x < w); x++) {
+				int c = gdImageGetTrueColorPixel(src, srcX + x,
+					srcY + y);
+				if (c != src->transparent) {
+					gdImageSetPixel(dst,
+						dstX + x,
+						dstY + y,
+						c);
+				}
+			}
+		}
+		return;
+	}	
 	for (i=0; (i<gdMaxColors); i++) {
 		colorMap[i] = (-1);
 	}
 				if (dst == src) {
 					nc = c;
 				} else { 
-					/* First look for an exact match */
-					nc = gdImageColorExact(dst,
+					/* Get best match possible. This
+						function never returns error. */
+					nc = gdImageColorResolveAlpha(
+						dst,
 						src->red[c], src->green[c],
-						src->blue[c]);
+						src->blue[c], src->alpha[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]);
 	}
 }			
 
+/* This function is a substitute for real alpha channel operations,
+	so it doesn't pay attention to the alpha channel. */
 void gdImageCopyMerge(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
 {
  
         int x, y;
         int tox, toy;
 	int ncR, ncG, ncB;
-#if 0
-        int i;
-        int colorMap[gdMaxColors];
-        for (i=0; (i<gdMaxColors); i++) {
-                colorMap[i] = (-1);
-        }
-#endif
         toy = dstY;
         for (y=srcY; (y < (srcY + h)); y++) {
                 tox = dstX;
                                 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 {
-					dc = gdImageGetPixel(dst, tox, toy);
-
-					ncR = src->red[c] * (pct/100.0) 
-						+ dst->red[dc] * ((100-pct)/100.0);
-                                        ncG = src->green[c] * (pct/100.0)
-                                                + dst->green[dc] * ((100-pct)/100.0);
-                                        ncB = src->blue[c] * (pct/100.0)
-                                                + dst->blue[dc] * ((100-pct)/100.0);
-
-                                        /* First look for an exact match */
-                                        nc = gdImageColorExact(dst,ncR, ncG, ncB);
-                                	if (nc == (-1)) {
-                                        	/* No, so try to allocate it */
-                                        	nc = gdImageColorAllocate(dst, ncR, ncG, ncB);
-                                        	/* If we're out of colors, go for the
-                                                	closest color */
-                                        	if (nc == (-1)) {
-                                                	nc = gdImageColorClosest(dst, ncR, ncG, ncB);
-                                        	}
-					}
-                                }
-                                /*colorMap[c] = nc; */
-                        /*} */
+			/* If it's the same image, mapping is trivial */
+			if (dst == src) {
+				nc = c;
+			} else {
+				dc = gdImageGetPixel(dst, tox, toy);
+
+				ncR = gdImageRed(src, c) * (pct/100.0) 
+					+ gdImageRed(dst, dc) * ((100-pct)/100.0);
+				ncG = gdImageGreen(src, c) * (pct/100.0)
+					+ gdImageGreen(dst, dc) * ((100-pct)/100.0);
+				ncB = gdImageBlue(src, c) * (pct/100.0)
+					+ gdImageBlue(dst, dc) * ((100-pct)/100.0);
+
+				/* Find a reasonable color */
+				nc = gdImageColorResolve(dst,ncR, ncG, ncB);
+			}
                         gdImageSetPixel(dst, tox, toy, nc);
                         tox++;
                 }
         }
 }
 
+/* This function is a substitute for real alpha channel operations,
+	so it doesn't pay attention to the alpha channel. */
 void gdImageCopyMergeGray(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
 {
  
         int tox, toy;
 	int ncR, ncG, ncB;
 	float g;
-#if 0
-        int i;
-        int colorMap[gdMaxColors];
-
-        for (i=0; (i<gdMaxColors); i++) {
-                colorMap[i] = (-1);
-        }
-#endif
         toy = dstY;
         for (y=srcY; (y < (srcY + h)); y++) {
                 tox = dstX;
                                 tox++;
                                 continue;
                         }
-
-
-			dc = gdImageGetPixel(dst, tox, toy);
-			g = 0.29900*dst->red[dc] 
-				+ 0.58700*dst->green[dc]
-				+ 0.11400*dst->blue[dc];
-
-			ncR = src->red[c] * (pct/100.0) 
-				+ g * ((100-pct)/100.0);
-			ncG = src->green[c] * (pct/100.0)
-				+ g * ((100-pct)/100.0);
-			ncB = src->blue[c] * (pct/100.0)
-				+ g * ((100-pct)/100.0);
-
-			/* First look for an exact match */
-			nc = gdImageColorExact(dst,ncR, ncG, ncB);
-			if (nc == (-1)) {
-				/* No, so try to allocate it */
-				nc = gdImageColorAllocate(dst, ncR, ncG, ncB);
-				/* If we're out of colors, go for the
-					closest color */
+			/* If it's the same image, mapping is trivial */
+			if (dst == src) {
+				nc = c;
+			} else {
+				dc = gdImageGetPixel(dst, tox, toy);
+				g = 0.29900*dst->red[dc] 
+					+ 0.58700*dst->green[dc]
+					+ 0.11400*dst->blue[dc];
+
+				ncR = gdImageRed(src, c) * (pct/100.0) 
+					+ gdImageRed(dst, dc) * g *
+					((100-pct)/100.0);
+				ncG = gdImageGreen(src, c) * (pct/100.0)
+					+ gdImageGreen(dst, dc) * g *
+					((100-pct)/100.0);
+				ncB = gdImageBlue(src, c) * (pct/100.0)
+					+ gdImageBlue(dst, dc) * g *
+					((100-pct)/100.0);
+
+				/* First look for an exact match */
+				nc = gdImageColorExact(dst,ncR, ncG, ncB);
 				if (nc == (-1)) {
-					nc = gdImageColorClosest(dst, ncR, ncG, ncB);
+					/* No, so try to allocate it */
+					nc = gdImageColorAllocate(dst, ncR, ncG, ncB);
+					/* If we're out of colors, go for the
+						closest color */
+					if (nc == (-1)) {
+						nc = gdImageColorClosest(dst, ncR, ncG, ncB);
+					}
 				}
 			}
                         gdImageSetPixel(dst, tox, toy, nc);
         }
 }
 
-
 void gdImageCopyResized(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
 {
 	int c;
 			tox = dstX;
 			for (x=srcX; (x < (srcX + srcW)); x++) {
 				int nc;
+				int mapTo;
 				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]);
+				if (dst->trueColor) {
+						int d;
+						mapTo = gdImageGetTrueColorPixel(src, x, y);
+						/* Added 7/24/95: support transparent copies */
+						if (gdImageGetTransparent(src) == mapTo) {
+							tox++;
+							continue;
 						}
+				} else {
+						c = gdImageGetPixel(src, x, y);
+						/* Added 7/24/95: support transparent copies */
+						if (gdImageGetTransparent(src) == c) {
+							tox += stx[x-srcX];
+							continue;
+						}
+						if (src->trueColor) {
+							/* Remap to the palette available in the
+								destination image. This is slow and
+								works badly. */
+							mapTo = gdImageColorResolveAlpha(dst,
+								gdTrueColorGetRed(c), 
+								gdTrueColorGetGreen(c), 
+								gdTrueColorGetBlue(c),
+								gdTrueColorGetAlpha(c));
+					    } else {			
+								/* 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 { 
+										/* Find or create the best match */
+										mapTo = gdImageColorResolveAlpha(dst,
+											gdTrueColorGetRed(c), 
+											gdTrueColorGetGreen(c), 
+											gdTrueColorGetBlue(c),
+											gdTrueColorGetAlpha(c));
+									}	
+									colorMap[c] = nc;
+							}
+							mapTo = colorMap[c];
 					}
-					colorMap[c] = nc;
 				}
 				for (i=0; (i < stx[x - srcX]); i++) {
-					gdImageSetPixel(dst, tox, toy, colorMap[c]);
+					gdImageSetPixel(dst, tox, toy, mapTo);
 					tox++;
 				}
 			}
 	gdFree(sty);
 }
 
+/* When gd 1.x was first created, floating point was to be avoided.
+	These days it is often faster than table lookups or integer
+	arithmetic. The routine below is shamelessly, gloriously
+	floating point. TBB */
+
+void gdImageCopyResampled(gdImagePtr dst, 
+	gdImagePtr src, 
+	int dstX, int dstY, 
+	int srcX, int srcY, 
+	int dstW, int dstH, 
+	int srcW, int srcH)
+{
+	int x, y;
+	float sx, sy;
+	if (!dst->trueColor) {
+		return gdImageCopyResized(
+			dst, src, dstX, dstY, srcX, srcY, dstW, dstH,
+			srcW, srcH);
+	}
+	for (y = dstY; (y < dstY + dstH); y++) {	
+		for (x = dstX; (x < dstX + dstW); x++) {	
+			int pd = gdImageGetPixel(dst, x, y);
+			float sy1, sy2, sx1, sx2;
+			float sx, sy;
+			float spixels = 0;
+			float red = 0.0, green = 0.0, blue = 0.0, alpha = 0.0;
+			sy1 = ((float) y - (float) dstY) * (float) srcH / 
+				(float) dstH;	 
+			sy2 = ((float) (y + 1) - (float) dstY) * (float) srcH / 
+				(float) dstH;	 
+			sy = sy1;
+			do {
+				float yportion;
+				if (floor(sy) == floor(sy1)) {
+					yportion = 1.0 - (sy - floor(sy));
+					if (yportion > sy2 - sy1) {
+						yportion = sy2 - sy1;
+					}
+					sy = floor(sy);
+				} else if (sy == floor(sy2)) {
+					yportion = sy2 - floor(sy2);
+				} else {
+					yportion = 1.0;
+				}
+				sx1 = ((float) x - (float) dstX) * (float) srcW / 
+					dstW;	 
+				sx2 = ((float) (x + 1) - (float) dstX) * (float) srcW / 
+					dstW;	 
+				sx = sx1;
+				do {
+					float xportion;
+					float pcontribution;
+					int p;
+					float yportion;
+					if (floor(sx) == floor(sx1)) {
+						xportion = 1.0 - (sx - floor(sx));
+						if (xportion > sx2 - sx1) {
+							xportion = sx2 - sx1;
+						}
+						sx = floor(sx);
+					} else if (sx == floor(sx2)) {
+						xportion = sx2 - floor(sx2);
+					} else {
+						xportion = 1.0;
+					}					
+					pcontribution = xportion * yportion;
+					p = gdImageGetTrueColorPixel(
+						src,
+						(int) sx,
+						(int) sy);	
+					red += gdTrueColorGetRed(p) * pcontribution;
+					green += gdTrueColorGetGreen(p) * pcontribution;
+					blue += gdTrueColorGetBlue(p) * pcontribution;
+					alpha += gdTrueColorGetAlpha(p) * pcontribution;
+					spixels += xportion * yportion;
+					sx += 1.0;	
+				} while (sx < sx2);
+				sy += 1.0;	
+			} while (sy < sy2);
+			if (spixels != 0.0) {
+					red /= spixels;				
+					green /= spixels;				
+					blue /= spixels;				
+					alpha /= spixels;				
+			}
+			/* Clamping to allow for rounding errors above */
+			if (red > 255.0) {
+				red = 255.0;
+			}
+			if (green > 255.0) {
+				green = 255.0;
+			}
+			if (blue > 255.0) {
+				blue = 255.0;
+			}
+			if (alpha > gdAlphaMax) {
+				alpha = gdAlphaMax;
+			}
+			gdImageSetPixel(dst, 
+				x, y, 
+				gdTrueColorAlpha((int) red, 
+					(int) green, 
+					(int) blue, 
+					(int) alpha));
+		}
+	}
+}
+
 gdImagePtr
 gdImageCreateFromXbm(FILE *fd)
 {
 /* THANKS to Kirsten Schulz for the polygon fixes! */
 
 /* The intersection finding technique of this code could be improved  */
-/* by remembering the previous intertersection, and by using the slope.*/
+/* by remembering the previous intertersection, and by using the slope. */
 /* That could help to adjust intersections  to produce a nice */
 /* interior_extrema. */
 
 	im->stylePos = 0;
 }
 
+void gdImageSetThickness(gdImagePtr im, int thickness)
+{
+	im->thick = thickness;
+}
+
 void gdImageSetBrush(gdImagePtr im, gdImagePtr brush)
 {
 	int i;
 	im->brush = brush;
-	for (i=0; (i < gdImageColorsTotal(brush)); i++) {
-		int index;
-		index = gdImageColorExact(im, 
-			gdImageRed(brush, i),
-			gdImageGreen(brush, i),
-			gdImageBlue(brush, i));
-		if (index == (-1)) {
-			index = gdImageColorAllocate(im,
+	if ((!im->trueColor) && (!im->brush->trueColor)) {
+		for (i=0; (i < gdImageColorsTotal(brush)); i++) {
+			int index;
+			index = gdImageColorResolveAlpha(im, 
 				gdImageRed(brush, i),
 				gdImageGreen(brush, i),
-				gdImageBlue(brush, i));
-			if (index == (-1)) {
-				index = gdImageColorClosest(im,
-					gdImageRed(brush, i),
-					gdImageGreen(brush, i),
-					gdImageBlue(brush, i));
-			}
+				gdImageBlue(brush, i),
+				gdImageAlpha(brush, i));
+			im->brushColorMap[i] = index;
 		}
-		im->brushColorMap[i] = index;
 	}
 }
 	
 {
 	int i;
 	im->tile = tile;
-	for (i=0; (i < gdImageColorsTotal(tile)); i++) {
-		int index;
-		index = gdImageColorExact(im, 
-			gdImageRed(tile, i),
-			gdImageGreen(tile, i),
-			gdImageBlue(tile, i));
-		if (index == (-1)) {
-			index = gdImageColorAllocate(im,
+	if ((!im->trueColor) && (!im->tile->trueColor)) {
+		for (i=0; (i < gdImageColorsTotal(tile)); i++) {
+			int index;
+			index = gdImageColorResolveAlpha(im, 
 				gdImageRed(tile, i),
 				gdImageGreen(tile, i),
-				gdImageBlue(tile, i));
-			if (index == (-1)) {
-				index = gdImageColorClosest(im,
-					gdImageRed(tile, i),
-					gdImageGreen(tile, i),
-					gdImageBlue(tile, i));
-			}
+				gdImageBlue(tile, i),
+				gdImageAlpha(tile, i));
+			im->tileColorMap[i] = index;
 		}
-		im->tileColorMap[i] = index;
 	}
 }
 
 		cmpStatus |= GD_CMP_TRANSPARENT;
 	}
 
+	if (im1->trueColor != im2->trueColor) {
+		cmpStatus |= GD_CMP_TRUECOLOR;
+	}
+
 	sx = im1->sx;
 	if (im1->sx != im2->sx) {
 		cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE;
 		for ( x = 0 ; (x < sx) ; x++ ) {
 			p1 = im1->pixels[y][x];
 			p2 = im2->pixels[y][x];
-
-			if (im1->red[p1] != im2->red[p2]) {
+			if (gdImageRed(im1, p1) != gdImageRed(im2, p2)) {
 				cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
 				break;
 			}
-
-			if (im1->green[p1] != im2->green[p2]) {
+			if (gdImageGreen(im1, p1) != gdImageGreen(im2, p2)) {
 				cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
 				break;
 			}
-
-
-			if (im1->blue[p1] != im2->blue[p2]) {
+			if (gdImageBlue(im1, p1) != gdImageBlue(im2, p2)) {
+				cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;	
+				break;
+			}
+#if 0
+			/* Soon we'll add alpha channel to palettes */
+			if (gdImageAlpha(im1, p1) != gdImageAlpha(im2, p2)) {
 				cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;	
 				break;
 			}
+#endif
 		}
 		if (cmpStatus & GD_CMP_COLOR) { break; };
 	}
 	return cmpStatus;
 }
 
+int gdAlphaBlend(int dst, int src)
+{
+	return (((((gdAlphaTransparent - gdTrueColorGetAlpha(src)) * 
+		gdTrueColorGetRed(src) / gdAlphaMax) +
+		(gdTrueColorGetAlpha(src) * 
+		gdTrueColorGetRed(dst)) / gdAlphaMax) << 16) +
+		((((gdAlphaTransparent - gdTrueColorGetAlpha(src)) * 
+		gdTrueColorGetGreen(src) / gdAlphaMax) +
+		(gdTrueColorGetAlpha(src) * 
+		gdTrueColorGetGreen(dst)) / gdAlphaMax) << 8) +
+		(((gdAlphaTransparent - gdTrueColorGetAlpha(src)) * 
+		gdTrueColorGetBlue(src) / gdAlphaMax) +
+		(gdTrueColorGetAlpha(src) * 
+		gdTrueColorGetBlue(dst)) / gdAlphaMax));
+}
+
+void gdImageAlphaBlending(gdImagePtr im, int alphaBlendingArg)
+{
+	im->alphaBlendingFlag = alphaBlendingArg;
+}
+
+void gdImageSaveAlpha(gdImagePtr im, int saveAlphaArg)
+{
+	im->saveAlphaFlag = saveAlphaArg;
+}
+
 #include <stdio.h>
 #include "gd_io.h"
 
-/* This can't be changed in the current palette-only version of gd. */
+/* The maximum number of palette entries in palette-based images.
+	In the wonderful new world of gd 2.0, you can of course have
+	many more colors when using truecolor mode. */
 
 #define gdMaxColors 256
 
 	access sx, sy, the color table, and colorsTotal for 
 	read-only purposes. */
 
+/* If 'truecolor' is set true, the image is truecolor; 
+	pixels are represented by integers, which
+	must be 32 bits wide or more. 
+
+	True colors are repsented as follows:
+
+	ARGB
+
+	Where 'A' (alpha channel) occupies only the
+	LOWER 7 BITS of the MSB. This very small 
+	loss of alpha channel resolution allows gd 2.x
+	to keep backwards compatibility by allowing
+	signed integers to be used to represent colors,
+	and negative numbers to represent special cases,
+	just as in gd 1.x. */
+
+#define gdAlphaMax 127
+#define gdAlphaOpaque 0
+#define gdAlphaTransparent 127
+#define gdRedMax 255
+#define gdGreenMax 255
+#define gdBlueMax 255
+#define gdTrueColorGetAlpha(c) (((c) & 0x7F000000) >> 24)
+#define gdTrueColorGetRed(c) (((c) & 0xFF0000) >> 16)
+#define gdTrueColorGetGreen(c) (((c) & 0x00FF00) >> 8)
+#define gdTrueColorGetBlue(c) ((c) & 0x0000FF)
+
+/* This function accepts truecolor pixel values only. The 
+	source color is composited with the destination color
+	based on the alpha channel value of the source color.
+	The resulting color is opaque. */
+
+int gdAlphaBlend(int dest, int src);
+
 typedef struct gdImageStruct {
+	/* Palette-based image pixels */
 	unsigned char ** pixels;
 	int sx;
 	int sy;
+	/* These are valid in palette images only. See also
+	/* 'alpha', which appears later in the structure to
+		preserve binary backwards compatibility */
 	int colorsTotal;
 	int red[gdMaxColors];
 	int green[gdMaxColors];
 	int blue[gdMaxColors]; 
 	int open[gdMaxColors];
+	/* For backwards compatibility, this is set to the
+		first palette entry with 100% transparency,
+		and is also set and reset by the 
+		gdImageColorTransparent function. Newer
+		applications can allocate palette entries
+		with any desired level of transparency; however,
+		bear in mind that many viewers, notably
+		many web browsers, fail to implement
+		full alpha channel for PNG and provide
+		support for full opacity or transparency only. */
 	int transparent;
 	int *polyInts;
 	int polyAllocated;
 	int stylePos;
 	int *style;
 	int interlace;
+	/* New in 2.0: thickness of line. Initialized to 1. */
+	int thick;
+	/* New in 2.0: alpha channel for palettes. Note that only
+		Macintosh Internet Explorer and (possibly) Netscape 6
+		really support multiple levels of transparency in
+		palettes, to my knowledge, as of 2/15/01. Most
+		common browsers will display 100% opaque and
+		100% transparent correctly, and do something 
+		unpredictable and/or undesirable for levels
+		in between. TBB */
+	int alpha[gdMaxColors]; 
+	/* Truecolor flag and pixels. New 2.0 fields appear here at the
+		end to minimize breakage of existing object code. */
+	int trueColor;
+	int ** tpixels;
+	/* Should alpha channel be copied, or applied, each time a
+		pixel is drawn? This applies to truecolor images only.
+		No attempt is made to alpha-blend in palette images,
+		even if semitransparent palette entries exist. 
+		To do that, build your image as a truecolor image,
+		then quantize down to 8 bits. */
+	int alphaBlendingFlag;
+	/* Should the alpha channel of the image be saved? This affects
+		PNG at the moment; other future formats may also
+		have that capability. JPEG doesn't. */
+	int saveAlphaFlag;
 } gdImage;
 
 typedef gdImage * gdImagePtr;
 
 /* Functions to manipulate images. */
 
+/* Creates a palette-based image (up to 256 colors). */
 gdImagePtr gdImageCreate(int sx, int sy);
+
+/* An alternate name for the above (2.0). */
+#define gdImageCreatePalette gdImageCreate
+
+/* Creates a truecolor image (millions of colors). */
+gdImagePtr gdImageCreateTrueColor(int sx, int sy);
+
+/* Creates an image from various file types. These functions
+	return a palette or truecolor image based on the
+	nature of the file being loaded. Truecolor PNG
+	stays truecolor; palette PNG stays palette-based;
+	JPEG is always truecolor. */
 gdImagePtr gdImageCreateFromPng(FILE *fd);
 gdImagePtr gdImageCreateFromPngCtx(gdIOCtxPtr in);
 gdImagePtr gdImageCreateFromWBMP(FILE *inFile);
 gdImagePtr gdImageCreateFromXbm(FILE *fd);
 
 void gdImageDestroy(gdImagePtr im);
+
+/* Replaces or blends with the background depending on the
+	most recent call to gdImageAlphaBlending and the
+	alpha channel value of 'color'; default is to overwrite. 
+	Tiling and line styling are also implemented
+	here. All other gd drawing functions pass through this call, 
+	allowing for many useful effects. */
+	
 void gdImageSetPixel(gdImagePtr im, int x, int y, int color);
+
 int gdImageGetPixel(gdImagePtr im, int x, int y);
+
 void gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
+
 /* For backwards compatibility only. Use gdImageSetStyle()
 	for much more flexible line drawing. */
 void gdImageDashedLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
 void gdImageString16(gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color);
 void gdImageStringUp16(gdImagePtr im, gdFontPtr f, int x, int y, unsigned short *s, int color);
 
-/* FreeType 1.x text output (DEPRECATED) */
+/* Calls gdImageStringFT. Provided for backwards compatibility only. */
 char *gdImageStringTTF(gdImage *im, int *brect, int fg, char *fontlist,
                 double ptsize, double angle, int x, int y, char *string);
 
-/* FreeType 2 text output (NIFTY) */
+/* FreeType 2 text output */
 char *gdImageStringFT(gdImage *im, int *brect, int fg, char *fontlist,
                 double ptsize, double angle, int x, int y, char *string);
 
 void gdImagePolygon(gdImagePtr im, gdPointPtr p, int n, int c);
 void gdImageFilledPolygon(gdImagePtr im, gdPointPtr p, int n, int c);
 
+/* These functions still work with truecolor images, 
+	for which they never return error. */
 int gdImageColorAllocate(gdImagePtr im, int r, int g, int b);
+/* gd 2.0: palette entries with non-opaque transparency are permitted. */
+int gdImageColorAllocateAlpha(gdImagePtr im, int r, int g, int b, int a);
+/* Assumes opaque is the preferred alpha channel value */
 int gdImageColorClosest(gdImagePtr im, int r, int g, int b);
+/* Closest match taking all four parameters into account.
+	A slightly different color with the same transparency
+	beats the exact same color with radically different
+	transparency */
+int gdImageColorClosestAlpha(gdImagePtr im, int r, int g, int b, int a);
+/* Returns exact, 100% opaque matches only */
 int gdImageColorExact(gdImagePtr im, int r, int g, int b);
+/* Returns an exact match only, including alpha */
+int gdImageColorExactAlpha(gdImagePtr im, int r, int g, int b, int a);
+/* Opaque only */
 int gdImageColorResolve(gdImagePtr im, int r, int g, int b);
+/* Based on gdImageColorExactAlpha and gdImageColorClosestAlpha */
+int gdImageColorResolveAlpha(gdImagePtr im, int r, int g, int b, int a);
+
+/* A simpler way to obtain an opaque truecolor value for drawing on a
+	truecolor image. Not for use with palette images! */
+
+#define gdTrueColor(r, g, b) (((r) << 16) + \
+	((g) << 8) + \
+	(b))
+
+/* Returns a truecolor value with an alpha channel component.
+	gdAlphaMax (127, **NOT 255**) is transparent, 0 is completely
+	opaque. */
+
+#define gdTrueColorAlpha(r, g, b, a) (((a) << 24) + \
+	((r) << 16) + \
+	((g) << 8) + \
+	(b))
+
 void gdImageColorDeallocate(gdImagePtr im, int color);
+
+/* Converts a truecolor image to a palette-based image,
+	using a high-quality two-pass quantization routine
+	which attempts to preserve alpha channel information
+	as well as R/G/B color information when creating
+	a palette. If ditherFlag is set, the image will be
+	dithered to approximate colors better, at the expense
+	of some obvious "speckling." colorsWanted can be
+	anything up to 256. If the original source image
+	includes photographic information or anything that
+	came out of a JPEG, 256 is strongly recommended.
+
+	Better yet, don't use this function -- write real
+	truecolor PNGs and JPEGs. The disk space gain of
+        conversion to palette is not great (for small images
+        it can be negative) and the quality loss is ugly. */
+
+void gdImageTrueColorToPalette(gdImagePtr im, int ditherFlag, int colorsWanted);
+
+/* Specifies a color index (if a palette image) or an
+	RGB color (if a truecolor image) which should be
+	considered 100% transparent. FOR TRUECOLOR IMAGES,
+	THIS IS IGNORED IF AN ALPHA CHANNEL IS BEING
+	SAVED. Use gdImageSaveAlpha(im, 0); to
+	turn off the saving of a full alpha channel in
+	a truecolor image. Note that gdImageColorTransparent
+	is usually compatible with older browsers that
+	do not understand full alpha channels well. TBB */
 void gdImageColorTransparent(gdImagePtr im, int color);
+
 void gdImagePaletteCopy(gdImagePtr dst, gdImagePtr src);
 void gdImagePng(gdImagePtr im, FILE *out);
 void gdImagePngCtx(gdImagePtr im, gdIOCtx *out);
 /* Best to free this memory with gdFree(), not free() */
 void* gdImageGd2Ptr(gdImagePtr im, int cs, int fmt, int *size);
 
+void gdImageEllipse(gdImagePtr im, int cx, int cy, int w, int h, int color);
 void gdImageArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color);
+
+#define gdPie   0
+#define gdChord 1
+
+void gdImageFilledEllipse(gdImagePtr im, int cx, int cy, int w, int h, int color, int style);
+void gdImageFilledArc(gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style);
 void gdImageFillToBorder(gdImagePtr im, int x, int y, int border, int color);
 void gdImageFill(gdImagePtr im, int x, int y, int color);
 void gdImageCopy(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h);
 void gdImageCopyMergeGray(gdImagePtr dst, gdImagePtr src, int dstX, int dstY,
                         int srcX, int srcY, int w, int h, int pct);
 
-/* Stretches or shrinks to fit, as needed */
+/* Stretches or shrinks to fit, as needed. Does NOT attempt
+	to average the entire set of source pixels that scale down onto the
+	destination pixel. */
 void gdImageCopyResized(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH);
+
+/* gd 2.0: stretches or shrinks to fit, as needed. When called with a
+	truecolor destination image, this function averages the
+	entire set of source pixels that scale down onto the
+	destination pixel, taking into account what portion of the
+	destination pixel each source pixel represents. This is a
+	floating point operation, but this is not a performance issue
+	on modern hardware, except for some embedded devices. If the 
+	destination is a palette image, gdImageCopyResized is 
+	substituted automatically. */
+void gdImageCopyResampled(gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH);
+
 void gdImageSetBrush(gdImagePtr im, gdImagePtr brush);
 void gdImageSetTile(gdImagePtr im, gdImagePtr tile);
 void gdImageSetStyle(gdImagePtr im, int *style, int noOfPixels);
-/* On or off (1 or 0) */
+/* Line thickness (defaults to 1). Affects lines, ellipses, 
+	rectangles, polygons and so forth. */
+void gdImageSetThickness(gdImagePtr im, int thickness);
+/* On or off (1 or 0) for all three of these. */
 void gdImageInterlace(gdImagePtr im, int interlaceArg);
+void gdImageAlphaBlending(gdImagePtr im, int alphaBlendingArg);
+void gdImageSaveAlpha(gdImagePtr im, int saveAlphaArg);
+
+/* Macros to access information about images. */
+
+/* Returns nonzero if the image is a truecolor image,
+	zero for a palette image. */
+
+#define gdImageTrueColor(im) ((im)->trueColor)
 
-/* Macros to access information about images. READ ONLY. Changing
-	these values will NOT have the desired result. */
 #define gdImageSX(im) ((im)->sx)
 #define gdImageSY(im) ((im)->sy)
 #define gdImageColorsTotal(im) ((im)->colorsTotal)
-#define gdImageRed(im, c) ((im)->red[(c)])
-#define gdImageGreen(im, c) ((im)->green[(c)])
-#define gdImageBlue(im, c) ((im)->blue[(c)])
+#define gdImageRed(im, c) ((im)->trueColor ? gdTrueColorGetRed(c) : \
+	(im)->red[(c)])
+#define gdImageGreen(im, c) ((im)->trueColor ? gdTrueColorGetGreen(c) : \
+	(im)->green[(c)])
+#define gdImageBlue(im, c) ((im)->trueColor ? gdTrueColorGetBlue(c) : \
+	(im)->blue[(c)])
+#define gdImageAlpha(im, c) ((im)->trueColor ? gdTrueColorGetAlpha(c) : \
+	(im)->alpha[(c)])
 #define gdImageGetTransparent(im) ((im)->transparent)
 #define gdImageGetInterlaced(im) ((im)->interlace)
 
+/* These macros provide direct access to pixels in
+	palette-based and truecolor images, respectively.
+	If you use these macros, you must perform your own
+	bounds checking. Use of the macro for the correct type
+	of image is also your responsibility. */
+#define gdImagePalettePixel(im, x, y) (im)->pixels[(y)][(x)]
+#define gdImageTrueColorPixel(im, x, y) (im)->tpixels[(y)][(x)]
+
 /* I/O Support routines. */
 
 gdIOCtx* gdNewFileCtx(FILE*);
 #define GD2_CHUNKSIZE_MIN	64
 #define GD2_CHUNKSIZE_MAX       4096
 
-#define GD2_VERS                1
+#define GD2_VERS                2
 #define GD2_ID                  "gd2"
 #define GD2_FMT_RAW             1
 #define GD2_FMT_COMPRESSED      2
 #define GD_CMP_TRANSPARENT	32	/* Transparent colour */
 #define GD_CMP_BACKGROUND	64	/* Background colour */
 #define GD_CMP_INTERLACE	128	/* Interlaced setting */
+#define GD_CMP_TRUECOLOR	256	/* Truecolor vs palette differs */
 
 /* resolution affects ttf font rendering, particularly hinting */
 #define GD_RESOLUTION           96      /* pixels per inch */
+#include "gd.h"
+#include <math.h>
+
+/* Courtesy of F J Franklin. */
+
+static gdPoint gdArcClosest (int width,int height,int angle);
+
+void gdImageFilledEllipse(gdImagePtr im, int cx, int cy, int width, int height, int color, int style)
+{
+	gdImageFilledArc(im, cx, cy, width, height, 0, 360, color, style);
+}
+
+void gdImageFilledArc(gdImagePtr im, int cx, int cy, int width, int height, int s, int e, int color, int style)
+{	gdPoint pt[7];
+	gdPoint axis_pt[4];
+
+	int angle;
+
+	int have_s = 0;
+	int have_e = 0;
+
+	int flip_x = 0;
+	int flip_y = 0;
+
+	int conquer = 0;
+
+	int i;
+
+	int a;
+	int b;
+
+	int x;
+	int y;
+
+	long s_sin = 0;
+	long s_cos = 0;
+	long e_sin = 0;
+	long e_cos = 0;
+
+	long w;    /* a * 2 */
+	long h;    /* b * 2 */
+
+	long x2;   /* x * 2 */
+	long y2;   /* y * 2 */
+	long lx2;  /* x * 2 (line) */
+	long ly2;  /* y * 2 (line) */
+
+	long ws;   /* (a * 2)^2 */
+	long hs;   /* (b * 2)^2 */
+
+	long whs;  /* (a * 2)^2 * (b * 2)^2 */
+
+	long g;    /* decision variable */
+	long lg;   /* decision variable (line) */
+
+	width  = (width  & 1) ? (width  + 1) : (width );
+	height = (height & 1) ? (height + 1) : (height);
+
+	a = width  / 2;
+	b = height / 2;
+
+	axis_pt[0].x =  a;
+	axis_pt[0].y =  0;
+	axis_pt[1].x =  0;
+	axis_pt[1].y =  b;
+	axis_pt[2].x = -a;
+	axis_pt[2].y =  0;
+	axis_pt[3].x =  0;
+	axis_pt[3].y = -b;
+
+	if (s == e) return;
+
+	if ((e - s) >= 360)
+	{	s = 0;
+		e = 0;
+	}
+
+	while (s <    0) s += 360;
+	while (s >= 360) s -= 360;
+	while (e <    0) e += 360;
+	while (e >= 360) e -= 360;
+
+	if (e <= s) e += 360;
+
+	/* I'm assuming a chord-rule at the moment. Need to add origin to get a
+	 * pie-rule, but will need to set chord-rule before recursion...
+	 */
+
+	for (i = 0; i < 4; i++)
+	{	if ((s < (i+1)*90) && (e > (i+1)*90))
+		{	gdImageFilledArc (im,cx,cy,width,height,s,(i+1)*90,color,gdChord);
+			pt[0] = gdArcClosest (width,height,s);
+			pt[0].x += cx;
+			pt[0].y += cy;
+			pt[1].x = cx + axis_pt[(i+1)&3].x;
+			pt[1].y = cy + axis_pt[(i+1)&3].y;
+			if (e <= (i+2)*90)
+			{	gdImageFilledArc (im,cx,cy,width,height,(i+1)*90,e,color,gdChord);
+				pt[2] = gdArcClosest (width,height,e);
+				pt[2].x += cx;
+				pt[2].y += cy;
+				if (style == gdChord)
+				{	gdImageFilledPolygon (im,pt,3,color);
+					gdImagePolygon (im,pt,3,color);
+				}
+				else if (style == gdPie)
+				{	pt[3].x = cx;
+					pt[3].y = cy;
+					gdImageFilledPolygon (im,pt,4,color);
+					gdImagePolygon (im,pt,4,color);
+				}
+			}
+			else
+			{	gdImageFilledArc (im,cx,cy,width,height,(i+1)*90,(i+2)*90,color,gdChord);
+				pt[2].x = cx + axis_pt[(i+2)&3].x;
+				pt[2].y = cy + axis_pt[(i+2)&3].y;
+				if (e <= (i+3)*90)
+				{	gdImageFilledArc (im,cx,cy,width,height,(i+2)*90,e,color,gdChord);
+					pt[3] = gdArcClosest (width,height,e);
+					pt[3].x += cx;
+					pt[3].y += cy;
+					if (style == gdChord)
+					{	gdImageFilledPolygon (im,pt,4,color);
+						gdImagePolygon (im,pt,4,color);
+					}
+					else if (style == gdPie)
+					{	pt[4].x = cx;
+						pt[4].y = cy;
+						gdImageFilledPolygon (im,pt,5,color);
+						gdImagePolygon (im,pt,5,color);
+					}
+				}
+				else
+				{	gdImageFilledArc (im,cx,cy,width,height,(i+2)*90,(i+3)*90,color,gdChord);
+					pt[3].x = cx + axis_pt[(i+3)&3].x;
+					pt[3].y = cy + axis_pt[(i+3)&3].y;
+					if (e <= (i+4)*90)
+					{	gdImageFilledArc (im,cx,cy,width,height,(i+3)*90,e,color,gdChord);
+						pt[4] = gdArcClosest (width,height,e);
+						pt[4].x += cx;
+						pt[4].y += cy;
+						if (style == gdChord)
+						{	gdImageFilledPolygon (im,pt,5,color);
+							gdImagePolygon (im,pt,5,color);
+						}
+						else if (style == gdPie)
+						{	pt[5].x = cx;
+							pt[5].y = cy;
+							gdImageFilledPolygon (im,pt,6,color);
+							gdImagePolygon (im,pt,6,color);
+						}
+					}
+					else
+					{	gdImageFilledArc (im,cx,cy,width,height,(i+3)*90,(i+4)*90,color,gdChord);
+						pt[4].x = cx + axis_pt[(i+4)&3].x;
+						pt[4].y = cy + axis_pt[(i+4)&3].y;
+
+						gdImageFilledArc (im,cx,cy,width,height,(i+4)*90,e,color,gdChord);
+						pt[5] = gdArcClosest (width,height,e);
+						pt[5].x += cx;
+						pt[5].y += cy;
+						if (style == gdChord)
+						{	gdImageFilledPolygon (im,pt,6,color);
+							gdImagePolygon (im,pt,6,color);
+						}
+						else if (style == gdPie)
+						{	pt[6].x = cx;
+							pt[6].y = cy;
+							gdImageFilledPolygon (im,pt,7,color);
+							gdImagePolygon (im,pt,7,color);
+						}
+					}
+				}
+			}
+			return;
+		}
+	}
+
+	/* At this point we have only arcs that lies within a quadrant -
+	 * map this to first quadrant...
+	 */
+
+	if ((s >= 90) && (e <= 180))
+	{	angle = s;
+		s = 180 - e;
+		e = 180 - angle;
+		flip_x = 1;
+	}
+	if ((s >= 180) && (e <= 270))
+	{	s = s - 180;
+		e = e - 180;
+		flip_x = 1;
+		flip_y = 1;
+	}
+	if ((s >= 270) && (e <= 360))
+	{	angle = s;
+		s = 360 - e;
+		e = 360 - angle;
+		flip_y = 1;
+	}
+
+	if (s == 0)
+	{	s_sin = 0;
+		s_cos = (long) ((double) 32768);
+	}
+	else
+	{	s_sin = (long) ((double) 32768 * sin ((double) s * M_PI / (double) 180));
+		s_cos = (long) ((double) 32768 * cos ((double) s * M_PI / (double) 180));
+	}
+	if (e == 0)
+	{	e_sin = (long) ((double) 32768);
+		e_cos = 0;
+	}
+	else
+	{	e_sin = (long) ((double) 32768 * sin ((double) e * M_PI / (double) 180));
+		e_cos = (long) ((double) 32768 * cos ((double) e * M_PI / (double) 180));
+	}
+
+	w = (long) width;
+	h = (long) height;
+
+	ws = w * w;
+	hs = h * h;
+
+	whs = 1;
+	while ((ws > 32768) || (hs > 32768))
+	{	ws = (ws + 1) / 2; /* Unfortunate limitations on integers makes */
+		hs = (hs + 1) / 2; /* drawing large  ellipses problematic...    */
+		whs *= 2;
+	}
+	while ((ws * hs) > (0x04000000L / whs))
+	{	ws = (ws + 1) / 2;
+		hs = (hs + 1) / 2;
+		whs *= 2;
+	}
+	whs *= ws * hs;
+
+	pt[0].x = w / 2;
+	pt[0].y = 0;
+
+	pt[2].x = 0;
+	pt[2].y = h / 2;
+
+	have_s = 0;
+	have_e = 0;
+
+	if (s ==  0) have_s = 1;
+	if (e == 90) have_e = 1;
+
+	x2 = w;
+	y2 = 0; /* Starting point is exactly on ellipse */
+
+	g = x2 - 1;
+	g = g * g * hs + 4 * ws - whs;
+
+	while ((x2 * hs) > (y2 * ws)) /* Keep |tangent| > 1 */
+	{	y2 += 2;
+		g += ws * 4 * (y2 + 1);
+
+		if (g > 0) /* Need to drop */
+		{	x2 -= 2;
+			g -= hs * 4 * x2;
+		}
+
+		if ((have_s == 0) && ((s_sin * x2) <= (y2 * s_cos)))
+		{	pt[0].x = (int) (x2 / 2);
+			pt[0].y = (int) (y2 / 2);
+			have_s = 1;
+		}
+
+		if ((have_e == 0) && ((e_sin * x2) <= (y2 * e_cos)))
+		{	pt[2].x = (int) (x2 / 2);
+			pt[2].y = (int) (y2 / 2);
+			have_e = 1;
+		}
+	}
+	pt[1].x = (int) (x2 / 2);
+	pt[1].y = (int) (y2 / 2);
+
+	x2 = 0;
+	y2 = h; /* Starting point is exactly on ellipse */
+
+	g = y2 - 1;
+	g = g * g * ws + 4 * hs - whs;
+
+	while ((x2 * hs) < (y2 * ws))
+	{	x2 += 2;
+		g += hs * 4 * (x2 + 1);
+
+		if (g > 0) /* Need to drop */
+		{	y2 -= 2;
+			g -= ws * 4 * y2;
+		}
+
+		if ((have_s == 0) && ((s_sin * x2) >= (y2 * s_cos)))
+		{	pt[0].x = (int) (x2 / 2);
+			pt[0].y = (int) (y2 / 2);
+			have_s = 1;
+		}
+
+		if ((have_e == 0) && ((e_sin * x2) >= (y2 * e_cos)))
+		{	pt[2].x = (int) (x2 / 2);
+			pt[2].y = (int) (y2 / 2);
+			have_e = 1;
+		}
+	}
+
+	if ((have_s == 0) || (have_e == 0)) return; /* Bizarre case */
+
+	if (style == gdPie)
+	{	pt[3] = pt[0];
+		pt[4] = pt[1];
+		pt[5] = pt[2];
+
+		pt[0].x = cx + (flip_x ? (-pt[0].x) : pt[0].x);
+		pt[0].y = cy + (flip_y ? (-pt[0].y) : pt[0].y);
+		pt[1].x = cx;
+		pt[1].y = cy;
+		pt[2].x = cx + (flip_x ? (-pt[2].x) : pt[2].x);
+		pt[2].y = cy + (flip_y ? (-pt[2].y) : pt[2].y);
+		gdImageFilledPolygon (im,pt,3,color);
+		gdImagePolygon (im,pt,3,color);
+
+		pt[0] = pt[3];
+		pt[1] = pt[4];
+		pt[2] = pt[5];
+	}
+
+	if (((s_cos * hs) > (s_sin * ws)) && ((e_cos * hs) < (e_sin * ws)))
+	{	/* the points are on different parts of the curve...
+		 * this is too tricky to try to handle, so divide and conquer:
+		 */
+		pt[3] = pt[0];
+		pt[4] = pt[1];
+		pt[5] = pt[2];
+
+		pt[0].x = cx + (flip_x ? (-pt[0].x) : pt[0].x);
+		pt[0].y = cy + (flip_y ? (-pt[0].y) : pt[0].y);
+		pt[1].x = cx + (flip_x ? (-pt[1].x) : pt[1].x);
+		pt[1].y = cy + (flip_y ? (-pt[1].y) : pt[1].y);
+		pt[2].x = cx + (flip_x ? (-pt[2].x) : pt[2].x);
+		pt[2].y = cy + (flip_y ? (-pt[2].y) : pt[2].y);
+		gdImageFilledPolygon (im,pt,3,color);
+		gdImagePolygon (im,pt,3,color);
+
+		pt[0] = pt[3];
+		pt[2] = pt[4];
+
+		conquer = 1;
+	}
+
+	if (conquer || (((s_cos * hs) > (s_sin * ws)) && ((e_cos * hs) > (e_sin * ws))))
+	{	/* This is the best bit... */
+		/* steep line + ellipse */
+		/* go up & left from pt[0] to pt[2] */
+
+		x2 = w;
+		y2 = 0; /* Starting point is exactly on ellipse */
+
+		g = x2 - 1;
+		g = g * g * hs + 4 * ws - whs;
+