/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "ags/lib/allegro/flood.h" #include "ags/lib/allegro/gfx.h" #include "ags/shared/core/types.h" #include "common/array.h" namespace AGS3 { struct FLOODED_LINE { short flags; /* status of the segment */ short lpos, rpos; /* left and right ends of segment */ short y; /* y coordinate of the segment */ int next; /* linked list if several per line */ }; #define FLOOD_IN_USE 1 #define FLOOD_TODO_ABOVE 2 #define FLOOD_TODO_BELOW 4 #define FLOOD_LINE(c) (&scratchMem[c]) template static bool scanLine(BITMAP *bmp, int x, int y, int src_color, int &left, int &right) { // check start pixel const SIZE *pixel = (const SIZE *)bmp->getBasePtr(x, y); if (*pixel != src_color) return false; // Work left from starting point const SIZE *pixelLeft = pixel - 1; for (left = x - 1; left >= bmp->cl; left--, pixelLeft--) { if (*pixelLeft != src_color) break; } // Work right from starting point const SIZE *pixelRight = pixel + 1; for (right = x + 1; right < bmp->cr; right++, pixelRight++) { if (*pixelRight != src_color) break; } return true; } /** * 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(BITMAP *bmp, int x, int y, int src_color, int dest_color, Common::Array &scratchMem) { FLOODED_LINE *p; int left = 0, right = 0; int c; assert(bmp); bool ret; switch (bmp->format.bytesPerPixel) { case 1: ret = scanLine(bmp, x, y, src_color, left, right); break; case 2: ret = scanLine(bmp, x, y, src_color, left, right); break; case 4: ret = scanLine(bmp, x, y, src_color, left, right); break; default: error("Unknown format"); } if (!ret) return x + 1; left++; right--; /* draw the line */ bmp->hLine(left, y, right, dest_color); /* 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 = scratchMem.size(); scratchMem.resize(scratchMem.size() + 1); p = FLOOD_LINE(c); } p->flags = FLOOD_IN_USE; p->lpos = left; p->rpos = right; p->y = y; p->next = 0; if (y > bmp->ct) p->flags |= FLOOD_TODO_ABOVE; if (y + 1 < bmp->cb) p->flags |= FLOOD_TODO_BELOW; return right + 2; } /** * 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 bool check_flood_line(BITMAP *bmp, int y, int left, int right, int src_color, int dest_color, Common::Array &scratchMem) { int c; FLOODED_LINE *p; bool 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(bmp, left, y, src_color, dest_color, scratchMem); ret = true; break; } } } return ret; } void floodfill(BITMAP *bmp, int x, int y, int color) { int src_color; int c; bool done; FLOODED_LINE *p; Common::Array scratchMem; /* make sure we have a valid starting point */ if ((x < bmp->cl) || (x >= bmp->cr) || (y < bmp->ct) || (y >= bmp->cb)) return; /* what color to replace? */ src_color = getpixel(bmp, x, y); if (src_color == color) { return; } /* set up the list of flooded segments */ scratchMem.resize(bmp->cb); p = FLOOD_LINE(0); for (c = 0; c < (int)scratchMem.size(); c++) { p[c].flags = 0; p[c].lpos = INT16_MAX; p[c].rpos = INT16_MIN; p[c].y = y; p[c].next = 0; } /* start up the flood algorithm */ flooder(bmp, x, y, src_color, color, scratchMem); /* continue as long as there are some segments still to test */ do { done = true; /* for each line on the screen */ for (c = 0; c < (int)scratchMem.size(); c++) { p = FLOOD_LINE(c); /* check below the segment? */ if (p->flags & FLOOD_TODO_BELOW) { p->flags &= ~FLOOD_TODO_BELOW; if (check_flood_line(bmp, p->y + 1, p->lpos, p->rpos, src_color, color, scratchMem)) { 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(bmp, p->y - 1, p->lpos, p->rpos, src_color, color, scratchMem)) { done = false; /* special case shortcut for going backwards */ if ((c < bmp->cb) && (c > 0)) c -= 2; } } } } while (!done); } } // namespace AGS3