/* check_flood_line: * Checks a line segment, using the scratch buffer is to store a list of * segments which have already been drawn in order to minimise the required * number of tests. */ static int check_flood_line(Image* image, int y, int left, int right, const gfx::Rect& bounds, int src_color, int tolerance, void *data, AlgoHLine proc) { int c; FLOODED_LINE *p; int ret = false; while (left <= right) { c = y; for (;;) { p = FLOOD_LINE(c); if ((left >= p->lpos) && (left <= p->rpos)) { left = p->rpos+2; break; } c = p->next; if (!c) { left = flooder(image, left, y, bounds, src_color, tolerance, data, proc); ret = true; break; } } } return ret; }
/* floodfill: * Fills an enclosed area (starting at point x, y) with the specified color. */ void algo_floodfill(Image* image, int x, int y, const gfx::Rect& bounds, int tolerance, bool contiguous, void* data, AlgoHLine proc) { // Make sure we have a valid starting point if ((x < 0) || (x >= image->width()) || (y < 0) || (y >= image->height())) return; // What color to replace? color_t src_color = get_pixel(image, x, y); // Non-contiguous case, we replace colors in the whole image. if (!contiguous) { switch (image->pixelFormat()) { case IMAGE_RGB: replace_color<RgbTraits>(image, bounds, src_color, tolerance, data, proc); break; case IMAGE_GRAYSCALE: replace_color<GrayscaleTraits>(image, bounds, src_color, tolerance, data, proc); break; case IMAGE_INDEXED: replace_color<IndexedTraits>(image, bounds, src_color, tolerance, data, proc); break; } return; } /* set up the list of flooded segments */ flood_buf.resize(image->height()); flood_count = image->height(); FLOODED_LINE* p = (FLOODED_LINE*)&flood_buf[0]; for (int c=0; c<flood_count; c++) { p[c].flags = 0; p[c].lpos = SHRT_MAX; p[c].rpos = SHRT_MIN; p[c].y = y; p[c].next = 0; } // Start up the flood algorithm flooder(image, x, y, bounds, src_color, tolerance, data, proc); // Continue as long as there are some segments still to test bool done; do { done = true; // For each line on the screen for (int c=0; c<flood_count; c++) { p = FLOOD_LINE(c); // Check below the segment? if (p->flags & FLOOD_TODO_BELOW) { p->flags &= ~FLOOD_TODO_BELOW; if (check_flood_line(image, p->y+1, p->lpos, p->rpos, bounds, src_color, tolerance, data, proc)) { done = false; p = FLOOD_LINE(c); } } // Check above the segment? if (p->flags & FLOOD_TODO_ABOVE) { p->flags &= ~FLOOD_TODO_ABOVE; if (check_flood_line(image, p->y-1, p->lpos, p->rpos, bounds, src_color, tolerance, data, proc)) { done = false; // Special case shortcut for going backwards if ((c > bounds.y) && (c < bounds.y2())) c -= 2; } } } } while (!done); }
/* flooder: * Fills a horizontal line around the specified position, and adds it * to the list of drawn segments. Returns the first x coordinate after * the part of the line which it has dealt with. */ static int flooder(Image *image, int x, int y, const gfx::Rect& bounds, color_t src_color, int tolerance, void *data, AlgoHLine proc) { FLOODED_LINE *p; int left = 0, right = 0; int c; switch (image->pixelFormat()) { case IMAGE_RGB: { uint32_t* address = reinterpret_cast<uint32_t*>(image->getPixelAddress(0, y)); // Check start pixel if (!color_equal_32((int)*(address+x), src_color, tolerance)) return x+1; // Work left from starting point for (left=x-1; left>=bounds.x; left--) { if (!color_equal_32((int)*(address+left), src_color, tolerance)) break; } // Work right from starting point for (right=x+1; right<bounds.x2(); right++) { if (!color_equal_32((int)*(address+right), src_color, tolerance)) break; } } break; case IMAGE_GRAYSCALE: { uint16_t* address = reinterpret_cast<uint16_t*>(image->getPixelAddress(0, y)); // Check start pixel if (!color_equal_16((int)*(address+x), src_color, tolerance)) return x+1; // Work left from starting point for (left=x-1; left>=bounds.x; left--) { if (!color_equal_16((int)*(address+left), src_color, tolerance)) break; } // Work right from starting point for (right=x+1; right<bounds.x2(); right++) { if (!color_equal_16((int)*(address+right), src_color, tolerance)) break; } } break; case IMAGE_INDEXED: { uint8_t* address = image->getPixelAddress(0, y); // Check start pixel if (!color_equal_8((int)*(address+x), src_color, tolerance)) return x+1; // Work left from starting point for (left=x-1; left>=bounds.x; left--) { if (!color_equal_8((int)*(address+left), src_color, tolerance)) break; } // Work right from starting point for (right=x+1; right<bounds.x2(); right++) { if (!color_equal_8((int)*(address+right), src_color, tolerance)) break; } } break; default: // Check start pixel if (get_pixel(image, x, y) != src_color) return x+1; // Work left from starting point for (left=x-1; left>=bounds.x; left--) { if (get_pixel(image, left, y) != src_color) break; } // Work right from starting point for (right=x+1; right<bounds.x2(); right++) { if (get_pixel(image, right, y) != src_color) break; } break; } left++; right--; /* draw the line */ (*proc)(left, y, right, data); /* store it in the list of flooded segments */ c = y; p = FLOOD_LINE(c); if (p->flags) { while (p->next) { c = p->next; p = FLOOD_LINE(c); } p->next = c = flood_count++; flood_buf.resize(flood_count); p = FLOOD_LINE(c); } p->flags = FLOOD_IN_USE; p->lpos = left; p->rpos = right; p->y = y; p->next = 0; if (y > bounds.y) p->flags |= FLOOD_TODO_ABOVE; if (y+1 < bounds.y2()) p->flags |= FLOOD_TODO_BELOW; return right+2; }
/* flooder: * Fills a horizontal line around the specified position, and adds it * to the list of drawn segments. Returns the first x coordinate after * the part of the line which it has dealt with. */ static int flooder (Image *image, int x, int y, int src_color, int tolerance, void *data, AlgoHLine proc) { FLOODED_LINE *p; int left = 0, right = 0; int c; switch (image->getPixelFormat()) { case IMAGE_RGB: { uint32_t* address = ((uint32_t**)image->line)[y]; /* check start pixel */ if (!color_equal_32((int)*(address+x), src_color, tolerance)) return x+1; /* work left from starting point */ for (left=x-1; left>=0; left--) { if (!color_equal_32((int)*(address+left), src_color, tolerance)) break; } /* work right from starting point */ for (right=x+1; right<image->w; right++) { if (!color_equal_32((int)*(address+right), src_color, tolerance)) break; } } break; case IMAGE_GRAYSCALE: { uint16_t* address = ((uint16_t**)image->line)[y]; /* check start pixel */ if (!color_equal_16((int)*(address+x), src_color, tolerance)) return x+1; /* work left from starting point */ for (left=x-1; left>=0; left--) { if (!color_equal_16((int)*(address+left), src_color, tolerance)) break; } /* work right from starting point */ for (right=x+1; right<image->w; right++) { if (!color_equal_16((int)*(address+right), src_color, tolerance)) break; } } break; case IMAGE_INDEXED: { uint8_t* address = ((uint8_t**)image->line)[y]; /* check start pixel */ if (!color_equal_8((int)*(address+x), src_color, tolerance)) return x+1; /* work left from starting point */ for (left=x-1; left>=0; left--) { if (!color_equal_8((int)*(address+left), src_color, tolerance)) break; } /* work right from starting point */ for (right=x+1; right<image->w; right++) { if (!color_equal_8((int)*(address+right), src_color, tolerance)) break; } } break; default: /* check start pixel */ if (image_getpixel(image, x, y) != src_color) return x+1; /* work left from starting point */ for (left=x-1; left>=0; left--) { if (image_getpixel(image, left, y) != src_color) break; } /* work right from starting point */ for (right=x+1; right<image->w; right++) { if (image_getpixel(image, right, y) != src_color) break; } break; } left++; right--; /* draw the line */ (*proc)(left, y, right, data); /* store it in the list of flooded segments */ c = y; p = FLOOD_LINE(c); if (p->flags) { while (p->next) { c = p->next; p = FLOOD_LINE(c); } p->next = c = flood_count++; _grow_scratch_mem(sizeof(FLOODED_LINE) * flood_count); p = FLOOD_LINE(c); } p->flags = FLOOD_IN_USE; p->lpos = left; p->rpos = right; p->y = y; p->next = 0; if (y > 0) p->flags |= FLOOD_TODO_ABOVE; if (y+1 < image->h) p->flags |= FLOOD_TODO_BELOW; return right+2; }
/* floodfill: * Fills an enclosed area (starting at point x, y) with the specified color. */ void algo_floodfill(Image* image, int x, int y, int tolerance, void *data, AlgoHLine proc) { int src_color; int c, done; FLOODED_LINE *p; /* make sure we have a valid starting point */ if ((x < 0) || (x >= image->w) || (y < 0) || (y >= image->h)) return; /* what color to replace? */ src_color = image_getpixel (image, x, y); /* set up the list of flooded segments */ _grow_scratch_mem(sizeof(FLOODED_LINE) * image->h); flood_count = image->h; p = (FLOODED_LINE*)_scratch_mem; for (c=0; c<flood_count; c++) { p[c].flags = 0; p[c].lpos = SHRT_MAX; p[c].rpos = SHRT_MIN; p[c].y = y; p[c].next = 0; } /* start up the flood algorithm */ flooder(image, x, y, src_color, tolerance, data, proc); /* continue as long as there are some segments still to test */ do { done = true; /* for each line on the screen */ for (c=0; c<flood_count; c++) { p = FLOOD_LINE(c); /* check below the segment? */ if (p->flags & FLOOD_TODO_BELOW) { p->flags &= ~FLOOD_TODO_BELOW; if (check_flood_line(image, p->y+1, p->lpos, p->rpos, src_color, tolerance, data, proc)) { done = false; p = FLOOD_LINE(c); } } /* check above the segment? */ if (p->flags & FLOOD_TODO_ABOVE) { p->flags &= ~FLOOD_TODO_ABOVE; if (check_flood_line(image, p->y-1, p->lpos, p->rpos, src_color, tolerance, data, proc)) { done = false; /* special case shortcut for going backwards */ if ((c < image->h) && (c > 0)) c -= 2; } } } } while (!done); }
/* * FloodFillGenerator: */ BOOL GUIAPI FloodFillGenerator (void* context, const RECT* dst_rc, int x, int y, CB_EQUAL_PIXEL cb_equal_pixel, CB_FLOOD_FILL cb_flood_fill) { FLOODER_INFO fi; int c, done; FLOODED_LINE *p; /* make sure we have a valid starting point */ if ((x < dst_rc->left) || (x >= dst_rc->right) || (y < dst_rc->top) || (y >= dst_rc->bottom)) { return TRUE; } /* set up the list of flooded segments */ fi.flood_count = RECTHP (dst_rc); fi.flooded_lines = (FLOODED_LINE*) malloc (sizeof(FLOODED_LINE) * fi.flood_count); if (!(p = fi.flooded_lines)) { return FALSE; } fi.context = context; fi.dst_rc = dst_rc; fi.cb_equal_pixel = cb_equal_pixel; fi.cb_flood_fill = cb_flood_fill; for (c = 0; c < fi.flood_count; c++) { p[c].flags = 0; p[c].lpos = SHRT_MAX; p[c].rpos = SHRT_MIN; p[c].y = y; p[c].next = 0; } /* start up the flood algorithm */ flooder (&fi, x, y); /* continue as long as there are some segments still to test */ do { done = TRUE; /* for each line on the screen */ for (c = 0; c < fi.flood_count; c++) { p = FLOOD_LINE (c); /* check below the segment? */ if (p->flags & FLOOD_TODO_BELOW) { p->flags &= ~FLOOD_TODO_BELOW; if (check_flood_line (&fi, p->y+1, p->lpos, p->rpos)) { done = FALSE; p = FLOOD_LINE (c); } } /* check above the segment? */ if (p->flags & FLOOD_TODO_ABOVE) { p->flags &= ~FLOOD_TODO_ABOVE; if (check_flood_line (&fi, p->y-1, p->lpos, p->rpos)) { done = FALSE; /* special case shortcut for going backwards */ if ((c < RECTHP (dst_rc)) && (c > 0)) c -= 2; } } } } while (!done); free (fi.flooded_lines); return TRUE; }