- Fix #bug24594

Rewrite the imagefill function (non recursive, uses of connected points
  algorithm)
This commit is contained in:
Pierre Joye 2003-07-23 00:08:57 +00:00
parent 7a85edd370
commit 74c2027573

View File

@ -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<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);
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 (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);
}
}
}
/* 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;
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; i<im->sy;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 (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++) {
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;