aboutsummaryrefslogtreecommitdiff
path: root/engines/mohawk/myst_graphics.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/mohawk/myst_graphics.cpp')
-rw-r--r--engines/mohawk/myst_graphics.cpp105
1 files changed, 83 insertions, 22 deletions
diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp
index 49f97cca63..427fba4d22 100644
--- a/engines/mohawk/myst_graphics.cpp
+++ b/engines/mohawk/myst_graphics.cpp
@@ -40,15 +40,14 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) {
if (_vm->getFeatures() & GF_ME) {
// High color
- initGraphics(_viewport.width(), _viewport.height(), true, NULL);
+ initGraphics(_viewport.width(), _viewport.height(), true, nullptr);
if (_vm->_system->getScreenFormat().bytesPerPixel == 1)
error("Myst ME requires greater than 256 colors to run");
} else {
// Paletted
initGraphics(_viewport.width(), _viewport.height(), true);
- setBasePalette();
- setPaletteToScreen();
+ clearScreenPalette();
}
_pixelFormat = _vm->_system->getScreenFormat();
@@ -73,7 +72,7 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
// if it's a PICT or WDIB resource. If it's Myst ME it's most likely a PICT, and if it's
// original it's definitely a WDIB. However, Myst ME throws us another curve ball in
// that PICT resources can contain WDIB's instead of PICT's.
- Common::SeekableReadStream *dataStream = NULL;
+ Common::SeekableReadStream *dataStream = nullptr;
if (_vm->getFeatures() & GF_ME && _vm->hasResource(ID_PICT, id)) {
// The PICT resource exists. However, it could still contain a MystBitmap
@@ -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.
@@ -95,7 +94,7 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
dataStream->seek(0);
}
- MohawkSurface *mhkSurface = 0;
+ MohawkSurface *mhkSurface = nullptr;
if (isPict) {
Image::PICTDecoder pict;
@@ -103,12 +102,17 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) {
if (!pict.loadStream(*dataStream))
error("Could not decode Myst ME PICT");
+ delete dataStream;
+
mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat));
} else {
mhkSurface = _bmpDecoder->decodeImage(dataStream);
- if (_vm->getFeatures() & GF_ME)
+ if (_vm->getFeatures() & GF_ME) {
mhkSurface->convertToTrueColor();
+ } else {
+ remapSurfaceToSystemPalette(mhkSurface);
+ }
}
assert(mhkSurface);
@@ -202,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();
}
}
@@ -225,9 +229,8 @@ void MystGraphics::copyBackBufferToScreen(Common::Rect r) {
void MystGraphics::runTransition(TransitionType type, Common::Rect rect, uint16 steps, uint16 delay) {
- // Do not artificially delay during transitions
- int oldEnableDrawingTimeSimulation = _enableDrawingTimeSimulation;
- _enableDrawingTimeSimulation = 0;
+ // Transitions are barely visible without adding delays between the draw calls
+ enableDrawingTimeSimulation(true);
switch (type) {
case kTransitionLeftToRight: {
@@ -288,7 +291,10 @@ void MystGraphics::runTransition(TransitionType type, Common::Rect rect, uint16
debugC(kDebugView, "Dissolve");
for (int16 step = 0; step < 8; step++) {
- simulatePreviousDrawDelay(rect);
+ // Only one eighth of the rect pixels are updated by a draw step,
+ // delay by one eighth of the regular time
+ simulatePreviousDrawDelay(Common::Rect(rect.width() / 8, rect.height()));
+
transitionDissolve(rect, step);
}
}
@@ -367,7 +373,7 @@ void MystGraphics::runTransition(TransitionType type, Common::Rect rect, uint16
error("Unknown transition %d", type);
}
- _enableDrawingTimeSimulation = oldEnableDrawingTimeSimulation;
+ enableDrawingTimeSimulation(false);
}
void MystGraphics::transitionDissolve(Common::Rect rect, uint step) {
@@ -639,8 +645,10 @@ void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) {
// Do not draw anything new too quickly after the previous draw call
// so that images stay at least a little while on screen
// This is enabled only for scripted draw calls
- if (time < _nextAllowedDrawTime)
+ if (time < _nextAllowedDrawTime) {
+ debugC(kDebugView, "Delaying draw call by %d ms", _nextAllowedDrawTime - time);
_vm->_system->delayMillis(_nextAllowedDrawTime - time);
+ }
}
// Next draw call allowed at DELAY + AERA * COEFF milliseconds from now
@@ -697,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,
@@ -723,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();
+
+ // 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;
+ }
+ }
- 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));
+ // 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() {