From: Ilia Alshanetsky Date: Sat, 5 Apr 2003 17:24:16 +0000 (+0000) Subject: MFH (gd upgrade). X-Git-Tag: php-4.3.2RC2~150 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=edea55ecd7ab998f9df2f75e219ec8dcd25d2cc6;p=php MFH (gd upgrade). --- diff --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c index 4b1f7ec021..f2411d6d98 100644 --- a/ext/gd/libgd/gd.c +++ b/ext/gd/libgd/gd.c @@ -87,8 +87,9 @@ static const unsigned char gd_toascii[256] = 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); @@ -117,148 +118,152 @@ void php_gd_error(const char *format, ...) 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 @@ -288,72 +293,66 @@ gdImageColorClosestAlpha (gdImagePtr im, int r, int g, int b, int a) */ 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; } @@ -362,147 +361,125 @@ HWB_Diff (int r1, int g1, int b1, int r2, int g2, int b2) * 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; } /* @@ -516,14 +493,12 @@ gdImageColorAllocateAlpha (gdImagePtr im, int r, int g, int b, int a) * 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; @@ -581,15 +556,13 @@ gdImageColorResolveAlpha (gdImagePtr im, int r, int g, int b, int a) 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) @@ -607,61 +580,43 @@ 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 @@ -734,92 +689,80 @@ static int clip_1d(int *x0, int *y0, int *x1, int *y1, int maxdim) { 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; @@ -830,36 +773,54 @@ gdImageBrushApply (gdImagePtr im, int x, int y) 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), @@ -877,72 +838,149 @@ gdImageBrushApply (gdImagePtr im, int x, int y) } } -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; @@ -953,6 +991,25 @@ gdImageLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color) 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); @@ -1019,8 +1076,14 @@ gdImageLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color) } } 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; } @@ -1075,6 +1138,11 @@ gdImageLine (gdImagePtr im, int x1, int y1, int x2, int y2, int color) } } } + + /* If this is the only line we are drawing, go ahead and blend. */ + if (color == gdAntiAliased && !im->AA_polygon) { + gdImageAABlend(im); + } } @@ -1210,317 +1278,266 @@ void gdImageAALine (gdImagePtr im, int x1, int y1, int x2, int y2, int col) } } -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 @@ -1531,97 +1548,75 @@ lsqrt (long n) 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); - } } - } } @@ -1795,178 +1790,132 @@ void gdImageFillToBorder (gdImagePtr im, int x, int y, int border, int 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; @@ -1994,14 +1943,14 @@ gdImageFilledRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color } } -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 @@ -2034,14 +1983,14 @@ gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int s 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++; @@ -2050,13 +1999,13 @@ gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int s } /* 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); @@ -2093,58 +2042,51 @@ gdImageCopy (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int srcX, int s /* 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; @@ -2192,8 +2134,7 @@ gdImageCopyMergeGray (gdImagePtr dst, gdImagePtr src, int dstX, int dstY, int sr } } -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; @@ -2757,7 +2698,6 @@ gdImagePtr gdImageRotate45 (gdImagePtr src, double dAngle, int clrBack) gdImageSkewY(dst2, dst1, u, iShear, (dOffset - iShear), clrBack); } - /* 3rd shear */ gdImageDestroy(dst1); @@ -2843,8 +2783,7 @@ gdImagePtr gdImageRotate (gdImagePtr src, double dAngle, int clrBack) } /* 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; @@ -2854,6 +2793,14 @@ gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c) 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 { @@ -2862,283 +2809,269 @@ gdImagePolygon (gdImagePtr im, gdPointPtr p, int n, int c) 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) + @@ -3152,28 +3085,24 @@ gdAlphaBlend (int dst, int src) 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); @@ -3186,8 +3115,7 @@ gdFullAlphaBlend (int dst, int 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; @@ -3212,8 +3140,7 @@ gdAlphaBlendColor( int b1, int b2, int a1, int a2 ) 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); @@ -3225,8 +3152,7 @@ gdLayerOverlay (int dst, int src) ); } -static int -gdAlphaOverlayColor( int src, int dst, int max ) +static int gdAlphaOverlayColor (int src, int dst, int max ) { /* this function implements the algorithm * @@ -3246,3 +3172,43 @@ gdAlphaOverlayColor( int src, int dst, int max ) 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; +} diff --git a/ext/gd/libgd/gd.h b/ext/gd/libgd/gd.h index 6fa9914191..2188d2ccec 100644 --- a/ext/gd/libgd/gd.h +++ b/ext/gd/libgd/gd.h @@ -147,6 +147,31 @@ typedef struct gdImageStruct { 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; @@ -184,6 +209,8 @@ typedef gdFont *gdFontPtr; 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). */ @@ -245,6 +272,8 @@ void gdImageSetPixel(gdImagePtr im, int x, int y, int color); 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); @@ -256,6 +285,8 @@ void gdImageDashedLine(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); @@ -275,10 +306,22 @@ char *gdImageStringFT(gdImage *im, int *brect, int fg, char *fontlist, 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 * @@ -373,6 +416,13 @@ void gdImageColorTransparent(gdImagePtr im, int color); 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); @@ -416,6 +466,7 @@ void* gdImagePngPtr(gdImagePtr im, int *size); /* 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); @@ -474,6 +525,8 @@ gdImagePtr gdImageRotate (gdImagePtr src, double dAngle, int clrBack); 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. */ diff --git a/ext/gd/libgd/gd_gd.c b/ext/gd/libgd/gd_gd.c index 53b1c71f99..28755a8412 100644 --- a/ext/gd/libgd/gd_gd.c +++ b/ext/gd/libgd/gd_gd.c @@ -20,252 +20,246 @@ extern void gdImageGd (gdImagePtr im, FILE * out); /* */ /* 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; } diff --git a/ext/gd/libgd/gd_gd2.c b/ext/gd/libgd/gd_gd2.c index 59c711763b..00a69e9d87 100644 --- a/ext/gd/libgd/gd_gd2.c +++ b/ext/gd/libgd/gd_gd2.c @@ -293,7 +293,7 @@ gdImagePtr gdImageCreateFromGd2Ctx (gdIOCtxPtr in) 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; } @@ -391,9 +391,11 @@ gdImagePtr gdImageCreateFromGd2PartCtx (gdIOCtx * in, int srcx, int srcy, int w, 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; diff --git a/ext/gd/libgd/gd_io.h b/ext/gd/libgd/gd_io.h index c28379abd8..a2da9cf031 100644 --- a/ext/gd/libgd/gd_io.h +++ b/ext/gd/libgd/gd_io.h @@ -2,6 +2,10 @@ #define GD_IO_H 1 #include + +#ifdef VMS +#define Putchar gdPutchar +#endif typedef struct gdIOCtx { int (*getC)(struct gdIOCtx*); diff --git a/ext/gd/libgd/gd_jpeg.c b/ext/gd/libgd/gd_jpeg.c index 7794dc890b..d6ea05f699 100644 --- a/ext/gd/libgd/gd_jpeg.c +++ b/ext/gd/libgd/gd_jpeg.c @@ -46,8 +46,7 @@ typedef struct _jmpbuf_wrapper } 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; @@ -76,16 +75,14 @@ fatal_jpeg_error (j_common_ptr cinfo) * 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); @@ -98,8 +95,7 @@ gdImageJpegPtr (gdImagePtr im, int *size, int quality) 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; @@ -210,12 +206,11 @@ gdImageJpegCtx (gdImagePtr im, gdIOCtx * outfile, int quality) 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; @@ -227,8 +222,7 @@ void jpeg_gdIOCtx_src (j_decompress_ptr cinfo, gdIOCtx * infile); * Create a gd-format image from the JPEG-format INFILE. Returns the * image, or NULL upon error. */ -gdImagePtr -gdImageCreateFromJpegCtx (gdIOCtx * infile) +gdImagePtr gdImageCreateFromJpegCtx (gdIOCtx * infile) { struct jpeg_decompress_struct cinfo; struct jpeg_error_mgr jerr; @@ -388,8 +382,7 @@ typedef my_source_mgr *my_src_ptr; * 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; @@ -436,21 +429,21 @@ init_source (j_decompress_ptr cinfo) #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; } @@ -490,8 +483,7 @@ fill_input_buffer (j_decompress_ptr cinfo) * 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; @@ -530,8 +522,7 @@ skip_input_data (j_decompress_ptr cinfo, long num_bytes) * for error exit. */ -void -term_source (j_decompress_ptr cinfo) +void term_source (j_decompress_ptr cinfo) { #if 0 * never used */ @@ -546,8 +537,7 @@ term_source (j_decompress_ptr cinfo) * 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; @@ -595,8 +585,7 @@ typedef my_destination_mgr *my_dest_ptr; * 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; @@ -631,8 +620,7 @@ init_destination (j_compress_ptr cinfo) * 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; @@ -656,8 +644,7 @@ empty_output_buffer (j_compress_ptr cinfo) * 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; @@ -675,8 +662,7 @@ term_destination (j_compress_ptr cinfo) * 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; diff --git a/ext/gd/libgd/gd_png.c b/ext/gd/libgd/gd_png.c index a5850c6511..9e01872f0c 100644 --- a/ext/gd/libgd/gd_png.c +++ b/ext/gd/libgd/gd_png.c @@ -38,696 +38,650 @@ #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 */ diff --git a/ext/gd/libgd/gd_topal.c b/ext/gd/libgd/gd_topal.c index b3cf0d5b87..77fb4139b2 100644 --- a/ext/gd/libgd/gd_topal.c +++ b/ext/gd/libgd/gd_topal.c @@ -1,29 +1,50 @@ - +/* 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" @@ -31,6 +52,85 @@ #include #include +/* (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 @@ -69,33 +169,41 @@ * 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. @@ -123,44 +231,42 @@ * 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<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. @@ -268,436 +395,417 @@ prescan_quantize (gdImagePtr im, my_cquantize_ptr cquantize) 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 } @@ -759,17 +867,14 @@ select_colors (gdImagePtr im, my_cquantize_ptr cquantize, int desired_colors) #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<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. @@ -978,48 +1052,59 @@ find_best_colors (gdImagePtr im, my_cquantize_ptr cquantize, * 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; @@ -1031,21 +1116,14 @@ find_best_colors (gdImagePtr im, my_cquantize_ptr cquantize, 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; @@ -1057,67 +1135,75 @@ find_best_colors (gdImagePtr im, my_cquantize_ptr cquantize, } -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 + } } - } } - } } @@ -1125,429 +1211,583 @@ fill_inverse_cmap (gdImagePtr im, my_cquantize_ptr cquantize, * 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; } @@ -1556,37 +1796,55 @@ gdImageTrueColorToPalette (gdImagePtr im, int dither, int colorsWanted) 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]); } } @@ -1602,13 +1860,13 @@ gdImageTrueColorToPalette (gdImagePtr im, int dither, int colorsWanted) 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 */ @@ -1629,7 +1887,7 @@ gdImageColorMatch (gdImagePtr im1, gdImagePtr im2) buf = (unsigned long *)gdMalloc( sizeof(unsigned long) * 5 * im2->colorsTotal ); memset( buf, 0, sizeof(unsigned long) * 5 * im2->colorsTotal ); - for( x=0; xsx; x++ ) { + for (x=0; xsx; x++) { for( y=0; ysy; y++ ) { color = im2->pixels[y][x]; rgb = im1->tpixels[y][x]; @@ -1642,7 +1900,7 @@ gdImageColorMatch (gdImagePtr im1, gdImagePtr im2) } } bp = buf; - for( color=0; colorcolorsTotal; color++ ) { + for (color=0; colorcolorsTotal; color++) { count = *(bp++); if( count > 0 ) { im2->red[color] = *(bp++) / count; @@ -1656,3 +1914,6 @@ gdImageColorMatch (gdImagePtr im1, gdImagePtr im2) gdFree(buf); return 0; } + + +#endif diff --git a/ext/gd/libgd/gdcache.c b/ext/gd/libgd/gdcache.c index 1c32e5623f..e2219c0cbd 100644 --- a/ext/gd/libgd/gdcache.c +++ b/ext/gd/libgd/gdcache.c @@ -18,7 +18,7 @@ * 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 diff --git a/ext/gd/libgd/gdcache.h b/ext/gd/libgd/gdcache.h index 2446d614bd..45d037475d 100644 --- a/ext/gd/libgd/gdcache.h +++ b/ext/gd/libgd/gdcache.h @@ -5,7 +5,7 @@ * 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 diff --git a/ext/gd/libgd/gdft.c b/ext/gd/libgd/gdft.c index f93cd7e23b..f196a980fb 100644 --- a/ext/gd/libgd/gdft.c +++ b/ext/gd/libgd/gdft.c @@ -2,7 +2,7 @@ /********************************************/ /* gd interface to freetype library */ /* */ -/* John Ellson ellson@lucent.com */ +/* John Ellson ellson@graphviz.org */ /********************************************/ #include @@ -16,6 +16,7 @@ #include #else #include +#define R_OK 04 /* Needed in Windows */ #endif #ifdef WIN32 @@ -83,7 +84,7 @@ gdImageStringFT (gdImage * im, int *brect, int fg, char *fontlist, /* * 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. */ @@ -789,6 +790,7 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi 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! */ @@ -847,13 +849,49 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi 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; @@ -879,57 +917,65 @@ gdImageStringFTEx (gdImage * im, int *brect, int fg, char *fontlist, double ptsi 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 */ diff --git a/ext/gd/libgd/gdkanji.c b/ext/gd/libgd/gdkanji.c index 30609cde13..ad839c8bb3 100644 --- a/ext/gd/libgd/gdkanji.c +++ b/ext/gd/libgd/gdkanji.c @@ -362,8 +362,7 @@ do_convert (unsigned char *to, unsigned char *from, const char *code) 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)