From b40b87fdb5b4ee869cce55d852af409896b20c98 Mon Sep 17 00:00:00 2001 From: Willem Jan Palenstijn Date: Fri, 4 Mar 2011 18:24:46 +0100 Subject: SCI: Fix flood fill matching in EGA mode In EGA games a pixel in the framebuffer is only 4 bits. We store a full byte per pixel to allow undithering, but when comparing pixels for flood-fill purposes, we should only compare the visible color of a pixel. This fixes bug #3078365 in Iceman. --- engines/sci/graphics/picture.cpp | 24 +++++++++++++++++++----- engines/sci/graphics/screen.cpp | 29 ++++++++++++++++++++--------- engines/sci/graphics/screen.h | 2 +- 3 files changed, 40 insertions(+), 15 deletions(-) (limited to 'engines/sci') diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index 7e71c1a258..91fa2e71e8 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -866,6 +866,8 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by byte matchedMask, matchMask; int16 w, e, a_set, b_set; + bool isEGA = (_resMan->getViewType() == kViewEga); + p.x = x + curPort->left; p.y = y + curPort->top; stack.push(p); @@ -874,6 +876,18 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by byte searchPriority = _screen->getPriority(p.x, p.y); byte searchControl = _screen->getControl(p.x, p.y); + if (isEGA) { + // In EGA games a pixel in the framebuffer is only 4 bits. We store + // a full byte per pixel to allow undithering, but when comparing + // pixels for flood-fill purposes, we should only compare the + // visible color of a pixel. + + if ((x ^ y) & 1) + searchColor = (searchColor ^ (searchColor >> 4)) & 0x0F; + else + searchColor = searchColor & 0x0F; + } + // This logic was taken directly from sierra sci, floodfill will get aborted on various occations if (screenMask & GFX_SCREEN_MASK_VISUAL) { if ((color == _screen->getColorWhite()) || (searchColor != _screen->getColorWhite())) @@ -913,20 +927,20 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by int b = curPort->rect.bottom + curPort->top - 1; while (stack.size()) { p = stack.pop(); - if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl)) == 0) // already filled + if ((matchedMask = _screen->isFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)) == 0) // already filled continue; _screen->putPixel(p.x, p.y, screenMask, color, priority, control); w = p.x; e = p.x; // moving west and east pointers as long as there is a matching color to fill - while (w > l && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl))) + while (w > l && (matchedMask = _screen->isFillMatch(w - 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA))) _screen->putPixel(--w, p.y, screenMask, color, priority, control); - while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl))) + while (e < r && (matchedMask = _screen->isFillMatch(e + 1, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA))) _screen->putPixel(++e, p.y, screenMask, color, priority, control); // checking lines above and below for possible flood targets a_set = b_set = 0; while (w <= e) { - if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl))) { // one line above + if (p.y > t && (matchedMask = _screen->isFillMatch(w, p.y - 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line above if (a_set == 0) { p1.x = w; p1.y = p.y - 1; @@ -936,7 +950,7 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by } else a_set = 0; - if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl))) { // one line below + if (p.y < b && (matchedMask = _screen->isFillMatch(w, p.y + 1, matchMask, searchColor, searchPriority, searchControl, isEGA))) { // one line below if (b_set == 0) { p1.x = w; p1.y = p.y + 1; diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index ef2bffce76..ace69c6972 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -353,19 +353,30 @@ byte GfxScreen::getControl(int x, int y) { return _controlScreen[y * _width + x]; } -byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con) { +byte GfxScreen::isFillMatch(int16 x, int16 y, byte screenMask, byte t_color, byte t_pri, byte t_con, bool isEGA) { int offset = y * _width + x; byte match = 0; // FIXME: - // This does not behave properly in EGA games where a pixel in the - // framebuffer is only 4 bits. We store a full byte per pixel to allow - // undithering, but when comparing pixels for flood-fill purposes, we - // should only compare the visible color of a pixel (with dithering - // enabled). See bug #3078365. Also see IS_BOUNDARY in FreeSCI's picfill. - - if ((screenMask & GFX_SCREEN_MASK_VISUAL) && *(_visualScreen + offset) == t_color) - match |= GFX_SCREEN_MASK_VISUAL; + if (screenMask & GFX_SCREEN_MASK_VISUAL) { + if (!isEGA) { + if (*(_visualScreen + offset) == t_color) + match |= GFX_SCREEN_MASK_VISUAL; + } else { + // In EGA games a pixel in the framebuffer is only 4 bits. We store + // a full byte per pixel to allow undithering, but when comparing + // pixels for flood-fill purposes, we should only compare the + // visible color of a pixel. + + byte c = *(_visualScreen + offset); + if ((x ^ y) & 1) + c = (c ^ (c >> 4)) & 0x0F; + else + c = c & 0x0F; + if (c == t_color) + match |= GFX_SCREEN_MASK_VISUAL; + } + } if ((screenMask & GFX_SCREEN_MASK_PRIORITY) && *(_priorityScreen + offset) == t_pri) match |= GFX_SCREEN_MASK_PRIORITY; if ((screenMask & GFX_SCREEN_MASK_CONTROL) && *(_controlScreen + offset) == t_con) diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h index fc1f38ed41..6f9b08deff 100644 --- a/engines/sci/graphics/screen.h +++ b/engines/sci/graphics/screen.h @@ -99,7 +99,7 @@ public: byte getVisual(int x, int y); byte getPriority(int x, int y); byte getControl(int x, int y); - byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con); + byte isFillMatch(int16 x, int16 y, byte drawMask, byte t_color, byte t_pri, byte t_con, bool isEGA); int bitsGetDataSize(Common::Rect rect, byte mask); void bitsSave(Common::Rect rect, byte mask, byte *memoryPtr); -- cgit v1.2.3