extern int gdCosT[];
extern int gdSinT[];
-static void gdImageBrushApply (gdImagePtr im, int x, int y);
-static void gdImageTileApply (gdImagePtr im, int x, int y);
+static void gdImageBrushApply(gdImagePtr im, int x, int y);
+static void gdImageTileApply(gdImagePtr im, int x, int y);
+static void gdImageAntiAliasedApply(gdImagePtr im, int x, int y);
static int gdFullAlphaBlend(int dst, int src);
static int gdLayerOverlay(int dst, int src);
static int gdAlphaBlendColor(int b1, int b2, int a1, int a2);
va_end(args);
}
-gdImagePtr
-gdImageCreate (int sx, int sy)
-{
- int i;
- gdImagePtr im;
- im = (gdImage *) gdMalloc (sizeof (gdImage));
- 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->brush = 0;
- im->tile = 0;
- im->style = 0;
- for (i = 0; (i < sy); i++)
- {
- /* Row-major ever since gd 1.3 */
- im->pixels[i] = (unsigned char *) gdCalloc (
- sx, sizeof (unsigned char));
- }
- im->sx = sx;
- im->sy = sy;
- 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;
- /* 2.0.2: alpha blending is now on by default, and saving of alpha is
- off by default. This allows font antialiasing to work as expected
- on the first try in JPEGs -- quite important -- and also allows
- for smaller PNGs when saving of alpha channel is not really
- desired, which it usually isn't! */
- im->saveAlphaFlag = 0;
- im->alphaBlendingFlag = 1;
- im->thick = 1;
- return im;
-}
-
-void
-gdImageDestroy (gdImagePtr im)
-{
- int i;
- if (im->pixels)
- {
- for (i = 0; (i < im->sy); i++)
- {
- gdFree (im->pixels[i]);
+gdImagePtr gdImageCreate (int sx, int sy)
+{
+ int i;
+ gdImagePtr im;
+ im = (gdImage *) gdMalloc(sizeof(gdImage));
+ memset(im, 0, sizeof(gdImage));
+ /* Row-major ever since gd 1.3 */
+ im->pixels = (unsigned char **) gdMalloc(sizeof(unsigned char *) * sy);
+ im->AA_opacity = (unsigned char **) gdMalloc(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++) {
+ /* Row-major ever since gd 1.3 */
+ im->pixels[i] = (unsigned char *) gdCalloc(sx, sizeof(unsigned char));
+ im->AA_opacity[i] = (unsigned char *) gdCalloc(sx, sizeof(unsigned char));
+ }
+ im->sx = sx;
+ im->sy = sy;
+ im->colorsTotal = 0;
+ im->transparent = (-1);
+ im->interlace = 0;
+ im->thick = 1;
+ im->AA = 0;
+ im->AA_polygon = 0;
+ 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;
+ im->cx1 = 0;
+ im->cy1 = 0;
+ im->cx2 = im->sx - 1;
+ im->cy2 = im->sy - 1;
+ 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->AA_opacity = (unsigned char **) gdMalloc(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++) {
+ im->tpixels[i] = (int *) gdCalloc(sx, sizeof(int));
+ im->AA_opacity[i] = (unsigned char *) gdCalloc(sx, sizeof(unsigned char));
+ }
+ im->sx = sx;
+ im->sy = sy;
+ im->transparent = (-1);
+ im->interlace = 0;
+ im->trueColor = 1;
+ /* 2.0.2: alpha blending is now on by default, and saving of alpha is
+ * off by default. This allows font antialiasing to work as expected
+ * on the first try in JPEGs -- quite important -- and also allows
+ * for smaller PNGs when saving of alpha channel is not really
+ * desired, which it usually isn't!
+ */
+ im->saveAlphaFlag = 0;
+ im->alphaBlendingFlag = 1;
+ im->thick = 1;
+ im->AA = 0;
+ im->AA_polygon = 0;
+ im->cx1 = 0;
+ im->cy1 = 0;
+ im->cx2 = im->sx - 1;
+ im->cy2 = im->sy - 1;
+ return im;
+}
+
+void gdImageDestroy (gdImagePtr im)
+{
+ int i;
+ if (im->pixels) {
+ for (i = 0; i < im->sy; i++) {
+ gdFree(im->pixels[i]);
+ }
+ gdFree(im->pixels);
}
- gdFree (im->pixels);
- }
- if (im->tpixels)
- {
- for (i = 0; (i < im->sy); i++)
- {
- gdFree (im->tpixels[i]);
+ if (im->tpixels) {
+ for (i = 0; i < im->sy; i++) {
+ gdFree(im->tpixels[i]);
+ }
+ gdFree(im->tpixels);
}
- gdFree (im->tpixels);
- }
- if (im->polyInts)
- {
- gdFree (im->polyInts);
- }
- if (im->style)
- {
- gdFree (im->style);
- }
- gdFree (im);
+ if (im->AA_opacity) {
+ for (i = 0; i < im->sy; i++) {
+ gdFree(im->AA_opacity[i]);
+ }
+ gdFree(im->AA_opacity);
+ }
+ if (im->polyInts) {
+ gdFree(im->polyInts);
+ }
+ if (im->style) {
+ gdFree(im->style);
+ }
+ gdFree(im);
}
-int
-gdImageColorClosest (gdImagePtr im, int r, int g, int b)
+int gdImageColorClosest (gdImagePtr im, int r, int g, int b)
{
- return gdImageColorClosestAlpha (im, r, g, b, gdAlphaOpaque);
+ return gdImageColorClosestAlpha (im, r, g, b, gdAlphaOpaque);
}
-int
-gdImageColorClosestAlpha (gdImagePtr im, int r, int g, int b, int a)
+int gdImageColorClosestAlpha (gdImagePtr im, int r, int g, int b, int a)
{
- int i;
- 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])
- {
- continue;
+ int i;
+ long rd, gd, bd, ad;
+ int ct = (-1);
+ int first = 1;
+ long mindist = 0;
+
+ if (im->trueColor) {
+ return gdTrueColorAlpha(r, g, b, a);
}
- rd = (im->red[i] - r);
- gd = (im->green[i] - g);
- bd = (im->blue[i] - b);
- /* gd 2.02: whoops, was - b (thanks to David Marwood) */
- ad = (im->blue[i] - a);
- dist = rd * rd + gd * gd + bd * bd + ad * ad;
- if (first || (dist < mindist))
- {
- mindist = dist;
- ct = i;
- first = 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;
+ /* gd 2.02: whoops, was - b (thanks to David Marwood) */
+ ad = im->blue[i] - a;
+ dist = rd * rd + gd * gd + bd * bd + ad * ad;
+ if (first || (dist < mindist)) {
+ mindist = dist;
+ ct = i;
+ first = 0;
+ }
}
- }
- return ct;
+ return ct;
}
/* This code is taken from http://www.acm.org/jgt/papers/SmithLyons96/hwb_rgb.html, an article
*/
typedef struct
{
- float R, G, B;
+ float R, G, B;
}
RGBType;
typedef struct
- {
- float H, W, B;
- }
+{
+ float H, W, B;
+}
HWBType;
-static HWBType *
-RGB_to_HWB (RGBType RGB, HWBType * HWB)
+static HWBType * RGB_to_HWB (RGBType RGB, HWBType * HWB)
{
+ /*
+ * RGB are each on [0, 1]. W and B are returned on [0, 1] and H is
+ * returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B.
+ */
- /*
- * RGB are each on [0, 1]. W and B are returned on [0, 1] and H is
- * returned on [0, 6]. Exception: H is returned UNDEFINED if W == 1 - B.
- */
-
- float R = RGB.R, G = RGB.G, B = RGB.B, w, v, b, f;
- int i;
-
- w = MIN3 (R, G, B);
- v = MAX3 (R, G, B);
- b = 1 - v;
- if (v == w)
- RETURN_HWB (HWB_UNDEFINED, w, b);
- f = (R == w) ? G - B : ((G == w) ? B - R : R - G);
- i = (R == w) ? 3 : ((G == w) ? 5 : 1);
- RETURN_HWB (i - f / (v - w), w, b);
+ float R = RGB.R, G = RGB.G, B = RGB.B, w, v, b, f;
+ int i;
+ w = MIN3 (R, G, B);
+ v = MAX3 (R, G, B);
+ b = 1 - v;
+ if (v == w) {
+ RETURN_HWB(HWB_UNDEFINED, w, b);
+ }
+ f = (R == w) ? G - B : ((G == w) ? B - R : R - G);
+ i = (R == w) ? 3 : ((G == w) ? 5 : 1);
+
+ RETURN_HWB(i - f / (v - w), w, b);
}
-static float
-HWB_Diff (int r1, int g1, int b1, int r2, int g2, int b2)
+static float HWB_Diff (int r1, int g1, int b1, int r2, int g2, int b2)
{
- RGBType RGB1, RGB2;
- HWBType HWB1, HWB2;
- float diff;
+ RGBType RGB1, RGB2;
+ HWBType HWB1, HWB2;
+ float diff;
- SETUP_RGB (RGB1, r1, g1, b1);
- SETUP_RGB (RGB2, r2, g2, b2);
+ SETUP_RGB(RGB1, r1, g1, b1);
+ SETUP_RGB(RGB2, r2, g2, b2);
- RGB_to_HWB (RGB1, &HWB1);
- RGB_to_HWB (RGB2, &HWB2);
+ RGB_to_HWB(RGB1, &HWB1);
+ RGB_to_HWB(RGB2, &HWB2);
- /*
- * I made this bit up; it seems to produce OK results, and it is certainly
- * more visually correct than the current RGB metric. (PJW)
- */
+ /*
+ * I made this bit up; it seems to produce OK results, and it is certainly
+ * more visually correct than the current RGB metric. (PJW)
+ */
- if ((HWB1.H == HWB_UNDEFINED) || (HWB2.H == HWB_UNDEFINED))
- {
- diff = 0.0f; /* Undefined hues always match... */
- }
- else
- {
- diff = fabsf(HWB1.H - HWB2.H);
- if (diff > 3.0f)
- {
- diff = 6.0f - diff; /* Remember, it's a colour circle */
+ if ((HWB1.H == HWB_UNDEFINED) || (HWB2.H == HWB_UNDEFINED)) {
+ diff = 0.0f; /* Undefined hues always match... */
+ } else {
+ diff = fabsf(HWB1.H - HWB2.H);
+ if (diff > 3.0f) {
+ diff = 6.0f - diff; /* Remember, it's a colour circle */
+ }
}
- }
- diff = diff * diff + (HWB1.W - HWB2.W) * (HWB1.W - HWB2.W) + (HWB1.B - HWB2.B) * (HWB1.B - HWB2.B);
+ diff = diff * diff + (HWB1.W - HWB2.W) * (HWB1.W - HWB2.W) + (HWB1.B - HWB2.B) * (HWB1.B - HWB2.B);
- return diff;
+ return diff;
}
* This is not actually used, but is here for completeness, in case someone wants to
* use the HWB stuff for anything else...
*/
-static RGBType *
-HWB_to_RGB (HWBType HWB, RGBType * RGB)
-{
-
- /*
- * H is given on [0, 6] or UNDEFINED. W and B are given on [0, 1].
- * RGB are each returned on [0, 1].
- */
-
- float h = HWB.H, w = HWB.W, b = HWB.B, v, n, f;
- int i;
-
- v = 1 - b;
- if (h == HWB_UNDEFINED)
- RETURN_RGB (v, v, v);
- i = floor (h);
- f = h - i;
- if (i & 1)
- f = 1 - f; /* if i is odd */
- n = w + f * (v - w); /* linear interpolation between w and v */
- switch (i)
- {
- case 6:
- case 0:
- RETURN_RGB (v, n, w);
- case 1:
- RETURN_RGB (n, v, w);
- case 2:
- RETURN_RGB (w, v, n);
- case 3:
- RETURN_RGB (w, n, v);
- case 4:
- RETURN_RGB (n, w, v);
- case 5:
- RETURN_RGB (v, w, n);
- }
+static RGBType * HWB_to_RGB (HWBType HWB, RGBType * RGB)
+{
+ /*
+ * H is given on [0, 6] or UNDEFINED. W and B are given on [0, 1].
+ * RGB are each returned on [0, 1].
+ */
- return RGB;
+ float h = HWB.H, w = HWB.W, b = HWB.B, v, n, f;
+ int i;
+ v = 1 - b;
+ if (h == HWB_UNDEFINED) {
+ RETURN_RGB(v, v, v);
+ }
+ i = floor(h);
+ f = h - i;
+ if (i & 1) {
+ f = 1 - f; /* if i is odd */
+ }
+ n = w + f * (v - w); /* linear interpolation between w and v */
+ switch (i) {
+ case 6:
+ case 0:
+ RETURN_RGB(v, n, w);
+ case 1:
+ RETURN_RGB(n, v, w);
+ case 2:
+ RETURN_RGB(w, v, n);
+ case 3:
+ RETURN_RGB(w, n, v);
+ case 4:
+ RETURN_RGB(n, w, v);
+ case 5:
+ RETURN_RGB(v, w, n);
+ }
+
+ return RGB;
}
#endif
-int
-gdImageColorClosestHWB (gdImagePtr im, int r, int g, int b)
+int gdImageColorClosestHWB (gdImagePtr im, int r, int g, int b)
{
- int i;
- /* long rd, gd, bd; */
- int ct = (-1);
- int first = 1;
- float mindist = 0;
- if (im->trueColor)
- {
- return gdTrueColor (r, g, b);
- }
- for (i = 0; (i < (im->colorsTotal)); i++)
- {
- float dist;
- if (im->open[i])
- {
- continue;
+ int i;
+ /* long rd, gd, bd; */
+ int ct = (-1);
+ int first = 1;
+ float mindist = 0;
+ if (im->trueColor) {
+ return gdTrueColor(r, g, b);
}
- dist = HWB_Diff (im->red[i], im->green[i], im->blue[i], r, g, b);
- if (first || (dist < mindist))
- {
- mindist = dist;
- ct = i;
- first = 0;
+ for (i = 0; i < im->colorsTotal; i++) {
+ float dist;
+ if (im->open[i]) {
+ continue;
+ }
+ dist = HWB_Diff(im->red[i], im->green[i], im->blue[i], r, g, b);
+ if (first || (dist < mindist)) {
+ mindist = dist;
+ ct = i;
+ first = 0;
+ }
}
- }
- return ct;
+ return ct;
}
-int
-gdImageColorExact (gdImagePtr im, int r, int g, int b)
+int gdImageColorExact (gdImagePtr im, int r, int g, int b)
{
- return gdImageColorExactAlpha (im, r, g, b, gdAlphaOpaque);
+ return gdImageColorExactAlpha (im, r, g, b, gdAlphaOpaque);
}
-int
-gdImageColorExactAlpha (gdImagePtr im, int r, int g, int b, int a)
+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;
+ int i;
+ if (im->trueColor) {
+ return gdTrueColorAlpha(r, g, b, a);
}
- if ((im->red[i] == r) &&
- (im->green[i] == g) &&
- (im->blue[i] == b) &&
- (im->alpha[i] == a))
- {
- return 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) && (im->alpha[i] == a)) {
+ return i;
+ }
}
- }
- return -1;
+ return -1;
}
-int
-gdImageColorAllocate (gdImagePtr im, int r, int g, int b)
+int gdImageColorAllocate (gdImagePtr im, int r, int g, int b)
{
- return gdImageColorAllocateAlpha (im, r, g, b, gdAlphaOpaque);
+ return gdImageColorAllocateAlpha (im, r, g, b, gdAlphaOpaque);
}
-int
-gdImageColorAllocateAlpha (gdImagePtr im, int r, int g, int b, int a)
+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;
- break;
+ int i;
+ int ct = (-1);
+ if (im->trueColor) {
+ return gdTrueColorAlpha(r, g, b, a);
}
- }
- if (ct == (-1))
- {
- ct = im->colorsTotal;
- if (ct == gdMaxColors)
- {
- return -1;
+ for (i = 0; i < im->colorsTotal; i++) {
+ if (im->open[i]) {
+ ct = i;
+ break;
+ }
}
- im->colorsTotal++;
- }
- im->red[ct] = r;
- im->green[ct] = g;
- im->blue[ct] = b;
- im->alpha[ct] = a;
- im->open[ct] = 0;
- return ct;
+ 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->alpha[ct] = a;
+ im->open[ct] = 0;
+
+ return ct;
}
/*
* return a color index in one search over the color table.
*/
-int
-gdImageColorResolve (gdImagePtr im, int r, int g, int b)
+int gdImageColorResolve (gdImagePtr im, int r, int g, int b)
{
- return gdImageColorResolveAlpha (im, r, g, b, gdAlphaOpaque);
+ return gdImageColorResolveAlpha(im, r, g, b, gdAlphaOpaque);
}
-int
-gdImageColorResolveAlpha (gdImagePtr im, int r, int g, int b, int a)
+int gdImageColorResolveAlpha (gdImagePtr im, int r, int g, int b, int a)
{
int c;
int ct = -1;
return op; /* Return newly allocated color */
}
-void
-gdImageColorDeallocate (gdImagePtr im, int color)
+void gdImageColorDeallocate (gdImagePtr im, int color)
{
- if (im->trueColor)
- {
- return;
- }
- /* Mark it open. */
- im->open[color] = 1;
+ if (im->trueColor) {
+ return;
+ }
+ /* Mark it open. */
+ im->open[color] = 1;
}
void gdImageColorTransparent (gdImagePtr im, int color)
im->transparent = color;
}
-void
-gdImagePaletteCopy (gdImagePtr to, gdImagePtr from)
+void gdImagePaletteCopy (gdImagePtr to, gdImagePtr from)
{
- int i;
- int x, y, p;
- int xlate[256];
- if (to->trueColor)
- {
- return;
- }
- if (from->trueColor)
- {
- return;
- }
+ int i;
+ int x, y, p;
+ int xlate[256];
+ if (to->trueColor || from->trueColor) {
+ return;
+ }
- for (i = 0; i < 256; i++)
- {
- xlate[i] = -1;
- };
+ for (i = 0; i < 256; i++) {
+ xlate[i] = -1;
+ }
- for (x = 0; x < (to->sx); x++)
- {
- for (y = 0; y < (to->sy); y++)
- {
- p = gdImageGetPixel (to, x, y);
- if (xlate[p] == -1)
- {
- /* 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, %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;
- };
-
- for (i = from->colorsTotal; (i < to->colorsTotal); i++)
- {
- to->open[i] = 1;
- };
+ for (x = 0; x < to->sx; x++) {
+ for (y = 0; y < to->sy; y++) {
+ p = gdImageGetPixel(to, x, y);
+ if (xlate[p] == -1) {
+ /* 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]);
+ }
+ gdImageSetPixel(to, x, y, xlate[p]);
+ }
+ }
- to->colorsTotal = from->colorsTotal;
+ for (i = 0; i < from->colorsTotal; 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;
+ }
+ for (i = from->colorsTotal; i < to->colorsTotal; i++) {
+ to->open[i] = 1;
+ }
+
+ to->colorsTotal = from->colorsTotal;
}
/* 2.0.10: before the drawing routines, some code to clip points that are
return 1;
}
-void
-gdImageSetPixel (gdImagePtr im, int x, int y, int 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)) {
- if (im->trueColor) {
- switch( im->alphaBlendingFlag )
- {
- default:
- case gdEffectReplace :
- im->tpixels[y][x] = color;
- break;
- case gdEffectAlphaBlend :
- im->tpixels[y][x] = gdAlphaBlend(im->tpixels[y][x], color);
- break;
- case gdEffectNormal :
- im->tpixels[y][x] = gdFullAlphaBlend(im->tpixels[y][x], color);
- break;
- case gdEffectOverlay :
- im->tpixels[y][x] = gdLayerOverlay(im->tpixels[y][x], color);
- break;
+ int p;
+ switch (color) {
+ case gdStyled:
+ if (!im->style) {
+ /* Refuse to draw if no style is set. */
+ return;
+ } else {
+ p = im->style[im->stylePos++];
}
- } else {
- im->pixels[y][x] = color;
- }
- }
- break;
- }
+ 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;
+ case gdAntiAliased:
+ gdImageAntiAliasedApply(im, x, y);
+ break;
+ default:
+ if (gdImageBoundsSafe(im, x, y)) {
+ if (im->trueColor) {
+ switch (im->alphaBlendingFlag) {
+ default:
+ case gdEffectReplace:
+ im->tpixels[y][x] = color;
+ break;
+ case gdEffectAlphaBlend:
+ im->tpixels[y][x] = gdAlphaBlend(im->tpixels[y][x], color);
+ break;
+ case gdEffectNormal:
+ im->tpixels[y][x] = gdFullAlphaBlend(im->tpixels[y][x], color);
+ break;
+ case gdEffectOverlay :
+ im->tpixels[y][x] = gdLayerOverlay(im->tpixels[y][x], color);
+ break;
+ }
+ } else {
+ im->pixels[y][x] = color;
+ }
+ }
+ break;
+ }
}
-static int
-gdImageGetTrueColorPixel (gdImagePtr im, int x, int y)
+static 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;
- }
+ 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;
+ }
}
-static void
-gdImageBrushApply (gdImagePtr im, int x, int y)
+static void gdImageBrushApply (gdImagePtr im, int x, int y)
{
int lx, ly;
int hy, hx;
return;
}
- hy = gdImageSY (im->brush) / 2;
+ hy = gdImageSY(im->brush) / 2;
y1 = y - hy;
- y2 = y1 + gdImageSY (im->brush);
- hx = gdImageSX (im->brush) / 2;
+ y2 = y1 + gdImageSY(im->brush);
+ hx = gdImageSX(im->brush) / 2;
x1 = x - hx;
- x2 = x1 + gdImageSX (im->brush);
+ x2 = x1 + gdImageSX(im->brush);
srcy = 0;
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);
- /* 2.0.9, Thomas Winzig: apply simple full transparency */
- if (p != gdImageGetTransparent (im->brush)) {
- gdImageSetPixel (im, lx, ly, p);
- }
- srcx++;
+ if (im->brush->trueColor) {
+ for (ly = y1; ly < y2; ly++) {
+ srcx = 0;
+ for (lx = x1; (lx < x2); lx++) {
+ int p;
+ p = gdImageGetTrueColorPixel(im->brush, srcx, srcy);
+ /* 2.0.9, Thomas Winzig: apply simple full transparency */
+ if (p != gdImageGetTransparent(im->brush)) {
+ gdImageSetPixel(im, lx, ly, p);
+ }
+ srcx++;
+ }
+ srcy++;
+ }
+ } else {
+ /* 2.0.12: Brush palette, image truecolor (thanks to Thorben Kundinger for pointing out the issue) */
+ for (ly = y1; ly < y2; ly++) {
+ srcx = 0;
+ for (lx = x1; lx < x2; lx++) {
+ int p, tc;
+ p = gdImageGetPixel(im->brush, srcx, srcy);
+ tc = gdImageGetTrueColorPixel(im->brush, srcx, srcy);
+ /* 2.0.9, Thomas Winzig: apply simple full transparency */
+ if (p != gdImageGetTransparent(im->brush)) {
+ gdImageSetPixel(im, lx, ly, tc);
+ }
+ srcx++;
+ }
+ srcy++;
}
- srcy++;
}
} else {
- for (ly = y1; (ly < y2); ly++) {
+ for (ly = y1; ly < y2; ly++) {
srcx = 0;
- for (lx = x1; (lx < x2); lx++) {
+ for (lx = x1; lx < x2; lx++) {
int p;
- p = gdImageGetPixel (im->brush, srcx, srcy);
+ p = gdImageGetPixel(im->brush, srcx, srcy);
/* Allow for non-square brushes! */
- if (p != gdImageGetTransparent (im->brush)) {
+ 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),
}
}
-static void
-gdImageTileApply (gdImagePtr im, int x, int y)
+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);
- if (im->trueColor)
- {
- p = gdImageGetTrueColorPixel (im->tile, srcx, srcy);
- gdImageSetPixel (im, x, y, p);
- }
- else
- {
- p = gdImageGetPixel(im->tile, srcx, srcy);
- /* 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 srcx, srcy;
+ int p;
+ if (!im->tile) {
+ return;
+ }
+ srcx = x % gdImageSX(im->tile);
+ srcy = y % gdImageSY(im->tile);
+ if (im->trueColor) {
+ p = gdImageGetTrueColorPixel(im->tile, srcx, srcy);
+ gdImageSetPixel(im, x, y, p);
+ } else {
+ p = gdImageGetPixel(im->tile, srcx, srcy);
+ /* 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)
+static void gdImageAntiAliasedApply (gdImagePtr im, int px, int py)
{
- if (gdImageBoundsSafe (im, x, y))
- {
- if (im->trueColor)
- {
- return im->tpixels[y][x];
+ float p_dist, p_alpha;
+ unsigned char opacity;
+
+ /*
+ * Find the perpendicular distance from point C (px, py) to the line
+ * segment AB that is being drawn. (Adapted from an algorithm from the
+ * comp.graphics.algorithms FAQ.)
+ */
+
+ int LAC_2, LBC_2;
+
+ int Ax_Cx = im->AAL_x1 - px;
+ int Ay_Cy = im->AAL_y1 - py;
+
+ int Bx_Cx = im->AAL_x2 - px;
+ int By_Cy = im->AAL_y2 - py;
+
+ /* Get the squares of the lengths of the segemnts AC and BC. */
+ LAC_2 = (Ax_Cx * Ax_Cx) + (Ay_Cy * Ay_Cy);
+ LBC_2 = (Bx_Cx * Bx_Cx) + (By_Cy * By_Cy);
+
+ if (((im->AAL_LAB_2 + LAC_2) >= LBC_2) && ((im->AAL_LAB_2 + LBC_2) >= LAC_2)) {
+ /* The two angles are acute. The point lies inside the portion of the
+ * plane spanned by the line segment.
+ */
+ p_dist = fabs ((float) ((Ay_Cy * im->AAL_Bx_Ax) - (Ax_Cx * im->AAL_By_Ay)) / im->AAL_LAB);
+ } else {
+ /* The point is past an end of the line segment. It's length from the
+ * segment is the shorter of the lengths from the endpoints, but call
+ * the distance -1, so as not to compute the alpha nor draw the pixel.
+ */
+ p_dist = -1;
}
- else
- {
- return im->pixels[y][x];
+
+ if ((p_dist >= 0) && (p_dist <= (float) (im->thick))) {
+ p_alpha = pow (1.0 - (p_dist / 1.5), 2);
+
+ if (p_alpha > 0) {
+ if (p_alpha >= 1) {
+ opacity = 255;
+ } else {
+ opacity = (unsigned char) (p_alpha * 255.0);
+ }
+ if (!im->AA_polygon || (im->AA_opacity[py][px] < opacity)) {
+ im->AA_opacity[py][px] = opacity;
+ }
+ }
}
- }
- else
- {
- return 0;
- }
}
+
+int gdImageGetPixel (gdImagePtr im, int x, int y)
+{
+ if (gdImageBoundsSafe(im, x, y)) {
+ if (im->trueColor) {
+ return im->tpixels[y][x];
+ } else {
+ return im->pixels[y][x];
+ }
+ } else {
+ return 0;
+ }
+}
+
+void gdImageAABlend (gdImagePtr im)
+{
+ float p_alpha, old_alpha;
+ int color = im->AA_color, color_red, color_green, color_blue;
+ int old_color, old_red, old_green, old_blue;
+ int p_color, p_red, p_green, p_blue;
+ int px, py;
+
+ color_red = gdImageRed(im, color);
+ color_green = gdImageGreen(im, color);
+ color_blue = gdImageBlue(im, color);
+
+ /* Impose the anti-aliased drawing on the image. */
+ for (py = 0; py < im->sy; py++) {
+ for (px = 0; px < im->sx; px++) {
+ if (im->AA_opacity[py][px] != 0) {
+ old_color = gdImageGetPixel(im, px, py);
+
+ if ((old_color != color) && ((old_color != im->AA_dont_blend) || (im->AA_opacity[py][px] == 255))) {
+ /* Only blend with different colors that aren't the dont_blend color. */
+ p_alpha = (float) (im->AA_opacity[py][px]) / 255.0;
+ old_alpha = 1.0 - p_alpha;
+
+ if (p_alpha >= 1.0) {
+ p_color = color;
+ } else {
+ old_red = gdImageRed(im, old_color);
+ old_green = gdImageGreen(im, old_color);
+ old_blue = gdImageBlue(im, old_color);
+
+ p_red = (int) (((float) color_red * p_alpha) + ((float) old_red * old_alpha));
+ p_green = (int) (((float) color_green * p_alpha) + ((float) old_green * old_alpha));
+ p_blue = (int) (((float) color_blue * p_alpha) + ((float) old_blue * old_alpha));
+ p_color = gdImageColorResolve(im, p_red, p_green, p_blue);
+ }
+ gdImageSetPixel(im, px, py, p_color);
+ }
+ }
+ }
+ /* Clear the AA_opacity array behind us. */
+ memset(im->AA_opacity[py], 0, im->sx);
+ }
+}
+
+
/* Bresenham as presented in Foley & Van Dam */
-void
-gdImageLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
+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;
if (!clip_1d(&x1,&y1,&x2,&y2,gdImageSX(im)) || !clip_1d(&y1,&x1,&y2,&x2,gdImageSY(im))) {
return;
}
+
+ /* gdAntiAliased passed as color: set anti-aliased line (AAL) global vars. */
+ if (color == gdAntiAliased) {
+ im->AAL_x1 = x1;
+ im->AAL_y1 = y1;
+ im->AAL_x2 = x2;
+ im->AAL_y2 = y2;
+
+ /* Compute what we can for point-to-line distance calculation later. */
+ im->AAL_Bx_Ax = x2 - x1;
+ im->AAL_By_Ay = y2 - y1;
+ im->AAL_LAB_2 = (im->AAL_Bx_Ax * im->AAL_Bx_Ax) + (im->AAL_By_Ay * im->AAL_By_Ay);
+ im->AAL_LAB = sqrt (im->AAL_LAB_2);
+
+ /* For AA, we must draw pixels outside the width of the line. Keep in
+ * mind that this will be curtailed by cos/sin of theta later.
+ */
+ thick += 4;
+ }
dx = abs(x2 - x1);
dy = abs(y2 - y1);
}
} else {
/* More-or-less vertical. use wid for horizontal stroke */
- wid = (int)(thick * sin (atan2 (dy, dx)));
- if (wid == 0) {
+ /* 2.0.12: Michael Schwartz: divide rather than multiply;
+ TBB: but watch out for /0! */
+ double as = sin(atan2(dy, dx));
+ if (as != 0) {
+ if (!(wid = thick / as)) {
+ wid = 1;
+ }
+ } else {
wid = 1;
}
}
}
}
+
+ /* If this is the only line we are drawing, go ahead and blend. */
+ if (color == gdAntiAliased && !im->AA_polygon) {
+ gdImageAABlend(im);
+ }
}
}
}
-static void dashedSet (gdImagePtr im, int x, int y, int color,
- int *onP, int *dashStepP, int wid, int vert);
+static void dashedSet (gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, int vert);
-void
-gdImageDashedLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
+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 vert;
- int thick = im->thick;
+ int dx, dy, incr1, incr2, d, x, y, xend, yend, xdirflag, ydirflag;
+ int dashStep = 0;
+ int on = 1;
+ int wid;
+ int 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 = (int)(thick * sin (atan2 (dy, dx)));
- vert = 1;
-
- 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, wid, vert);
- 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, wid, vert);
- }
- }
- else
- {
- while (x < xend)
- {
- x++;
- if (d < 0)
- {
- d += incr1;
+ dx = abs(x2 - x1);
+ dy = abs(y2 - y1);
+ if (dy <= dx) {
+ /* More-or-less horizontal. use wid for vertical stroke */
+ /* 2.0.12: Michael Schwartz: divide rather than multiply;
+ TBB: but watch out for /0! */
+ double as = sin(atan2(dy, dx));
+ if (as != 0) {
+ wid = thick / as;
+ } else {
+ wid = 1;
}
- else
- {
- y--;
- d += incr2;
+ wid = (int)(thick * sin(atan2(dy, dx)));
+ vert = 1;
+
+ 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, wid, vert);
- }
- }
- }
- else
- {
- /* More-or-less vertical. use wid for horizontal stroke */
- wid = (int)(thick * sin (atan2 (dy, dx)));
- vert = 0;
-
- 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, wid, vert);
- if (((x2 - x1) * xdirflag) > 0)
- {
- while (y < yend)
- {
- y++;
- if (d < 0)
- {
- d += incr1;
+ dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
+ 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, wid, vert);
+ }
+ } else {
+ while (x < xend) {
+ x++;
+ if (d < 0) {
+ d += incr1;
+ } else {
+ y--;
+ d += incr2;
+ }
+ dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
+ }
}
- else
- {
- x++;
- d += incr2;
+ } else {
+ /* 2.0.12: Michael Schwartz: divide rather than multiply;
+ TBB: but watch out for /0! */
+ double as = sin (atan2 (dy, dx));
+ if (as != 0) {
+ wid = thick / as;
+ } else {
+ wid = 1;
}
- dashedSet (im, x, y, color, &on, &dashStep, wid, vert);
- }
- }
- else
- {
- while (y < yend)
- {
- y++;
- if (d < 0)
- {
- d += incr1;
+ vert = 0;
+
+ 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;
}
- else
- {
- x--;
- d += incr2;
+ dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
+ 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, wid, vert);
+ }
+ } else {
+ while (y < yend) {
+ y++;
+ if (d < 0) {
+ d += incr1;
+ } else {
+ x--;
+ d += incr2;
+ }
+ dashedSet(im, x, y, color, &on, &dashStep, wid, vert);
+ }
}
- 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 wid, int vert)
+static void dashedSet (gdImagePtr im, int x, int y, int color, int *onP, int *dashStepP, int wid, int vert)
{
- int dashStep = *dashStepP;
- int on = *onP;
- int w, wstart;
+ int dashStep = *dashStepP;
+ int on = *onP;
+ int w, wstart;
- dashStep++;
- if (dashStep == gdDashSize)
- {
- dashStep = 0;
- on = !on;
- }
- if (on)
- {
- if (vert)
- {
- wstart = y - wid / 2;
- for (w = wstart; w < wstart + wid; w++)
- gdImageSetPixel (im, x, w, color);
+ dashStep++;
+ if (dashStep == gdDashSize) {
+ dashStep = 0;
+ on = !on;
}
- else
- {
- wstart = x - wid / 2;
- for (w = wstart; w < wstart + wid; w++)
- gdImageSetPixel (im, w, y, color);
+ if (on) {
+ 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;
+ *dashStepP = dashStep;
+ *onP = on;
}
-void
-gdImageChar (gdImagePtr im, gdFontPtr f, int x, int y,
- int c, int color)
+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;
+ int cx, cy;
+ int px, py;
+ int fline;
+ cx = 0;
+ cy = 0;
#ifdef CHARSET_EBCDIC
- c = ASC (c);
+ c = ASC (c);
#endif /*CHARSET_EBCDIC */
- 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++;
+ 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++;
}
- cx = 0;
- cy++;
- }
}
-void
-gdImageCharUp (gdImagePtr im, gdFontPtr f,
- int x, int y, int c, int color)
+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;
+ int cx, cy;
+ int px, py;
+ int fline;
+ cx = 0;
+ cy = 0;
#ifdef CHARSET_EBCDIC
- c = ASC (c);
+ c = ASC (c);
#endif /*CHARSET_EBCDIC */
- 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++;
+ 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++;
}
- cy = 0;
- cx++;
- }
}
-void
-gdImageString (gdImagePtr im, gdFontPtr f,
- int x, int y, unsigned char *s, int color)
+void gdImageString (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color)
{
- int i;
- int l;
- l = strlen ((char *) s);
- for (i = 0; (i < l); i++)
- {
- gdImageChar (im, f, x, y, s[i], color);
- x += f->w;
- }
+ int i;
+ int l;
+ l = strlen ((char *) 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)
+void gdImageStringUp (gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color)
{
- int i;
- int l;
- l = strlen ((char *) s);
- for (i = 0; (i < l); i++)
- {
- gdImageCharUp (im, f, x, y, s[i], color);
- y -= f->w;
- }
+ int i;
+ int l;
+ l = strlen ((char *) 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)
+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;
- }
+ 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)
+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;
- }
+ 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)
+static int strlen16 (unsigned short *s)
{
- int len = 0;
- while (*s)
- {
- s++;
- len++;
- }
- return len;
+ int len = 0;
+ while (*s) {
+ s++;
+ len++;
+ }
+ 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 lsqrt (long n)
{
- long result = (long) sqrt ((double) n);
- return result;
+ long result = (long) sqrt ((double) n);
+ return result;
}
#endif
See gd_arc_f_buggy.c for a better version that doesn't
seem to be bug-free yet. */
-void
-gdImageArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color)
+void gdImageArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color)
{
- if( (s%360)==(e%360) ){
+ if ((s % 360) == (e % 360)) {
gdImageEllipse(im, cx, cy, w, h, color);
} else {
- gdImageFilledArc (im, cx, cy, w, h, s, e, color, gdNoFill);
+ gdImageFilledArc(im, cx, cy, w, h, s, e, color, gdNoFill);
}
}
-void
-gdImageFilledArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style)
+void gdImageFilledArc (gdImagePtr im, int cx, int cy, int w, int h, int s, int e, int color, int style)
{
- gdPoint pts[3];
- int i;
- int lx = 0, ly = 0;
- int fx = 0, fy = 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)
- {
- if (!(style & gdChord))
- {
- if (style & gdNoFill)
- {
- gdImageLine (im, lx, ly, x, y, color);
- }
- else
- {
- /* This is expensive! */
- pts[0].x = lx;
- pts[0].y = ly;
- pts[1].x = x;
- pts[1].y = y;
- pts[2].x = cx;
- pts[2].y = cy;
- gdImageFilledPolygon (im, pts, 3, color);
+ gdPoint pts[3];
+ int i;
+ int lx = 0, ly = 0;
+ int fx = 0, fy = 0;
+
+ while (e < s) {
+ e += 360;
+ }
+ for (i = s; i <= e; i++) {
+ int x, y;
+ x = ((long) gdCosT[i % 360] * (long) w / (2 * 1024)) + cx;
+ y = ((long) gdSinT[i % 360] * (long) h / (2 * 1024)) + cy;
+ if (i != s) {
+ if (!(style & gdChord)) {
+ if (style & gdNoFill) {
+ gdImageLine(im, lx, ly, x, y, color);
+ } else {
+ /* This is expensive! */
+ pts[0].x = lx;
+ pts[0].y = ly;
+ pts[1].x = x;
+ pts[1].y = y;
+ pts[2].x = cx;
+ pts[2].y = cy;
+ gdImageFilledPolygon(im, pts, 3, color);
+ }
+ }
+ } else {
+ fx = x;
+ fy = y;
+ }
+ lx = x;
+ ly = y;
+ }
+ if (style & gdChord) {
+ if (style & gdNoFill) {
+ if (style & gdEdged) {
+ gdImageLine(im, cx, cy, lx, ly, color);
+ gdImageLine(im, cx, cy, fx, fy, color);
+ }
+ gdImageLine(im, fx, fy, lx, ly, color);
+ } else {
+ pts[0].x = fx;
+ pts[0].y = fy;
+ pts[1].x = lx;
+ pts[1].y = ly;
+ pts[2].x = cx;
+ pts[2].y = cy;
+ gdImageFilledPolygon(im, pts, 3, color);
+ }
+ } else {
+ if (style & gdNoFill) {
+ if (style & gdEdged) {
+ gdImageLine(im, cx, cy, lx, ly, color);
+ gdImageLine(im, cx, cy, fx, fy, color);
+ }
}
- }
- }
- else
- {
- fx = x;
- fy = y;
- }
- lx = x;
- ly = y;
- }
- if (style & gdChord)
- {
- if (style & gdNoFill)
- {
- if (style & gdEdged)
- {
- gdImageLine (im, cx, cy, lx, ly, color);
- gdImageLine (im, cx, cy, fx, fy, color);
- }
- gdImageLine (im, fx, fy, lx, ly, color);
- }
- else
- {
- pts[0].x = fx;
- pts[0].y = fy;
- pts[1].x = lx;
- pts[1].y = ly;
- pts[2].x = cx;
- pts[2].y = cy;
- gdImageFilledPolygon (im, pts, 3, color);
- }
- }
- else
- {
- if (style & gdNoFill)
- {
- if (style & gdEdged)
- {
- gdImageLine (im, cx, cy, lx, ly, color);
- gdImageLine (im, cx, cy, fx, fy, color);
- }
}
- }
}
}
}
-void
-gdImageFill (gdImagePtr im, int x, int y, int color)
+void gdImageFill (gdImagePtr im, int x, int y, int color)
{
- int lastBorder;
- int old;
- int leftLimit, rightLimit;
- int i;
-
- if (x >= im->sx) {
- x = im->sx - 1;
- }
-
- if (y >= im->sy) {
- y = im->sy - 1;
- }
+ 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);
- if (im->trueColor)
- {
- tileColor = p;
+ if (x >= im->sx) {
+ x = im->sx - 1;
}
- else
- {
- if (im->tile->trueColor)
- {
- tileColor = gdImageColorResolveAlpha (im,
- gdTrueColorGetRed (p),
- gdTrueColorGetGreen (p),
- gdTrueColorGetBlue (p),
- gdTrueColorGetAlpha (p));
- }
- else
- {
- tileColor = im->tileColorMap[p];
- }
+
+ if (y >= im->sy) {
+ y = im->sy - 1;
}
- if (old == tileColor)
- {
- /* Nothing to be done */
- return;
+
+ 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);
+ 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;
+ }
+ } else {
+ if (old == color) {
+ /* 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;
}
- }
- /* Seek left */
- leftLimit = (-1);
- for (i = x; (i >= 0); i--)
- {
- if (gdImageGetPixel (im, i, y) != old)
- {
- break;
+ if (leftLimit == (-1)) {
+ return;
}
- 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;
+ /* 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;
}
- 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;
+ /* 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 && c == old) {
+ gdImageFill (im, i, y - 1, color);
+ lastBorder = 0;
+ } else if (c != old) {
+ lastBorder = 1;
+ }
}
- }
- 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;
+ /* Below */
+ if (y < (im->sy - 1)) {
+ lastBorder = 1;
+ for (i = leftLimit; i <= rightLimit; i++) {
+ int c;
+ c = gdImageGetPixel(im, i, y + 1);
+ if (lastBorder && c == old) {
+ gdImageFill(im, i, y + 1, color);
+ lastBorder = 0;
+ } else if (c != old) {
+ lastBorder = 1;
+ }
}
- }
- else if (c != old)
- {
- lastBorder = 1;
- }
}
- }
}
-void
-gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
+void gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int 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;
+ int x1h = x1, x1v = x1, y1h = y1, y1v = y1, x2h = x2, x2v = x2, y2h = y2, y2v = y2;
+ int thick = im->thick;
- if (y1 < y2)
- {
- y1v = y1h - half;
- y2v = y2h + half1 - 1;
- }
- else
- {
- y1v = y1h + half1 - 1;
- y2v = y2h - half;
+ 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);
+ 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)
+void gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)
{
int x, y;
}
}
-void
-gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h)
+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];
+
if (dst->trueColor) {
/* 2.0: much easier when the destination is truecolor. */
/* 2.0.10: needs a transparent-index check that is still valid if
toy = dstY;
for (y = srcY; (y < (srcY + h)); y++) {
tox = dstX;
- for (x = srcX; (x < (srcX + w)); x++) {
+ for (x = srcX; x < (srcX + w); x++) {
int nc;
c = gdImageGetPixel (src, x, y);
/* Get best match possible. */
- nc = gdImageColorResolveAlpha (dst, gdTrueColorGetRed(c), gdTrueColorGetGreen(c), gdTrueColorGetBlue(c), gdTrueColorGetAlpha(c));
+ nc = gdImageColorResolveAlpha(dst, gdTrueColorGetRed(c), gdTrueColorGetGreen(c), gdTrueColorGetBlue(c), gdTrueColorGetAlpha(c));
- gdImageSetPixel (dst, tox, toy, nc);
+ gdImageSetPixel(dst, tox, toy, nc);
tox++;
}
toy++;
}
/* Palette based to palette based */
- for (i = 0; (i < gdMaxColors); i++) {
+ for (i = 0; i < gdMaxColors; i++) {
colorMap[i] = (-1);
}
toy = dstY;
- for (y = srcY; (y < (srcY + h)); y++) {
+ for (y = srcY; y < (srcY + h); y++) {
tox = dstX;
- for (x = srcX; (x < (srcX + w)); x++) {
+ for (x = srcX; x < (srcX + w); x++) {
int nc;
int mapTo;
c = gdImageGetPixel (src, x, y);
/* 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)
+void gdImageCopyMerge (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
{
+ int c, dc;
+ int x, y;
+ int tox, toy;
+ int ncR, ncG, ncB;
+ toy = dstY;
- int c, dc;
- int x, y;
- int tox, toy;
- int ncR, ncG, ncB;
- 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;
- }
- /* If it's the same image, mapping is trivial */
- if (dst == src)
- {
- nc = c;
- }
- else
- {
- dc = gdImageGetPixel (dst, tox, toy);
+ 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;
+ }
+ /*
+ * If it's the same image, mapping is NOT trivial since we
+ * merge with greyscale target, but if pct is 100, the grey
+ * value is not used, so it becomes trivial. pjw 2.0.12.
+ */
+ if (dst == src && pct == 100) {
+ nc = c;
+ } else {
+ dc = gdImageGetPixel(dst, tox, toy);
- ncR = (int)(gdImageRed (src, c) * (pct / 100.0f)
- + gdImageRed (dst, dc) * ((100 - pct) / 100.0f));
- ncG = (int)(gdImageGreen (src, c) * (pct / 100.0f)
- + gdImageGreen (dst, dc) * ((100 - pct) / 100.0f));
- ncB = (int)(gdImageBlue (src, c) * (pct / 100.0f)
- + gdImageBlue (dst, dc) * ((100 - pct) / 100.0f));
+ ncR = (int)(gdImageRed (src, c) * (pct / 100.0f) + ((100 - pct) / 100.0f));
+ ncG = (int)(gdImageGreen (src, c) * (pct / 100.0f) + ((100 - pct) / 100.0f));
+ ncB = (int)(gdImageBlue (src, c) * (pct / 100.0f) + ((100 - pct) / 100.0f));
- /* Find a reasonable color */
- nc = gdImageColorResolve (dst, ncR, ncG, ncB);
- }
- gdImageSetPixel (dst, tox, toy, nc);
- tox++;
+ /* Find a reasonable color */
+ nc = gdImageColorResolve (dst, ncR, ncG, ncB);
+ }
+ gdImageSetPixel (dst, tox, toy, nc);
+ tox++;
+ }
+ toy++;
}
- toy++;
- }
}
/* 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)
+void gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int w, int h, int pct)
{
int c, dc;
int x, y;
}
}
-void
-gdImageCopyResized (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)
+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;
gdImageSkewY(dst2, dst1, u, iShear, (dOffset - iShear), clrBack);
}
-
/* 3rd shear */
gdImageDestroy(dst1);
}
/* End Rotate function */
-void
-gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c)
+void gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c)
{
int i;
int lx, ly;
if (!n) {
return;
}
+
+ /* Let it be known that we are drawing a polygon so that the opacity
+ * mask doesn't get cleared after each line.
+ */
+ if (c == gdAntiAliased) {
+ im->AA_polygon = 1;
+ }
+
if ( im->antialias) {
draw_line = gdImageAALine;
} else {
lx = p->x;
ly = p->y;
draw_line(im, lx, ly, p[n - 1].x, p[n - 1].y, c);
- for (i = 1; (i < n); i++) {
+ for (i = 1; i < n; i++) {
p++;
draw_line(im, lx, ly, p->x, p->y, c);
lx = p->x;
ly = p->y;
}
+
+ if (c == gdAntiAliased) {
+ im->AA_polygon = 0;
+ gdImageAABlend(im);
+ }
}
int gdCompareInt (const void *a, const void *b);
/* 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. */
-/* That could help to adjust intersections to produce a nice */
-/* interior_extrema. */
-
-void
-gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c)
-{
- int i;
- int y;
- int miny, maxy;
- int x1, y1;
- int x2, y2;
- int ind1, ind2;
- int ints;
- if (!n)
- {
- return;
- }
- if (!im->polyAllocated)
- {
- im->polyInts = (int *) gdMalloc (sizeof (int) * n);
- im->polyAllocated = n;
- }
- if (im->polyAllocated < n)
- {
- while (im->polyAllocated < n)
- {
- im->polyAllocated *= 2;
+/* The intersection finding technique of this code could be improved
+ * by remembering the previous intertersection, and by using the slope.
+ * That could help to adjust intersections to produce a nice
+ * interior_extrema.
+ */
+
+void gdImageFilledPolygon (gdImagePtr im, gdPointPtr p, int n, int c)
+{
+ int i;
+ int y;
+ int miny, maxy;
+ int x1, y1;
+ int x2, y2;
+ int ind1, ind2;
+ int ints;
+ int fill_color;
+
+ if (!n) {
+ return;
}
- im->polyInts = (int *) gdRealloc (im->polyInts,
- sizeof (int) * im->polyAllocated);
- }
- miny = p[0].y;
- maxy = p[0].y;
- for (i = 1; (i < n); i++)
- {
- if (p[i].y < miny)
- {
- miny = p[i].y;
+
+ if (c == gdAntiAliased) {
+ fill_color = im->AA_color;
+ } else {
+ fill_color = c;
}
- if (p[i].y > maxy)
- {
- maxy = p[i].y;
+
+ if (!im->polyAllocated) {
+ im->polyInts = (int *) gdMalloc(sizeof(int) * n);
+ im->polyAllocated = n;
}
- }
- /* Fix in 1.3: count a vertex only once */
- for (y = miny; (y <= maxy); y++)
- {
-/*1.4 int interLast = 0; */
-/* int dirLast = 0; */
-/* int interFirst = 1; */
- ints = 0;
- for (i = 0; (i < n); i++)
- {
- if (!i)
- {
- ind1 = n - 1;
- ind2 = 0;
- }
- else
- {
- ind1 = i - 1;
- ind2 = i;
- }
- y1 = p[ind1].y;
- y2 = p[ind2].y;
- if (y1 < y2)
- {
- x1 = p[ind1].x;
- x2 = p[ind2].x;
- }
- else if (y1 > y2)
- {
- y2 = p[ind1].y;
- y1 = p[ind2].y;
- x2 = p[ind1].x;
- x1 = p[ind2].x;
- }
- else
- {
- continue;
- }
- if ((y >= y1) && (y < y2))
- {
- im->polyInts[ints++] = (y - y1) * (x2 - x1) / (y2 - y1) + x1;
- }
- else if ((y == maxy) && (y > y1) && (y <= y2))
- {
- im->polyInts[ints++] = (y - y1) * (x2 - x1) / (y2 - y1) + x1;
- }
+ if (im->polyAllocated < n) {
+ while (im->polyAllocated < n) {
+ im->polyAllocated *= 2;
+ }
+ im->polyInts = (int *) gdRealloc(im->polyInts, sizeof(int) * im->polyAllocated);
+ }
+ miny = p[0].y;
+ maxy = p[0].y;
+ for (i = 1; i < n; i++) {
+ if (p[i].y < miny) {
+ miny = p[i].y;
+ }
+ if (p[i].y > maxy) {
+ maxy = p[i].y;
+ }
+ }
+ /* Fix in 1.3: count a vertex only once */
+ for (y = miny; y <= maxy; y++) {
+ /*1.4 int interLast = 0; */
+ /* int dirLast = 0; */
+ /* int interFirst = 1; */
+ ints = 0;
+ for (i = 0; i < n; i++) {
+ if (!i) {
+ ind1 = n - 1;
+ ind2 = 0;
+ } else {
+ ind1 = i - 1;
+ ind2 = i;
+ }
+ y1 = p[ind1].y;
+ y2 = p[ind2].y;
+ if (y1 < y2) {
+ x1 = p[ind1].x;
+ x2 = p[ind2].x;
+ } else if (y1 > y2) {
+ y2 = p[ind1].y;
+ y1 = p[ind2].y;
+ x2 = p[ind1].x;
+ x1 = p[ind2].x;
+ } else {
+ continue;
+ }
+ /* Do the following math as float intermediately, and round to ensure
+ * that Polygon and FilledPolygon for the same set of points have the
+ * same footprint.
+ */
+ if (y >= y1 && y < y2) {
+ im->polyInts[ints++] = (float) ((y - y1) * (x2 - x1)) / (float) (y2 - y1) + 0.5 + x1;
+ } else if (y == maxy && y > y1 && y <= y2) {
+ im->polyInts[ints++] = (float) ((y - y1) * (x2 - x1)) / (float) (y2 - y1) + 0.5 + x1;
+ }
+ }
+ qsort(im->polyInts, ints, sizeof(int), gdCompareInt);
+
+ for (i = 0; i < ints; i += 2) {
+ gdImageLine(im, im->polyInts[i], y, im->polyInts[i + 1], y, fill_color);
+ }
}
- qsort (im->polyInts, ints, sizeof (int), gdCompareInt);
- for (i = 0; (i < (ints)); i += 2)
- {
- gdImageLine (im, im->polyInts[i], y,
- im->polyInts[i + 1], y, c);
+ /* If we are drawing this AA, then redraw the border with AA lines. */
+ if (c == gdAntiAliased) {
+ gdImagePolygon(im, p, n, c);
}
- }
}
-int
-gdCompareInt (const void *a, const void *b)
+int gdCompareInt (const void *a, const void *b)
{
- return (*(const int *) a) - (*(const int *) b);
+ return (*(const int *) a) - (*(const int *) b);
}
-void
-gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels)
+void gdImageSetStyle (gdImagePtr im, int *style, int noOfPixels)
{
- if (im->style)
- {
- gdFree (im->style);
- }
- im->style = (int *)
- gdMalloc (sizeof (int) * noOfPixels);
- memcpy (im->style, style, sizeof (int) * noOfPixels);
- im->styleLength = noOfPixels;
- im->stylePos = 0;
+ if (im->style) {
+ gdFree(im->style);
+ }
+ im->style = (int *) gdMalloc(sizeof(int) * noOfPixels);
+ memcpy(im->style, style, sizeof(int) * noOfPixels);
+ im->styleLength = noOfPixels;
+ im->stylePos = 0;
}
-void
-gdImageSetThickness (gdImagePtr im, int thickness)
+void gdImageSetThickness (gdImagePtr im, int thickness)
{
- im->thick = thickness;
+ im->thick = thickness;
}
-void
-gdImageSetBrush (gdImagePtr im, gdImagePtr brush)
+void gdImageSetBrush (gdImagePtr im, gdImagePtr brush)
{
- int i;
- im->brush = brush;
- 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),
- gdImageAlpha (brush, i));
- im->brushColorMap[i] = index;
+ int i;
+ im->brush = brush;
+ 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), gdImageAlpha(brush, i));
+ im->brushColorMap[i] = index;
+ }
}
- }
}
-void
-gdImageSetTile (gdImagePtr im, gdImagePtr tile)
+void gdImageSetTile (gdImagePtr im, gdImagePtr tile)
{
- int i;
- im->tile = tile;
- 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),
- gdImageAlpha (tile, i));
- im->tileColorMap[i] = index;
+ int i;
+ im->tile = tile;
+ 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), gdImageAlpha(tile, i));
+ im->tileColorMap[i] = index;
+ }
}
- }
}
-void
-gdImageInterlace (gdImagePtr im, int interlaceArg)
+void gdImageSetAntiAliased (gdImagePtr im, int c)
{
- im->interlace = interlaceArg;
+ im->AA = 1;
+ im->AA_color = c;
+ im->AA_dont_blend = -1;
}
-int
-gdImageCompare (gdImagePtr im1, gdImagePtr im2)
+void gdImageSetAntiAliasedDontBlend (gdImagePtr im, int c, int dont_blend)
{
- int x, y;
- int p1, p2;
- int cmpStatus = 0;
- int sx, sy;
+ im->AA = 1;
+ im->AA_color = c;
+ im->AA_dont_blend = dont_blend;
+}
- if (im1->interlace != im2->interlace)
- {
- cmpStatus |= GD_CMP_INTERLACE;
- }
- if (im1->transparent != im2->transparent)
- {
- cmpStatus |= GD_CMP_TRANSPARENT;
- }
+void gdImageInterlace (gdImagePtr im, int interlaceArg)
+{
+ im->interlace = interlaceArg;
+}
- if (im1->trueColor != im2->trueColor)
- {
- cmpStatus |= GD_CMP_TRUECOLOR;
- }
+int gdImageCompare (gdImagePtr im1, gdImagePtr im2)
+{
+ int x, y;
+ int p1, p2;
+ int cmpStatus = 0;
+ int sx, sy;
- sx = im1->sx;
- if (im1->sx != im2->sx)
- {
- cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE;
- if (im2->sx < im1->sx)
- {
- sx = im2->sx;
+ if (im1->interlace != im2->interlace) {
+ cmpStatus |= GD_CMP_INTERLACE;
}
- }
- sy = im1->sy;
- if (im1->sy != im2->sy)
- {
- cmpStatus |= GD_CMP_SIZE_Y + GD_CMP_IMAGE;
- if (im2->sy < im1->sy)
- {
- sy = im2->sy;
+ if (im1->transparent != im2->transparent) {
+ cmpStatus |= GD_CMP_TRANSPARENT;
}
- }
- if (im1->colorsTotal != im2->colorsTotal)
- {
- cmpStatus |= GD_CMP_NUM_COLORS;
- }
+ if (im1->trueColor != im2->trueColor) {
+ cmpStatus |= GD_CMP_TRUECOLOR;
+ }
- for (y = 0; (y < sy); y++)
- {
- for (x = 0; (x < sx); x++)
- {
- p1 = im1->pixels[y][x];
- p2 = im2->pixels[y][x];
- if (gdImageRed (im1, p1) != gdImageRed (im2, p2))
- {
- cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
- break;
- }
- if (gdImageGreen (im1, p1) != gdImageGreen (im2, p2))
- {
- cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
- break;
- }
- if (gdImageBlue (im1, p1) != gdImageBlue (im2, p2))
- {
- cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
- break;
- }
+ sx = im1->sx;
+ if (im1->sx != im2->sx) {
+ cmpStatus |= GD_CMP_SIZE_X + GD_CMP_IMAGE;
+ if (im2->sx < im1->sx) {
+ sx = im2->sx;
+ }
+ }
+
+ sy = im1->sy;
+ if (im1->sy != im2->sy) {
+ cmpStatus |= GD_CMP_SIZE_Y + GD_CMP_IMAGE;
+ if (im2->sy < im1->sy) {
+ sy = im2->sy;
+ }
+ }
+
+ if (im1->colorsTotal != im2->colorsTotal) {
+ cmpStatus |= GD_CMP_NUM_COLORS;
+ }
+
+ for (y = 0; y < sy; y++) {
+ for (x = 0; x < sx; x++) {
+ p1 = im1->trueColor ? gdImageTrueColorPixel(im1, x, y) : gdImagePalettePixel(im1, x, y);
+ p2 = im2->trueColor ? gdImageTrueColorPixel(im2, x, y) : gdImagePalettePixel(im2, x, y);
+
+ if (gdImageRed(im1, p1) != gdImageRed(im2, p2)) {
+ cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
+ break;
+ }
+ if (gdImageGreen(im1, p1) != gdImageGreen(im2, p2)) {
+ cmpStatus |= GD_CMP_COLOR + GD_CMP_IMAGE;
+ break;
+ }
+ 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;
- }
+ /* 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;
+ }
}
- if (cmpStatus & GD_CMP_COLOR)
- {
- break;
- };
- }
- return cmpStatus;
+ return cmpStatus;
}
int
gdAlphaBlend (int dst, int src)
{
- return (((((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
+ /* 2.0.12: TBB: alpha in the destination should be a
+ * component of the result. Thanks to Frank Warmerdam for
+ * pointing out the issue.
+ */
+ return ((((gdTrueColorGetAlpha (src) *
+ gdTrueColorGetAlpha (dst)) / gdAlphaMax) << 24) +
+ ((((gdAlphaTransparent - gdTrueColorGetAlpha (src)) *
gdTrueColorGetRed (src) / gdAlphaMax) +
(gdTrueColorGetAlpha (src) *
gdTrueColorGetRed (dst)) / gdAlphaMax) << 16) +
gdTrueColorGetBlue (dst)) / gdAlphaMax));
}
-void
-gdImageAlphaBlending (gdImagePtr im, int alphaBlendingArg)
+void gdImageAlphaBlending (gdImagePtr im, int alphaBlendingArg)
{
- im->alphaBlendingFlag = alphaBlendingArg;
+ im->alphaBlendingFlag = alphaBlendingArg;
}
-void
-gdImageAntialias (gdImagePtr im, int antialias)
+void gdImageAntialias (gdImagePtr im, int antialias)
{
if (im->trueColor){
im->antialias = antialias;
}
}
-void
-gdImageSaveAlpha (gdImagePtr im, int saveAlphaArg)
+void gdImageSaveAlpha (gdImagePtr im, int saveAlphaArg)
{
- im->saveAlphaFlag = saveAlphaArg;
+ im->saveAlphaFlag = saveAlphaArg;
}
-static int
-gdFullAlphaBlend (int dst, int src)
+static int gdFullAlphaBlend (int dst, int src)
{
int a1, a2;
a1 = gdAlphaTransparent - gdTrueColorGetAlpha(src);
);
}
-static int
-gdAlphaBlendColor( int b1, int b2, int a1, int a2 )
+static int gdAlphaBlendColor( int b1, int b2, int a1, int a2 )
{
int c;
int w;
return ( a1 * b1 + ( gdAlphaMax - a1 ) * c ) / gdAlphaMax;
}
-static int
-gdLayerOverlay (int dst, int src)
+static int gdLayerOverlay (int dst, int src)
{
int a1, a2;
a1 = gdAlphaMax - gdTrueColorGetAlpha(dst);
);
}
-static int
-gdAlphaOverlayColor( int src, int dst, int max )
+static int gdAlphaOverlayColor (int src, int dst, int max )
{
/* this function implements the algorithm
*
return dst * src / max;
}
}
+
+void gdImageSetClip (gdImagePtr im, int x1, int y1, int x2, int y2)
+{
+ if (x1 < 0) {
+ x1 = 0;
+ }
+ if (x1 >= im->sx) {
+ x1 = im->sx - 1;
+ }
+ if (x2 < 0) {
+ x2 = 0;
+ }
+ if (x2 >= im->sx) {
+ x2 = im->sx - 1;
+ }
+ if (y1 < 0) {
+ y1 = 0;
+ }
+ if (y1 >= im->sy) {
+ y1 = im->sy - 1;
+ }
+ if (y2 < 0) {
+ y2 = 0;
+ }
+ if (y2 >= im->sy) {
+ y2 = im->sy - 1;
+ }
+ im->cx1 = x1;
+ im->cy1 = y1;
+ im->cx2 = x2;
+ im->cy2 = y2;
+}
+
+void gdImageGetClip (gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P)
+{
+ *x1P = im->cx1;
+ *y1P = im->cy1;
+ *x2P = im->cx2;
+ *y2P = im->cy2;
+}
PNG at the moment; other future formats may also
have that capability. JPEG doesn't. */
int saveAlphaFlag;
+
+
+ /* 2.0.12: anti-aliased globals */
+ int AA;
+ int AA_color;
+ int AA_dont_blend;
+ unsigned char **AA_opacity;
+ int AA_polygon;
+ /* Stored and pre-computed variables for determining the perpendicular
+ * distance from a point to the anti-aliased line being drawn:
+ */
+ int AAL_x1;
+ int AAL_y1;
+ int AAL_x2;
+ int AAL_y2;
+ int AAL_Bx_Ax;
+ int AAL_By_Ay;
+ int AAL_LAB_2;
+ float AAL_LAB;
+
+ /* 2.0.12: simple clipping rectangle. These values must be checked for safety when set; please use gdImageSetClip */
+ int cx1;
+ int cy1;
+ int cx2;
+ int cy2;
} gdImage;
typedef gdImage * gdImagePtr;
This is used in line styles only. */
#define gdTransparent (-6)
+#define gdAntiAliased (-7)
+
/* Functions to manipulate images. */
/* Creates a palette-based image (up to 256 colors). */
int gdImageGetPixel(gdImagePtr im, int x, int y);
+void gdImageAABlend(gdImagePtr im);
+
void gdImageLine(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
void gdImageAALine(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
void gdImageRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
/* Solid bar. Upper left corner first, lower right corner second. */
void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color);
+void gdImageSetClip(gdImagePtr im, int x1, int y1, int x2, int y2);
+void gdImageGetClip(gdImagePtr im, int *x1P, int *y1P, int *x2P, int *y2P);
void gdImageChar(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color);
void gdImageCharUp(gdImagePtr im, gdFontPtr f, int x, int y, int c, int color);
void gdImageString(gdImagePtr im, gdFontPtr f, int x, int y, unsigned char *s, int color);
double ptsize, double angle, int x, int y, char *string);
typedef struct {
- int flags; /* for future expansion logical OR of gdFTEX_ values */
- double linespacing; /* fine tune line spacing for '\n' */
-} gdFTStringExtra, *gdFTStringExtraPtr;
+ double linespacing; /* fine tune line spacing for '\n' */
+ int flags; /* Logical OR of gdFTEX_ values */
+ int charmap; /* TBB: 2.0.12: may be gdFTEX_Unicode,
+ gdFTEX_Shift_JIS, or gdFTEX_Big5;
+ when not specified, maps are searched
+ for in the above order. */
+}
+ gdFTStringExtra, *gdFTStringExtraPtr;
+
#define gdFTEX_LINESPACE 1
+#define gdFTEX_CHARMAP 2
+
+/* These are NOT flags; set one in 'charmap' if you set the gdFTEX_CHARMAP bit in 'flags'. */
+#define gdFTEX_Unicode 0
+#define gdFTEX_Shift_JIS 1
+#define gdFTEX_Big5 2
/* FreeType 2 text output with fine tuning */
char *
void gdImagePaletteCopy(gdImagePtr dst, gdImagePtr src);
void gdImagePng(gdImagePtr im, FILE *out);
void gdImagePngCtx(gdImagePtr im, gdIOCtx *out);
+/* 2.0.12: Compression level: 0-9 or -1, where 0 is NO COMPRESSION at all,
+ * 1 is FASTEST but produces larger files, 9 provides the best
+ * compression (smallest files) but takes a long time to compress, and
+ * -1 selects the default compiled into the zlib library.
+ */
+void gdImagePngEx(gdImagePtr im, FILE * out, int level);
+void gdImagePngCtxEx(gdImagePtr im, gdIOCtx * out, int level);
void gdImageWBMP(gdImagePtr image, int fg, FILE *out);
void gdImageWBMPCtx(gdImagePtr image, int fg, gdIOCtx *out);
/* Best to free this memory with gdFree(), not free() */
void* gdImageGdPtr(gdImagePtr im, int *size);
+void *gdImagePngPtrEx(gdImagePtr im, int *size, int level);
/* Best to free this memory with gdFree(), not free() */
void* gdImageGd2Ptr(gdImagePtr im, int cs, int fmt, int *size);
void gdImageSetBrush(gdImagePtr im, gdImagePtr brush);
void gdImageSetTile(gdImagePtr im, gdImagePtr tile);
+void gdImageSetAntiAliased(gdImagePtr im, int c);
+void gdImageSetAntiAliasedDontBlend(gdImagePtr im, int c, int dont_blend);
void gdImageSetStyle(gdImagePtr im, int *style, int noOfPixels);
/* Line thickness (defaults to 1). Affects lines, ellipses,
rectangles, polygons and so forth. */
/* */
/* Shared code to read color tables from gd file. */
/* */
-int
-_gdGetColors (gdIOCtx * in, gdImagePtr im, int gd2xFlag)
+int _gdGetColors (gdIOCtx * in, gdImagePtr im, int gd2xFlag)
{
- int i;
- if (gd2xFlag)
- {
- if (!gdGetByte (&im->trueColor, in))
- {
- goto fail1;
+ int i;
+ if (gd2xFlag) {
+ int trueColorFlag;
+ if (!gdGetByte(&trueColorFlag, in)) {
+ goto fail1;
+ }
+ /* 2.0.12: detect bad truecolor .gd files created by pre-2.0.12.
+ * Beginning in 2.0.12 truecolor is indicated by the initial 2-byte
+ * signature.
+ */
+ if (trueColorFlag != im->trueColor) {
+ goto fail1;
+ }
+ /* This should have been a word all along */
+ if (!im->trueColor) {
+ if (!gdGetWord(&im->colorsTotal, in)) {
+ goto fail1;
+ }
+ }
+ /* Int to accommodate truecolor single-color transparency */
+ if (!gdGetInt(&im->transparent, in)) {
+ goto fail1;
+ }
+ } else {
+ if (!gdGetByte(&im->colorsTotal, in)) {
+ goto fail1;
+ }
+ if (!gdGetWord(&im->transparent, in)) {
+ goto fail1;
+ }
+ if (im->transparent == 257) {
+ im->transparent = (-1);
+ }
}
- /* This should have been a word all along */
- if (!im->trueColor)
- {
- if (!gdGetWord (&im->colorsTotal, in))
- {
- goto fail1;
- }
- }
- /* Int to accommodate truecolor single-color transparency */
- if (!gdGetInt (&im->transparent, in))
- {
- goto fail1;
- }
- }
- else
- {
- if (!gdGetByte (&im->colorsTotal, in))
- {
- goto fail1;
- }
- if (!gdGetWord (&im->transparent, in))
- {
- goto fail1;
- }
- if (im->transparent == 257)
- {
- im->transparent = (-1);
- }
- }
- GD2_DBG (printf ("Pallette had %d colours (T=%d)\n", im->colorsTotal, im->transparent));
-
- for (i = 0; (i < gdMaxColors); i++)
- {
- if (!gdGetByte (&im->red[i], in))
- {
- goto fail1;
- }
- if (!gdGetByte (&im->green[i], in))
- {
- goto fail1;
- }
- if (!gdGetByte (&im->blue[i], in))
- {
- goto fail1;
+
+ GD2_DBG(printf("Pallette had %d colours (T=%d)\n", im->colorsTotal, im->transparent));
+
+ if (im->trueColor) {
+ return TRUE;
}
- if (gd2xFlag)
- {
- if (!gdGetByte (&im->alpha[i], in))
- {
- goto fail1;
- }
+
+ for (i = 0; i < gdMaxColors; i++) {
+ if (!gdGetByte(&im->red[i], in)) {
+ goto fail1;
+ }
+ if (!gdGetByte(&im->green[i], in)) {
+ goto fail1;
+ }
+ if (!gdGetByte(&im->blue[i], in)) {
+ goto fail1;
+ }
+ if (gd2xFlag) {
+ if (!gdGetByte(&im->alpha[i], in)) {
+ goto fail1;
+ }
+ }
}
- }
- for (i = 0; (i < im->colorsTotal); i++)
- {
- im->open[i] = 0;
- };
+ for (i = 0; i < im->colorsTotal; i++) {
+ im->open[i] = 0;
+ }
- return TRUE;
+ return TRUE;
fail1:
- return FALSE;
+ return FALSE;
}
/* */
/* Use the common basic header info to make the image object. */
-/* This is also called from _gd2CreateFromFile */
/* */
-static
- gdImagePtr
-_gdCreateFromFile (gdIOCtx * in, int *sx, int *sy)
+static gdImagePtr _gdCreateFromFile (gdIOCtx * in, int *sx, int *sy)
{
- gdImagePtr im;
- int gd2xFlag = 0;
- if (!gdGetWord (sx, in))
- {
- goto fail1;
- }
- if (*sx == 65535)
- {
- /* This is a gd 2.0 .gd file */
- gd2xFlag = 1;
- if (!gdGetWord (sx, in))
- {
- goto fail1;
- }
- }
- if (!gdGetWord (sy, in))
- {
- goto fail1;
- }
+ gdImagePtr im;
+ int gd2xFlag = 0;
+ int trueColorFlag = 0;
- GD2_DBG (printf ("Image is %dx%d\n", *sx, *sy));
+ if (!gdGetWord(sx, in)) {
+ goto fail1;
+ }
+ if (*sx == 65535 || *sx == 65534) {
+ /* This is a gd 2.0 .gd file */
+ gd2xFlag = 1;
+ /* 2.0.12: 65534 signals a truecolor .gd file. There is a slight redundancy here but we can live with it. */
+ if (*sx == 65534) {
+ trueColorFlag = 1;
+ }
+ if (!gdGetWord(sx, in)) {
+ goto fail1;
+ }
+ }
+ if (!gdGetWord(sy, in)) {
+ goto fail1;
+ }
- im = gdImageCreate (*sx, *sy);
+ GD2_DBG(printf("Image is %dx%d\n", *sx, *sy));
- if (!_gdGetColors (in, im, gd2xFlag))
- {
- goto fail2;
- }
+ if (trueColorFlag) {
+ im = gdImageCreateTrueColor(*sx, *sy);
+ } else {
+ im = gdImageCreate(*sx, *sy);
+ }
+ if (!_gdGetColors(in, im, gd2xFlag)) {
+ goto fail2;
+ }
- return im;
+ return im;
fail2:
- gdImageDestroy (im);
+ gdImageDestroy(im);
fail1:
- return 0;
+ return 0;
}
-gdImagePtr
-gdImageCreateFromGd (FILE * inFile)
+gdImagePtr gdImageCreateFromGd (FILE * inFile)
{
- gdImagePtr im;
- gdIOCtx *in;
+ gdImagePtr im;
+ gdIOCtx *in;
- in = gdNewFileCtx (inFile);
- im = gdImageCreateFromGdCtx (in);
+ in = gdNewFileCtx(inFile);
+ im = gdImageCreateFromGdCtx(in);
- in->gd_free (in);
+ in->gd_free(in);
- return im;
+ return im;
}
-gdImagePtr
-gdImageCreateFromGdCtx (gdIOCtxPtr in)
+gdImagePtr gdImageCreateFromGdCtx (gdIOCtxPtr in)
{
- int sx, sy;
- int x, y;
- gdImagePtr im;
-
- /* Read the header */
- im = _gdCreateFromFile (in, &sx, &sy);
-
- if (im == NULL)
- {
- goto fail1;
- };
-
- /* Then the data... */
- for (y = 0; (y < sy); y++)
- {
- for (x = 0; (x < sx); x++)
- {
- int ch;
- ch = gdGetC (in);
- if (ch == EOF)
- {
- goto fail2;
- }
- /* ROW-MAJOR IN GD 1.3 */
- im->pixels[y][x] = ch;
+ int sx, sy;
+ int x, y;
+ gdImagePtr im;
+
+ /* Read the header */
+ im = _gdCreateFromFile(in, &sx, &sy);
+
+ if (im == NULL) {
+ goto fail1;
}
- }
- return im;
+ /* Then the data... */
+ /* 2.0.12: support truecolor properly in .gd as well as in .gd2. Problem reported by Andreas Pfaller. */
+ if (im->trueColor) {
+ for (y = 0; y < sy; y++) {
+ for (x = 0; x < sx; x++) {
+ int pix;
+ if (!gdGetInt(&pix, in)) {
+ goto fail2;
+ }
+ im->tpixels[y][x] = pix;
+ }
+ }
+ } else {
+ for (y = 0; y < sy; y++) {
+ for (x = 0; x < sx; x++) {
+ int ch;
+ ch = gdGetC(in);
+ if (ch == EOF) {
+ goto fail2;
+ }
+ /* ROW-MAJOR IN GD 1.3 */
+ im->pixels[y][x] = ch;
+ }
+ }
+ }
+
+ return im;
fail2:
- gdImageDestroy (im);
+ gdImageDestroy (im);
fail1:
- return 0;
+ return 0;
}
-void
-_gdPutColors (gdImagePtr im, gdIOCtx * out)
+void _gdPutColors (gdImagePtr im, gdIOCtx * out)
{
- int i;
-
- gdPutC (im->trueColor, out);
- if (!im->trueColor)
- {
- gdPutWord (im->colorsTotal, out);
- }
- gdPutInt (im->transparent, out);
- if (!im->trueColor)
- {
- for (i = 0; (i < gdMaxColors); i++)
- {
- gdPutC ((unsigned char) im->red[i], out);
- gdPutC ((unsigned char) im->green[i], out);
- gdPutC ((unsigned char) im->blue[i], out);
- gdPutC ((unsigned char) im->alpha[i], out);
+ int i;
+
+ gdPutC(im->trueColor, out);
+ if (!im->trueColor) {
+ gdPutWord(im->colorsTotal, out);
+ }
+ gdPutInt(im->transparent, out);
+ if (!im->trueColor) {
+ for (i = 0; i < gdMaxColors; i++) {
+ gdPutC((unsigned char) im->red[i], out);
+ gdPutC((unsigned char) im->green[i], out);
+ gdPutC((unsigned char) im->blue[i], out);
+ gdPutC((unsigned char) im->alpha[i], out);
+ }
}
- }
}
-static
-void
-_gdPutHeader (gdImagePtr im, gdIOCtx * out)
+static void _gdPutHeader (gdImagePtr im, gdIOCtx * out)
{
- /* 65535 indicates this is a gd 2.x .gd file. */
- gdPutWord (65535, out);
- gdPutWord (im->sx, out);
- gdPutWord (im->sy, out);
-
- _gdPutColors (im, out);
+ /* 65535 indicates this is a gd 2.x .gd file.
+ * 2.0.12: 65534 indicates truecolor.
+ */
+ if (im->trueColor) {
+ gdPutWord(65534, out);
+ } else {
+ gdPutWord(65535, out);
+ }
+ gdPutWord(im->sx, out);
+ gdPutWord(im->sy, out);
+ _gdPutColors(im, out);
}
-static void
-_gdImageGd (gdImagePtr im, gdIOCtx * out)
+static void _gdImageGd (gdImagePtr im, gdIOCtx * out)
{
- int x, y;
-
- _gdPutHeader (im, out);
-
- for (y = 0; (y < im->sy); y++)
- {
- for (x = 0; (x < im->sx); x++)
- {
- /* ROW-MAJOR IN GD 1.3 */
- if (im->trueColor)
- {
- gdPutInt (im->tpixels[y][x], out);
- }
- else
- {
- gdPutC ((unsigned char) im->pixels[y][x], out);
- }
+ int x, y;
+
+ _gdPutHeader(im, out);
+
+ for (y = 0; y < im->sy; y++) {
+ for (x = 0; x < im->sx; x++) {
+ /* ROW-MAJOR IN GD 1.3 */
+ if (im->trueColor) {
+ gdPutInt(im->tpixels[y][x], out);
+ } else {
+ gdPutC((unsigned char) im->pixels[y][x], out);
+ }
+ }
}
- }
}
-void
-gdImageGd (gdImagePtr im, FILE * outFile)
+void gdImageGd (gdImagePtr im, FILE * outFile)
{
- gdIOCtx *out = gdNewFileCtx (outFile);
- _gdImageGd (im, out);
- out->gd_free (out);
+ gdIOCtx *out = gdNewFileCtx(outFile);
+ _gdImageGd(im, out);
+ out->gd_free(out);
}
-void *
-gdImageGdPtr (gdImagePtr im, int *size)
+void *gdImageGdPtr (gdImagePtr im, int *size)
{
- void *rv;
- gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
- _gdImageGd (im, out);
- rv = gdDPExtractData (out, size);
- out->gd_free (out);
- return rv;
+ void *rv;
+ gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
+ _gdImageGd(im, out);
+ rv = gdDPExtractData(out, size);
+ out->gd_free(out);
+ return rv;
}
if (gd2_compressed(fmt)) {
chunkLen = chunkMax;
- if (!_gd2ReadChunk(chunkIdx[chunkNum].offset, compBuf, chunkIdx[chunkNum].size, chunkBuf, &chunkLen, in)) {
+ if (!_gd2ReadChunk(chunkIdx[chunkNum].offset, compBuf, chunkIdx[chunkNum].size, (char *) chunkBuf, &chunkLen, in)) {
GD2_DBG(php_gd_error("Error reading comproessed chunk\n"));
goto fail2;
}
int x, y, ylo, yhi, xlo, xhi;
int dstart, dpos;
int i;
- int ch, vers, fmt;
+ /* 2.0.12: unsigned is correct; fixes problems with color munging. Thanks to Steven Brown. */
+ unsigned int ch;
+ int vers, fmt;
t_chunk_info *chunkIdx = NULL;
- char *chunkBuf = NULL;
+ unsigned char *chunkBuf = NULL;
int chunkNum;
int chunkMax = 0;
uLongf chunkLen;
#define GD_IO_H 1
#include <stdio.h>
+
+#ifdef VMS
+#define Putchar gdPutchar
+#endif
typedef struct gdIOCtx {
int (*getC)(struct gdIOCtx*);
} jmpbuf_wrapper;
/* Called by the IJG JPEG library upon encountering a fatal error */
-static void
-fatal_jpeg_error (j_common_ptr cinfo)
+static void fatal_jpeg_error (j_common_ptr cinfo)
{
jmpbuf_wrapper *jmpbufw;
* library documentation for more details.
*/
-void
-gdImageJpeg (gdImagePtr im, FILE * outFile, int quality)
+void gdImageJpeg (gdImagePtr im, FILE * outFile, int quality)
{
gdIOCtx *out = gdNewFileCtx (outFile);
gdImageJpegCtx (im, out, quality);
out->gd_free (out);
}
-void *
-gdImageJpegPtr (gdImagePtr im, int *size, int quality)
+void *gdImageJpegPtr (gdImagePtr im, int *size, int quality)
{
void *rv;
gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
void jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile);
-void
-gdImageJpegCtx (gdImagePtr im, gdIOCtx * outfile, int quality)
+void gdImageJpegCtx (gdImagePtr im, gdIOCtx * outfile, int quality)
{
struct jpeg_compress_struct cinfo;
struct jpeg_error_mgr jerr;
gdFree (row);
}
-gdImagePtr
-gdImageCreateFromJpeg (FILE * inFile)
+gdImagePtr gdImageCreateFromJpeg (FILE * inFile)
{
gdImagePtr im;
- gdIOCtx *in = gdNewFileCtx (inFile);
- im = gdImageCreateFromJpegCtx (in);
+ gdIOCtx *in = gdNewFileCtx(inFile);
+ im = gdImageCreateFromJpegCtx(in);
in->gd_free (in);
return im;
* Create a gd-format image from the JPEG-format INFILE. Returns the
* image, or NULL upon error.
*/
-gdImagePtr
-gdImageCreateFromJpegCtx (gdIOCtx * infile)
+gdImagePtr gdImageCreateFromJpegCtx (gdIOCtx * infile)
{
struct jpeg_decompress_struct cinfo;
struct jpeg_error_mgr jerr;
* before any data is actually read.
*/
-void
-init_source (j_decompress_ptr cinfo)
+void init_source (j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
#define END_JPEG_SEQUENCE "\r\n[*]--:END JPEG:--[*]\r\n"
-safeboolean
-fill_input_buffer (j_decompress_ptr cinfo)
+safeboolean fill_input_buffer (j_decompress_ptr cinfo)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
- size_t nbytes = 0;
-
- /* size_t got; */
+ /* 2.0.12: signed size. Thanks to Geert Jansen */
+ ssize_t nbytes = 0;
+
+ /* ssize_t got; */
/* char *s; */
- memset (src->buffer, 0, INPUT_BUF_SIZE);
-
+ memset(src->buffer, 0, INPUT_BUF_SIZE);
+
while (nbytes < INPUT_BUF_SIZE) {
- int got = gdGetBuf (src->buffer + nbytes, INPUT_BUF_SIZE - nbytes, src->infile);
-
- if ((got == EOF) || (got == 0)) {
- /* EOF or error. If we got any data, don't worry about it. If we didn't, then this is unexpected. */
+ int got = gdGetBuf(src->buffer + nbytes, INPUT_BUF_SIZE - nbytes, src->infile);
+
+ if (got == EOF || got == 0) {
+ /* EOF or error. If we got any data, don't worry about it. If we didn't, then this is unexpected. */
if (!nbytes) {
nbytes = -1;
}
* buffer is the application writer's problem.
*/
-void
-skip_input_data (j_decompress_ptr cinfo, long num_bytes)
+void skip_input_data (j_decompress_ptr cinfo, long num_bytes)
{
my_src_ptr src = (my_src_ptr) cinfo->src;
* for error exit.
*/
-void
-term_source (j_decompress_ptr cinfo)
+void term_source (j_decompress_ptr cinfo)
{
#if 0
* never used */
* for closing it after finishing decompression.
*/
-void
-jpeg_gdIOCtx_src (j_decompress_ptr cinfo, gdIOCtx * infile)
+void jpeg_gdIOCtx_src (j_decompress_ptr cinfo, gdIOCtx * infile)
{
my_src_ptr src;
* before any data is actually written.
*/
-void
-init_destination (j_compress_ptr cinfo)
+void init_destination (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
* write it out when emptying the buffer externally.
*/
-safeboolean
-empty_output_buffer (j_compress_ptr cinfo)
+safeboolean empty_output_buffer (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
* for error exit.
*/
-void
-term_destination (j_compress_ptr cinfo)
+void term_destination (j_compress_ptr cinfo)
{
my_dest_ptr dest = (my_dest_ptr) cinfo->dest;
size_t datacount = OUTPUT_BUF_SIZE - dest->pub.free_in_buffer;
* for closing it after finishing compression.
*/
-void
-jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile)
+void jpeg_gdIOCtx_dest (j_compress_ptr cinfo, gdIOCtx * outfile)
{
my_dest_ptr dest;
#ifndef PNG_SETJMP_NOT_SUPPORTED
typedef struct _jmpbuf_wrapper
- {
- jmp_buf jmpbuf;
- }
-jmpbuf_wrapper;
+{
+ jmp_buf jmpbuf;
+} jmpbuf_wrapper;
static jmpbuf_wrapper gdPngJmpbufStruct;
-static void
-gdPngErrorHandler (png_structp png_ptr, png_const_charp msg)
+static void gdPngErrorHandler (png_structp png_ptr, png_const_charp msg)
{
- jmpbuf_wrapper *jmpbuf_ptr;
-
- /* This function, aside from the extra step of retrieving the "error
- * pointer" (below) and the fact that it exists within the application
- * rather than within libpng, is essentially identical to libpng's
- * default error handler. The second point is critical: since both
- * setjmp() and longjmp() are called from the same code, they are
- * guaranteed to have compatible notions of how big a jmp_buf is,
- * regardless of whether _BSD_SOURCE or anything else has (or has not)
- * been defined. */
-
- php_gd_error_ex(E_ERROR, "gd-png: fatal libpng error: %s\n", msg);
-
- jmpbuf_ptr = png_get_error_ptr (png_ptr);
- if (jmpbuf_ptr == NULL)
- { /* we are completely hosed now */
- php_gd_error_ex(E_ERROR, "gd-png: EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n");
- exit (99);
- }
+ jmpbuf_wrapper *jmpbuf_ptr;
+
+ /* This function, aside from the extra step of retrieving the "error
+ * pointer" (below) and the fact that it exists within the application
+ * rather than within libpng, is essentially identical to libpng's
+ * default error handler. The second point is critical: since both
+ * setjmp() and longjmp() are called from the same code, they are
+ * guaranteed to have compatible notions of how big a jmp_buf is,
+ * regardless of whether _BSD_SOURCE or anything else has (or has not)
+ * been defined.
+ */
+
+ php_gd_error_ex(E_ERROR, "gd-png: fatal libpng error: %s\n", msg);
+
+ jmpbuf_ptr = png_get_error_ptr (png_ptr);
+ if (jmpbuf_ptr == NULL) { /* we are completely hosed now */
+ php_gd_error_ex(E_ERROR, "gd-png: EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n");
+ }
- longjmp (jmpbuf_ptr->jmpbuf, 1);
+ longjmp (jmpbuf_ptr->jmpbuf, 1);
}
#endif
-static void
-gdPngReadData (png_structp png_ptr,
- png_bytep data, png_size_t length)
+static void gdPngReadData (png_structp png_ptr, png_bytep data, png_size_t length)
{
- gdGetBuf (data, length, (gdIOCtx *)
- png_get_io_ptr (png_ptr));
+ gdGetBuf(data, length, (gdIOCtx *) png_get_io_ptr(png_ptr));
}
-static void
-gdPngWriteData (png_structp png_ptr,
- png_bytep data, png_size_t length)
+static void gdPngWriteData (png_structp png_ptr, png_bytep data, png_size_t length)
{
- gdPutBuf (data, length, (gdIOCtx *)
- png_get_io_ptr (png_ptr));
+ gdPutBuf (data, length, (gdIOCtx *) png_get_io_ptr(png_ptr));
}
-static void
-gdPngFlushData (png_structp png_ptr)
+static void gdPngFlushData (png_structp png_ptr)
{
}
-gdImagePtr
-gdImageCreateFromPng (FILE * inFile)
+gdImagePtr gdImageCreateFromPng (FILE * inFile)
{
- gdImagePtr im;
- gdIOCtx *in = gdNewFileCtx (inFile);
- im = gdImageCreateFromPngCtx (in);
- in->gd_free (in);
- return im;
+ gdImagePtr im;
+ gdIOCtx *in = gdNewFileCtx(inFile);
+ im = gdImageCreateFromPngCtx(in);
+ in->gd_free(in);
+
+ return im;
}
/* This routine is based in part on the Chapter 13 demo code in "PNG: The
* Definitive Guide" (http://www.cdrom.com/pub/png/pngbook.html).
*/
-gdImagePtr
-gdImageCreateFromPngCtx (gdIOCtx * infile)
+gdImagePtr gdImageCreateFromPngCtx (gdIOCtx * infile)
{
- png_byte sig[8];
- png_structp png_ptr;
- png_infop info_ptr;
- png_uint_32 width, height, rowbytes, w, h;
- int bit_depth, color_type, interlace_type;
- int num_palette, num_trans;
- png_colorp palette;
- png_color_16p trans_gray_rgb;
- png_color_16p trans_color_rgb;
- png_bytep trans;
- png_bytep image_data = NULL;
- png_bytepp row_pointers = NULL;
- gdImagePtr im = NULL;
- int i, j, *open = NULL;
- volatile int transparent = -1;
- volatile int palette_allocated = FALSE;
-
- /* Make sure the signature can't match by dumb luck -- TBB */
- /* GRR: isn't sizeof(infile) equal to the size of the pointer? */
- memset (infile, 0, sizeof (infile));
-
- /* first do a quick check that the file really is a PNG image; could
- * have used slightly more general png_sig_cmp() function instead */
- gdGetBuf (sig, 8, infile);
- if (!png_check_sig (sig, 8))
- return NULL; /* bad signature */
+ png_byte sig[8];
+ png_structp png_ptr;
+ png_infop info_ptr;
+ png_uint_32 width, height, rowbytes, w, h;
+ int bit_depth, color_type, interlace_type;
+ int num_palette, num_trans;
+ png_colorp palette;
+ png_color_16p trans_gray_rgb;
+ png_color_16p trans_color_rgb;
+ png_bytep trans;
+ png_bytep image_data = NULL;
+ png_bytepp row_pointers = NULL;
+ gdImagePtr im = NULL;
+ int i, j, *open = NULL;
+ volatile int transparent = -1;
+ volatile int palette_allocated = FALSE;
+
+ /* Make sure the signature can't match by dumb luck -- TBB */
+ /* GRR: isn't sizeof(infile) equal to the size of the pointer? */
+ memset (infile, 0, sizeof(infile));
+
+ /* first do a quick check that the file really is a PNG image; could
+ * have used slightly more general png_sig_cmp() function instead
+ */
+ gdGetBuf(sig, 8, infile);
+ if (!png_check_sig (sig, 8)) { /* bad signature */
+ return NULL;
+ }
#ifndef PNG_SETJMP_NOT_SUPPORTED
- png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, &gdPngJmpbufStruct,
- gdPngErrorHandler, NULL);
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, &gdPngJmpbufStruct, gdPngErrorHandler, NULL);
#else
- png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
#endif
- if (png_ptr == NULL)
- {
- php_gd_error("gd-png error: cannot allocate libpng main struct\n");
- return NULL;
- }
+ if (png_ptr == NULL) {
+ php_gd_error("gd-png error: cannot allocate libpng main struct\n");
+ return NULL;
+ }
- info_ptr = png_create_info_struct (png_ptr);
- if (info_ptr == NULL)
- {
- php_gd_error("gd-png error: cannot allocate libpng info struct\n");
- png_destroy_read_struct (&png_ptr, NULL, NULL);
- return NULL;
- }
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL) {
+ php_gd_error("gd-png error: cannot allocate libpng info struct\n");
+ png_destroy_read_struct (&png_ptr, NULL, NULL);
- /* we could create a second info struct here (end_info), but it's only
- * useful if we want to keep pre- and post-IDAT chunk info separated
- * (mainly for PNG-aware image editors and converters) */
+ return NULL;
+ }
- /* setjmp() must be called in every non-callback function that calls a
- * PNG-reading libpng function */
+ /* we could create a second info struct here (end_info), but it's only
+ * useful if we want to keep pre- and post-IDAT chunk info separated
+ * (mainly for PNG-aware image editors and converters)
+ */
+
+ /* setjmp() must be called in every non-callback function that calls a
+ * PNG-reading libpng function
+ */
#ifndef PNG_SETJMP_NOT_SUPPORTED
- if (setjmp (gdPngJmpbufStruct.jmpbuf))
- {
- php_gd_error("gd-png error: setjmp returns error condition\n");
- png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
- return NULL;
- }
-#endif
+ if (setjmp(gdPngJmpbufStruct.jmpbuf)) {
+ php_gd_error("gd-png error: setjmp returns error condition\n");
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
- png_set_sig_bytes (png_ptr, 8); /* we already read the 8 signature bytes */
+ return NULL;
+ }
+#endif
- png_set_read_fn (png_ptr, (void *) infile, gdPngReadData);
- png_read_info (png_ptr, info_ptr); /* read all PNG info up to image data */
+ png_set_sig_bytes(png_ptr, 8); /* we already read the 8 signature bytes */
- png_get_IHDR (png_ptr, info_ptr, &width, &height, &bit_depth, &color_type,
- &interlace_type, NULL, NULL);
- if ((color_type == PNG_COLOR_TYPE_RGB) ||
- (color_type == PNG_COLOR_TYPE_RGB_ALPHA))
- {
- im = gdImageCreateTrueColor ((int) width, (int) height);
- }
- else
- {
- im = gdImageCreate ((int) width, (int) height);
- }
- if (im == NULL)
- {
- php_gd_error("gd-png error: cannot allocate gdImage struct\n");
- png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
- gdFree (image_data);
- gdFree (row_pointers);
- return NULL;
- }
+ png_set_read_fn(png_ptr, (void *) infile, gdPngReadData);
+ png_read_info(png_ptr, info_ptr); /* read all PNG info up to image data */
- if (bit_depth == 16)
- png_set_strip_16 (png_ptr);
- else if (bit_depth < 8)
- png_set_packing (png_ptr); /* expand to 1 byte per pixel */
- switch (color_type)
- {
- case PNG_COLOR_TYPE_PALETTE:
- png_get_PLTE (png_ptr, info_ptr, &palette, &num_palette);
-#ifdef DEBUG
- php_gd_error("gd-png color_type is palette, colors: %d\n",
- num_palette);
-#endif /* DEBUG */
- if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
- {
- /* gd 2.0: we support this rather thoroughly now. Grab the
- first fully transparent entry, if any, as the value of
- the simple-transparency index, mostly for backwards
- binary compatibility. The alpha channel is where it's
- really at these days. */
- int firstZero = 1;
- png_get_tRNS (png_ptr, info_ptr, &trans, &num_trans, NULL);
- for (i = 0; i < num_trans; ++i)
- {
- im->alpha[i] = gdAlphaMax - (trans[i] >> 1);
- if ((trans[i] == 0) && (firstZero))
- {
- transparent = i;
- firstZero = 0;
- }
- }
- }
- break;
-
- case PNG_COLOR_TYPE_GRAY:
- case PNG_COLOR_TYPE_GRAY_ALPHA:
- /* create a fake palette and check for single-shade transparency */
- if ((palette = (png_colorp) gdMalloc (256 * sizeof (png_color))) == NULL)
- {
- php_gd_error("gd-png error: cannot allocate gray palette\n");
- png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
- return NULL;
- }
- palette_allocated = TRUE;
- if (bit_depth < 8)
- {
- num_palette = 1 << bit_depth;
- for (i = 0; i < 256; ++i)
- {
- j = (255 * i) / (num_palette - 1);
- palette[i].red = palette[i].green = palette[i].blue = j;
- }
+ png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL);
+ if ((color_type == PNG_COLOR_TYPE_RGB) || (color_type == PNG_COLOR_TYPE_RGB_ALPHA)) {
+ im = gdImageCreateTrueColor((int) width, (int) height);
+ } else {
+ im = gdImageCreate((int) width, (int) height);
}
- else
- {
- num_palette = 256;
- for (i = 0; i < 256; ++i)
- {
- palette[i].red = palette[i].green = palette[i].blue = i;
- }
+ if (im == NULL) {
+ php_gd_error("gd-png error: cannot allocate gdImage struct\n");
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ gdFree(image_data);
+ gdFree(row_pointers);
+
+ return NULL;
}
- if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
- {
- png_get_tRNS (png_ptr, info_ptr, NULL, NULL, &trans_gray_rgb);
- if (bit_depth == 16) /* png_set_strip_16() not yet in effect */
- transparent = trans_gray_rgb->gray >> 8;
- else
- transparent = trans_gray_rgb->gray;
- /* Note slight error in 16-bit case: up to 256 16-bit shades
- * may get mapped to a single 8-bit shade, and only one of them
- * is supposed to be transparent. IOW, both opaque pixels and
- * transparent pixels will be mapped into the transparent entry.
- * There is no particularly good way around this in the case
- * that all 256 8-bit shades are used, but one could write some
- * custom 16-bit code to handle the case where there are gdFree
- * palette entries. This error will be extremely rare in
- * general, though. (Quite possibly there is only one such
- * image in existence.) */
+
+ if (bit_depth == 16) {
+ png_set_strip_16(png_ptr);
+ } else if (bit_depth < 8) {
+ png_set_packing (png_ptr); /* expand to 1 byte per pixel */
}
- break;
-
- case PNG_COLOR_TYPE_RGB:
- case PNG_COLOR_TYPE_RGB_ALPHA:
- /* gd 2.0: we now support truecolor. See the comment above
- for a rare situation in which the transparent pixel may not
- work properly with 16-bit channels. */
- if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS))
- {
- png_get_tRNS (png_ptr, info_ptr, NULL, NULL, &trans_color_rgb);
- if (bit_depth == 16) /* png_set_strip_16() not yet in effect */
- transparent = gdTrueColor (trans_color_rgb->red >> 8,
- trans_color_rgb->green >> 8,
- trans_color_rgb->blue >> 8);
- else
- transparent = gdTrueColor (trans_color_rgb->red,
- trans_color_rgb->green,
- trans_color_rgb->blue);
+
+ switch (color_type) {
+ case PNG_COLOR_TYPE_PALETTE:
+ png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
+#ifdef DEBUG
+ php_gd_error("gd-png color_type is palette, colors: %d\n", num_palette);
+#endif /* DEBUG */
+ if (png_get_valid (png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ /* gd 2.0: we support this rather thoroughly now. Grab the
+ * first fully transparent entry, if any, as the value of
+ * the simple-transparency index, mostly for backwards
+ * binary compatibility. The alpha channel is where it's
+ * really at these days.
+ */
+ int firstZero = 1;
+ png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL);
+ for (i = 0; i < num_trans; ++i) {
+ im->alpha[i] = gdAlphaMax - (trans[i] >> 1);
+ if ((trans[i] == 0) && (firstZero)) {
+ transparent = i;
+ firstZero = 0;
+ }
+ }
+ }
+ break;
+ case PNG_COLOR_TYPE_GRAY:
+ case PNG_COLOR_TYPE_GRAY_ALPHA:
+ /* create a fake palette and check for single-shade transparency */
+ if ((palette = (png_colorp) gdMalloc (256 * sizeof (png_color))) == NULL) {
+ php_gd_error("gd-png error: cannot allocate gray palette\n");
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+ return NULL;
+ }
+ palette_allocated = TRUE;
+ if (bit_depth < 8) {
+ num_palette = 1 << bit_depth;
+ for (i = 0; i < 256; ++i) {
+ j = (255 * i) / (num_palette - 1);
+ palette[i].red = palette[i].green = palette[i].blue = j;
+ }
+ } else {
+ num_palette = 256;
+ for (i = 0; i < 256; ++i) {
+ palette[i].red = palette[i].green = palette[i].blue = i;
+ }
+ }
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_gray_rgb);
+ if (bit_depth == 16) { /* png_set_strip_16() not yet in effect */
+ transparent = trans_gray_rgb->gray >> 8;
+ } else {
+ transparent = trans_gray_rgb->gray;
+ }
+ /* Note slight error in 16-bit case: up to 256 16-bit shades
+ * may get mapped to a single 8-bit shade, and only one of them
+ * is supposed to be transparent. IOW, both opaque pixels and
+ * transparent pixels will be mapped into the transparent entry.
+ * There is no particularly good way around this in the case
+ * that all 256 8-bit shades are used, but one could write some
+ * custom 16-bit code to handle the case where there are gdFree
+ * palette entries. This error will be extremely rare in
+ * general, though. (Quite possibly there is only one such
+ * image in existence.)
+ */
+ }
+ break;
+
+ case PNG_COLOR_TYPE_RGB:
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ /* gd 2.0: we now support truecolor. See the comment above
+ * for a rare situation in which the transparent pixel may not
+ * work properly with 16-bit channels.
+ */
+ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
+ png_get_tRNS(png_ptr, info_ptr, NULL, NULL, &trans_color_rgb);
+ if (bit_depth == 16) { /* png_set_strip_16() not yet in effect */
+ transparent = gdTrueColor(trans_color_rgb->red >> 8,
+ trans_color_rgb->green >> 8,
+ trans_color_rgb->blue >> 8);
+ } else {
+ transparent = gdTrueColor(trans_color_rgb->red,
+ trans_color_rgb->green,
+ trans_color_rgb->blue);
+ }
+ }
+ break;
}
- break;
- }
- png_read_update_info (png_ptr, info_ptr);
+ png_read_update_info(png_ptr, info_ptr);
- /* allocate space for the PNG image data */
- rowbytes = png_get_rowbytes (png_ptr, info_ptr);
- if ((image_data = (png_bytep) gdMalloc (rowbytes * height)) == NULL)
- {
- php_gd_error("gd-png error: cannot allocate image data\n");
- png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
- return NULL;
- }
- if ((row_pointers = (png_bytepp) gdMalloc (height * sizeof (png_bytep))) == NULL)
- {
- php_gd_error("gd-png error: cannot allocate row pointers\n");
- png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
- gdFree (image_data);
- return NULL;
- }
+ /* allocate space for the PNG image data */
+ rowbytes = png_get_rowbytes(png_ptr, info_ptr);
+ image_data = (png_bytep) gdMalloc (rowbytes * height);
- /* set the individual row_pointers to point at the correct offsets */
- for (h = 0; h < height; ++h)
- {
- row_pointers[h] = image_data + h * rowbytes;
- }
+ row_pointers = (png_bytepp) gdMalloc (height * sizeof (png_bytep));
- png_read_image (png_ptr, row_pointers); /* read whole image... */
- png_read_end (png_ptr, NULL); /* ...done! */
-
- if (!im->trueColor)
- {
- im->colorsTotal = num_palette;
- im->transparent = transparent;
- /* load the palette and mark all entries "open" (unused) for now */
- open = im->open;
- for (i = 0; i < num_palette; ++i)
- {
- im->red[i] = palette[i].red;
- im->green[i] = palette[i].green;
- im->blue[i] = palette[i].blue;
- open[i] = 1;
- }
- for (i = num_palette; i < gdMaxColors; ++i)
- {
- open[i] = 1;
- }
- }
- im->interlace = (interlace_type == PNG_INTERLACE_ADAM7);
-
- /* can't nuke structs until done with palette */
- png_destroy_read_struct (&png_ptr, &info_ptr, NULL);
- switch (color_type)
- {
- case PNG_COLOR_TYPE_RGB:
- for (h = 0; h < height; h++)
- {
- int boffset = 0;
- for (w = 0; w < width; w++)
- {
- register png_byte r = row_pointers[h][boffset++];
- register png_byte g = row_pointers[h][boffset++];
- register png_byte b = row_pointers[h][boffset++];
- im->tpixels[h][w] = gdTrueColor (r, g, b);
- }
+ /* set the individual row_pointers to point at the correct offsets */
+ for (h = 0; h < height; ++h) {
+ row_pointers[h] = image_data + h * rowbytes;
}
- break;
- case PNG_COLOR_TYPE_RGB_ALPHA:
- for (h = 0; h < height; h++)
- {
- int boffset = 0;
- for (w = 0; w < width; w++)
- {
- register png_byte r = row_pointers[h][boffset++];
- register png_byte g = row_pointers[h][boffset++];
- register png_byte b = row_pointers[h][boffset++];
- /* gd has only 7 bits of alpha channel resolution, and
- 127 is transparent, 0 opaque. A moment of convenience,
- a lifetime of compatibility. */
- register png_byte a = gdAlphaMax -
- (row_pointers[h][boffset++] >> 1);
- im->tpixels[h][w] = gdTrueColorAlpha (r, g, b, a);
- }
+
+ png_read_image(png_ptr, row_pointers); /* read whole image... */
+ png_read_end(png_ptr, NULL); /* ...done! */
+
+ if (!im->trueColor) {
+ im->colorsTotal = num_palette;
+ /* load the palette and mark all entries "open" (unused) for now */
+ open = im->open;
+ for (i = 0; i < num_palette; ++i) {
+ im->red[i] = palette[i].red;
+ im->green[i] = palette[i].green;
+ im->blue[i] = palette[i].blue;
+ open[i] = 1;
+ }
+ for (i = num_palette; i < gdMaxColors; ++i) {
+ open[i] = 1;
+ }
}
- break;
- default:
- /* Palette image, or something coerced to be one */
- for (h = 0; h < height; ++h)
- {
- for (w = 0; w < width; ++w)
- {
- register png_byte idx = row_pointers[h][w];
- im->pixels[h][w] = idx;
- open[idx] = 0;
- }
+ /* 2.0.12: Slaven Rezic: palette images are not the only images
+ * with a simple transparent color setting.
+ */
+ im->transparent = transparent;
+ im->interlace = (interlace_type == PNG_INTERLACE_ADAM7);
+
+ /* can't nuke structs until done with palette */
+ png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+ switch (color_type) {
+ case PNG_COLOR_TYPE_RGB:
+ for (h = 0; h < height; h++) {
+ int boffset = 0;
+ for (w = 0; w < width; w++) {
+ register png_byte r = row_pointers[h][boffset++];
+ register png_byte g = row_pointers[h][boffset++];
+ register png_byte b = row_pointers[h][boffset++];
+ im->tpixels[h][w] = gdTrueColor (r, g, b);
+ }
+ }
+ break;
+
+ case PNG_COLOR_TYPE_RGB_ALPHA:
+ for (h = 0; h < height; h++) {
+ int boffset = 0;
+ for (w = 0; w < width; w++) {
+ register png_byte r = row_pointers[h][boffset++];
+ register png_byte g = row_pointers[h][boffset++];
+ register png_byte b = row_pointers[h][boffset++];
+
+ /* gd has only 7 bits of alpha channel resolution, and
+ * 127 is transparent, 0 opaque. A moment of convenience,
+ * a lifetime of compatibility.
+ */
+
+ register png_byte a = gdAlphaMax - (row_pointers[h][boffset++] >> 1);
+ im->tpixels[h][w] = gdTrueColorAlpha(r, g, b, a);
+ }
+ }
+ break;
+
+ default:
+ /* Palette image, or something coerced to be one */
+ for (h = 0; h < height; ++h) {
+ for (w = 0; w < width; ++w) {
+ register png_byte idx = row_pointers[h][w];
+ im->pixels[h][w] = idx;
+ open[idx] = 0;
+ }
+ }
}
- }
#ifdef DEBUG
- if (!im->trueColor)
- {
- for (i = num_palette; i < gdMaxColors; ++i)
- {
- if (!open[i])
- {
- php_gd_error("gd-png warning: image data references out-of-range"
- " color index (%d)\n", i);
- }
+ if (!im->trueColor) {
+ for (i = num_palette; i < gdMaxColors; ++i) {
+ if (!open[i]) {
+ php_gd_error("gd-png warning: image data references out-of-range color index (%d)\n", i);
+ }
+ }
}
- }
#endif
- if (palette_allocated)
- gdFree (palette);
- gdFree (image_data);
- gdFree (row_pointers);
+ if (palette_allocated) {
+ gdFree(palette);
+ }
+ gdFree(image_data);
+ gdFree(row_pointers);
+
+ return im;
+}
+
+void gdImagePngEx (gdImagePtr im, FILE * outFile, int level)
+{
+ gdIOCtx *out = gdNewFileCtx(outFile);
+ gdImagePngCtxEx(im, out, level);
+ out->gd_free(out);
+}
- return im;
+void gdImagePng (gdImagePtr im, FILE * outFile)
+{
+ gdIOCtx *out = gdNewFileCtx(outFile);
+ gdImagePngCtxEx(im, out, -1);
+ out->gd_free(out);
}
+void * gdImagePngPtr (gdImagePtr im, int *size)
+{
+ void *rv;
+ gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
+ gdImagePngCtxEx(im, out, -1);
+ rv = gdDPExtractData(out, size);
+ out->gd_free(out);
+
+ return rv;
+}
-void
-gdImagePng (gdImagePtr im, FILE * outFile)
+void * gdImagePngPtrEx (gdImagePtr im, int *size, int level)
{
- gdIOCtx *out = gdNewFileCtx (outFile);
- gdImagePngCtx (im, out);
- out->gd_free (out);
+ void *rv;
+ gdIOCtx *out = gdNewDynamicCtx(2048, NULL);
+ gdImagePngCtxEx(im, out, level);
+ rv = gdDPExtractData(out, size);
+ out->gd_free(out);
+ return rv;
}
-void *
-gdImagePngPtr (gdImagePtr im, int *size)
+void gdImagePngCtx (gdImagePtr im, gdIOCtx * outfile)
{
- void *rv;
- gdIOCtx *out = gdNewDynamicCtx (2048, NULL);
- gdImagePngCtx (im, out);
- rv = gdDPExtractData (out, size);
- out->gd_free (out);
- return rv;
+ return gdImagePngCtxEx(im, outfile, -1);
}
/* This routine is based in part on code from Dale Lutz (Safe Software Inc.)
* and in part on demo code from Chapter 15 of "PNG: The Definitive Guide"
* (http://www.cdrom.com/pub/png/pngbook.html).
*/
-void
-gdImagePngCtx (gdImagePtr im, gdIOCtx * outfile)
+void gdImagePngCtxEx (gdImagePtr im, gdIOCtx * outfile, int level)
{
- int i, j, bit_depth = 0, interlace_type;
- int width = im->sx;
- int height = im->sy;
- int colors = im->colorsTotal;
- int *open = im->open;
- int mapping[gdMaxColors]; /* mapping[gd_index] == png_index */
- png_byte trans_values[256];
- png_color_16 trans_rgb_value;
- png_color palette[gdMaxColors];
- png_structp png_ptr;
- png_infop info_ptr;
- volatile int transparent = im->transparent;
- volatile int remap = FALSE;
-
+ int i, j, bit_depth = 0, interlace_type;
+ int width = im->sx;
+ int height = im->sy;
+ int colors = im->colorsTotal;
+ int *open = im->open;
+ int mapping[gdMaxColors]; /* mapping[gd_index] == png_index */
+ png_byte trans_values[256];
+ png_color_16 trans_rgb_value;
+ png_color palette[gdMaxColors];
+ png_structp png_ptr;
+ png_infop info_ptr;
+ volatile int transparent = im->transparent;
+ volatile int remap = FALSE;
#ifndef PNG_SETJMP_NOT_SUPPORTED
- png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING,
- &gdPngJmpbufStruct, gdPngErrorHandler, NULL);
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, &gdPngJmpbufStruct, gdPngErrorHandler, NULL);
#else
- png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
+ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
#endif
- if (png_ptr == NULL)
- {
- php_gd_error("gd-png error: cannot allocate libpng main struct\n");
- return;
- }
+ if (png_ptr == NULL) {
+ php_gd_error("gd-png error: cannot allocate libpng main struct\n");
+ return;
+ }
- info_ptr = png_create_info_struct (png_ptr);
- if (info_ptr == NULL)
- {
- php_gd_error("gd-png error: cannot allocate libpng info struct\n");
- png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
- return;
+ info_ptr = png_create_info_struct(png_ptr);
+ if (info_ptr == NULL) {
+ php_gd_error("gd-png error: cannot allocate libpng info struct\n");
+ png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
+
+ return;
}
#ifndef PNG_SETJMP_NOT_SUPPORTED
- if (setjmp (gdPngJmpbufStruct.jmpbuf))
- {
- php_gd_error("gd-png error: setjmp returns error condition\n");
- png_destroy_write_struct (&png_ptr, &info_ptr);
- return;
- }
+ if (setjmp (gdPngJmpbufStruct.jmpbuf)) {
+ php_gd_error("gd-png error: setjmp returns error condition\n");
+ png_destroy_write_struct (&png_ptr, &info_ptr);
+
+ return;
+ }
#endif
- png_set_write_fn (png_ptr, (void *) outfile, gdPngWriteData, gdPngFlushData);
+ png_set_write_fn(png_ptr, (void *) outfile, gdPngWriteData, gdPngFlushData);
- /* This is best for palette images, and libpng defaults to it for
- palette images anyway, so we don't need to do it explicitly.
- What to ideally do for truecolor images depends, alas, on the image.
- gd is intentionally imperfect and doesn't spend a lot of time
- fussing with such things. */
-/* png_set_filter(png_ptr, 0, PNG_FILTER_NONE); */
+ /* This is best for palette images, and libpng defaults to it for
+ * palette images anyway, so we don't need to do it explicitly.
+ * What to ideally do for truecolor images depends, alas, on the image.
+ * gd is intentionally imperfect and doesn't spend a lot of time
+ * fussing with such things.
+ */
+
+ /* png_set_filter(png_ptr, 0, PNG_FILTER_NONE); */
- /* may want to force maximum compression, but time penalty is large */
-/* png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); */
+ /* 2.0.12: this is finally a parameter */
+ png_set_compression_level(png_ptr, level);
- /* can set this to a smaller value without compromising compression if all
- * image data is 16K or less; will save some decoder memory [min == 8] */
-/* png_set_compression_window_bits(png_ptr, 15); */
+ /* can set this to a smaller value without compromising compression if all
+ * image data is 16K or less; will save some decoder memory [min == 8]
+ */
- if (!im->trueColor)
- {
- if (transparent >= im->colorsTotal ||
- (transparent >= 0 && open[transparent]))
- transparent = -1;
- }
- if (!im->trueColor)
- {
- for (i = 0; i < gdMaxColors; ++i)
- mapping[i] = -1;
- }
- if (!im->trueColor)
- {
- /* count actual number of colors used (colorsTotal == high-water mark) */
- colors = 0;
- for (i = 0; i < im->colorsTotal; ++i)
- {
- if (!open[i])
- {
- mapping[i] = colors;
- ++colors;
- }
- }
- if (colors < im->colorsTotal)
- {
- remap = TRUE;
- }
- if (colors <= 2)
- bit_depth = 1;
- else if (colors <= 4)
- bit_depth = 2;
- else if (colors <= 16)
- bit_depth = 4;
- else
- bit_depth = 8;
- }
- interlace_type = im->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
-
- if (im->trueColor)
- {
- if (im->saveAlphaFlag)
- {
- png_set_IHDR (png_ptr, info_ptr, width, height, 8,
- PNG_COLOR_TYPE_RGB_ALPHA, interlace_type,
- PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ /* png_set_compression_window_bits(png_ptr, 15); */
+
+ if (!im->trueColor) {
+ if (transparent >= im->colorsTotal || (transparent >= 0 && open[transparent])) {
+ transparent = -1;
+ }
+
+ for (i = 0; i < gdMaxColors; ++i) {
+ mapping[i] = -1;
+ }
+
+ /* count actual number of colors used (colorsTotal == high-water mark) */
+ colors = 0;
+ for (i = 0; i < im->colorsTotal; ++i) {
+ if (!open[i]) {
+ mapping[i] = colors;
+ ++colors;
+ }
+ }
+ if (colors < im->colorsTotal) {
+ remap = TRUE;
+ }
+ if (colors <= 2) {
+ bit_depth = 1;
+ } else if (colors <= 4) {
+ bit_depth = 2;
+ } else if (colors <= 16) {
+ bit_depth = 4;
+ } else {
+ bit_depth = 8;
+ }
}
- else
- {
- png_set_IHDR (png_ptr, info_ptr, width, height, 8,
- PNG_COLOR_TYPE_RGB, interlace_type,
- PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+
+ interlace_type = im->interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE;
+
+ if (im->trueColor) {
+ if (im->saveAlphaFlag) {
+ png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB_ALPHA, interlace_type,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ } else {
+ png_set_IHDR(png_ptr, info_ptr, width, height, 8, PNG_COLOR_TYPE_RGB, interlace_type,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
+ }
+ } else {
+ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, PNG_COLOR_TYPE_PALETTE, interlace_type,
+ PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
}
- }
- else
- {
- png_set_IHDR (png_ptr, info_ptr, width, height, bit_depth,
- PNG_COLOR_TYPE_PALETTE, interlace_type,
- PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
- }
- if (im->trueColor && (!im->saveAlphaFlag) && (transparent >= 0))
- {
- /* 2.0.9: fixed by Thomas Winzig */
- trans_rgb_value.red = gdTrueColorGetRed (im->transparent);
- trans_rgb_value.green = gdTrueColorGetGreen (im->transparent);
- trans_rgb_value.blue = gdTrueColorGetBlue (im->transparent);
- png_set_tRNS (png_ptr, info_ptr, 0, 0, &trans_rgb_value);
- }
- if (!im->trueColor)
- {
- /* Oy veh. Remap the PNG palette to put the
- entries with interesting alpha channel
- values first. This minimizes the size
- of the tRNS chunk and thus the size
- of the PNG file as a whole. */
- int tc = 0;
- int i;
- int j;
- int k;
- for (i = 0; (i < im->colorsTotal); i++)
- {
- if ((!im->open[i]) &&
- (im->alpha[i] != gdAlphaOpaque))
- {
- tc++;
- }
+
+ if (im->trueColor && !im->saveAlphaFlag && (transparent >= 0)) {
+ /* 2.0.9: fixed by Thomas Winzig */
+ trans_rgb_value.red = gdTrueColorGetRed (im->transparent);
+ trans_rgb_value.green = gdTrueColorGetGreen (im->transparent);
+ trans_rgb_value.blue = gdTrueColorGetBlue (im->transparent);
+ png_set_tRNS(png_ptr, info_ptr, 0, 0, &trans_rgb_value);
}
- if (tc)
- {
+
+ if (!im->trueColor) {
+ /* Oy veh. Remap the PNG palette to put the entries with interesting alpha channel
+ * values first. This minimizes the size of the tRNS chunk and thus the size
+ * of the PNG file as a whole.
+ */
+
+ int tc = 0;
+ int i;
+ int j;
+ int k;
+
+ for (i = 0; (i < im->colorsTotal); i++) {
+ if ((!im->open[i]) && (im->alpha[i] != gdAlphaOpaque)) {
+ tc++;
+ }
+ }
+ if (tc) {
#if 0
- for (i = 0; (i < im->colorsTotal); i++)
- {
- trans_values[i] = 255 -
- ((im->alpha[i] << 1) +
- (im->alpha[i] >> 6));
- }
- png_set_tRNS (png_ptr, info_ptr, trans_values, 256, NULL);
+ for (i = 0; (i < im->colorsTotal); i++) {
+ trans_values[i] = 255 - ((im->alpha[i] << 1) + (im->alpha[i] >> 6));
+ }
+ png_set_tRNS (png_ptr, info_ptr, trans_values, 256, NULL);
#endif
- if (!remap)
- {
- remap = TRUE;
- }
- /* (Semi-)transparent indexes come up from the bottom
- of the list of real colors; opaque
- indexes come down from the top */
- j = 0;
- k = colors - 1;
- for (i = 0; (i < im->colorsTotal); i++)
- {
- if (!im->open[i])
- {
- if (im->alpha[i] != gdAlphaOpaque)
- {
- /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
- trans_values[j] = 255 -
- ((im->alpha[i] << 1) +
- (im->alpha[i] >> 6));
- mapping[i] = j++;
- }
- else
- {
- mapping[i] = k--;
- }
+ if (!remap) {
+ remap = TRUE;
+ }
+
+ /* (Semi-)transparent indexes come up from the bottom of the list of real colors; opaque
+ * indexes come down from the top
+ */
+ j = 0;
+ k = colors - 1;
+
+ for (i = 0; i < im->colorsTotal; i++) {
+ if (!im->open[i]) {
+ if (im->alpha[i] != gdAlphaOpaque) {
+ /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
+ trans_values[j] = 255 - ((im->alpha[i] << 1) + (im->alpha[i] >> 6));
+ mapping[i] = j++;
+ } else {
+ mapping[i] = k--;
+ }
+ }
+ }
+ png_set_tRNS(png_ptr, info_ptr, trans_values, tc, NULL);
}
- }
- png_set_tRNS (png_ptr, info_ptr, trans_values, tc, NULL);
}
- }
-
- /* convert palette to libpng layout */
- if (!im->trueColor)
- {
- if (remap)
- for (i = 0; i < im->colorsTotal; ++i)
- {
- if (mapping[i] < 0)
- continue;
- palette[mapping[i]].red = im->red[i];
- palette[mapping[i]].green = im->green[i];
- palette[mapping[i]].blue = im->blue[i];
- }
- else
- for (i = 0; i < colors; ++i)
- {
- palette[i].red = im->red[i];
- palette[i].green = im->green[i];
- palette[i].blue = im->blue[i];
- }
- png_set_PLTE (png_ptr, info_ptr, palette, colors);
- }
- /* write out the PNG header info (everything up to first IDAT) */
- png_write_info (png_ptr, info_ptr);
-
- /* make sure < 8-bit images are packed into pixels as tightly as possible */
- png_set_packing (png_ptr);
-
- /* This code allocates a set of row buffers and copies the gd image data
- * into them only in the case that remapping is necessary; in gd 1.3 and
- * later, the im->pixels array is laid out identically to libpng's row
- * pointers and can be passed to png_write_image() function directly.
- * The remapping case could be accomplished with less memory for non-
- * interlaced images, but interlacing causes some serious complications. */
- if (im->trueColor)
- {
- int channels = im->saveAlphaFlag ? 4 : 3;
- /* Our little 7-bit alpha channel trick costs us a bit here. */
- png_bytep *row_pointers;
- row_pointers = gdMalloc (sizeof (png_bytep) * height);
- for (j = 0; j < height; ++j)
- {
- int bo = 0;
- row_pointers[j] = (png_bytep) gdMalloc (width * channels);
- for (i = 0; i < width; ++i)
- {
- unsigned char a;
- row_pointers[j][bo++] = gdTrueColorGetRed (im->tpixels[j][i]);
- row_pointers[j][bo++] = gdTrueColorGetGreen (im->tpixels[j][i]);
- row_pointers[j][bo++] = gdTrueColorGetBlue (im->tpixels[j][i]);
- if (im->saveAlphaFlag)
- {
- /* convert the 7-bit alpha channel to an 8-bit alpha channel.
- We do a little bit-flipping magic, repeating the MSB
- as the LSB, to ensure that 0 maps to 0 and
- 127 maps to 255. We also have to invert to match
- PNG's convention in which 255 is opaque. */
- a = gdTrueColorGetAlpha (im->tpixels[j][i]);
- /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
- row_pointers[j][bo++] = 255 - ((a << 1) + (a >> 6));
+ /* convert palette to libpng layout */
+ if (!im->trueColor) {
+ if (remap) {
+ for (i = 0; i < im->colorsTotal; ++i) {
+ if (mapping[i] < 0) {
+ continue;
+ }
+
+ palette[mapping[i]].red = im->red[i];
+ palette[mapping[i]].green = im->green[i];
+ palette[mapping[i]].blue = im->blue[i];
+ }
+ } else {
+ for (i = 0; i < colors; ++i) {
+ palette[i].red = im->red[i];
+ palette[i].green = im->green[i];
+ palette[i].blue = im->blue[i];
+ }
}
- }
+ png_set_PLTE(png_ptr, info_ptr, palette, colors);
}
- png_write_image (png_ptr, row_pointers);
- png_write_end (png_ptr, info_ptr);
+ /* write out the PNG header info (everything up to first IDAT) */
+ png_write_info(png_ptr, info_ptr);
+
+ /* make sure < 8-bit images are packed into pixels as tightly as possible */
+ png_set_packing(png_ptr);
+
+ /* This code allocates a set of row buffers and copies the gd image data
+ * into them only in the case that remapping is necessary; in gd 1.3 and
+ * later, the im->pixels array is laid out identically to libpng's row
+ * pointers and can be passed to png_write_image() function directly.
+ * The remapping case could be accomplished with less memory for non-
+ * interlaced images, but interlacing causes some serious complications.
+ */
+
+ if (im->trueColor) {
+ int channels = im->saveAlphaFlag ? 4 : 3;
+ /* Our little 7-bit alpha channel trick costs us a bit here. */
+ png_bytep *row_pointers;
+ row_pointers = gdMalloc(sizeof (png_bytep) * height);
+ for (j = 0; j < height; ++j) {
+ int bo = 0;
+ row_pointers[j] = (png_bytep) gdMalloc(width * channels);
+ for (i = 0; i < width; ++i) {
+ unsigned char a;
+ row_pointers[j][bo++] = gdTrueColorGetRed(im->tpixels[j][i]);
+ row_pointers[j][bo++] = gdTrueColorGetGreen(im->tpixels[j][i]);
+ row_pointers[j][bo++] = gdTrueColorGetBlue(im->tpixels[j][i]);
+ if (im->saveAlphaFlag) {
+ /* convert the 7-bit alpha channel to an 8-bit alpha channel.
+ * We do a little bit-flipping magic, repeating the MSB
+ * as the LSB, to ensure that 0 maps to 0 and
+ * 127 maps to 255. We also have to invert to match
+ * PNG's convention in which 255 is opaque.
+ */
+ a = gdTrueColorGetAlpha(im->tpixels[j][i]);
+ /* Andrew Hull: >> 6, not >> 7! (gd 2.0.5) */
+ row_pointers[j][bo++] = 255 - ((a << 1) + (a >> 6));
+ }
+ }
+ }
- for (j = 0; j < height; ++j)
- gdFree (row_pointers[j]);
- gdFree (row_pointers);
- }
- else
- {
- if (remap)
- {
- png_bytep *row_pointers;
- row_pointers = gdMalloc (sizeof (png_bytep) * height);
- for (j = 0; j < height; ++j)
- {
- row_pointers[j] = (png_bytep) gdMalloc (width);
- for (i = 0; i < width; ++i)
- row_pointers[j][i] = mapping[im->pixels[j][i]];
- }
-
- png_write_image (png_ptr, row_pointers);
- png_write_end (png_ptr, info_ptr);
-
- for (j = 0; j < height; ++j)
- gdFree (row_pointers[j]);
- gdFree (row_pointers);
- }
- else
- {
- png_write_image (png_ptr, im->pixels);
- png_write_end (png_ptr, info_ptr);
+ png_write_image(png_ptr, row_pointers);
+ png_write_end(png_ptr, info_ptr);
+
+ for (j = 0; j < height; ++j) {
+ gdFree(row_pointers[j]);
+ }
+
+ gdFree(row_pointers);
+ } else {
+ if (remap) {
+ png_bytep *row_pointers;
+ row_pointers = gdMalloc(sizeof (png_bytep) * height);
+ for (j = 0; j < height; ++j) {
+ row_pointers[j] = (png_bytep) gdMalloc(width);
+ for (i = 0; i < width; ++i) {
+ row_pointers[j][i] = mapping[im->pixels[j][i]];
+ }
+ }
+
+ png_write_image(png_ptr, row_pointers);
+ png_write_end(png_ptr, info_ptr);
+
+ for (j = 0; j < height; ++j) {
+ gdFree(row_pointers[j]);
+ }
+
+ gdFree(row_pointers);
+ } else {
+ png_write_image(png_ptr, im->pixels);
+ png_write_end(png_ptr, info_ptr);
+ }
}
- }
- /* 1.6.3: maybe we should give that memory BACK! TBB */
- png_destroy_write_struct (&png_ptr, &info_ptr);
+ /* 1.6.3: maybe we should give that memory BACK! TBB */
+ png_destroy_write_struct(&png_ptr, &info_ptr);
}
-
#endif /* HAVE_LIBPNG */
-
+/* 2.0.12: a new adaptation from the same original, this time
+ * by Barend Gahrels. My attempt to incorporate alpha channel
+ * into the result worked poorly and degraded the quality of
+ * palette conversion even when the source contained no
+ * alpha channel data. This version does not attempt to produce
+ * an output file with transparency in some of the palette
+ * indexes, which, in practice, doesn't look so hot anyway. TBB
+ */
/*
- * gd_topal.c
- *
- * This code is adapted pretty much entirely from jquant2.c,
- * Copyright (C) 1991-1996, Thomas G. Lane. That file is
- * part of the Independent JPEG Group's software. Conditions of
- * use are compatible with the gd license. See the gd license
- * statement and README-JPEG.TXT for additional information.
+ * gd_topal, adapted from jquant2.c
+ *
+ * Copyright (C) 1991-1996, Thomas G. Lane.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
*
* This file contains 2-pass color quantization (color mapping) routines.
* These routines provide selection of a custom color map for an image,
* followed by mapping of the image to that color map, with optional
* Floyd-Steinberg dithering.
- *
* It is also possible to use just the second pass to map to an arbitrary
* externally-given color map.
*
* Note: ordered dithering is not supported, since there isn't any fast
* way to compute intercolor distances; it's unclear that ordered dither's
* fundamental assumptions even hold with an irregularly spaced color map.
- *
- * SUPPORT FOR ALPHA CHANNELS WAS HACKED IN BY THOMAS BOUTELL, who also
- * adapted the code to work within gd rather than within libjpeg, and
- * may not have done a great job of either. It's not Thomas G. Lane's fault.
+ */
+
+#ifdef ORIGINAL_LIB_JPEG
+
+#define JPEG_INTERNALS
+
+#include "jinclude.h"
+#include "jpeglib.h"
+
+#else
+
+/*
+ * THOMAS BOUTELL & BAREND GEHRELS, february 2003
+ * adapted the code to work within gd rather than within libjpeg.
+ * If it is not working, it's not Thomas G. Lane's fault.
+ */
+
+/*
+ * SETTING THIS ONE CAUSES STRIPED IMAGE
+ * to be done: solve this
+ * #define ORIGINAL_LIB_JPEG_REVERSE_ODD_ROWS
*/
#include "gd.h"
#include <string.h>
#include <stdlib.h>
+/* (Re)define some defines known by libjpeg */
+#define QUANT_2PASS_SUPPORTED
+
+#define RGB_RED 0
+#define RGB_GREEN 1
+#define RGB_BLUE 2
+
+#define JSAMPLE unsigned char
+#define MAXJSAMPLE (gdMaxColors-1)
+#define BITS_IN_JSAMPLE 8
+
+#define JSAMPROW int*
+#define JDIMENSION int
+
+#define METHODDEF(type) static type
+#define LOCAL(type) static type
+
+
+/* We assume that right shift corresponds to signed division by 2 with
+ * rounding towards minus infinity. This is correct for typical "arithmetic
+ * shift" instructions that shift in copies of the sign bit. But some
+ * C compilers implement >> with an unsigned shift. For these machines you
+ * must define RIGHT_SHIFT_IS_UNSIGNED.
+ * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity.
+ * It is only applied with constant shift counts. SHIFT_TEMPS must be
+ * included in the variables of any routine using RIGHT_SHIFT.
+ */
+
+#ifdef RIGHT_SHIFT_IS_UNSIGNED
+#define SHIFT_TEMPS INT32 shift_temp;
+#define RIGHT_SHIFT(x,shft) \
+ ((shift_temp = (x)) < 0 ? \
+ (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \
+ (shift_temp >> (shft)))
+#else
+#define SHIFT_TEMPS
+#define RIGHT_SHIFT(x,shft) ((x) >> (shft))
+#endif
+
+
+#define range_limit(x) { if(x<0) x=0; if (x>255) x=255; }
+
+
+#ifndef INT16
+#define INT16 short
+#endif
+
+#ifndef UINT16
+#define UINT16 unsigned short
+#endif
+
+#ifndef INT32
+#define INT32 int
+#endif
+
+#ifndef FAR
+#define FAR
+#endif
+
+#ifndef boolean
+#define boolean int
+#endif
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define input_buf (im->tpixels)
+#define output_buf (im->pixels)
+
+#endif
+
+#ifdef QUANT_2PASS_SUPPORTED
+
+
/*
* This module implements the well-known Heckbert paradigm for color
* quantization. Most of the ideas used here can be traced back to
* The present code works in the post-conversion color space, typically RGB.
*
* To improve the visual quality of the results, we actually work in scaled
- * RGBA space, giving G distances more weight than R, and R in turn more than
- * B. Alpha is weighted least. To do everything in integer math, we must
- * use integer scale factors. The 2/3/1 scale factors used here correspond
- * loosely to the relative weights of the colors in the NTSC grayscale
- * equation.
+ * RGB space, giving G distances more weight than R, and R in turn more than
+ * B. To do everything in integer math, we must use integer scale factors.
+ * The 2/3/1 scale factors used here correspond loosely to the relative
+ * weights of the colors in the NTSC grayscale equation.
+ * If you want to use this code to quantize a non-RGB color space, you'll
+ * probably need to change these scale factors.
*/
-#ifndef TRUE
-#define TRUE 1
-#endif /* TRUE */
-
-#ifndef FALSE
-#define FALSE 0
-#endif /* FALSE */
-
#define R_SCALE 2 /* scale R distances by this much */
#define G_SCALE 3 /* scale G distances by this much */
#define B_SCALE 1 /* and B by this much */
-#define A_SCALE 4 /* and alpha by this much. This really
- only scales by 1 because alpha
- values are 7-bit to begin with. */
-/* Channel ordering (fixed in gd) */
+/* Relabel R/G/B as components 0/1/2, respecting the RGB ordering defined
+ * in jmorecfg.h. As the code stands, it will do the right thing for R,G,B
+ * and B,G,R orders. If you define some other weird order in jmorecfg.h,
+ * you'll get compile errors until you extend this logic. In that case
+ * you'll probably want to tweak the histogram sizes too.
+ */
+
+#if RGB_RED == 0
#define C0_SCALE R_SCALE
+#endif
+#if RGB_BLUE == 0
+#define C0_SCALE B_SCALE
+#endif
+#if RGB_GREEN == 1
#define C1_SCALE G_SCALE
+#endif
+#if RGB_RED == 2
+#define C2_SCALE R_SCALE
+#endif
+#if RGB_BLUE == 2
#define C2_SCALE B_SCALE
-#define C3_SCALE A_SCALE
+#endif
+
/*
* First we have the histogram data structure and routines for creating it.
* arrays are in far memory (same arrangement as we use for image arrays).
*/
-#define MAXNUMCOLORS (gdMaxColors) /* maximum size of colormap */
+#define MAXNUMCOLORS (MAXJSAMPLE+1) /* maximum size of colormap */
-#define HIST_C0_BITS 5 /* bits of precision in R histogram */
+/* These will do the right thing for either R,G,B or B,G,R color order,
+ * but you may not like the results for other color orders.
+ */
+#define HIST_C0_BITS 5 /* bits of precision in R/B histogram */
#define HIST_C1_BITS 6 /* bits of precision in G histogram */
-#define HIST_C2_BITS 5 /* bits of precision in B histogram */
-#define HIST_C3_BITS 3 /* bits of precision in A histogram */
+#define HIST_C2_BITS 5 /* bits of precision in B/R histogram */
/* Number of elements along histogram axes. */
#define HIST_C0_ELEMS (1<<HIST_C0_BITS)
#define HIST_C1_ELEMS (1<<HIST_C1_BITS)
#define HIST_C2_ELEMS (1<<HIST_C2_BITS)
-#define HIST_C3_ELEMS (1<<HIST_C3_BITS)
/* These are the amounts to shift an input value to get a histogram index. */
-#define C0_SHIFT (8-HIST_C0_BITS)
-#define C1_SHIFT (8-HIST_C1_BITS)
-#define C2_SHIFT (8-HIST_C2_BITS)
-/* Beware! Alpha is 7 bit to begin with */
-#define C3_SHIFT (7-HIST_C3_BITS)
+#define C0_SHIFT (BITS_IN_JSAMPLE-HIST_C0_BITS)
+#define C1_SHIFT (BITS_IN_JSAMPLE-HIST_C1_BITS)
+#define C2_SHIFT (BITS_IN_JSAMPLE-HIST_C2_BITS)
-typedef unsigned short histcell; /* histogram cell; prefer an unsigned type */
+typedef UINT16 histcell; /* histogram cell; prefer an unsigned type */
-typedef histcell *histptr; /* for pointers to histogram cells */
+typedef histcell FAR *histptr; /* for pointers to histogram cells */
-typedef histcell hist1d[HIST_C3_ELEMS]; /* typedefs for the array */
-typedef hist1d *hist2d; /* type for the 2nd-level pointers */
-typedef hist2d *hist3d; /* type for third-level pointer */
-typedef hist3d *hist4d; /* type for top-level pointer */
+typedef histcell hist1d[HIST_C2_ELEMS]; /* typedefs for the array */
+typedef hist1d FAR *hist2d; /* type for the 2nd-level pointers */
+typedef hist2d *hist3d; /* type for top-level pointer */
/* Declarations for Floyd-Steinberg dithering.
-
+ *
* Errors are accumulated into the array fserrors[], at a resolution of
* 1/16th of a pixel count. The error at a given pixel is propagated
* to its not-yet-processed neighbors using the standard F-S fractions,
- * ... (here) 7/16
- * 3/16 5/16 1/16
+ * ... (here) 7/16
+ * 3/16 5/16 1/16
* We work left-to-right on even rows, right-to-left on odd rows.
*
* We can get away with a single array (holding one row's worth of errors)
* each end saves us from special-casing the first and last pixels.
* Each entry is three values long, one value for each color component.
*
+ * Note: on a wide image, we might not have enough room in a PC's near data
+ * segment to hold the error array; so it is allocated with alloc_large.
*/
-typedef signed short FSERROR; /* 16 bits should be enough */
+#if BITS_IN_JSAMPLE == 8
+typedef INT16 FSERROR; /* 16 bits should be enough */
typedef int LOCFSERROR; /* use 'int' for calculation temps */
+#else
+typedef INT32 FSERROR; /* may need more than 16 bits */
+typedef INT32 LOCFSERROR; /* be sure calculation temps are big enough */
+#endif
-typedef FSERROR *FSERRPTR; /* pointer to error array */
+typedef FSERROR FAR *FSERRPTR; /* pointer to error array (in FAR storage!) */
-/* Private object */
+/* Private subobject */
typedef struct
- {
- hist4d histogram; /* pointer to the histogram */
- int needs_zeroed; /* TRUE if next pass must zero histogram */
-
- /* Variables for Floyd-Steinberg dithering */
- FSERRPTR fserrors; /* accumulated errors */
- int on_odd_row; /* flag to remember which row we are on */
- int *error_limiter; /* table for clamping the applied error */
- int *error_limiter_storage; /* gdMalloc'd storage for the above */
- int transparentIsPresent; /* TBB: for rescaling to ensure that */
- int opaqueIsPresent; /* 100% opacity & transparency are preserved */
- }
-my_cquantizer;
+{
+#ifdef ORIGINAL_LIB_JPEG
+ struct jpeg_color_quantizer pub; /* public fields */
+
+ /* Space for the eventually created colormap is stashed here */
+ JSAMPARRAY sv_colormap; /* colormap allocated at init time */
+ int desired; /* desired # of colors = size of colormap */
+ boolean needs_zeroed; /* TRUE if next pass must zero histogram */
+#endif
+
+ /* Variables for accumulating image statistics */
+ hist3d histogram; /* pointer to the histogram */
+
+
+ /* Variables for Floyd-Steinberg dithering */
+ FSERRPTR fserrors; /* accumulated errors */
+
+ boolean on_odd_row; /* flag to remember which row we are on */
+ int *error_limiter; /* table for clamping the applied error */
+#ifndef ORIGINAL_LIB_JPEG
+ int *error_limiter_storage; /* gdMalloc'd storage for the above */
+#endif
+} my_cquantizer;
typedef my_cquantizer *my_cquantize_ptr;
+
/*
- * Prescan the pixel array.
- *
- * The prescan simply updates the histogram, which has been
+ * Prescan some rows of pixels.
+ * In this module the prescan simply updates the histogram, which has been
* initialized to zeroes by start_pass.
- *
+ * An output_buf parameter is required by the method signature, but no data
+ * is actually output (in fact the buffer controller is probably passing a
+ * NULL pointer).
*/
-static void
+METHODDEF (void)
+#ifndef ORIGINAL_LIB_JPEG
prescan_quantize (gdImagePtr im, my_cquantize_ptr cquantize)
{
- register histptr histp;
- register hist4d histogram = cquantize->histogram;
- int row;
- int col;
- int *ptr;
- int width = im->sx;
-
- for (row = 0; row < im->sy; row++)
- {
- ptr = im->tpixels[row];
- for (col = width; col > 0; col--)
- {
- /* get pixel value and index into the histogram */
- int r, g, b, a;
- r = gdTrueColorGetRed (*ptr) >> C0_SHIFT;
- g = gdTrueColorGetGreen (*ptr) >> C1_SHIFT;
- b = gdTrueColorGetBlue (*ptr) >> C2_SHIFT;
- a = gdTrueColorGetAlpha (*ptr);
- /* We must have 100% opacity and transparency available
- in the color map to do an acceptable job with alpha
- channel, if opacity and transparency are present in the
- original, because of the visual properties of large
- flat-color border areas (requiring 100% transparency)
- and the behavior of poorly implemented browsers
- (requiring 100% opacity). Test for the presence of
- these here, and rescale the most opaque and transparent
- palette entries at the end if so. This avoids the need
- to develop a fuller understanding I have not been able
- to reach so far in my study of this subject. TBB */
- if (a == gdAlphaTransparent)
- {
- cquantize->transparentIsPresent = 1;
- }
- if (a == gdAlphaOpaque)
- {
- cquantize->opaqueIsPresent = 1;
- }
- a >>= C3_SHIFT;
- histp = &histogram[r][g][b][a];
- /* increment, check for overflow and undo increment if so. */
- if (++(*histp) <= 0)
- (*histp)--;
- ptr++;
+#else
+prescan_quantize (j_decompress_ptr cinfo, JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows)
+{
+ my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+#endif
+ register JSAMPROW ptr;
+ register histptr histp;
+ register hist3d histogram = cquantize->histogram;
+ int row;
+ JDIMENSION col;
+#ifdef ORIGINAL_LIB_JPEG
+ JDIMENSION width = cinfo->output_width;
+#else
+ int width = im->sx;
+ int num_rows = im->sy;
+#endif
+
+ for (row = 0; row < num_rows; row++) {
+ ptr = input_buf[row];
+ for (col = width; col > 0; col--) {
+#ifdef ORIGINAL_LIB_JPEG
+ int r = GETJSAMPLE(ptr[0]) >> C0_SHIFT;
+ int g = GETJSAMPLE(ptr[1]) >> C1_SHIFT;
+ int b = GETJSAMPLE(ptr[2]) >> C2_SHIFT;
+#else
+ int r = gdTrueColorGetRed(*ptr) >> C0_SHIFT;
+ int g = gdTrueColorGetGreen(*ptr) >> C1_SHIFT;
+ int b = gdTrueColorGetBlue(*ptr) >> C2_SHIFT;
+ /* 2.0.12: Steven Brown: support a single totally transparent color in the original. */
+ if ((im->transparent >= 0) && (*ptr == im->transparent)) {
+ ptr++;
+ continue;
+ }
+#endif
+ /* get pixel value and index into the histogram */
+ histp = &histogram[r][g][b];
+ /* increment, check for overflow and undo increment if so. */
+ if (++(*histp) == 0) {
+ (*histp)--;
+ }
+#ifdef ORIGINAL_LIB_JPEG
+ ptr += 3;
+#else
+ ptr++;
+#endif
+ }
}
- }
}
-
/*
* Next we have the really interesting routines: selection of a colormap
* given the completed histogram.
typedef struct
{
- /* The bounds of the box (inclusive); expressed as histogram indexes */
- int c0min, c0max;
- int c1min, c1max;
- int c2min, c2max;
- int c3min, c3max;
- /* The volume (actually 2-norm) of the box */
- int volume;
- /* The number of nonzero histogram cells within this box */
- long colorcount;
-}
-box;
+ /* The bounds of the box (inclusive); expressed as histogram indexes */
+ int c0min, c0max;
+ int c1min, c1max;
+ int c2min, c2max;
+ /* The volume (actually 2-norm) of the box */
+ INT32 volume;
+ /* The number of nonzero histogram cells within this box */
+ long colorcount;
+} box;
typedef box *boxptr;
-static boxptr
-find_biggest_color_pop (boxptr boxlist, int numboxes)
+LOCAL (boxptr) find_biggest_color_pop (boxptr boxlist, int numboxes)
/* Find the splittable box with the largest color population */
/* Returns NULL if no splittable boxes remain */
{
- register boxptr boxp;
- register int i;
- register long maxc = 0;
- boxptr which = NULL;
-
- for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++)
- {
- if (boxp->colorcount > maxc && boxp->volume > 0)
- {
- which = boxp;
- maxc = boxp->colorcount;
+ register boxptr boxp;
+ register int i;
+ register long maxc = 0;
+ boxptr which = NULL;
+
+ for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) {
+ if (boxp->colorcount > maxc && boxp->volume > 0) {
+ which = boxp;
+ maxc = boxp->colorcount;
+ }
}
- }
- return which;
+
+ return which;
}
-static boxptr
-find_biggest_volume (boxptr boxlist, int numboxes)
+LOCAL (boxptr) find_biggest_volume (boxptr boxlist, int numboxes)
/* Find the splittable box with the largest (scaled) volume */
/* Returns NULL if no splittable boxes remain */
{
- register boxptr boxp;
- register int i;
- register int maxv = 0;
- boxptr which = NULL;
-
- for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++)
- {
- if (boxp->volume > maxv)
- {
- which = boxp;
- maxv = boxp->volume;
+ register boxptr boxp;
+ register int i;
+ register INT32 maxv = 0;
+ boxptr which = NULL;
+
+ for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++) {
+ if (boxp->volume > maxv) {
+ which = boxp;
+ maxv = boxp->volume;
+ }
}
- }
- return which;
+
+ return which;
}
-static void
-update_box (gdImagePtr im, my_cquantize_ptr cquantize, boxptr boxp)
+LOCAL (void)
+#ifndef ORIGINAL_LIB_JPEG
+ update_box (gdImagePtr im, my_cquantize_ptr cquantize, boxptr boxp)
+{
+#else
+ update_box (j_decompress_ptr cinfo, boxptr boxp)
/* Shrink the min/max bounds of a box to enclose only nonzero elements, */
/* and recompute its volume and population */
{
- hist4d histogram = cquantize->histogram;
- histptr histp;
- int c0, c1, c2, c3;
- int c0min, c0max, c1min, c1max, c2min, c2max, c3min, c3max;
- int dist0, dist1, dist2, dist3;
- long ccount;
-
- c0min = boxp->c0min;
- c0max = boxp->c0max;
- c1min = boxp->c1min;
- c1max = boxp->c1max;
- c2min = boxp->c2min;
- c2max = boxp->c2max;
- c3min = boxp->c3min;
- c3max = boxp->c3max;
-
- if (c0max > c0min)
- {
- for (c0 = c0min; c0 <= c0max; c0++)
- {
- for (c1 = c1min; c1 <= c1max; c1++)
- {
- for (c2 = c2min; c2 <= c2max; c2++)
- {
- histp = &histogram[c0][c1][c2][c3min];
- for (c3 = c3min; c3 <= c3max; c3++)
- {
- if (*histp++ != 0)
- {
- boxp->c0min = c0min = c0;
- goto have_c0min;
+ my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+#endif
+ hist3d histogram = cquantize->histogram;
+ histptr histp;
+ int c0, c1, c2;
+ int c0min, c0max, c1min, c1max, c2min, c2max;
+ INT32 dist0, dist1, dist2;
+ long ccount;
+
+ c0min = boxp->c0min;
+ c0max = boxp->c0max;
+ c1min = boxp->c1min;
+ c1max = boxp->c1max;
+ c2min = boxp->c2min;
+ c2max = boxp->c2max;
+
+ if (c0max > c0min) {
+ for (c0 = c0min; c0 <= c0max; c0++) {
+ for (c1 = c1min; c1 <= c1max; c1++) {
+ histp = &histogram[c0][c1][c2min];
+ for (c2 = c2min; c2 <= c2max; c2++) {
+ if (*histp++ != 0) {
+ boxp->c0min = c0min = c0;
+ goto have_c0min;
+ }
+ }
}
- }
}
- }
}
- }
have_c0min:
- if (c0max > c0min)
- {
- for (c0 = c0max; c0 >= c0min; c0--)
- {
- for (c1 = c1min; c1 <= c1max; c1++)
- {
- for (c2 = c2min; c2 <= c2max; c2++)
- {
- histp = &histogram[c0][c1][c2][c3min];
- for (c3 = c3min; c3 <= c3max; c3++)
- {
- if (*histp++ != 0)
- {
- boxp->c0max = c0max = c0;
- goto have_c0max;
+ if (c0max > c0min) {
+ for (c0 = c0max; c0 >= c0min; c0--) {
+ for (c1 = c1min; c1 <= c1max; c1++) {
+ histp = &histogram[c0][c1][c2min];
+ for (c2 = c2min; c2 <= c2max; c2++) {
+ if (*histp++ != 0) {
+ boxp->c0max = c0max = c0;
+ goto have_c0max;
+ }
+ }
}
- }
}
- }
}
- }
have_c0max:
- if (c1max > c1min)
- for (c1 = c1min; c1 <= c1max; c1++)
- for (c0 = c0min; c0 <= c0max; c0++)
- {
- for (c2 = c2min; c2 <= c2max; c2++)
- {
- histp = &histogram[c0][c1][c2][c3min];
- for (c3 = c3min; c3 <= c3max; c3++)
- if (*histp++ != 0)
- {
- boxp->c1min = c1min = c1;
- goto have_c1min;
- }
- }
+ if (c1max > c1min) {
+ for (c1 = c1min; c1 <= c1max; c1++) {
+ for (c0 = c0min; c0 <= c0max; c0++) {
+ histp = &histogram[c0][c1][c2min];
+ for (c2 = c2min; c2 <= c2max; c2++) {
+ if (*histp++ != 0) {
+ boxp->c1min = c1min = c1;
+ goto have_c1min;
+ }
+ }
+ }
+ }
}
have_c1min:
- if (c1max > c1min)
- for (c1 = c1max; c1 >= c1min; c1--)
- for (c0 = c0min; c0 <= c0max; c0++)
- {
- for (c2 = c2min; c2 <= c2max; c2++)
- {
- histp = &histogram[c0][c1][c2][c3min];
- for (c3 = c3min; c3 <= c3max; c3++)
- if (*histp++ != 0)
- {
- boxp->c1max = c1max = c1;
- goto have_c1max;
- }
- }
+ if (c1max > c1min) {
+ for (c1 = c1max; c1 >= c1min; c1--) {
+ for (c0 = c0min; c0 <= c0max; c0++) {
+ histp = &histogram[c0][c1][c2min];
+ for (c2 = c2min; c2 <= c2max; c2++) {
+ if (*histp++ != 0) {
+ boxp->c1max = c1max = c1;
+ goto have_c1max;
+ }
+ }
+ }
+ }
}
have_c1max:
- /* The original version hand-rolled the array lookup a little, but
- with four dimensions, I don't even want to think about it. TBB */
- if (c2max > c2min)
- for (c2 = c2min; c2 <= c2max; c2++)
- for (c0 = c0min; c0 <= c0max; c0++)
- for (c1 = c1min; c1 <= c1max; c1++)
- for (c3 = c3min; c3 <= c3max; c3++)
- if (histogram[c0][c1][c2][c3] != 0)
- {
- boxp->c2min = c2min = c2;
- goto have_c2min;
- }
+ if (c2max > c2min) {
+ for (c2 = c2min; c2 <= c2max; c2++) {
+ for (c0 = c0min; c0 <= c0max; c0++) {
+ histp = &histogram[c0][c1min][c2];
+ for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) {
+ if (*histp != 0) {
+ boxp->c2min = c2min = c2;
+ goto have_c2min;
+ }
+ }
+ }
+ }
+ }
have_c2min:
- if (c2max > c2min)
- for (c2 = c2max; c2 >= c2min; c2--)
- for (c0 = c0min; c0 <= c0max; c0++)
- for (c1 = c1min; c1 <= c1max; c1++)
- for (c3 = c3min; c3 <= c3max; c3++)
- if (histogram[c0][c1][c2][c3] != 0)
- {
- boxp->c2max = c2max = c2;
- goto have_c2max;
- }
+ if (c2max > c2min) {
+ for (c2 = c2max; c2 >= c2min; c2--) {
+ for (c0 = c0min; c0 <= c0max; c0++) {
+ histp = &histogram[c0][c1min][c2];
+ for (c1 = c1min; c1 <= c1max; c1++, histp += HIST_C2_ELEMS) {
+ if (*histp != 0) {
+ boxp->c2max = c2max = c2;
+ goto have_c2max;
+ }
+ }
+ }
+ }
+ }
have_c2max:
- if (c3max > c3min)
- for (c3 = c3min; c3 <= c3max; c3++)
- for (c0 = c0min; c0 <= c0max; c0++)
- for (c1 = c1min; c1 <= c1max; c1++)
- for (c2 = c2min; c2 <= c2max; c2++)
- if (histogram[c0][c1][c2][c3] != 0)
- {
- boxp->c3min = c3min = c3;
- goto have_c3min;
- }
-have_c3min:
- if (c3max > c3min)
- for (c3 = c3max; c3 >= c3min; c3--)
- for (c0 = c0min; c0 <= c0max; c0++)
- for (c1 = c1min; c1 <= c1max; c1++)
- for (c2 = c2min; c2 <= c2max; c2++)
- if (histogram[c0][c1][c2][c3] != 0)
- {
- boxp->c3max = c3max = c3;
- goto have_c3max;
- }
-have_c3max:
- /* Update box volume.
- * We use 2-norm rather than real volume here; this biases the method
- * against making long narrow boxes, and it has the side benefit that
- * a box is splittable iff norm > 0.
- * Since the differences are expressed in histogram-cell units,
- * we have to shift back to 8-bit units to get consistent distances;
- * after which, we scale according to the selected distance scale factors.
- * TBB: alpha shifts back to 7 bit units. That was accounted for in the
- * alpha scale factor.
- */
- dist0 = ((c0max - c0min) << C0_SHIFT) * C0_SCALE;
- dist1 = ((c1max - c1min) << C1_SHIFT) * C1_SCALE;
- dist2 = ((c2max - c2min) << C2_SHIFT) * C2_SCALE;
- dist3 = ((c3max - c3min) << C3_SHIFT) * C3_SCALE;
- boxp->volume = dist0 * dist0 + dist1 * dist1 + dist2 * dist2 + dist3 * dist3;
-
- /* Now scan remaining volume of box and compute population */
- ccount = 0;
- for (c0 = c0min; c0 <= c0max; c0++)
- for (c1 = c1min; c1 <= c1max; c1++)
- for (c2 = c2min; c2 <= c2max; c2++)
- {
- histp = &histogram[c0][c1][c2][c3min];
- for (c3 = c3min; c3 <= c3max; c3++, histp++)
- if (*histp != 0)
- {
- ccount++;
- }
+
+ /* Update box volume.
+ * We use 2-norm rather than real volume here; this biases the method
+ * against making long narrow boxes, and it has the side benefit that
+ * a box is splittable iff norm > 0.
+ * Since the differences are expressed in histogram-cell units,
+ * we have to shift back to JSAMPLE units to get consistent distances;
+ * after which, we scale according to the selected distance scale factors.
+ */
+ dist0 = ((c0max - c0min) << C0_SHIFT) * C0_SCALE;
+ dist1 = ((c1max - c1min) << C1_SHIFT) * C1_SCALE;
+ dist2 = ((c2max - c2min) << C2_SHIFT) * C2_SCALE;
+ boxp->volume = dist0 * dist0 + dist1 * dist1 + dist2 * dist2;
+
+ /* Now scan remaining volume of box and compute population */
+ ccount = 0;
+ for (c0 = c0min; c0 <= c0max; c0++) {
+ for (c1 = c1min; c1 <= c1max; c1++) {
+ histp = &histogram[c0][c1][c2min];
+ for (c2 = c2min; c2 <= c2max; c2++, histp++) {
+ if (*histp != 0) {
+ ccount++;
+ }
+ }
+ }
}
- boxp->colorcount = ccount;
+ boxp->colorcount = ccount;
}
-static int
-median_cut (gdImagePtr im, my_cquantize_ptr cquantize,
- boxptr boxlist, int numboxes,
- int desired_colors)
+LOCAL (int)
+#ifdef ORIGINAL_LIB_JPEG
+median_cut (j_decompress_ptr cinfo, boxptr boxlist, int numboxes, int desired_colors)
+#else
+median_cut (gdImagePtr im, my_cquantize_ptr cquantize, boxptr boxlist, int numboxes, int desired_colors)
+#endif
/* Repeatedly select and split the largest box until we have enough boxes */
{
- int n, lb;
- int c0, c1, c2, c3, cmax;
- register boxptr b1, b2;
-
- while (numboxes < desired_colors)
- {
- /* Select box to split.
- * Current algorithm: by population for first half, then by volume.
- */
- if (numboxes * 2 <= desired_colors)
- {
- b1 = find_biggest_color_pop (boxlist, numboxes);
- }
- else
- {
- b1 = find_biggest_volume (boxlist, numboxes);
- }
- if (b1 == NULL) /* no splittable boxes left! */
- break;
- b2 = &boxlist[numboxes]; /* where new box will go */
- /* Copy the color bounds to the new box. */
- b2->c0max = b1->c0max;
- b2->c1max = b1->c1max;
- b2->c2max = b1->c2max;
- b2->c3max = b1->c3max;
- b2->c0min = b1->c0min;
- b2->c1min = b1->c1min;
- b2->c2min = b1->c2min;
- b2->c3min = b1->c3min;
- /* Choose which axis to split the box on.
- * Current algorithm: longest scaled axis.
- * See notes in update_box about scaling distances.
- */
- c0 = ((b1->c0max - b1->c0min) << C0_SHIFT) * C0_SCALE;
- c1 = ((b1->c1max - b1->c1min) << C1_SHIFT) * C1_SCALE;
- c2 = ((b1->c2max - b1->c2min) << C2_SHIFT) * C2_SCALE;
- c3 = ((b1->c3max - b1->c3min) << C3_SHIFT) * C3_SCALE;
- /* We want to break any ties in favor of green, then red, then blue,
- with alpha last. */
- cmax = c1;
- n = 1;
- if (c0 > cmax)
- {
- cmax = c0;
- n = 0;
- }
- if (c2 > cmax)
- {
- cmax = c2;
- n = 2;
- }
- if (c3 > cmax)
- {
- n = 3;
- }
- /* Choose split point along selected axis, and update box bounds.
- * Current algorithm: split at halfway point.
- * (Since the box has been shrunk to minimum volume,
- * any split will produce two nonempty subboxes.)
- * Note that lb value is max for lower box, so must be < old max.
- */
- switch (n)
- {
- case 0:
- lb = (b1->c0max + b1->c0min) / 2;
- b1->c0max = lb;
- b2->c0min = lb + 1;
- break;
- case 1:
- lb = (b1->c1max + b1->c1min) / 2;
- b1->c1max = lb;
- b2->c1min = lb + 1;
- break;
- case 2:
- lb = (b1->c2max + b1->c2min) / 2;
- b1->c2max = lb;
- b2->c2min = lb + 1;
- break;
- case 3:
- lb = (b1->c3max + b1->c3min) / 2;
- b1->c3max = lb;
- b2->c3min = lb + 1;
- break;
+ int n, lb;
+ int c0, c1, c2, cmax;
+ register boxptr b1, b2;
+
+ while (numboxes < desired_colors) {
+ /* Select box to split.
+ * Current algorithm: by population for first half, then by volume.
+ */
+ if (numboxes * 2 <= desired_colors) {
+ b1 = find_biggest_color_pop(boxlist, numboxes);
+ } else {
+ b1 = find_biggest_volume(boxlist, numboxes);
+ }
+ if (b1 == NULL) { /* no splittable boxes left! */
+ break;
+ }
+ b2 = &boxlist[numboxes]; /* where new box will go */
+ /* Copy the color bounds to the new box. */
+ b2->c0max = b1->c0max;
+ b2->c1max = b1->c1max;
+ b2->c2max = b1->c2max;
+ b2->c0min = b1->c0min;
+ b2->c1min = b1->c1min;
+ b2->c2min = b1->c2min;
+ /* Choose which axis to split the box on.
+ * Current algorithm: longest scaled axis.
+ * See notes in update_box about scaling distances.
+ */
+ c0 = ((b1->c0max - b1->c0min) << C0_SHIFT) * C0_SCALE;
+ c1 = ((b1->c1max - b1->c1min) << C1_SHIFT) * C1_SCALE;
+ c2 = ((b1->c2max - b1->c2min) << C2_SHIFT) * C2_SCALE;
+ /* We want to break any ties in favor of green, then red, blue last.
+ * This code does the right thing for R,G,B or B,G,R color orders only.
+ */
+#if RGB_RED == 0
+ cmax = c1;
+ n = 1;
+ if (c0 > cmax) {
+ cmax = c0;
+ n = 0;
+ }
+ if (c2 > cmax) {
+ n = 2;
+ }
+#else
+ cmax = c1;
+ n = 1;
+ if (c2 > cmax) {
+ cmax = c2;
+ n = 2;
+ }
+ if (c0 > cmax) {
+ n = 0;
+ }
+#endif
+ /* Choose split point along selected axis, and update box bounds.
+ * Current algorithm: split at halfway point.
+ * (Since the box has been shrunk to minimum volume,
+ * any split will produce two nonempty subboxes.)
+ * Note that lb value is max for lower box, so must be < old max.
+ */
+ switch (n) {
+ case 0:
+ lb = (b1->c0max + b1->c0min) / 2;
+ b1->c0max = lb;
+ b2->c0min = lb + 1;
+ break;
+ case 1:
+ lb = (b1->c1max + b1->c1min) / 2;
+ b1->c1max = lb;
+ b2->c1min = lb + 1;
+ break;
+ case 2:
+ lb = (b1->c2max + b1->c2min) / 2;
+ b1->c2max = lb;
+ b2->c2min = lb + 1;
+ break;
+ }
+ /* Update stats for boxes */
+#ifdef ORIGINAL_LIB_JPEG
+ update_box(cinfo, b1);
+ update_box(cinfo, b2);
+#else
+ update_box(im, cquantize, b1);
+ update_box(im, cquantize, b2);
+#endif
+ numboxes++;
}
- /* Update stats for boxes */
- update_box (im, cquantize, b1);
- update_box (im, cquantize, b2);
- numboxes++;
- }
- return numboxes;
+
+ return numboxes;
}
-static void
-compute_color (gdImagePtr im, my_cquantize_ptr cquantize,
- boxptr boxp, int icolor)
-/*
- Compute representative color for a box, put it in
- palette index icolor */
+LOCAL (void)
+#ifndef ORIGINAL_LIB_JPEG
+ compute_color (gdImagePtr im, my_cquantize_ptr cquantize, boxptr boxp, int icolor)
+{
+#else
+ compute_color (j_decompress_ptr cinfo, boxptr boxp, int icolor)
+/* Compute representative color for a box, put it in colormap[icolor] */
{
- /* Current algorithm: mean weighted by pixels (not colors) */
- /* Note it is important to get the rounding correct! */
- hist4d histogram = cquantize->histogram;
- histptr histp;
- int c0, c1, c2, c3;
- int c0min, c0max, c1min, c1max, c2min, c2max, c3min, c3max;
- long count;
- long total = 0;
- long c0total = 0;
- long c1total = 0;
- long c2total = 0;
- long c3total = 0;
-
- c0min = boxp->c0min;
- c0max = boxp->c0max;
- c1min = boxp->c1min;
- c1max = boxp->c1max;
- c2min = boxp->c2min;
- c2max = boxp->c2max;
- c3min = boxp->c3min;
- c3max = boxp->c3max;
-
- for (c0 = c0min; c0 <= c0max; c0++)
- {
- for (c1 = c1min; c1 <= c1max; c1++)
- {
- for (c2 = c2min; c2 <= c2max; c2++)
- {
- histp = &histogram[c0][c1][c2][c3min];
- for (c3 = c3min; c3 <= c3max; c3++)
- {
- if ((count = *histp++) != 0)
- {
- total += count;
- c0total += ((c0 << C0_SHIFT) + ((1 << C0_SHIFT) >> 1)) * count;
- c1total += ((c1 << C1_SHIFT) + ((1 << C1_SHIFT) >> 1)) * count;
- c2total += ((c2 << C2_SHIFT) + ((1 << C2_SHIFT) >> 1)) * count;
- c3total += ((c3 << C3_SHIFT) + ((1 << C3_SHIFT) >> 1)) * count;
- }
+ /* Current algorithm: mean weighted by pixels (not colors) */
+ /* Note it is important to get the rounding correct! */
+ my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+#endif
+ hist3d histogram = cquantize->histogram;
+ histptr histp;
+ int c0, c1, c2;
+ int c0min, c0max, c1min, c1max, c2min, c2max;
+ long count;
+ long total = 0;
+ long c0total = 0;
+ long c1total = 0;
+ long c2total = 0;
+
+ c0min = boxp->c0min;
+ c0max = boxp->c0max;
+ c1min = boxp->c1min;
+ c1max = boxp->c1max;
+ c2min = boxp->c2min;
+ c2max = boxp->c2max;
+
+ for (c0 = c0min; c0 <= c0max; c0++) {
+ for (c1 = c1min; c1 <= c1max; c1++) {
+ histp = &histogram[c0][c1][c2min];
+ for (c2 = c2min; c2 <= c2max; c2++) {
+ if ((count = *histp++) != 0) {
+ total += count;
+ c0total += ((c0 << C0_SHIFT) + ((1 << C0_SHIFT) >> 1)) * count;
+ c1total += ((c1 << C1_SHIFT) + ((1 << C1_SHIFT) >> 1)) * count;
+ c2total += ((c2 << C2_SHIFT) + ((1 << C2_SHIFT) >> 1)) * count;
+ }
+ }
}
- }
}
- }
- im->red[icolor] = (int) ((c0total + (total >> 1)) / total);
- im->green[icolor] = (int) ((c1total + (total >> 1)) / total);
- im->blue[icolor] = (int) ((c2total + (total >> 1)) / total);
- im->alpha[icolor] = (int) ((c3total + (total >> 1)) / total);
- im->open[icolor] = 0;
- if (im->colorsTotal <= icolor)
- {
- im->colorsTotal = icolor + 1;
- }
+
+#ifdef ORIGINAL_LIB_JPEG
+ cinfo->colormap[0][icolor] = (JSAMPLE) ((c0total + (total >> 1)) / total);
+ cinfo->colormap[1][icolor] = (JSAMPLE) ((c1total + (total >> 1)) / total);
+ cinfo->colormap[2][icolor] = (JSAMPLE) ((c2total + (total >> 1)) / total);
+#else
+ im->red[icolor] = (int) ((c0total + (total >> 1)) / total);
+ im->green[icolor] = (int) ((c1total + (total >> 1)) / total);
+ im->blue[icolor] = (int) ((c2total + (total >> 1)) / total);
+#endif
}
-static void
+
+LOCAL (void)
+#ifdef ORIGINAL_LIB_JPEG
+select_colors (j_decompress_ptr cinfo, int desired_colors)
+#else
select_colors (gdImagePtr im, my_cquantize_ptr cquantize, int desired_colors)
+#endif
/* Master routine for color selection */
{
- boxptr boxlist;
- int numboxes;
- int i;
-
- /* Allocate workspace for box list */
- boxlist = (boxptr) gdMalloc (desired_colors * sizeof (box));
- /* Initialize one box containing whole space */
- numboxes = 1;
- /* Note maxval for alpha is different */
- boxlist[0].c0min = 0;
- boxlist[0].c0max = 255 >> C0_SHIFT;
- boxlist[0].c1min = 0;
- boxlist[0].c1max = 255 >> C1_SHIFT;
- boxlist[0].c2min = 0;
- boxlist[0].c2max = 255 >> C2_SHIFT;
- boxlist[0].c3min = 0;
- boxlist[0].c3max = gdAlphaMax >> C3_SHIFT;
- /* Shrink it to actually-used volume and set its statistics */
- update_box (im, cquantize, &boxlist[0]);
- /* Perform median-cut to produce final box list */
- numboxes = median_cut (im, cquantize, boxlist, numboxes, desired_colors);
- /* Compute the representative color for each box, fill colormap */
- for (i = 0; i < numboxes; i++)
- compute_color (im, cquantize, &boxlist[i], i);
- /* TBB: if the image contains colors at both scaled ends
- of the alpha range, rescale slightly to make sure alpha
- covers the full spectrum from 100% transparent to 100%
- opaque. Even a faint distinct background color is
- generally considered failure with regard to alpha. */
-
- im->colorsTotal = numboxes;
- gdFree (boxlist);
+ boxptr boxlist;
+ int numboxes;
+ int i;
+
+ /* Allocate workspace for box list */
+#ifdef ORIGINAL_LIB_JPEG
+ boxlist = (boxptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, desired_colors * SIZEOF (box));
+#else
+ boxlist = (boxptr) gdMalloc (desired_colors * sizeof (box));
+#endif
+ /* Initialize one box containing whole space */
+ numboxes = 1;
+ boxlist[0].c0min = 0;
+ boxlist[0].c0max = MAXJSAMPLE >> C0_SHIFT;
+ boxlist[0].c1min = 0;
+ boxlist[0].c1max = MAXJSAMPLE >> C1_SHIFT;
+ boxlist[0].c2min = 0;
+ boxlist[0].c2max = MAXJSAMPLE >> C2_SHIFT;
+#ifdef ORIGINAL_LIB_JPEG
+ /* Shrink it to actually-used volume and set its statistics */
+ update_box(cinfo, &boxlist[0]);
+ /* Perform median-cut to produce final box list */
+ numboxes = median_cut(cinfo, boxlist, numboxes, desired_colors);
+ /* Compute the representative color for each box, fill colormap */
+ for (i = 0; i < numboxes; i++) {
+ compute_color(cinfo, &boxlist[i], i);
+ }
+ cinfo->actual_number_of_colors = numboxes;
+ TRACEMS1(cinfo, 1, JTRC_QUANT_SELECTED, numboxes);
+#else
+ /* Shrink it to actually-used volume and set its statistics */
+ update_box(im, cquantize, &boxlist[0]);
+ /* Perform median-cut to produce final box list */
+ numboxes = median_cut(im, cquantize, boxlist, numboxes, desired_colors);
+ /* Compute the representative color for each box, fill colormap */
+ for (i = 0; i < numboxes; i++) {
+ compute_color(im, cquantize, &boxlist[i], i);
+ }
+ im->colorsTotal = numboxes;
+
+ /* If we had a pure transparency color, add it as the last palette entry.
+ * Skip incrementing the color count so that the dither / matching phase
+ * won't use it on pixels that shouldn't have been transparent. We'll
+ * increment it after all that finishes.
+ */
+ if (im->transparent >= 0) {
+ /* Save the transparent color. */
+ im->red[im->colorsTotal] = gdTrueColorGetRed(im->transparent);
+ im->green[im->colorsTotal] = gdTrueColorGetGreen(im->transparent);
+ im->blue[im->colorsTotal] = gdTrueColorGetBlue(im->transparent);
+ im->alpha[im->colorsTotal] = gdAlphaTransparent;
+ im->open[im->colorsTotal] = 0;
+ }
+
+ gdFree(boxlist);
+#endif
}
#define BOX_C0_LOG (HIST_C0_BITS-3)
#define BOX_C1_LOG (HIST_C1_BITS-3)
#define BOX_C2_LOG (HIST_C2_BITS-3)
-#define BOX_C3_LOG (HIST_C3_BITS-3)
#define BOX_C0_ELEMS (1<<BOX_C0_LOG) /* # of hist cells in update box */
#define BOX_C1_ELEMS (1<<BOX_C1_LOG)
#define BOX_C2_ELEMS (1<<BOX_C2_LOG)
-#define BOX_C3_ELEMS (1<<BOX_C3_LOG)
#define BOX_C0_SHIFT (C0_SHIFT + BOX_C0_LOG)
#define BOX_C1_SHIFT (C1_SHIFT + BOX_C1_LOG)
#define BOX_C2_SHIFT (C2_SHIFT + BOX_C2_LOG)
-#define BOX_C3_SHIFT (C3_SHIFT + BOX_C3_LOG)
/*
* inner-loop variables.
*/
-static int
-find_nearby_colors (gdImagePtr im, my_cquantize_ptr cquantize,
- int minc0, int minc1, int minc2, int minc3, int colorlist[])
+LOCAL (int)
+find_nearby_colors (
+#ifdef ORIGINAL_LIB_JPEG
+ j_decompress_ptr cinfo,
+#else
+ gdImagePtr im, my_cquantize_ptr cquantize,
+#endif
+ int minc0, int minc1, int minc2, JSAMPLE colorlist[])
/* Locate the colormap entries close enough to an update box to be candidates
* for the nearest entry to some cell(s) in the update box. The update box
* is specified by the center coordinates of its first cell. The number of
* the colors that need further consideration.
*/
{
- int numcolors = im->colorsTotal;
- int maxc0, maxc1, maxc2, maxc3;
- int centerc0, centerc1, centerc2, centerc3;
- int i, x, ncolors;
- int minmaxdist, min_dist, max_dist, tdist;
- int mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */
-
- /* Compute true coordinates of update box's upper corner and center.
- * Actually we compute the coordinates of the center of the upper-corner
- * histogram cell, which are the upper bounds of the volume we care about.
- * Note that since ">>" rounds down, the "center" values may be closer to
- * min than to max; hence comparisons to them must be "<=", not "<".
- */
- maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT));
- centerc0 = (minc0 + maxc0) >> 1;
- maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT));
- centerc1 = (minc1 + maxc1) >> 1;
- maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT));
- centerc2 = (minc2 + maxc2) >> 1;
- maxc3 = minc3 + ((1 << BOX_C3_SHIFT) - (1 << C3_SHIFT));
- centerc3 = (minc3 + maxc3) >> 1;
-
- /* For each color in colormap, find:
- * 1. its minimum squared-distance to any point in the update box
- * (zero if color is within update box);
- * 2. its maximum squared-distance to any point in the update box.
- * Both of these can be found by considering only the corners of the box.
- * We save the minimum distance for each color in mindist[];
- * only the smallest maximum distance is of interest.
- */
- minmaxdist = 0x7FFFFFFFL;
-
- for (i = 0; i < numcolors; i++)
- {
- /* We compute the squared-c0-distance term, then add in the other three. */
- x = im->red[i];
- if (x < minc0)
- {
- tdist = (x - minc0) * C0_SCALE;
- min_dist = tdist * tdist;
- tdist = (x - maxc0) * C0_SCALE;
- max_dist = tdist * tdist;
- }
- else if (x > maxc0)
- {
- tdist = (x - maxc0) * C0_SCALE;
- min_dist = tdist * tdist;
- tdist = (x - minc0) * C0_SCALE;
- max_dist = tdist * tdist;
- }
- else
- {
- /* within cell range so no contribution to min_dist */
- min_dist = 0;
- if (x <= centerc0)
- {
- tdist = (x - maxc0) * C0_SCALE;
- max_dist = tdist * tdist;
- }
- else
- {
- tdist = (x - minc0) * C0_SCALE;
- max_dist = tdist * tdist;
- }
- }
+#ifdef ORIGINAL_LIB_JPEG
+ int numcolors = cinfo->actual_number_of_colors;
+#else
+ int numcolors = im->colorsTotal;
+#endif
+ int maxc0, maxc1, maxc2;
+ int centerc0, centerc1, centerc2;
+ int i, x, ncolors;
+ INT32 minmaxdist, min_dist, max_dist, tdist;
+ INT32 mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */
+
+ /* Compute true coordinates of update box's upper corner and center.
+ * Actually we compute the coordinates of the center of the upper-corner
+ * histogram cell, which are the upper bounds of the volume we care about.
+ * Note that since ">>" rounds down, the "center" values may be closer to
+ * min than to max; hence comparisons to them must be "<=", not "<".
+ */
+ maxc0 = minc0 + ((1 << BOX_C0_SHIFT) - (1 << C0_SHIFT));
+ centerc0 = (minc0 + maxc0) >> 1;
+ maxc1 = minc1 + ((1 << BOX_C1_SHIFT) - (1 << C1_SHIFT));
+ centerc1 = (minc1 + maxc1) >> 1;
+ maxc2 = minc2 + ((1 << BOX_C2_SHIFT) - (1 << C2_SHIFT));
+ centerc2 = (minc2 + maxc2) >> 1;
+
+ /* For each color in colormap, find:
+ * 1. its minimum squared-distance to any point in the update box
+ * (zero if color is within update box);
+ * 2. its maximum squared-distance to any point in the update box.
+ * Both of these can be found by considering only the corners of the box.
+ * We save the minimum distance for each color in mindist[];
+ * only the smallest maximum distance is of interest.
+ */
+ minmaxdist = 0x7FFFFFFFL;
+
+ for (i = 0; i < numcolors; i++) {
+ /* We compute the squared-c0-distance term, then add in the other two. */
+#ifdef ORIGINAL_LIB_JPEG
+ x = GETJSAMPLE(cinfo->colormap[0][i]);
+#else
+ x = im->red[i];
+#endif
+ if (x < minc0) {
+ tdist = (x - minc0) * C0_SCALE;
+ min_dist = tdist * tdist;
+ tdist = (x - maxc0) * C0_SCALE;
+ max_dist = tdist * tdist;
+ } else if (x > maxc0) {
+ tdist = (x - maxc0) * C0_SCALE;
+ min_dist = tdist * tdist;
+ tdist = (x - minc0) * C0_SCALE;
+ max_dist = tdist * tdist;
+ } else {
+ /* within cell range so no contribution to min_dist */
+ min_dist = 0;
+ if (x <= centerc0) {
+ tdist = (x - maxc0) * C0_SCALE;
+ max_dist = tdist * tdist;
+ } else {
+ tdist = (x - minc0) * C0_SCALE;
+ max_dist = tdist * tdist;
+ }
+ }
- x = im->green[i];
- if (x < minc1)
- {
- tdist = (x - minc1) * C1_SCALE;
- min_dist += tdist * tdist;
- tdist = (x - maxc1) * C1_SCALE;
- max_dist += tdist * tdist;
- }
- else if (x > maxc1)
- {
- tdist = (x - maxc1) * C1_SCALE;
- min_dist += tdist * tdist;
- tdist = (x - minc1) * C1_SCALE;
- max_dist += tdist * tdist;
- }
- else
- {
- /* within cell range so no contribution to min_dist */
- if (x <= centerc1)
- {
- tdist = (x - maxc1) * C1_SCALE;
- max_dist += tdist * tdist;
- }
- else
- {
- tdist = (x - minc1) * C1_SCALE;
- max_dist += tdist * tdist;
- }
- }
+#ifdef ORIGINAL_LIB_JPEG
+ x = GETJSAMPLE(cinfo->colormap[1][i]);
+#else
+ x = im->green[i];
+#endif
+ if (x < minc1) {
+ tdist = (x - minc1) * C1_SCALE;
+ min_dist += tdist * tdist;
+ tdist = (x - maxc1) * C1_SCALE;
+ max_dist += tdist * tdist;
+ } else if (x > maxc1) {
+ tdist = (x - maxc1) * C1_SCALE;
+ min_dist += tdist * tdist;
+ tdist = (x - minc1) * C1_SCALE;
+ max_dist += tdist * tdist;
+ } else {
+ /* within cell range so no contribution to min_dist */
+ if (x <= centerc1) {
+ tdist = (x - maxc1) * C1_SCALE;
+ max_dist += tdist * tdist;
+ } else {
+ tdist = (x - minc1) * C1_SCALE;
+ max_dist += tdist * tdist;
+ }
+ }
- x = im->blue[i];
- if (x < minc2)
- {
- tdist = (x - minc2) * C2_SCALE;
- min_dist += tdist * tdist;
- tdist = (x - maxc2) * C2_SCALE;
- max_dist += tdist * tdist;
- }
- else if (x > maxc2)
- {
- tdist = (x - maxc2) * C2_SCALE;
- min_dist += tdist * tdist;
- tdist = (x - minc2) * C2_SCALE;
- max_dist += tdist * tdist;
- }
- else
- {
- /* within cell range so no contribution to min_dist */
- if (x <= centerc2)
- {
- tdist = (x - maxc2) * C2_SCALE;
- max_dist += tdist * tdist;
- }
- else
- {
- tdist = (x - minc2) * C2_SCALE;
- max_dist += tdist * tdist;
- }
- }
+#ifdef ORIGINAL_LIB_JPEG
+ x = GETJSAMPLE (cinfo->colormap[2][i]);
+#else
+ x = im->blue[i];
+#endif
+ if (x < minc2) {
+ tdist = (x - minc2) * C2_SCALE;
+ min_dist += tdist * tdist;
+ tdist = (x - maxc2) * C2_SCALE;
+ max_dist += tdist * tdist;
+ } else if (x > maxc2) {
+ tdist = (x - maxc2) * C2_SCALE;
+ min_dist += tdist * tdist;
+ tdist = (x - minc2) * C2_SCALE;
+ max_dist += tdist * tdist;
+ } else {
+ /* within cell range so no contribution to min_dist */
+ if (x <= centerc2) {
+ tdist = (x - maxc2) * C2_SCALE;
+ max_dist += tdist * tdist;
+ } else {
+ tdist = (x - minc2) * C2_SCALE;
+ max_dist += tdist * tdist;
+ }
+ }
- x = im->alpha[i];
- if (x < minc3)
- {
- tdist = (x - minc3) * C3_SCALE;
- min_dist += tdist * tdist;
- tdist = (x - maxc3) * C3_SCALE;
- max_dist += tdist * tdist;
- }
- else if (x > maxc3)
- {
- tdist = (x - maxc3) * C3_SCALE;
- min_dist += tdist * tdist;
- tdist = (x - minc3) * C3_SCALE;
- max_dist += tdist * tdist;
- }
- else
- {
- /* within cell range so no contribution to min_dist */
- if (x <= centerc3)
- {
- tdist = (x - maxc3) * C3_SCALE;
- max_dist += tdist * tdist;
- }
- else
- {
- tdist = (x - minc3) * C3_SCALE;
- max_dist += tdist * tdist;
- }
+ mindist[i] = min_dist; /* save away the results */
+ if (max_dist < minmaxdist)
+ minmaxdist = max_dist;
}
- mindist[i] = min_dist; /* save away the results */
- if (max_dist < minmaxdist)
- minmaxdist = max_dist;
- }
-
- /* Now we know that no cell in the update box is more than minmaxdist
- * away from some colormap entry. Therefore, only colors that are
- * within minmaxdist of some part of the box need be considered.
- */
- ncolors = 0;
- for (i = 0; i < numcolors; i++)
- {
- if (mindist[i] <= minmaxdist)
- colorlist[ncolors++] = i;
- }
- return ncolors;
+ /* Now we know that no cell in the update box is more than minmaxdist
+ * away from some colormap entry. Therefore, only colors that are
+ * within minmaxdist of some part of the box need be considered.
+ */
+ ncolors = 0;
+ for (i = 0; i < numcolors; i++) {
+ if (mindist[i] <= minmaxdist) {
+ colorlist[ncolors++] = (JSAMPLE) i;
+ }
+ }
+ return ncolors;
}
-static void
-find_best_colors (gdImagePtr im, my_cquantize_ptr cquantize,
- int minc0, int minc1, int minc2, int minc3,
- int numcolors, int colorlist[], int bestcolor[])
+LOCAL (void) find_best_colors (
+#ifdef ORIGINAL_LIB_JPEG
+ j_decompress_ptr cinfo,
+#else
+ gdImagePtr im, my_cquantize_ptr cquantize,
+#endif
+ int minc0, int minc1, int minc2,
+ int numcolors, JSAMPLE colorlist[],
+ JSAMPLE bestcolor[])
/* Find the closest colormap entry for each cell in the update box,
* given the list of candidate colors prepared by find_nearby_colors.
* Return the indexes of the closest entries in the bestcolor[] array.
* find the distance from a colormap entry to successive cells in the box.
*/
{
- int ic0, ic1, ic2;
- int i, icolor;
- register int *bptr; /* pointer into bestdist[] array */
- int *cptr; /* pointer into bestcolor[] array */
- int dist0, dist1, dist2; /* initial distance values */
- int xx0, xx1, xx2; /* distance increments */
- int inc0, inc1, inc2, inc3; /* initial values for increments */
- /* This array holds the distance to the nearest-so-far color for each cell */
- int bestdist[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS * BOX_C3_ELEMS];
-
- /* Initialize best-distance for each cell of the update box */
- bptr = bestdist;
- for (i = BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS * BOX_C3_ELEMS - 1; i >= 0; i--)
- *bptr++ = 0x7FFFFFFFL;
-
- /* For each color selected by find_nearby_colors,
- * compute its distance to the center of each cell in the box.
- * If that's less than best-so-far, update best distance and color number.
- */
-
- /* Nominal steps between cell centers ("x" in Thomas article) */
-#define STEP_C0 ((1 << C0_SHIFT) * C0_SCALE)
-#define STEP_C1 ((1 << C1_SHIFT) * C1_SCALE)
-#define STEP_C2 ((1 << C2_SHIFT) * C2_SCALE)
-#define STEP_C3 ((1 << C3_SHIFT) * C3_SCALE)
-
- for (i = 0; i < numcolors; i++) {
+ int ic0, ic1, ic2;
+ int i, icolor;
+ register INT32 *bptr; /* pointer into bestdist[] array */
+ JSAMPLE *cptr; /* pointer into bestcolor[] array */
+ INT32 dist0, dist1; /* initial distance values */
+ register INT32 dist2; /* current distance in inner loop */
+ INT32 xx0, xx1; /* distance increments */
+ register INT32 xx2;
+ INT32 inc0, inc1, inc2; /* initial values for increments */
+ /* This array holds the distance to the nearest-so-far color for each cell */
+ INT32 bestdist[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS];
+
+ /* Initialize best-distance for each cell of the update box */
+ bptr = bestdist;
+ for (i = BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS - 1; i >= 0; i--) {
+ *bptr++ = 0x7FFFFFFFL;
+ }
+
+ /* For each color selected by find_nearby_colors,
+ * compute its distance to the center of each cell in the box.
+ * If that's less than best-so-far, update best distance and color number.
+ */
+
+ /* Nominal steps between cell centers ("x" in Thomas article) */
+#define STEP_C0 ((1 << C0_SHIFT) * C0_SCALE)
+#define STEP_C1 ((1 << C1_SHIFT) * C1_SCALE)
+#define STEP_C2 ((1 << C2_SHIFT) * C2_SCALE)
+
+ for (i = 0; i < numcolors; i++) {
+ int r, g, b;
+#ifdef ORIGINAL_LIB_JPEG
+ icolor = GETJSAMPLE(colorlist[i]);
+ r = GETJSAMPLE(cinfo->colormap[0][icolor]);
+ g = GETJSAMPLE(cinfo->colormap[1][icolor]);
+ b = GETJSAMPLE(cinfo->colormap[2][icolor]);
+#else
icolor = colorlist[i];
+ r = im->red[icolor];
+ g = im->green[icolor];
+ b = im->blue[icolor];
+#endif
+
/* Compute (square of) distance from minc0/c1/c2 to this color */
- inc0 = (minc0 - (im->red[icolor])) * C0_SCALE;
+ inc0 = (minc0 - r) * C0_SCALE;
dist0 = inc0 * inc0;
- inc1 = (minc1 - (im->green[icolor])) * C1_SCALE;
+ inc1 = (minc1 - g) * C1_SCALE;
dist0 += inc1 * inc1;
- inc2 = (minc2 - (im->blue[icolor])) * C2_SCALE;
+ inc2 = (minc2 - b) * C2_SCALE;
dist0 += inc2 * inc2;
- inc3 = (minc3 - (im->alpha[icolor])) * C3_SCALE;
- dist0 += inc3 * inc3;
/* Form the initial difference increments */
inc0 = inc0 * (2 * STEP_C0) + STEP_C0 * STEP_C0;
inc1 = inc1 * (2 * STEP_C1) + STEP_C1 * STEP_C1;
inc2 = inc2 * (2 * STEP_C2) + STEP_C2 * STEP_C2;
- inc3 = inc3 * (2 * STEP_C3) + STEP_C3 * STEP_C3;
/* Now loop over all cells in box, updating distance per Thomas method */
bptr = bestdist;
cptr = bestcolor;
dist2 = dist1;
xx2 = inc2;
for (ic2 = BOX_C2_ELEMS - 1; ic2 >= 0; ic2--) {
- register int dist3 = dist2; /* current distance in inner loop */
- register int xx3 = inc3;
- register int ic3;
- for (ic3 = BOX_C3_ELEMS - 1; ic3 >= 0; ic3--) {
- if (dist3 < *bptr) {
- *bptr = dist3;
- *cptr = icolor;
- }
- dist3 += xx3;
- xx3 += 2 * STEP_C3 * STEP_C3;
- bptr++;
- cptr++;
+ if (dist2 < *bptr) {
+ *bptr = dist2;
+ *cptr = (JSAMPLE) icolor;
}
dist2 += xx2;
xx2 += 2 * STEP_C2 * STEP_C2;
+ bptr++;
+ cptr++;
}
dist1 += xx1;
xx1 += 2 * STEP_C1 * STEP_C1;
}
-static void
-fill_inverse_cmap (gdImagePtr im, my_cquantize_ptr cquantize,
- int c0, int c1, int c2, int c3)
+LOCAL (void)
+fill_inverse_cmap (
+#ifdef ORIGINAL_LIB_JPEG
+ j_decompress_ptr cinfo,
+#else
+ gdImagePtr im, my_cquantize_ptr cquantize,
+#endif
+ int c0, int c1, int c2)
/* Fill the inverse-colormap entries in the update box that contains */
-/* histogram cell c0/c1/c2/c3. (Only that one cell MUST be filled, but */
+/* histogram cell c0/c1/c2. (Only that one cell MUST be filled, but */
/* we can fill as many others as we wish.) */
{
- hist4d histogram = cquantize->histogram;
- int minc0, minc1, minc2, minc3; /* lower left corner of update box */
- int ic0, ic1, ic2, ic3;
- register int *cptr; /* pointer into bestcolor[] array */
- register histptr cachep; /* pointer into main cache array */
- /* This array lists the candidate colormap indexes. */
- int colorlist[MAXNUMCOLORS];
- int numcolors; /* number of candidate colors */
- /* This array holds the actually closest colormap index for each cell. */
- int bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS * BOX_C3_ELEMS];
-
- /* Convert cell coordinates to update box ID */
- c0 >>= BOX_C0_LOG;
- c1 >>= BOX_C1_LOG;
- c2 >>= BOX_C2_LOG;
- c3 >>= BOX_C3_LOG;
-
- /* Compute true coordinates of update box's origin corner.
- * Actually we compute the coordinates of the center of the corner
- * histogram cell, which are the lower bounds of the volume we care about.
- */
- minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1);
- minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1);
- minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1);
- minc3 = (c3 << BOX_C3_SHIFT) + ((1 << C3_SHIFT) >> 1);
- /* Determine which colormap entries are close enough to be candidates
- * for the nearest entry to some cell in the update box.
- */
- numcolors = find_nearby_colors (im, cquantize, minc0, minc1, minc2, minc3, colorlist);
-
- /* Determine the actually nearest colors. */
- find_best_colors (im, cquantize, minc0, minc1, minc2, minc3, numcolors, colorlist,
- bestcolor);
-
- /* Save the best color numbers (plus 1) in the main cache array */
- c0 <<= BOX_C0_LOG; /* convert ID back to base cell indexes */
- c1 <<= BOX_C1_LOG;
- c2 <<= BOX_C2_LOG;
- c3 <<= BOX_C3_LOG;
- cptr = bestcolor;
- for (ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++)
- {
- for (ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++)
- {
- for (ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++)
- {
- cachep = &histogram[c0 + ic0][c1 + ic1][c2 + ic2][c3];
- for (ic3 = 0; ic3 < BOX_C3_ELEMS; ic3++)
- {
- *cachep++ = (histcell) ((*cptr++) + 1);
+#ifdef ORIGINAL_LIB_JPEG
+ my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+#endif
+ hist3d histogram = cquantize->histogram;
+ int minc0, minc1, minc2; /* lower left corner of update box */
+ int ic0, ic1, ic2;
+ register JSAMPLE *cptr; /* pointer into bestcolor[] array */
+ register histptr cachep; /* pointer into main cache array */
+ /* This array lists the candidate colormap indexes. */
+ JSAMPLE colorlist[MAXNUMCOLORS];
+ int numcolors; /* number of candidate colors */
+ /* This array holds the actually closest colormap index for each cell. */
+ JSAMPLE bestcolor[BOX_C0_ELEMS * BOX_C1_ELEMS * BOX_C2_ELEMS];
+
+ /* Convert cell coordinates to update box ID */
+ c0 >>= BOX_C0_LOG;
+ c1 >>= BOX_C1_LOG;
+ c2 >>= BOX_C2_LOG;
+
+ /* Compute true coordinates of update box's origin corner.
+ * Actually we compute the coordinates of the center of the corner
+ * histogram cell, which are the lower bounds of the volume we care about.
+ */
+ minc0 = (c0 << BOX_C0_SHIFT) + ((1 << C0_SHIFT) >> 1);
+ minc1 = (c1 << BOX_C1_SHIFT) + ((1 << C1_SHIFT) >> 1);
+ minc2 = (c2 << BOX_C2_SHIFT) + ((1 << C2_SHIFT) >> 1);
+
+ /* Determine which colormap entries are close enough to be candidates
+ * for the nearest entry to some cell in the update box.
+ */
+#ifdef ORIGINAL_LIB_JPEG
+ numcolors = find_nearby_colors(cinfo, minc0, minc1, minc2, colorlist);
+
+ /* Determine the actually nearest colors. */
+ find_best_colors(cinfo, minc0, minc1, minc2, numcolors, colorlist, bestcolor);
+#else
+ numcolors = find_nearby_colors(im, cquantize, minc0, minc1, minc2, colorlist);
+ find_best_colors(im, cquantize, minc0, minc1, minc2, numcolors, colorlist, bestcolor);
+#endif
+
+ /* Save the best color numbers (plus 1) in the main cache array */
+ c0 <<= BOX_C0_LOG; /* convert ID back to base cell indexes */
+ c1 <<= BOX_C1_LOG;
+ c2 <<= BOX_C2_LOG;
+ cptr = bestcolor;
+ for (ic0 = 0; ic0 < BOX_C0_ELEMS; ic0++) {
+ for (ic1 = 0; ic1 < BOX_C1_ELEMS; ic1++) {
+ cachep = &histogram[c0 + ic0][c1 + ic1][c2];
+ for (ic2 = 0; ic2 < BOX_C2_ELEMS; ic2++) {
+#ifdef ORIGINAL_LIB_JPEG
+ *cachep++ = (histcell) (GETJSAMPLE (*cptr++) + 1);
+#else
+ *cachep++ = (histcell) ((*cptr++) + 1);
+#endif
+ }
}
- }
}
- }
}
* Map some rows of pixels to the output colormapped representation.
*/
-void
+METHODDEF (void)
+#ifndef ORIGINAL_LIB_JPEG
pass2_no_dither (gdImagePtr im, my_cquantize_ptr cquantize)
+{
+ register int *inptr;
+ register unsigned char *outptr;
+ int width = im->sx;
+ int num_rows = im->sy;
+#else
+pass2_no_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows)
/* This version performs no dithering */
{
- hist4d histogram = cquantize->histogram;
- register int *inptr;
- register unsigned char *outptr;
- register histptr cachep;
- register int c0, c1, c2, c3;
- int row;
- int col;
- int width = im->sx;
- int num_rows = im->sy;
- for (row = 0; row < num_rows; row++)
- {
- inptr = im->tpixels[row];
- outptr = im->pixels[row];
- for (col = 0; col < width; col++)
- {
- int r, g, b, a;
- /* get pixel value and index into the cache */
- r = gdTrueColorGetRed (*inptr);
- g = gdTrueColorGetGreen (*inptr);
- b = gdTrueColorGetBlue (*inptr);
- a = gdTrueColorGetAlpha (*inptr++);
- c0 = r >> C0_SHIFT;
- c1 = g >> C1_SHIFT;
- c2 = b >> C2_SHIFT;
- c3 = a >> C3_SHIFT;
- cachep = &histogram[c0][c1][c2][c3];
- /* If we have not seen this color before, find nearest colormap entry */
- /* and update the cache */
- if (*cachep == 0)
- {
-#if 0
- /* TBB: quick and dirty approach for use when testing
- fill_inverse_cmap for errors */
- int i;
- int best = -1;
- int mindist = 0x7FFFFFFF;
- for (i = 0; (i < im->colorsTotal); i++)
- {
- int rdist = (im->red[i] >> C0_SHIFT) - c0;
- int gdist = (im->green[i] >> C1_SHIFT) - c1;
- int bdist = (im->blue[i] >> C2_SHIFT) - c2;
- int adist = (im->alpha[i] >> C3_SHIFT) - c3;
- int dist = (rdist * rdist) * R_SCALE +
- (gdist * gdist) * G_SCALE +
- (bdist * bdist) * B_SCALE +
- (adist * adist) * A_SCALE;
- if (dist < mindist)
- {
- best = i;
- mindist = dist;
- }
- }
- *cachep = best + 1;
+ my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+ register JSAMPROW inptr, outptr;
+ JDIMENSION width = cinfo->output_width;
#endif
- fill_inverse_cmap (im, cquantize, c0, c1, c2, c3);
- }
- /* Now emit the colormap index for this cell */
- *outptr++ = (*cachep - 1);
- }
- }
-}
-
-/* We assume that right shift corresponds to signed division by 2 with
- * rounding towards minus infinity. This is correct for typical "arithmetic
- * shift" instructions that shift in copies of the sign bit. But some
- * C compilers implement >> with an unsigned shift. For these machines you
- * must define RIGHT_SHIFT_IS_UNSIGNED.
- * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity.
- * It is only applied with constant shift counts. SHIFT_TEMPS must be
- * included in the variables of any routine using RIGHT_SHIFT.
- */
-
-#ifdef RIGHT_SHIFT_IS_UNSIGNED
-#define SHIFT_TEMPS INT32 shift_temp;
-#define RIGHT_SHIFT(x,shft) \
- ((shift_temp = (x)) < 0 ? \
- (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \
- (shift_temp >> (shft)))
+ hist3d histogram = cquantize->histogram;
+ register int c0, c1, c2;
+ int row;
+ JDIMENSION col;
+ register histptr cachep;
+
+
+ for (row = 0; row < num_rows; row++) {
+ inptr = input_buf[row];
+ outptr = output_buf[row];
+ for (col = width; col > 0; col--){
+ /* get pixel value and index into the cache */
+ int r, g, b;
+#ifdef ORIGINAL_LIB_JPEG
+ r = GETJSAMPLE(*inptr++);
+ g = GETJSAMPLE(*inptr++);
+ b = GETJSAMPLE(*inptr++);
#else
-#define SHIFT_TEMPS
-#define RIGHT_SHIFT(x,shft) ((x) >> (shft))
+ r = gdTrueColorGetRed(*inptr);
+ g = gdTrueColorGetGreen(*inptr);
+ b = gdTrueColorGetBlue(*inptr++);
+
+ /* If the pixel is transparent, we assign it the palette index that
+ * will later be added at the end of the palette as the transparent
+ * index.
+ */
+ if ((im->transparent >= 0) && (im->transparent == *inptr)) {
+ *outptr++ = im->colorsTotal;
+ continue;
+ }
#endif
+ c0 = r >> C0_SHIFT;
+ c1 = g >> C1_SHIFT;
+ c2 = b >> C2_SHIFT;
+ cachep = &histogram[c0][c1][c2];
+ /* If we have not seen this color before, find nearest colormap entry
+ * and update the cache
+ */
+ if (*cachep == 0) {
+#ifdef ORIGINAL_LIB_JPEG
+ fill_inverse_cmap(cinfo, c0, c1, c2);
+#else
+ fill_inverse_cmap(im, cquantize, c0, c1, c2);
+#endif
+ }
+ /* Now emit the colormap index for this cell */
+#ifdef ORIGINAL_LIB_JPEG
+ *outptr++ = (JSAMPLE) (*cachep - 1);
+#else
+ *outptr++ = (*cachep - 1);
+#endif
+ }
+ }
+}
-void
+METHODDEF (void)
+#ifndef ORIGINAL_LIB_JPEG
pass2_fs_dither (gdImagePtr im, my_cquantize_ptr cquantize)
-
+{
+#else
+pass2_fs_dither (j_decompress_ptr cinfo, JSAMPARRAY input_buf, JSAMPARRAY output_buf, int num_rows)
/* This version performs Floyd-Steinberg dithering */
{
- hist4d histogram = cquantize->histogram;
- register LOCFSERROR cur0, cur1, cur2, cur3; /* current error or pixel value */
- LOCFSERROR belowerr0, belowerr1, belowerr2, belowerr3; /* error for pixel below cur */
- LOCFSERROR bpreverr0, bpreverr1, bpreverr2, bpreverr3; /* error for below/prev col */
- register FSERRPTR errorptr; /* => fserrors[] at column before current */
- int *inptr; /* => current input pixel */
- unsigned char *outptr; /* => current output pixel */
- histptr cachep;
- int dir; /* +1 or -1 depending on direction */
- int dir4; /* 4*dir, for advancing errorptr */
- int row;
- int col;
- int width = im->sx;
- int num_rows = im->sy;
- int *error_limit = cquantize->error_limiter;
- int *colormap0 = im->red;
- int *colormap1 = im->green;
- int *colormap2 = im->blue;
- int *colormap3 = im->alpha;
- SHIFT_TEMPS
-
- for (row = 0; row < num_rows; row++)
- {
- inptr = im->tpixels[row];
- outptr = im->pixels[row];
- if (cquantize->on_odd_row)
- {
- /* work right to left in this row */
- inptr += (width - 1); /* so point to rightmost pixel */
- outptr += width - 1;
- dir = -1;
- dir4 = -4;
- errorptr = cquantize->fserrors + (width + 1) * 4; /* => entry after last column */
- cquantize->on_odd_row = FALSE; /* flip for next time */
- }
- else
- {
- /* work left to right in this row */
- dir = 1;
- dir4 = 4;
- errorptr = cquantize->fserrors; /* => entry before first real column */
- cquantize->on_odd_row = TRUE; /* flip for next time */
- }
- /* Preset error values: no error propagated to first pixel from left */
- cur0 = cur1 = cur2 = cur3 = 0;
- /* and no error propagated to row below yet */
- belowerr0 = belowerr1 = belowerr2 = belowerr3 = 0;
- bpreverr0 = bpreverr1 = bpreverr2 = bpreverr3 = 0;
-
- for (col = width; col > 0; col--)
- {
- int a;
- /* curN holds the error propagated from the previous pixel on the
- * current line. Add the error propagated from the previous line
- * to form the complete error correction term for this pixel, and
- * round the error term (which is expressed * 16) to an integer.
- * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct
- * for either sign of the error value.
- * Note: errorptr points to *previous* column's array entry.
- */
- cur0 = RIGHT_SHIFT (cur0 + errorptr[dir4 + 0] + 8, 4);
- cur1 = RIGHT_SHIFT (cur1 + errorptr[dir4 + 1] + 8, 4);
- cur2 = RIGHT_SHIFT (cur2 + errorptr[dir4 + 2] + 8, 4);
- cur3 = RIGHT_SHIFT (cur3 + errorptr[dir4 + 3] + 8, 4);
- /* Limit the error using transfer function set by init_error_limit.
- * See comments with init_error_limit for rationale.
- */
- cur0 = error_limit[cur0];
- cur1 = error_limit[cur1];
- cur2 = error_limit[cur2];
- cur3 = error_limit[cur3];
- /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE.
- * The maximum error is +- MAXJSAMPLE (or less with error limiting);
- * but we'll be lazy and just clamp this with an if test (TBB).
- */
- cur0 += gdTrueColorGetRed (*inptr);
- cur1 += gdTrueColorGetGreen (*inptr);
- cur2 += gdTrueColorGetBlue (*inptr);
- /* Expand to 8 bits for consistency with dithering algorithm -- TBB */
- a = gdTrueColorGetAlpha (*inptr);
- cur3 += (a << 1) + (a >> 6);
- if (cur0 < 0)
- {
- cur0 = 0;
- }
- if (cur0 > 255)
- {
- cur0 = 255;
- }
- if (cur1 < 0)
- {
- cur1 = 0;
- }
- if (cur1 > 255)
- {
- cur1 = 255;
- }
- if (cur2 < 0)
- {
- cur2 = 0;
- }
- if (cur2 > 255)
- {
- cur2 = 255;
- }
- if (cur3 < 0)
- {
- cur3 = 0;
- }
- if (cur3 > 255)
- {
- cur3 = 255;
- }
- /* Index into the cache with adjusted pixel value */
- cachep = &histogram
- [cur0 >> C0_SHIFT]
- [cur1 >> C1_SHIFT]
- [cur2 >> C2_SHIFT]
- [cur3 >> (C3_SHIFT + 1)];
- /* If we have not seen this color before, find nearest colormap */
- /* entry and update the cache */
- if (*cachep == 0)
- fill_inverse_cmap (im, cquantize,
- cur0 >> C0_SHIFT, cur1 >> C1_SHIFT, cur2 >> C2_SHIFT,
- cur3 >> (C3_SHIFT + 1));
- /* Now emit the colormap index for this cell */
- {
- register int pixcode = *cachep - 1;
- *outptr = pixcode;
- /* Compute representation error for this pixel */
- cur0 -= colormap0[pixcode];
- cur1 -= colormap1[pixcode];
- cur2 -= colormap2[pixcode];
- cur3 -= ((colormap3[pixcode] << 1) + (colormap3[pixcode] >> 6));
- }
- /* Compute error fractions to be propagated to adjacent pixels.
- * Add these into the running sums, and simultaneously shift the
- * next-line error sums left by 1 column.
- */
- {
- register LOCFSERROR bnexterr, delta;
-
- bnexterr = cur0; /* Process component 0 */
- delta = cur0 * 2;
- cur0 += delta; /* form error * 3 */
- errorptr[0] = (FSERROR) (bpreverr0 + cur0);
- cur0 += delta; /* form error * 5 */
- bpreverr0 = belowerr0 + cur0;
- belowerr0 = bnexterr;
- cur0 += delta; /* form error * 7 */
- bnexterr = cur1; /* Process component 1 */
- delta = cur1 * 2;
- cur1 += delta; /* form error * 3 */
- errorptr[1] = (FSERROR) (bpreverr1 + cur1);
- cur1 += delta; /* form error * 5 */
- bpreverr1 = belowerr1 + cur1;
- belowerr1 = bnexterr;
- cur1 += delta; /* form error * 7 */
- bnexterr = cur2; /* Process component 2 */
- delta = cur2 * 2;
- cur2 += delta; /* form error * 3 */
- errorptr[2] = (FSERROR) (bpreverr2 + cur2);
- cur2 += delta; /* form error * 5 */
- bpreverr2 = belowerr2 + cur2;
- belowerr2 = bnexterr;
- cur2 += delta; /* form error * 7 */
- bnexterr = cur3; /* Process component 3 */
- delta = cur3 * 2;
- cur3 += delta; /* form error * 3 */
- errorptr[3] = (FSERROR) (bpreverr3 + cur3);
- cur3 += delta; /* form error * 5 */
- bpreverr3 = belowerr3 + cur3;
- belowerr3 = bnexterr;
- cur3 += delta; /* form error * 7 */
- }
- /* At this point curN contains the 7/16 error value to be propagated
- * to the next pixel on the current line, and all the errors for the
- * next line have been shifted over. We are therefore ready to move on.
- */
- inptr += dir; /* Advance pixel pointers to next column */
- outptr += dir;
- errorptr += dir4; /* advance errorptr to current column */
+ my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+ JSAMPROW inptr; /* => current input pixel */
+#endif
+ hist3d histogram = cquantize->histogram;
+ register LOCFSERROR cur0, cur1, cur2; /* current error or pixel value */
+ LOCFSERROR belowerr0, belowerr1, belowerr2; /* error for pixel below cur */
+ LOCFSERROR bpreverr0, bpreverr1, bpreverr2; /* error for below/prev col */
+ register FSERRPTR errorptr; /* => fserrors[] at column before current */
+ histptr cachep;
+ int dir; /* +1 or -1 depending on direction */
+ int dir3; /* 3*dir, for advancing inptr & errorptr */
+ int row;
+ JDIMENSION col;
+#ifdef ORIGINAL_LIB_JPEG
+ JSAMPROW outptr; /* => current output pixel */
+ JDIMENSION width = cinfo->output_width;
+ JSAMPLE *range_limit = cinfo->sample_range_limit;
+ JSAMPROW colormap0 = cinfo->colormap[0];
+ JSAMPROW colormap1 = cinfo->colormap[1];
+ JSAMPROW colormap2 = cinfo->colormap[2];
+#else
+ int *inptr; /* => current input pixel */
+ unsigned char *outptr; /* => current output pixel */
+ int width = im->sx;
+ int num_rows = im->sy;
+ int *colormap0 = im->red;
+ int *colormap1 = im->green;
+ int *colormap2 = im->blue;
+#endif
+ int *error_limit = cquantize->error_limiter;
+
+
+ SHIFT_TEMPS for (row = 0; row < num_rows; row++) {
+ inptr = input_buf[row];
+ outptr = output_buf[row];
+ if (cquantize->on_odd_row) {
+ /* work right to left in this row */
+ inptr += (width - 1) * 3; /* so point to rightmost pixel */
+ outptr += width - 1;
+ dir = -1;
+ dir3 = -3;
+ errorptr = cquantize->fserrors + (width + 1) * 3; /* => entry after last column */
+#ifdef ORIGINAL_LIB_JPEG_REVERSE_ODD_ROWS
+ cquantize->on_odd_row = FALSE; /* flip for next time */
+#endif
+ } else {
+ /* work left to right in this row */
+ dir = 1;
+ dir3 = 3;
+ errorptr = cquantize->fserrors; /* => entry before first real column */
+#ifdef ORIGINAL_LIB_JPEG_REVERSE_ODD_ROWS
+ cquantize->on_odd_row = TRUE; /* flip for next time */
+#endif
+ }
+ /* Preset error values: no error propagated to first pixel from left */
+ cur0 = cur1 = cur2 = 0;
+ /* and no error propagated to row below yet */
+ belowerr0 = belowerr1 = belowerr2 = 0;
+ bpreverr0 = bpreverr1 = bpreverr2 = 0;
+
+ for (col = width; col > 0; col--) {
+ /* If this pixel is transparent, we want to assign it to the special
+ * transparency color index past the end of the palette rather than
+ * go through matching / dithering. */
+ if ((im->transparent >= 0) && (*inptr == im->transparent)) {
+ *outptr = im->colorsTotal;
+ errorptr[0] = 0;
+ errorptr[1] = 0;
+ errorptr[2] = 0;
+ errorptr[3] = 0;
+ inptr += dir;
+ outptr += dir;
+ errorptr += dir3;
+ continue;
+ }
+ /* curN holds the error propagated from the previous pixel on the
+ * current line. Add the error propagated from the previous line
+ * to form the complete error correction term for this pixel, and
+ * round the error term (which is expressed * 16) to an integer.
+ * RIGHT_SHIFT rounds towards minus infinity, so adding 8 is correct
+ * for either sign of the error value.
+ * Note: errorptr points to *previous* column's array entry.
+ */
+ cur0 = RIGHT_SHIFT (cur0 + errorptr[dir3 + 0] + 8, 4);
+ cur1 = RIGHT_SHIFT (cur1 + errorptr[dir3 + 1] + 8, 4);
+ cur2 = RIGHT_SHIFT (cur2 + errorptr[dir3 + 2] + 8, 4);
+ /* Limit the error using transfer function set by init_error_limit.
+ * See comments with init_error_limit for rationale.
+ */
+ cur0 = error_limit[cur0];
+ cur1 = error_limit[cur1];
+ cur2 = error_limit[cur2];
+ /* Form pixel value + error, and range-limit to 0..MAXJSAMPLE.
+ * The maximum error is +- MAXJSAMPLE (or less with error limiting);
+ * this sets the required size of the range_limit array.
+ */
+#ifdef ORIGINAL_LIB_JPEG
+ cur0 += GETJSAMPLE (inptr[0]);
+ cur1 += GETJSAMPLE (inptr[1]);
+ cur2 += GETJSAMPLE (inptr[2]);
+ cur0 = GETJSAMPLE (range_limit[cur0]);
+ cur1 = GETJSAMPLE (range_limit[cur1]);
+ cur2 = GETJSAMPLE (range_limit[cur2]);
+#else
+ cur0 += gdTrueColorGetRed (*inptr);
+ cur1 += gdTrueColorGetGreen (*inptr);
+ cur2 += gdTrueColorGetBlue (*inptr);
+ range_limit (cur0);
+ range_limit (cur1);
+ range_limit (cur2);
+#endif
+
+ /* Index into the cache with adjusted pixel value */
+ cachep = &histogram[cur0 >> C0_SHIFT][cur1 >> C1_SHIFT][cur2 >> C2_SHIFT];
+ /* If we have not seen this color before, find nearest colormap */
+ /* entry and update the cache */
+ if (*cachep == 0) {
+#ifdef ORIGINAL_LIB_JPEG
+ fill_inverse_cmap(cinfo, cur0 >> C0_SHIFT, cur1 >> C1_SHIFT, cur2 >> C2_SHIFT);
+#else
+ fill_inverse_cmap(im, cquantize, cur0 >> C0_SHIFT, cur1 >> C1_SHIFT, cur2 >> C2_SHIFT);
+#endif
+ }
+ /* Now emit the colormap index for this cell */
+ {
+ register int pixcode = *cachep - 1;
+ *outptr = (JSAMPLE) pixcode;
+ /* Compute representation error for this pixel */
+#define GETJSAMPLE
+ cur0 -= GETJSAMPLE(colormap0[pixcode]);
+ cur1 -= GETJSAMPLE(colormap1[pixcode]);
+ cur2 -= GETJSAMPLE(colormap2[pixcode]);
+#undef GETJSAMPLE
+ }
+ /* Compute error fractions to be propagated to adjacent pixels.
+ * Add these into the running sums, and simultaneously shift the
+ * next-line error sums left by 1 column.
+ */
+ {
+ register LOCFSERROR bnexterr, delta;
+
+ bnexterr = cur0; /* Process component 0 */
+ delta = cur0 * 2;
+ cur0 += delta; /* form error * 3 */
+ errorptr[0] = (FSERROR) (bpreverr0 + cur0);
+ cur0 += delta; /* form error * 5 */
+ bpreverr0 = belowerr0 + cur0;
+ belowerr0 = bnexterr;
+ cur0 += delta; /* form error * 7 */
+ bnexterr = cur1; /* Process component 1 */
+ delta = cur1 * 2;
+ cur1 += delta; /* form error * 3 */
+ errorptr[1] = (FSERROR) (bpreverr1 + cur1);
+ cur1 += delta; /* form error * 5 */
+ bpreverr1 = belowerr1 + cur1;
+ belowerr1 = bnexterr;
+ cur1 += delta; /* form error * 7 */
+ bnexterr = cur2; /* Process component 2 */
+ delta = cur2 * 2;
+ cur2 += delta; /* form error * 3 */
+ errorptr[2] = (FSERROR) (bpreverr2 + cur2);
+ cur2 += delta; /* form error * 5 */
+ bpreverr2 = belowerr2 + cur2;
+ belowerr2 = bnexterr;
+ cur2 += delta; /* form error * 7 */
+ }
+ /* At this point curN contains the 7/16 error value to be propagated
+ * to the next pixel on the current line, and all the errors for the
+ * next line have been shifted over. We are therefore ready to move on.
+ */
+#ifdef ORIGINAL_LIB_JPEG
+ inptr += dir3; /* Advance pixel pointers to next column */
+#else
+ inptr += dir; /* Advance pixel pointers to next column */
+#endif
+ outptr += dir;
+ errorptr += dir3; /* advance errorptr to current column */
+ }
+ /* Post-loop cleanup: we must unload the final error values into the
+ * final fserrors[] entry. Note we need not unload belowerrN because
+ * it is for the dummy column before or after the actual array.
+ */
+ errorptr[0] = (FSERROR) bpreverr0; /* unload prev errs into array */
+ errorptr[1] = (FSERROR) bpreverr1;
+ errorptr[2] = (FSERROR) bpreverr2;
}
- /* Post-loop cleanup: we must unload the final error values into the
- * final fserrors[] entry. Note we need not unload belowerrN because
- * it is for the dummy column before or after the actual array.
- */
- errorptr[0] = (FSERROR) bpreverr0; /* unload prev errs into array */
- errorptr[1] = (FSERROR) bpreverr1;
- errorptr[2] = (FSERROR) bpreverr2;
- errorptr[3] = (FSERROR) bpreverr3;
- }
}
/*
* Initialize the error-limiting transfer function (lookup table).
* The raw F-S error computation can potentially compute error values of up to
- * +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be
- * much less, otherwise obviously wrong pixels will be created. (Typical
+ * +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be
+ * much less, otherwise obviously wrong pixels will be created. (Typical
* effects include weird fringes at color-area boundaries, isolated bright
- * pixels in a dark area, etc.) The standard advice for avoiding this problem
+ * pixels in a dark area, etc.) The standard advice for avoiding this problem
* is to ensure that the "corners" of the color cube are allocated as output
* colors; then repeated errors in the same direction cannot cause cascading
- * error buildup. However, that only prevents the error from getting
+ * error buildup. However, that only prevents the error from getting
* completely out of hand; Aaron Giles reports that error limiting improves
* the results even with corner colors allocated.
* A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty
- * well, but the smoother transfer function used below is even better. Thanks
+ * well, but the smoother transfer function used below is even better. Thanks
* to Aaron Giles for this idea.
*/
-static int
+LOCAL (void)
+#ifdef ORIGINAL_LIB_JPEG
+init_error_limit (j_decompress_ptr cinfo)
+#else
init_error_limit (gdImagePtr im, my_cquantize_ptr cquantize)
+#endif
/* Allocate and fill in the error_limiter table */
{
- int *table;
- int in, out;
-
- cquantize->error_limiter_storage = (int *) gdMalloc ((255 * 2 + 1) * sizeof (int));
- if (!cquantize->error_limiter_storage)
- {
- return 0;
- }
- /* so can index -MAXJSAMPLE .. +MAXJSAMPLE */
- cquantize->error_limiter = cquantize->error_limiter_storage + 255;
- table = cquantize->error_limiter;
-#define STEPSIZE ((255+1)/16)
- /* Map errors 1:1 up to +- MAXJSAMPLE/16 */
- out = 0;
- for (in = 0; in < STEPSIZE; in++, out++)
- {
- table[in] = out;
- table[-in] = -out;
- }
- /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */
- for (; in < STEPSIZE * 3; in++, out += (in & 1) ? 0 : 1)
- {
- table[in] = out;
- table[-in] = -out;
- }
- /* Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) */
- for (; in <= 255; in++)
- {
- table[in] = out;
- table[-in] = -out;
- }
+ int *table;
+ int in, out;
+#ifdef ORIGINAL_LIB_JPEG
+ my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+ table = (int *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, (MAXJSAMPLE * 2 + 1) * SIZEOF (int));
+#else
+ cquantize->error_limiter_storage = (int *) gdMalloc ((MAXJSAMPLE * 2 + 1) * sizeof (int));
+ if (!cquantize->error_limiter_storage) {
+ return;
+ }
+ table = cquantize->error_limiter_storage;
+#endif
+
+ table += MAXJSAMPLE; /* so can index -MAXJSAMPLE .. +MAXJSAMPLE */
+ cquantize->error_limiter = table;
+
+#define STEPSIZE ((MAXJSAMPLE+1)/16)
+ /* Map errors 1:1 up to +- MAXJSAMPLE/16 */
+ out = 0;
+ for (in = 0; in < STEPSIZE; in++, out++){
+ table[in] = out;
+ table[-in] = -out;
+ }
+ /* Map errors 1:2 up to +- 3*MAXJSAMPLE/16 */
+ for (; in < STEPSIZE * 3; in++, out += (in & 1) ? 0 : 1) {
+ table[in] = out;
+ table[-in] = -out;
+ }
+ /* Clamp the rest to final out value (which is (MAXJSAMPLE+1)/8) */
+ for (; in <= MAXJSAMPLE; in++) {
+ table[in] = out;
+ table[-in] = -out;
+ }
#undef STEPSIZE
- return 1;
}
-static void
-zeroHistogram (hist4d histogram)
+
+/*
+ * Finish up at the end of each pass.
+ */
+
+#ifdef ORIGINAL_LIB_JPEG
+METHODDEF (void) finish_pass1 (j_decompress_ptr cinfo)
+{
+ my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+
+ /* Select the representative colors and fill in cinfo->colormap */
+ cinfo->colormap = cquantize->sv_colormap;
+ select_colors (cinfo, cquantize->desired);
+ /* Force next pass to zero the color index table */
+ cquantize->needs_zeroed = TRUE;
+}
+
+
+METHODDEF (void) finish_pass2 (j_decompress_ptr cinfo)
+{
+ /* no work */
+}
+
+/*
+ * Initialize for each processing pass.
+ */
+
+METHODDEF (void) start_pass_2_quant (j_decompress_ptr cinfo, boolean is_pre_scan)
+{
+ my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+ hist3d histogram = cquantize->histogram;
+ int i;
+
+ /* Only F-S dithering or no dithering is supported. */
+ /* If user asks for ordered dither, give him F-S. */
+ if (cinfo->dither_mode != JDITHER_NONE) {
+ cinfo->dither_mode = JDITHER_FS;
+ }
+
+ if (is_pre_scan){
+ /* Set up method pointers */
+ cquantize->pub.color_quantize = prescan_quantize;
+ cquantize->pub.finish_pass = finish_pass1;
+ cquantize->needs_zeroed = TRUE; /* Always zero histogram */
+ } else {
+ /* Set up method pointers */
+ if (cinfo->dither_mode == JDITHER_FS) {
+ cquantize->pub.color_quantize = pass2_fs_dither;
+ } else {
+ cquantize->pub.color_quantize = pass2_no_dither;
+ }
+ cquantize->pub.finish_pass = finish_pass2;
+
+ /* Make sure color count is acceptable */
+ i = cinfo->actual_number_of_colors;
+ if (i < 1) {
+ ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 1);
+ }
+ if (i > MAXNUMCOLORS) {
+ ERREXIT1(cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS);
+ }
+
+ if (cinfo->dither_mode == JDITHER_FS) {
+ size_t arraysize = (size_t) ((cinfo->output_width + 2) * (3 * SIZEOF (FSERROR)));
+ /* Allocate Floyd-Steinberg workspace if we didn't already. */
+ if (cquantize->fserrors == NULL) {
+ cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, arraysize);
+ }
+ /* Initialize the propagated errors to zero. */
+ jzero_far((void FAR *) cquantize->fserrors, arraysize);
+ /* Make the error-limit table if we didn't already. */
+ if (cquantize->error_limiter == NULL) {
+ init_error_limit(cinfo);
+ }
+ cquantize->on_odd_row = FALSE;
+ }
+
+ }
+ /* Zero the histogram or inverse color map, if necessary */
+ if (cquantize->needs_zeroed) {
+ for (i = 0; i < HIST_C0_ELEMS; i++) {
+ jzero_far((void FAR *) histogram[i], HIST_C1_ELEMS * HIST_C2_ELEMS * SIZEOF (histcell));
+ }
+ cquantize->needs_zeroed = FALSE;
+ }
+}
+
+
+/*
+ * Switch to a new external colormap between output passes.
+ */
+
+METHODDEF (void) new_color_map_2_quant (j_decompress_ptr cinfo)
{
- int i;
- int j;
- /* Zero the histogram or inverse color map */
- for (i = 0; i < HIST_C0_ELEMS; i++)
- {
- for (j = 0; j < HIST_C1_ELEMS; j++)
- {
- memset (histogram[i][j],
- 0,
- HIST_C2_ELEMS * HIST_C3_ELEMS * sizeof (histcell));
+ my_cquantize_ptr cquantize = (my_cquantize_ptr) cinfo->cquantize;
+
+ /* Reset the inverse color map */
+ cquantize->needs_zeroed = TRUE;
+}
+#else
+static void zeroHistogram (hist3d histogram)
+{
+ int i;
+ /* Zero the histogram or inverse color map */
+ for (i = 0; i < HIST_C0_ELEMS; i++) {
+ memset (histogram[i], 0, HIST_C1_ELEMS * HIST_C2_ELEMS * sizeof (histcell));
}
- }
}
+#endif
+
+/*
+ * Module initialization routine for 2-pass color quantization.
+ */
-/* Here we go at last. */
+#ifdef ORIGINAL_LIB_JPEG
+GLOBAL (void)
+jinit_2pass_quantizer (j_decompress_ptr cinfo)
+#else
void
gdImageTrueColorToPalette (gdImagePtr im, int dither, int colorsWanted)
+#endif
{
- my_cquantize_ptr cquantize = 0;
+ my_cquantize_ptr cquantize = NULL;
int i;
+
+#ifndef ORIGINAL_LIB_JPEG
+ /* Allocate the JPEG palette-storage */
size_t arraysize;
- if (!im->trueColor || colorsWanted <= 0) {
+ int maxColors = gdMaxColors;
+ if (!im->trueColor) {
/* Nothing to do! */
return;
}
-
- if (colorsWanted > gdMaxColors) {
- colorsWanted = gdMaxColors;
- }
- im->pixels = gdCalloc (sizeof (unsigned char *), im->sy);
+ /* If we have a transparent color (the alphaless mode of transparency), we
+ * must reserve a palette entry for it at the end of the palette. */
+ if (im->transparent >= 0){
+ maxColors--;
+ }
+ if (colorsWanted > maxColors) {
+ colorsWanted = maxColors;
+ }
+ im->pixels = gdCalloc(sizeof (unsigned char *), im->sy);
+ for (i = 0; (i < im->sy); i++) {
+ im->pixels[i] = gdCalloc(sizeof (unsigned char *), im->sx);
+ }
+#endif
- for (i = 0; i < im->sy; i++) {
- im->pixels[i] = gdCalloc (sizeof (unsigned char *), im->sx);
+#ifdef ORIGINAL_LIB_JPEG
+ cquantize = (my_cquantize_ptr) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, SIZEOF (my_cquantizer));
+ cinfo->cquantize = (struct jpeg_color_quantizer *) cquantize;
+ cquantize->pub.start_pass = start_pass_2_quant;
+ cquantize->pub.new_color_map = new_color_map_2_quant;
+ /* Make sure jdmaster didn't give me a case I can't handle */
+ if (cinfo->out_color_components != 3) {
+ ERREXIT (cinfo, JERR_NOTIMPL);
}
- cquantize = (my_cquantize_ptr) gdCalloc (sizeof (my_cquantizer), 1);
+#else
+ cquantize = (my_cquantize_ptr) gdCalloc(sizeof (my_cquantizer), 1);
+#endif
+ cquantize->fserrors = NULL; /* flag optional arrays not allocated */
+ cquantize->error_limiter = NULL;
/* Allocate the histogram/inverse colormap storage */
- cquantize->histogram = (hist4d) gdMalloc (HIST_C0_ELEMS * sizeof (hist3d));
+#ifdef ORIGINAL_LIB_JPEG
+ cquantize->histogram = (hist3d) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, HIST_C0_ELEMS * SIZEOF (hist2d));
+ for (i = 0; i < HIST_C0_ELEMS; i++) {
+ cquantize->histogram[i] = (hist2d) (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, HIST_C1_ELEMS * HIST_C2_ELEMS * SIZEOF (histcell));
+ }
+ cquantize->needs_zeroed = TRUE; /* histogram is garbage now */
+#else
+ cquantize->histogram = (hist3d) gdMalloc (HIST_C0_ELEMS * sizeof (hist2d));
for (i = 0; i < HIST_C0_ELEMS; i++) {
- int j;
- cquantize->histogram[i] = (hist3d) gdCalloc (HIST_C1_ELEMS, sizeof (hist2d));
- for (j = 0; j < HIST_C1_ELEMS; j++) {
- cquantize->histogram[i][j] = (hist2d) gdCalloc (HIST_C2_ELEMS * HIST_C3_ELEMS, sizeof (histcell));
+ cquantize->histogram[i] = (hist2d) gdMalloc(HIST_C1_ELEMS * HIST_C2_ELEMS * sizeof (histcell));
+ }
+#endif
+
+#ifdef ORIGINAL_LIB_JPEG
+ /* Allocate storage for the completed colormap, if required.
+ * We do this now since it is FAR storage and may affect
+ * the memory manager's space calculations.
+ */
+ if (cinfo->enable_2pass_quant) {
+ /* Make sure color count is acceptable */
+ int desired = cinfo->desired_number_of_colors;
+ /* Lower bound on # of colors ... somewhat arbitrary as long as > 0 */
+ if (desired < 8) {
+ ERREXIT1(cinfo, JERR_QUANT_FEW_COLORS, 8);
+ }
+ /* Make sure colormap indexes can be represented by JSAMPLEs */
+ if (desired > MAXNUMCOLORS) {
+ ERREXIT1 (cinfo, JERR_QUANT_MANY_COLORS, MAXNUMCOLORS);
}
+ cquantize->sv_colormap = (*cinfo->mem->alloc_sarray) ((j_common_ptr) cinfo, JPOOL_IMAGE, (JDIMENSION) desired, (JDIMENSION) 3);
+ cquantize->desired = desired;
+ } else {
+ cquantize->sv_colormap = NULL;
+ }
+
+ /* Only F-S dithering or no dithering is supported. */
+ /* If user asks for ordered dither, give him F-S. */
+ if (cinfo->dither_mode != JDITHER_NONE) {
+ cinfo->dither_mode = JDITHER_FS;
}
- cquantize->fserrors = (FSERRPTR) gdMalloc (4 * sizeof (FSERROR));
+ /* Allocate Floyd-Steinberg workspace if necessary.
+ * This isn't really needed until pass 2, but again it is FAR storage.
+ * Although we will cope with a later change in dither_mode,
+ * we do not promise to honor max_memory_to_use if dither_mode changes.
+ */
+ if (cinfo->dither_mode == JDITHER_FS) {
+ cquantize->fserrors = (FSERRPTR) (*cinfo->mem->alloc_large) ((j_common_ptr) cinfo, JPOOL_IMAGE, (size_t) ((cinfo->output_width + 2) * (3 * SIZEOF (FSERROR))));
+ /* Might as well create the error-limiting table too. */
+ init_error_limit(cinfo);
+ }
+#else
+
+ cquantize->fserrors = (FSERRPTR) gdMalloc(3 * sizeof (FSERROR));
init_error_limit (im, cquantize);
- arraysize = (size_t) ((im->sx + 2) * (4 * sizeof (FSERROR)));
+ arraysize = (size_t) ((im->sx + 2) * (3 * sizeof (FSERROR)));
gdFree(cquantize->fserrors);
/* Allocate Floyd-Steinberg workspace. */
- cquantize->fserrors = gdCalloc (arraysize, 1);
+ cquantize->fserrors = gdCalloc(arraysize, 1);
cquantize->on_odd_row = FALSE;
/* Do the work! */
- zeroHistogram (cquantize->histogram);
- prescan_quantize (im, cquantize);
- select_colors (im, cquantize, colorsWanted);
-
- zeroHistogram (cquantize->histogram);
+ zeroHistogram(cquantize->histogram);
+ prescan_quantize(im, cquantize);
+ /* TBB 2.0.5: pass colorsWanted, not 256! */
+ select_colors(im, cquantize, colorsWanted);
+ zeroHistogram(cquantize->histogram);
if (dither) {
- pass2_fs_dither (im, cquantize);
+ pass2_fs_dither(im, cquantize);
} else {
- pass2_no_dither (im, cquantize);
+ pass2_no_dither(im, cquantize);
}
+#if 0 /* 2.0.12; we no longer attempt full alpha in palettes */
if (cquantize->transparentIsPresent) {
int mt = -1;
int mtIndex = -1;
- for (i = 0; i < im->colorsTotal; i++) {
+ for (i = 0; (i < im->colorsTotal); i++) {
if (im->alpha[i] > mt) {
mtIndex = i;
mt = im->alpha[i];
}
}
- for (i = 0; i < im->colorsTotal; i++) {
+ for (i = 0; (i < im->colorsTotal); i++) {
if (im->alpha[i] == mt) {
im->alpha[i] = gdAlphaTransparent;
}
if (cquantize->opaqueIsPresent) {
int mo = 128;
int moIndex = -1;
- for (i = 0; i < im->colorsTotal; i++) {
+ for (i = 0; (i < im->colorsTotal); i++) {
if (im->alpha[i] < mo) {
moIndex = i;
mo = im->alpha[i];
}
}
- for (i = 0; i < im->colorsTotal; i++) {
+ for (i = 0; (i < im->colorsTotal); i++) {
if (im->alpha[i] == mo) {
im->alpha[i] = gdAlphaOpaque;
}
}
}
-
+#endif
+
+ /* If we had a 'transparent' color, increment the color count so it's
+ * officially in the palette and convert the transparent variable to point to
+ * an index rather than a color (Its data already exists and transparent
+ * pixels have already been mapped to it by this point, it is done late as to
+ * avoid color matching / dithering with it). */
+ if (im->transparent >= 0) {
+ im->transparent = im->colorsTotal;
+ im->colorsTotal++;
+ }
+
/* Success! Get rid of the truecolor image data. */
im->trueColor = 0;
/* Junk the truecolor pixels */
for (i = 0; i < im->sy; i++) {
gdFree(im->tpixels[i]);
}
- gdFree (im->tpixels);
+ gdFree(im->tpixels);
im->tpixels = 0;
/* Tediously free stuff. */
+ if (im->trueColor) {
+ /* On failure only */
+ for (i = 0; i < im->sy; i++) {
+ if (im->pixels[i]) {
+ gdFree (im->pixels[i]);
+ }
+ }
+ if (im->pixels) {
+ gdFree (im->pixels);
+ }
+ im->pixels = 0;
+ }
+
for (i = 0; i < HIST_C0_ELEMS; i++) {
if (cquantize->histogram[i]) {
- int j;
- for (j = 0; j < HIST_C1_ELEMS; j++) {
- if (cquantize->histogram[i][j]) {
- gdFree(cquantize->histogram[i][j]);
- }
- }
gdFree(cquantize->histogram[i]);
}
}
if (cquantize) {
gdFree(cquantize);
}
+#endif
}
/* bring the palette colors in im2 to be closer to im1
*
*/
-int
-gdImageColorMatch (gdImagePtr im1, gdImagePtr im2)
+int gdImageColorMatch (gdImagePtr im1, gdImagePtr im2)
{
unsigned long *buf; /* stores our calculations */
unsigned long *bp; /* buf ptr */
buf = (unsigned long *)gdMalloc( sizeof(unsigned long) * 5 * im2->colorsTotal );
memset( buf, 0, sizeof(unsigned long) * 5 * im2->colorsTotal );
- for( x=0; x<im1->sx; x++ ) {
+ for (x=0; x<im1->sx; x++) {
for( y=0; y<im1->sy; y++ ) {
color = im2->pixels[y][x];
rgb = im1->tpixels[y][x];
}
}
bp = buf;
- for( color=0; color<im2->colorsTotal; color++ ) {
+ for (color=0; color<im2->colorsTotal; color++) {
count = *(bp++);
if( count > 0 ) {
im2->red[color] = *(bp++) / count;
gdFree(buf);
return 0;
}
+
+
+#endif
* element is replaced in the event of a cache miss after the cache has
* reached a given size.
*
- * John Ellson (ellson@lucent.com) Oct 31, 1997
+ * John Ellson (ellson@graphviz.org) Oct 31, 1997
*
* Test this with:
* gcc -o gdcache -g -Wall -DTEST gdcache.c
* element is replaced in the event of a cache miss after the cache has
* reached a given size.
*
- * John Ellson (ellson@lucent.com) Oct 31, 1997
+ * John Ellson (ellson@graphviz.org) Oct 31, 1997
*
* Test this with:
* gcc -o gdcache -g -Wall -DTEST gdcache.c
/********************************************/
/* gd interface to freetype library */
/* */
-/* John Ellson ellson@lucent.com */
+/* John Ellson ellson@graphviz.org */
/********************************************/
#include <stdio.h>
#include <unistd.h>
#else
#include <io.h>
+#define R_OK 04 /* Needed in Windows */
#endif
#ifdef WIN32
/*
* DEFAULT_FONTPATH and PATHSEPARATOR are host type dependent and
- * are normally set by configure in gvconfig.h. These are just
+ * are normally set by configure in config.h. These are just
* some last resort values that might match some Un*x system
* if building this version of gd separate from graphviz.
*/
int render = (im && (im->trueColor || (fg <= 255 && fg >= -255)));
FT_BitmapGlyph bm;
int render_mode = FT_LOAD_RENDER | FT_LOAD_FORCE_AUTOHINT;
+ int m, mfound;
/* Now tuneable thanks to Wez Furlong */
double linespace = LINESPACE;
/* 2.0.6: put this declaration with the other declarations! */
if (fg < 0) {
render_mode |= FT_LOAD_MONOCHROME;
}
+ /* 2.0.12: allow explicit specification of the preferred map;
+ * but we still fall back if it is not available.
+ */
+ m = gdFTEX_Unicode;
+ if (strex && (strex->flags & gdFTEX_CHARMAP)) {
+ m = strex->charmap;
+ }
+ /* Try all three types of maps, but start with the specified one */
+ mfound = 0;
+ for (i = 0; i < 3; i++) {
+ switch (m) {
+ case gdFTEX_Unicode:
+ if (font->have_char_map_unicode) {
+ mfound = 1;
+ }
+ break;
+ case gdFTEX_Shift_JIS:
+ if (font->have_char_map_sjis) {
+ mfound = 1;
+ }
+ break;
+ case gdFTEX_Big5:
+ /* This was the 'else' case, we can't really 'detect' it */
+ mfound = 1;
+ break;
+ }
+ if (mfound) {
+ break;
+ }
+ m++;
+ m %= 3;
+ }
+ if (!mfound) {
+ /* No character set found! */
+ return "No character set found";
+ }
#ifndef JISX0208
if (!font->have_char_map_sjis) {
next = string;
} else
#endif
- tmpstr = (char *) gdMalloc (BUFSIZ);
+ tmpstr = (char *) gdMalloc(BUFSIZ);
while (*next) {
ch = *next;
next++;
continue;
}
-
- if (font->have_char_map_unicode) {
- /* use UTF-8 mapping from ASCII */
- len = gdTcl_UtfToUniChar (next, &ch);
- next += len;
- } else if (font->have_char_map_sjis) {
- unsigned char c;
- int jiscode;
-
- c = *next;
- if (0xA1 <= c && c <= 0xFE) {
- next++;
- jiscode = 0x100 * (c & 0x7F) + ((*next) & 0x7F);
-
- ch = (jiscode >> 8) & 0xFF;
- jiscode &= 0xFF;
-
- if (ch & 1) {
- jiscode += 0x40 - 0x21;
- } else {
- jiscode += 0x9E - 0x21;
- }
-
- if (jiscode >= 0x7F) {
- jiscode++;
+ switch (m) {
+ case gdFTEX_Unicode:
+ if (font->have_char_map_unicode) {
+ /* use UTF-8 mapping from ASCII */
+ len = gdTcl_UtfToUniChar(next, &ch);
+ next += len;
}
- ch = (ch - 0x21) / 2 + 0x81;
- if (ch >= 0xA0) {
- ch += 0x40;
+ break;
+ case gdFTEX_Shift_JIS:
+ if (font->have_char_map_sjis) {
+ unsigned char c;
+ int jiscode;
+ c = *next;
+ if (0xA1 <= c && c <= 0xFE) {
+ next++;
+ jiscode = 0x100 * (c & 0x7F) + ((*next) & 0x7F);
+
+ ch = (jiscode >> 8) & 0xFF;
+ jiscode &= 0xFF;
+
+ if (ch & 1) {
+ jiscode += 0x40 - 0x21;
+ } else {
+ jiscode += 0x9E - 0x21;
+ }
+
+ if (jiscode >= 0x7F) {
+ jiscode++;
+ }
+ ch = (ch - 0x21) / 2 + 0x81;
+ if (ch >= 0xA0) {
+ ch += 0x40;
+ }
+
+ ch = (ch << 8) + jiscode;
+ } else {
+ ch = c & 0xFF; /* don't extend sign */
+ }
+ next++;
}
-
- ch = (ch << 8) + jiscode;
- } else {
- ch = c & 0xFF; /* don't extend sign */
- }
- next++;
- } else {
- /*
- * Big 5 mapping:
- * use "JIS-8 half-width katakana" coding from 8-bit characters. Ref:
- * ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs
- */
- ch = (*next) & 0xFF; /* don't extend sign */
- next++;
- /* first code of JIS-8 pair */
- if (ch >= 161 && *next) { /* don't advance past '\0' */
- /* TBB: Fix from Kwok Wah On: & 255 needed */
- ch = (ch * 256) + ((*next) & 255);
+ break;
+ case gdFTEX_Big5: {
+ /*
+ * Big 5 mapping:
+ * use "JIS-8 half-width katakana" coding from 8-bit characters. Ref:
+ * ftp://ftp.ora.com/pub/examples/nutshell/ujip/doc/japan.inf-032092.sjs
+ */
+ ch = (*next) & 0xFF; /* don't extend sign */
next++;
+ if (ch >= 161 /* first code of JIS-8 pair */
+ && *next) { /* don't advance past '\0' */
+ /* TBB: Fix from Kwok Wah On: & 255 needed */
+ ch = (ch * 256) + ((*next) & 255);
+ next++;
+ }
}
+ break;
}
+
/* set rotation transform */
FT_Set_Transform(face, &matrix, NULL);
/* Convert character code to glyph index */
from_len = strlen ((const char *) from) + 1;
to_len = BUFSIZ;
- if (iconv (cd, (char **) &from, &from_len,
- (char **) &to, &to_len) == -1)
+ if ((int) iconv(cd, (char **) &from, &from_len, (char **) &to, &to_len) == -1)
{
#ifdef HAVE_ERRNO_H
if (errno == EINVAL)