diff --git a/ext/gd/libgd/gd.c b/ext/gd/libgd/gd.c index 37ca736e0d2..a7b337a6508 100644 --- a/ext/gd/libgd/gd.c +++ b/ext/gd/libgd/gd.c @@ -868,6 +868,36 @@ static void gdImageTileApply (gdImagePtr im, int x, int y) } } + +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 (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; +} + + static void gdImageAntiAliasedApply (gdImagePtr im, int px, int py) { float p_dist, p_alpha; @@ -1799,107 +1829,172 @@ void gdImageFillToBorder (gdImagePtr im, int x, int y, int border, int color) } } -void gdImageFill (gdImagePtr im, int x, int y, int color) + +/* + * 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. + */ + +/* 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=0 && Y+(DY)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); + +void gdImageFill(gdImagePtr im, int x, int y, int nc) { - 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; - } - - 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; - } - } - /* Seek left */ - leftLimit = (-1); - for (i = x; i >= 0; i--) { - if (gdImageGetPixel(im, i, y) != old) { - break; - } - gdImageSetPixel(im, i, y, color); - leftLimit = i; - } - if (leftLimit == (-1)) { + int l, x1, x2, dy; + int oc; /* old pixel value */ + int wx2,wy2; + /* stack of filled segments */ + //struct seg stack[FILL_MAX],*sp = stack;; + struct seg *stack; + struct seg *sp; + + if (nc==gdTiled){ + _gdImageFillTiled(im,x,y,nc); return; } - /* Seek right */ - rightLimit = x; - for (i = (x + 1); i < im->sx; i++) { - if (gdImageGetPixel(im, i, y) != old) { - break; + + stack = (struct seg *)emalloc(sizeof(struct seg) * ((int)(im->sy*im->sx)/4)+1); + sp = stack; + + wx2=im->sx;wy2=im->sy; + oc = gdImageGetPixel(im, x, y); + if (oc==nc || x<0 || x>wx2 || y<0 || y>wy2) return; + /* 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); } - 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 && c == old) { - gdImageFill (im, i, y - 1, color); - lastBorder = 0; - } else if (c != old) { - lastBorder = 1; + if (x>=x1) { + goto skip; + } + l = x+1; + + /* leak on left? */ + if (lsy - 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; + 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); } + efree(stack); } +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; + + nc = gdImageTileGet(im,x,y); + pts = (int **) ecalloc(sizeof(int *) * im->sy, sizeof(int)); + + for (i=0; isy;i++) { + pts[i] = (int *) ecalloc(im->sx, sizeof(int)); + } + + stack = (struct seg *)emalloc(sizeof(struct seg) * ((int)(im->sy*im->sx)/4)+1); + 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 (lx2+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; isy;i++) { + efree(pts[i]); + } + efree(pts); + efree(stack); +} + + + 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;