/* ScummVM - Scumm Interpreter * Copyright (C) 2001 Ludvig Strigeus * Copyright (C) 2001-2006 The ScummVM project * * 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 2 * 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Header$ * */ #include "common/stdafx.h" #include "scumm/intern.h" #include "scumm/scumm.h" #include "scumm/floodfill_he.h" namespace Scumm { static bool floodFillPixelCheck(int x, int y, const FloodFillState *ffs) { int diffColor = ffs->color1 - ffs->color2; if (x >= 0 && x < ffs->dst_w && y >= 0 && y < ffs->dst_h) { uint8 color = *(ffs->dst + y * ffs->dst_w + x); diffColor = color - ffs->color1; } return diffColor == 0; } static void floodFillProcessRect(FloodFillState *ffs, const Common::Rect *r) { Common::Rect *dr = &ffs->dstBox; if (dr->right >= dr->left && dr->top <= dr->bottom) { int rw = r->right - r->left + 1; int rh = r->bottom - r->top + 1; assert(r->top + rh <= ffs->dst_h); assert(r->left + rw <= ffs->dst_w); uint8 *dst = ffs->dst + r->top * ffs->dst_w + r->left; if (rw <= 1) { --rh; while (rh >= 0) { *dst = ffs->color2; dst += ffs->dst_w; --rh; } } else { --rh; while (rh >= 0) { memset(dst, ffs->color2, rw); dst += ffs->dst_w; --rh; } } dr->extend(*r); } else { *dr = *r; } } static void floodFillAddLine(FloodFillLine **ffl, int y, int x1, int x2, int dy) { (*ffl)->y = y; (*ffl)->x1 = x1; (*ffl)->x2 = x2; (*ffl)->inc = dy; (*ffl)++; } static void floodFillProcess(int x, int y, FloodFillState *ffs, FloodFillPixelCheckCallback pixelCheckCallback) { ffs->dstBox.left = ffs->dstBox.top = 12345; ffs->dstBox.right = ffs->dstBox.bottom = -12345; FloodFillLine **fillLineCur = &ffs->fillLineTableCur; FloodFillLine **fillLineEnd = &ffs->fillLineTableEnd; assert(*fillLineCur < *fillLineEnd); if (ffs->srcBox.top <= y + 1 && ffs->srcBox.bottom >= y + 1) { (*fillLineCur)->y = y; (*fillLineCur)->x1 = x; (*fillLineCur)->x2 = x; (*fillLineCur)->inc = 1; (*fillLineCur)++; } assert(*fillLineCur < *fillLineEnd); if (ffs->srcBox.top <= y && ffs->srcBox.bottom >= y) { (*fillLineCur)->y = y + 1; (*fillLineCur)->x1 = x; (*fillLineCur)->x2 = x; (*fillLineCur)->inc = -1; (*fillLineCur)++; } assert(ffs->fillLineTable <= *fillLineCur); FloodFillLine **fillLineStart = fillLineCur; while (ffs->fillLineTable < *fillLineStart) { Common::Rect r; int x_start; FloodFillLine *fflCur = --(*fillLineCur); int dy = fflCur->inc; int x_end = fflCur->x2; int x1 = fflCur->x1; int x2 = fflCur->x1 + 1; r.bottom = r.top = y = fflCur->y + fflCur->inc; r.left = x2; r.right = x1; x = x1; while (ffs->srcBox.left <= x) { if (!(*pixelCheckCallback)(x, y, ffs)) { break; } r.left = x; --x; } if (r.right >= r.left && r.top <= r.bottom) { floodFillProcessRect(ffs, &r); } if (x >= x1) goto skip; x_start = x + 1; if (x1 > x_start) { assert(*fillLineEnd > *fillLineCur); if (ffs->srcBox.top <= y - dy && ffs->srcBox.bottom >= y - dy) { --x1; floodFillAddLine(fillLineCur, y, x_start, x1, -dy); } } x = x2; while (x_start <= x_end) { r.left = x; r.top = y; r.right = x - 1; r.bottom = y; while (ffs->srcBox.right >= x) { if (!(*pixelCheckCallback)(x, y, ffs)) { break; } r.right = x; ++x; } if (r.right >= r.left && r.top <= r.bottom) { floodFillProcessRect(ffs, &r); } assert(ffs->fillLineTableCur < ffs->fillLineTableEnd); if (ffs->srcBox.top <= y + dy && ffs->srcBox.bottom >= y + dy) { floodFillAddLine(&ffs->fillLineTableCur, y, x_start, x - 1, dy); } x_start = x_end + 1; if (x > x_start) { assert(ffs->fillLineTableCur < ffs->fillLineTableEnd); if (ffs->srcBox.top <= y - dy && ffs->srcBox.bottom >= y - dy) { floodFillAddLine(&ffs->fillLineTableCur, y, x_start, x - 1, -dy); } } skip: ++x; while (x <= x_end) { if ((*pixelCheckCallback)(x, y, ffs)) { break; } ++x; } x_start = x; } } } void floodFill(FloodFillParameters *ffp, ScummEngine_v90he *vm) { uint8 *dst; VirtScreen *vs = &vm->virtscr[kMainVirtScreen]; if (ffp->flags & 0x8000) { dst = vs->getBackPixels(0, vs->topline); } else { dst = vs->getPixels(0, vs->topline); } uint8 color = ffp->flags & 0xFF; Common::Rect r; r.left = r.top = 12345; r.right = r.bottom = -12345; FloodFillState *ffs = new FloodFillState; ffs->fillLineTableCount = vs->h * 2; ffs->fillLineTable = new FloodFillLine[ffs->fillLineTableCount]; ffs->color2 = color; ffs->dst = dst; ffs->dst_w = vs->w; ffs->dst_h = vs->h; ffs->srcBox = ffp->box; ffs->fillLineTableCur = &ffs->fillLineTable[0]; ffs->fillLineTableEnd = &ffs->fillLineTable[ffs->fillLineTableCount]; if (ffp->x < 0 || ffp->y < 0 || ffp->x >= vs->w || ffp->y >= vs->h) { ffs->color1 = color; } else { ffs->color1 = *(dst + ffp->y * vs->w + ffp->x); } debug(5, "floodFill() x=%d y=%d color1=%d ffp->flags=0x%X", ffp->x, ffp->y, ffs->color1, ffp->flags); if (ffs->color1 != color) { floodFillProcess(ffp->x, ffp->y, ffs, floodFillPixelCheck); r = ffs->dstBox; } r.debugPrint(5, "floodFill() dirty_rect"); delete[] ffs->fillLineTable; delete ffs; vm->VAR(119) = 1; if (r.left <= r.right && r.top <= r.bottom) { if (ffp->flags & 0x8000) { vm->gdi.copyVirtScreenBuffers(r); } else { ++r.bottom; vm->markRectAsDirty(kMainVirtScreen, r); } } } void Wiz::fillWizFlood(const WizParameters *params) { if (params->processFlags & kWPFClipBox2) { int px = params->box2.left; int py = params->box2.top; uint8 *dataPtr = _vm->getResourceAddress(rtImage, params->img.resNum); if (dataPtr) { int state = 0; if (params->processFlags & kWPFNewState) { state = params->img.state; } uint8 *wizh = _vm->findWrappedBlock(MKID('WIZH'), dataPtr, state, 0); assert(wizh); int c = READ_LE_UINT32(wizh + 0x0); int w = READ_LE_UINT32(wizh + 0x4); int h = READ_LE_UINT32(wizh + 0x8); assert(c == 0); Common::Rect imageRect(w, h); if (params->processFlags & kWPFClipBox) { if (!imageRect.intersects(params->box)) { return; } imageRect.clip(params->box); } uint8 color = _vm->VAR(93); if (params->processFlags & kWPFFillColor) { color = params->fillColor; } if (imageRect.contains(px, py)) { uint8 *wizd = _vm->findWrappedBlock(MKID('WIZD'), dataPtr, state, 0); assert(wizd); FloodFillState *ffs = new FloodFillState; ffs->fillLineTableCount = h * 2; ffs->fillLineTable = new FloodFillLine[ffs->fillLineTableCount]; ffs->color2 = color; ffs->dst = wizd; ffs->dst_w = w; ffs->dst_h = h; ffs->srcBox = imageRect; ffs->fillLineTableCur = &ffs->fillLineTable[0]; ffs->fillLineTableEnd = &ffs->fillLineTable[ffs->fillLineTableCount]; if (px < 0 || py < 0 || px >= w || py >= h) { ffs->color1 = color; } else { ffs->color1 = *(wizd + py * w + px); } debug(0, "floodFill() x=%d y=%d color1=%d", px, py, ffs->color1); if (ffs->color1 != color) { floodFillProcess(px, py, ffs, floodFillPixelCheck); } delete[] ffs->fillLineTable; delete ffs; } } } } } // End of namespace Scumm