mirror of
https://github.com/libretro/scummvm.git
synced 2025-01-23 02:44:56 +00:00
MOHAWK: Remap bitmaps not to use undefined colors
The Spanish version of Myst has bitmaps that use palette indices in the system reserved range. Affected pixels previously used colors from the Windows system palette instead of the bitmap's own palette, resulting in visual glitches. Bitmaps are now remapped to the screen palette which is made of the Windows reserved palette and part of the bitmap palette. The original engine used GDI's StretchDIBits with DIB_RGB_COLORS to achieve the same result. Fixes #7153.
This commit is contained in:
parent
cedcdbc48d
commit
3391c726cf
@ -34,8 +34,8 @@
|
||||
#include "graphics/wincursor.h"
|
||||
|
||||
#ifdef ENABLE_MYST
|
||||
#include "mohawk/bitmap.h"
|
||||
#include "mohawk/myst.h"
|
||||
#include "mohawk/myst_graphics.h"
|
||||
#endif
|
||||
|
||||
namespace Mohawk {
|
||||
@ -86,11 +86,9 @@ void DefaultCursorManager::setCursor(uint16 id) {
|
||||
#ifdef ENABLE_MYST
|
||||
|
||||
MystCursorManager::MystCursorManager(MohawkEngine_Myst *vm) : _vm(vm) {
|
||||
_bmpDecoder = new MystBitmap();
|
||||
}
|
||||
|
||||
MystCursorManager::~MystCursorManager() {
|
||||
delete _bmpDecoder;
|
||||
}
|
||||
|
||||
void MystCursorManager::showCursor() {
|
||||
@ -111,17 +109,18 @@ void MystCursorManager::setCursor(uint16 id) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Both Myst and Myst ME use the "MystBitmap" format for cursor images.
|
||||
MohawkSurface *mhkSurface = _bmpDecoder->decodeImage(_vm->getResource(ID_WDIB, id));
|
||||
Graphics::Surface *surface = mhkSurface->getSurface();
|
||||
Common::SeekableReadStream *clrcStream = _vm->getResource(ID_CLRC, id);
|
||||
uint16 hotspotX = clrcStream->readUint16LE();
|
||||
uint16 hotspotY = clrcStream->readUint16LE();
|
||||
delete clrcStream;
|
||||
|
||||
// Both Myst and Myst ME use the "MystBitmap" format for cursor images.
|
||||
MohawkSurface *mhkSurface = _vm->_gfx->findImage(id);
|
||||
Graphics::Surface *surface = mhkSurface->getSurface();
|
||||
|
||||
// Myst ME stores some cursors as 24bpp images instead of 8bpp
|
||||
if (surface->format.bytesPerPixel == 1) {
|
||||
CursorMan.replaceCursor(surface->getPixels(), surface->w, surface->h, hotspotX, hotspotY, 0);
|
||||
CursorMan.replaceCursor(surface->getPixels(), surface->w, surface->h, hotspotX, hotspotY, 255);
|
||||
|
||||
// We're using the screen palette for the original game, but we need
|
||||
// to use this for any 8bpp cursor in ME.
|
||||
@ -133,7 +132,6 @@ void MystCursorManager::setCursor(uint16 id) {
|
||||
}
|
||||
|
||||
_vm->_needsUpdate = true;
|
||||
delete mhkSurface;
|
||||
}
|
||||
|
||||
void MystCursorManager::setDefaultCursor() {
|
||||
|
@ -102,7 +102,6 @@ enum {
|
||||
};
|
||||
|
||||
class MohawkEngine_Myst;
|
||||
class MystBitmap;
|
||||
|
||||
// The cursor manager for Myst
|
||||
// Uses WDIB + CLRC resources
|
||||
@ -119,7 +118,6 @@ public:
|
||||
|
||||
private:
|
||||
MohawkEngine_Myst *_vm;
|
||||
MystBitmap *_bmpDecoder;
|
||||
};
|
||||
|
||||
#endif // ENABLE_MYST
|
||||
|
@ -74,6 +74,10 @@ public:
|
||||
// Free all surfaces in the cache
|
||||
void clearCache();
|
||||
|
||||
// findImage will search the cache to find the image.
|
||||
// If not found, it will call decodeImage to get a new one.
|
||||
MohawkSurface *findImage(uint16 id);
|
||||
|
||||
void preloadImage(uint16 image);
|
||||
virtual void setPalette(uint16 id);
|
||||
void copyAnimImageToScreen(uint16 image, int left = 0, int top = 0);
|
||||
@ -85,10 +89,6 @@ public:
|
||||
protected:
|
||||
void copyAnimImageSectionToScreen(MohawkSurface *image, Common::Rect src, Common::Rect dest);
|
||||
|
||||
// findImage will search the cache to find the image.
|
||||
// If not found, it will call decodeImage to get a new one.
|
||||
MohawkSurface *findImage(uint16 id);
|
||||
|
||||
// decodeImage will always return a new image.
|
||||
virtual MohawkSurface *decodeImage(uint16 id) = 0;
|
||||
virtual Common::Array<MohawkSurface *> decodeImages(uint16 id);
|
||||
|
@ -47,8 +47,7 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) {
|
||||
} else {
|
||||
// Paletted
|
||||
initGraphics(_viewport.width(), _viewport.height(), true);
|
||||
setBasePalette();
|
||||
setPaletteToScreen();
|
||||
clearScreenPalette();
|
||||
}
|
||||
|
||||
_pixelFormat = _vm->_system->getScreenFormat();
|
||||
@ -86,7 +85,7 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
|
||||
|
||||
bool isPict = false;
|
||||
|
||||
if (_vm->getFeatures() & GF_ME) {
|
||||
if ((_vm->getFeatures() & GF_ME) && dataStream->size() > 512 + 10 + 4) {
|
||||
// Here we detect whether it's really a PICT or a WDIB. Since a MystBitmap
|
||||
// would be compressed, there's no way to detect for the BM without a hack.
|
||||
// So, we search for the PICT version opcode for detection.
|
||||
@ -109,8 +108,11 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
|
||||
} else {
|
||||
mhkSurface = _bmpDecoder->decodeImage(dataStream);
|
||||
|
||||
if (_vm->getFeatures() & GF_ME)
|
||||
if (_vm->getFeatures() & GF_ME) {
|
||||
mhkSurface->convertToTrueColor();
|
||||
} else {
|
||||
remapSurfaceToSystemPalette(mhkSurface);
|
||||
}
|
||||
}
|
||||
|
||||
assert(mhkSurface);
|
||||
@ -204,7 +206,7 @@ void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src,
|
||||
if (!(_vm->getFeatures() & GF_ME)) {
|
||||
// Make sure the palette is set
|
||||
assert(mhkSurface->getPalette());
|
||||
memcpy(_palette + 10 * 3, mhkSurface->getPalette() + 10 * 3, (256 - 10 * 2) * 3);
|
||||
memcpy(_palette, mhkSurface->getPalette(), 256 * 3);
|
||||
setPaletteToScreen();
|
||||
}
|
||||
}
|
||||
@ -703,10 +705,10 @@ void MystGraphics::clearScreenPalette() {
|
||||
_vm->_system->getPaletteManager()->setPalette(palette, 0, 256);
|
||||
}
|
||||
|
||||
void MystGraphics::setBasePalette() {
|
||||
void MystGraphics::remapSurfaceToSystemPalette(MohawkSurface *mhkSurface) {
|
||||
// Entries [0, 9] of the palette
|
||||
static const byte lowPalette[] = {
|
||||
0xFF, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00,
|
||||
0x80, 0x00, 0x00,
|
||||
0x00, 0x80, 0x00,
|
||||
0x80, 0x80, 0x00,
|
||||
@ -729,15 +731,68 @@ void MystGraphics::setBasePalette() {
|
||||
0x00, 0x00, 0xFF,
|
||||
0xFF, 0x00, 0xFF,
|
||||
0x00, 0xFF, 0xFF,
|
||||
0x00, 0x00, 0x00
|
||||
0xFF, 0xFF, 0xFF
|
||||
};
|
||||
|
||||
// Note that 0 and 255 are different from normal Windows.
|
||||
// Myst seems to hack that to white, resp. black (probably for Mac compat).
|
||||
byte *originalPalette = mhkSurface->getPalette();
|
||||
|
||||
memcpy(_palette, lowPalette, sizeof(lowPalette));
|
||||
memset(_palette + sizeof(lowPalette), 0, sizeof(_palette) - sizeof(lowPalette) - sizeof(highPalette));
|
||||
memcpy(_palette + sizeof(_palette) - sizeof(highPalette), highPalette, sizeof(highPalette));
|
||||
// The target palette is made of the Windows reserved palette, and colors 10 to 245
|
||||
// of the bitmap palette. Entries 0 to 9 and 246 to 255 of the bitmap palette are
|
||||
// discarded.
|
||||
byte targetPalette[256 * 3];
|
||||
memcpy(targetPalette, lowPalette, sizeof(lowPalette));
|
||||
memcpy(targetPalette + sizeof(lowPalette), originalPalette + sizeof(lowPalette), sizeof(_palette) - sizeof(lowPalette) - sizeof(highPalette));
|
||||
memcpy(targetPalette + sizeof(_palette) - sizeof(highPalette), highPalette, sizeof(highPalette));
|
||||
|
||||
// Remap the discarded entries from the bitmap palette using the target palette.
|
||||
byte lowColorMap[ARRAYSIZE(lowPalette) / 3];
|
||||
byte highColorMap[ARRAYSIZE(highPalette) / 3];
|
||||
|
||||
for (uint i = 0; i < ARRAYSIZE(lowColorMap); i++) {
|
||||
uint colorIndex = 3 * i;
|
||||
byte red = originalPalette[colorIndex + 0];
|
||||
byte green = originalPalette[colorIndex + 1];
|
||||
byte blue = originalPalette[colorIndex + 2];
|
||||
|
||||
lowColorMap[i] = getColorIndex(targetPalette, red, green, blue);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < ARRAYSIZE(highColorMap); i++) {
|
||||
uint colorIndex = 3 * (i + 246);
|
||||
byte red = originalPalette[colorIndex + 0];
|
||||
byte green = originalPalette[colorIndex + 1];
|
||||
byte blue = originalPalette[colorIndex + 2];
|
||||
|
||||
highColorMap[i] = getColorIndex(targetPalette, red, green, blue);
|
||||
}
|
||||
|
||||
// Replace the original palette with the target palette
|
||||
memcpy(originalPalette, targetPalette, sizeof(targetPalette));
|
||||
|
||||
// Remap the pixel data to the target palette
|
||||
Graphics::Surface *surface = mhkSurface->getSurface();
|
||||
byte *pixels = (byte *) surface->getPixels();
|
||||
|
||||
for (int i = 0; i < surface->w * surface->h; i++) {
|
||||
if (pixels[i] < ARRAYSIZE(lowColorMap)) {
|
||||
pixels[i] = lowColorMap[pixels[i]];
|
||||
} else if (pixels[i] >= 246) {
|
||||
pixels[i] = highColorMap[pixels[i] - 246];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
byte MystGraphics::getColorIndex(const byte *palette, byte red, byte green, byte blue) {
|
||||
for (uint i = 0; i < 256; i++) {
|
||||
if (palette[(3 * i) + 0] == red && palette[(3 * i) + 1] == green && palette[(3 * i) + 2] == blue) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
// GDI actually chooses the nearest color if no exact match is found,
|
||||
// but this should not happen in Myst
|
||||
debug(1, "Color (%d, %d, %d) not in target palette", red, green, blue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MystGraphics::setPaletteToScreen() {
|
||||
|
@ -56,7 +56,6 @@ public:
|
||||
void fadeFromBlack();
|
||||
|
||||
void clearScreenPalette();
|
||||
void setBasePalette();
|
||||
void setPaletteToScreen();
|
||||
const byte *getPalette() const { return _palette; }
|
||||
|
||||
@ -86,6 +85,9 @@ private:
|
||||
void transitionSlideToBottom(Common::Rect rect, uint16 steps, uint16 delay);
|
||||
void transitionPartialToRight(Common::Rect rect, uint32 width, uint32 steps);
|
||||
void transitionPartialToLeft(Common::Rect rect, uint32 width, uint32 steps);
|
||||
|
||||
void remapSurfaceToSystemPalette(MohawkSurface *mhkSurface);
|
||||
byte getColorIndex(const byte *palette, byte red, byte green, byte blue);
|
||||
};
|
||||
|
||||
} // End of namespace Mohawk
|
||||
|
Loading…
x
Reference in New Issue
Block a user