Attached is a patch that replace the gdImageFill for libgd2 2.0.33 to the fixed version
from 2.0.35 (http://bugs.libgd.org/4).
-- Benoit Plessis +33 6 77 42 78 32 <[EMAIL PROTECTED]> +33 4 67 28 06 96
--- libgd2-2.0.33/gd.c 2008-01-13 19:12:03.000000000 +0100 +++ libgd2-2.0.33.patched/gd.c 2008-01-13 19:08:03.000000000 +0100 @@ -1674,135 +1674,281 @@ } } -BGD_DECLARE(void) gdImageFill (gdImagePtr im, int x, int y, int color) -{ - int lastBorder; - int old; - int leftLimit, rightLimit; - int i; - old = gdImageGetPixel (im, x, y); - if (color == gdTiled) - { - /* Tile fill -- got to watch out! */ - int p, tileColor; - int srcx, srcy; - if (!im->tile) - { - return; - } - /* Refuse to flood-fill with a transparent pattern -- - I can't do it without allocating another image */ - if (gdImageGetTransparent (im->tile) != (-1)) - { - return; - } - srcx = x % gdImageSX (im->tile); - srcy = y % gdImageSY (im->tile); - p = gdImageGetPixel (im->tile, srcx, srcy); - if (im->trueColor) - { - tileColor = p; +/* + * set the pixel at (x,y) and its 4-connected neighbors + * with the same pixel value to the new pixel value nc (new color). + * A 4-connected neighbor: pixel above, below, left, or right of a pixel. + * ideas from comp.graphics discussions. + * For tiled fill, the use of a flag buffer is mandatory. As the tile image can + * contain the same color as the color to fill. To do not bloat normal filling + * code I added a 2nd private function. + */ + +static int gdImageTileGet (gdImagePtr im, int x, int y) +{ + int srcx, srcy; + int tileColor,p; + if (!im->tile) { + return -1; + } + srcx = x % gdImageSX(im->tile); + srcy = y % gdImageSY(im->tile); + p = gdImageGetPixel(im->tile, srcx, srcy); + if (p == im->tile->transparent) { + tileColor = im->transparent; + } else if (im->trueColor) { + if (im->tile->trueColor) { + tileColor = p; + } else { + tileColor = gdTrueColorAlpha( gdImageRed(im->tile,p), gdImageGreen(im->tile,p), gdImageBlue (im->tile,p), gdImageAlpha (im->tile,p)); + } + } else { + if (im->tile->trueColor) { + tileColor = gdImageColorResolveAlpha(im, gdTrueColorGetRed (p), gdTrueColorGetGreen (p), gdTrueColorGetBlue (p), gdTrueColorGetAlpha (p)); + } else { + tileColor = p; + tileColor = gdImageColorResolveAlpha(im, gdImageRed (im->tile,p), gdImageGreen (im->tile,p), gdImageBlue (im->tile,p), gdImageAlpha (im->tile,p)); + } + } + return tileColor; +} + +/* horizontal segment of scan line y */ +struct seg {int y, xl, xr, dy;}; + +/* max depth of stack */ +#define FILL_MAX 1200000 +#define FILL_PUSH(Y, XL, XR, DY) \ + if (sp<stack+FILL_MAX*10 && Y+(DY)>=0 && Y+(DY)<wy2) \ + {sp->y = Y; sp->xl = XL; sp->xr = XR; sp->dy = DY; sp++;} + +#define FILL_POP(Y, XL, XR, DY) \ + {sp--; Y = sp->y+(DY = sp->dy); XL = sp->xl; XR = sp->xr;} + +void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc); +BGD_DECLARE(void) gdImageFill(gdImagePtr im, int x, int y, int nc) +{ + int l, x1, x2, dy; + int oc; /* old pixel value */ + int wx2,wy2; + + int alphablending_bak; + + /* stack of filled segments */ + /* struct seg stack[FILL_MAX],*sp = stack;; */ + struct seg *stack; + struct seg *sp; + + if (!im->trueColor && nc > (im->colorsTotal - 1)) { + return; } - else - { - if (im->tile->trueColor) - { - tileColor = gdImageColorResolveAlpha (im, - gdTrueColorGetRed (p), - gdTrueColorGetGreen (p), - gdTrueColorGetBlue (p), - gdTrueColorGetAlpha (p)); - } - else - { - tileColor = im->tileColorMap[p]; - } + + alphablending_bak = im->alphaBlendingFlag; + im->alphaBlendingFlag = 0; + + if (nc==gdTiled) { + _gdImageFillTiled(im,x,y,nc); + im->alphaBlendingFlag = alphablending_bak; + return; } - if (old == tileColor) - { - /* Nothing to be done */ - return; + + wx2=im->sx;wy2=im->sy; + oc = gdImageGetPixel(im, x, y); + if (oc==nc || x<0 || x>wx2 || y<0 || y>wy2) { + im->alphaBlendingFlag = alphablending_bak; + return; } - } - else - { - if (old == color) - { - /* Nothing to be done */ - return; + + /* Do not use the 4 neighbors implementation with + * small images + */ + if (im->sx < 4) { + int ix = x, iy = y, c; + do { + c = gdImageGetPixel(im, ix, iy); + if (c != oc) { + goto done; + } + gdImageSetPixel(im, ix, iy, nc); + } while(ix++ < (im->sx -1)); + ix = x; iy = y + 1; + do { + c = gdImageGetPixel(im, ix, iy); + if (c != oc) { + goto done; + } + gdImageSetPixel(im, ix, iy, nc); + } while(ix++ < (im->sx -1)); + goto done; } - } - /* Seek left */ - leftLimit = (-1); - for (i = x; (i >= 0); i--) - { - if (gdImageGetPixel (im, i, y) != old) - { - break; + + if(overflow2(im->sy, im->sx)) { + 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; + + if(overflow2(sizeof(struct seg), ((im->sy * im->sx) / 4))) { + return; } - gdImageSetPixel (im, i, y, color); - rightLimit = i; - } - /* Look at lines above and below and start paints */ - /* Above */ - if (y > 0) - { - lastBorder = 1; - for (i = leftLimit; (i <= rightLimit); i++) - { - int c; - c = gdImageGetPixel (im, i, y - 1); - if (lastBorder) - { - if (c == old) - { - gdImageFill (im, i, y - 1, color); - lastBorder = 0; - } - } - else if (c != old) - { - lastBorder = 1; - } + + stack = (struct seg *)gdMalloc(sizeof(struct seg) * ((int)(im->sy*im->sx)/4)); + if (!stack) { + return; } - } - /* Below */ - if (y < ((im->sy) - 1)) - { - lastBorder = 1; - for (i = leftLimit; (i <= rightLimit); i++) - { - int c; - c = gdImageGetPixel (im, i, y + 1); - if (lastBorder) - { - if (c == old) - { - gdImageFill (im, i, y + 1, color); - lastBorder = 0; - } - } - else if (c != old) - { - lastBorder = 1; - } + sp = stack; + + /* required! */ + FILL_PUSH(y,x,x,1); + /* seed segment (popped 1st) */ + FILL_PUSH(y+1, x, x, -1); + while (sp>stack) { + FILL_POP(y, x1, x2, dy); + + for (x=x1; x>=0 && gdImageGetPixel(im,x, y)==oc; x--) { + gdImageSetPixel(im,x, y, nc); + } + if (x>=x1) { + goto skip; + } + l = x+1; + + /* leak on left? */ + if (l<x1) { + FILL_PUSH(y, l, x1-1, -dy); + } + x = x1+1; + do { + for (; x<=wx2 && gdImageGetPixel(im,x, y)==oc; x++) { + gdImageSetPixel(im, x, y, nc); + } + FILL_PUSH(y, l, x-1, dy); + /* leak on right? */ + if (x>x2+1) { + FILL_PUSH(y, x2+1, x-1, -dy); + } +skip: for (x++; x<=x2 && (gdImageGetPixel(im, x, y)!=oc); x++); + + l = x; + } while (x<=x2); } - } + + gdFree(stack); + +done: + im->alphaBlendingFlag = alphablending_bak; +} + +void _gdImageFillTiled(gdImagePtr im, int x, int y, int nc) +{ + int i,l, x1, x2, dy; + int oc; /* old pixel value */ + int tiled; + int wx2,wy2; + /* stack of filled segments */ + struct seg *stack; + struct seg *sp; + + int **pts; + if(!im->tile){ + return; + } + + wx2=im->sx;wy2=im->sy; + tiled = nc==gdTiled; + + if(overflow2(sizeof(int *), im->sy)) { + return; + } + + if(overflow2((sizeof(int *) * im->sy), sizeof(int))) { + return; + } + + if(overflow2(im->sx, sizeof(int))) { + return; + } + + if(overflow2(im->sy, im->sx)) { + return; + } + + if(overflow2(sizeof(struct seg), ((im->sy * im->sx) / 4))) { + return; + } + + nc = gdImageTileGet(im,x,y); + pts = (int **) gdCalloc(sizeof(int *) * im->sy, sizeof(int)); + if (!pts) { + return; + } + + for (i=0; i<im->sy;i++) { + pts[i] = (int *) gdCalloc(im->sx, sizeof(int)); + + if (!pts[i]) { + for (--i ; i >= 0; i--) { + gdFree(pts[i]); + } + return; + } + } + + stack = (struct seg *)gdMalloc(sizeof(struct seg) * ((int)(im->sy*im->sx)/4)); + if (!stack) { + return; + } + sp = stack; + + oc = gdImageGetPixel(im, x, y); + + /* required! */ + FILL_PUSH(y,x,x,1); + /* seed segment (popped 1st) */ + FILL_PUSH(y+1, x, x, -1); + while (sp>stack) { + FILL_POP(y, x1, x2, dy); + for (x=x1; x>=0 && (!pts[y][x] && gdImageGetPixel(im,x,y)==oc); x--) { + if (pts[y][x]){ + /* we should never be here */ + break; + } + nc = gdImageTileGet(im,x,y); + pts[y][x]=1; + gdImageSetPixel(im,x, y, nc); + } + if (x>=x1) { + goto skip; + } + l = x+1; + + /* leak on left? */ + if (l<x1) { + FILL_PUSH(y, l, x1-1, -dy); + } + x = x1+1; + do { + for (; x<wx2 && (!pts[y][x] && gdImageGetPixel(im,x, y)==oc) ; x++) { + if (pts[y][x]){ + /* we should never be here */ + break; + } + nc = gdImageTileGet(im,x,y); + pts[y][x]=1; + gdImageSetPixel(im, x, y, nc); + } + FILL_PUSH(y, l, x-1, dy); + /* leak on right? */ + if (x>x2+1) { + FILL_PUSH(y, x2+1, x-1, -dy); + } +skip: for (x++; x<=x2 && (pts[y][x] || gdImageGetPixel(im,x, y)!=oc); x++); + l = x; + } while (x<=x2); + } + for (i=0; i<im->sy;i++) { + gdFree(pts[i]); + } + gdFree(pts); + gdFree(stack); } BGD_DECLARE(void) gdImageRectangle (gdImagePtr im, int x1, int y1, int x2, int y2, int color)