From a5992a08a02d86e33eb9c30dc8c6dd6fcb0bdf77 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Thu, 22 Aug 2013 20:37:16 -0400 Subject: MOHAWK: Run Myst in 8bpp --- engines/mohawk/cursors.cpp | 6 +- engines/mohawk/myst.cpp | 12 ++- engines/mohawk/myst_graphics.cpp | 165 +++++++++++++++++++++++++----------- engines/mohawk/myst_graphics.h | 27 +++--- engines/mohawk/myst_stacks/myst.cpp | 20 +++-- engines/mohawk/video.cpp | 46 +++++++++- engines/mohawk/video.h | 4 + 7 files changed, 205 insertions(+), 75 deletions(-) (limited to 'engines/mohawk') diff --git a/engines/mohawk/cursors.cpp b/engines/mohawk/cursors.cpp index f1baac02e2..4b66829e6a 100644 --- a/engines/mohawk/cursors.cpp +++ b/engines/mohawk/cursors.cpp @@ -122,7 +122,11 @@ void MystCursorManager::setCursor(uint16 id) { // 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.replaceCursorPalette(mhkSurface->getPalette(), 0, 256); + + // We're using the screen palette for the original game, but we need + // to use this for any 8bpp cursor in ME. + if (_vm->getFeatures() & GF_ME) + CursorMan.replaceCursorPalette(mhkSurface->getPalette(), 0, 256); } else { Graphics::PixelFormat pixelFormat = g_system->getScreenFormat(); CursorMan.replaceCursor(surface->getPixels(), surface->w, surface->h, hotspotX, hotspotY, pixelFormat.RGBToColor(255, 255, 255), false, &pixelFormat); diff --git a/engines/mohawk/myst.cpp b/engines/mohawk/myst.cpp index 7634e8d88a..b6a6c27329 100644 --- a/engines/mohawk/myst.cpp +++ b/engines/mohawk/myst.cpp @@ -413,7 +413,12 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS // Fill screen with black and empty cursor _cursor->setCursor(0); - _system->fillScreen(_system->getScreenFormat().RGBToColor(0, 0, 0)); + + if (getFeatures() & GF_ME) + _system->fillScreen(_system->getScreenFormat().RGBToColor(0, 0, 0)); + else + _gfx->clearScreenPalette(); + _system->updateScreen(); _sound->stopSound(); @@ -495,9 +500,10 @@ void MohawkEngine_Myst::changeToStack(uint16 stack, uint16 card, uint16 linkSrcS _cache.clear(); _gfx->clearCache(); - // Play Flyby Entry Movie on Masterpiece Edition. - const char *flyby = 0; if (getFeatures() & GF_ME) { + // Play Flyby Entry Movie on Masterpiece Edition. + const char *flyby = 0; + switch (_curStack) { case kSeleniticStack: flyby = "selenitic flyby"; diff --git a/engines/mohawk/myst_graphics.cpp b/engines/mohawk/myst_graphics.cpp index 9ea9f15444..49f97cca63 100644 --- a/engines/mohawk/myst_graphics.cpp +++ b/engines/mohawk/myst_graphics.cpp @@ -28,6 +28,7 @@ #include "common/system.h" #include "common/textconsole.h" #include "engines/util.h" +#include "graphics/palette.h" #include "image/pict.h" namespace Mohawk { @@ -37,16 +38,20 @@ MystGraphics::MystGraphics(MohawkEngine_Myst* vm) : GraphicsManager(), _vm(vm) { _viewport = Common::Rect(544, 332); - // The original version of Myst could run in 8bpp color too. - // However, it dithered videos to 8bpp and they looked considerably - // worse (than they already did :P). So we're not even going to - // support 8bpp mode in Myst (Myst ME required >8bpp anyway). - initGraphics(_viewport.width(), _viewport.height(), true, NULL); // What an odd screen size! + if (_vm->getFeatures() & GF_ME) { + // High color + initGraphics(_viewport.width(), _viewport.height(), true, NULL); - _pixelFormat = _vm->_system->getScreenFormat(); + 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(); + } - if (_pixelFormat.bytesPerPixel == 1) - error("Myst requires greater than 256 colors to run"); + _pixelFormat = _vm->_system->getScreenFormat(); // Initialize our buffer _backBuffer = new Graphics::Surface(); @@ -101,7 +106,9 @@ MohawkSurface *MystGraphics::decodeImage(uint16 id) { mhkSurface = new MohawkSurface(pict.getSurface()->convertTo(_pixelFormat)); } else { mhkSurface = _bmpDecoder->decodeImage(dataStream); - mhkSurface->convertToTrueColor(); + + if (_vm->getFeatures() & GF_ME) + mhkSurface->convertToTrueColor(); } assert(mhkSurface); @@ -151,7 +158,8 @@ void MystGraphics::copyImageSectionToScreen(uint16 image, Common::Rect src, Comm } void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src, Common::Rect dest) { - Graphics::Surface *surface = findImage(image)->getSurface(); + MohawkSurface *mhkSurface = findImage(image); + Graphics::Surface *surface = mhkSurface->getSurface(); // Make sure the image is bottom aligned in the dest rect dest.top = dest.bottom - MIN(surface->h, dest.height()); @@ -190,6 +198,13 @@ void MystGraphics::copyImageSectionToBackBuffer(uint16 image, Common::Rect src, for (uint16 i = 0; i < height; i++) memcpy(_backBuffer->getBasePtr(dest.left, i + dest.top), surface->getBasePtr(src.left, top + i), width * surface->format.bytesPerPixel); + + 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); + setPaletteToScreen(); + } } void MystGraphics::copyImageToScreen(uint16 image, Common::Rect dest) { @@ -419,12 +434,16 @@ void MystGraphics::transitionDissolve(Common::Rect rect, uint step) { for (uint16 x = rect.left; x < rect.right; x++) { if (linePattern[x % 4]) { - if (_pixelFormat.bytesPerPixel == 2) { - uint16 *dst = (uint16 *)screen->getBasePtr(x, y); - *dst = *(const uint16 *)_backBuffer->getBasePtr(x, y); - } else { - uint32 *dst = (uint32 *)screen->getBasePtr(x, y); - *dst = *(const uint32 *)_backBuffer->getBasePtr(x, y); + switch (_pixelFormat.bytesPerPixel) { + case 1: + *((byte *)screen->getBasePtr(x, y)) = *((const byte *)_backBuffer->getBasePtr(x, y)); + break; + case 2: + *((uint16 *)screen->getBasePtr(x, y)) = *((const uint16 *)_backBuffer->getBasePtr(x, y)); + break; + case 4: + *((uint32 *)screen->getBasePtr(x, y)) = *((const uint32 *)_backBuffer->getBasePtr(x, y)); + break; } } } @@ -588,11 +607,11 @@ void MystGraphics::drawRect(Common::Rect rect, RectState state) { Graphics::Surface *screen = _vm->_system->lockScreen(); if (state == kRectEnabled) - screen->frameRect(rect, _pixelFormat.RGBToColor(0, 255, 0)); + screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(0, 255, 0) : 250); else if (state == kRectUnreachable) - screen->frameRect(rect, _pixelFormat.RGBToColor(0, 0, 255)); + screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(0, 0, 255) : 252); else - screen->frameRect(rect, _pixelFormat.RGBToColor(255, 0, 0)); + screen->frameRect(rect, (_vm->getFeatures() & GF_ME) ? _pixelFormat.RGBToColor(255, 0, 0) : 249); _vm->_system->unlockScreen(); } @@ -629,50 +648,94 @@ void MystGraphics::simulatePreviousDrawDelay(const Common::Rect &dest) { _nextAllowedDrawTime = time + _constantDrawDelay + dest.height() * dest.width() / _proportionalDrawDelay; } -void MystGraphics::copyBackBufferToScreenWithSaturation(int16 saturation) { - Graphics::Surface *screen = _vm->_system->lockScreen(); +void MystGraphics::fadeToBlack() { + // This is only for the demo + assert(!(_vm->getFeatures() & GF_ME)); - for (uint16 y = 0; y < _viewport.height(); y++) - for (uint16 x = 0; x < _viewport.width(); x++) { - uint32 color; - uint8 r, g, b; + // Linear fade in 64 steps + for (int i = 63; i >= 0; i--) { + byte palette[256 * 3]; + byte *src = _palette; + byte *dst = palette; - if (_pixelFormat.bytesPerPixel == 2) - color = *(const uint16 *)_backBuffer->getBasePtr(x, y); - else - color = *(const uint32 *)_backBuffer->getBasePtr(x, y); + for (uint j = 0; j < sizeof(palette); j++) + *dst++ = *src++ * i / 64; - _pixelFormat.colorToRGB(color, r, g, b); + _vm->_system->getPaletteManager()->setPalette(palette, 0, 256); + _vm->_system->updateScreen(); + } +} - r = CLIP((int16)r - saturation, 0, 255); - g = CLIP((int16)g - saturation, 0, 255); - b = CLIP((int16)b - saturation, 0, 255); +void MystGraphics::fadeFromBlack() { + // This is only for the demo + assert(!(_vm->getFeatures() & GF_ME)); - color = _pixelFormat.RGBToColor(r, g, b); + copyBackBufferToScreen(_viewport); - if (_pixelFormat.bytesPerPixel == 2) { - uint16 *dst = (uint16 *)screen->getBasePtr(x, y); - *dst = color; - } else { - uint32 *dst = (uint32 *)screen->getBasePtr(x, y); - *dst = color; - } - } + // Linear fade in 64 steps + for (int i = 0; i < 64; i++) { + byte palette[256 * 3]; + byte *src = _palette; + byte *dst = palette; - _vm->_system->unlockScreen(); + for (uint j = 0; j < sizeof(palette); j++) + *dst++ = *src++ * i / 64; + + _vm->_system->getPaletteManager()->setPalette(palette, 0, 256); + _vm->_system->updateScreen(); + } + + // Set the full palette + _vm->_system->getPaletteManager()->setPalette(_palette, 0, 256); _vm->_system->updateScreen(); } -void MystGraphics::fadeToBlack() { - for (int16 i = 0; i < 256; i += 32) { - copyBackBufferToScreenWithSaturation(i); - } +void MystGraphics::clearScreenPalette() { + // Set the palette to all black + byte palette[256 * 3]; + memset(palette, 0, sizeof(palette)); + _vm->_system->getPaletteManager()->setPalette(palette, 0, 256); } -void MystGraphics::fadeFromBlack() { - for (int16 i = 256; i >= 0; i -= 32) { - copyBackBufferToScreenWithSaturation(i); - } +void MystGraphics::setBasePalette() { + // Entries [0, 9] of the palette + static const byte lowPalette[] = { + 0xFF, 0xFF, 0xFF, + 0x80, 0x00, 0x00, + 0x00, 0x80, 0x00, + 0x80, 0x80, 0x00, + 0x00, 0x00, 0x80, + 0x80, 0x00, 0x80, + 0x00, 0x80, 0x80, + 0xC0, 0xC0, 0xC0, + 0xC0, 0xDC, 0xC0, + 0xA6, 0xCA, 0xF0 + }; + + // Entries [246, 255] of the palette + static const byte highPalette[] = { + 0xFF, 0xFB, 0xF0, + 0xA0, 0xA0, 0xA4, + 0x80, 0x80, 0x80, + 0xFF, 0x00, 0x00, + 0x00, 0xFF, 0x00, + 0xFF, 0xFF, 0x00, + 0x00, 0x00, 0xFF, + 0xFF, 0x00, 0xFF, + 0x00, 0xFF, 0xFF, + 0x00, 0x00, 0x00 + }; + + // Note that 0 and 255 are different from normal Windows. + // Myst seems to hack that to white, resp. black (probably for Mac compat). + + 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)); +} + +void MystGraphics::setPaletteToScreen() { + _vm->_system->getPaletteManager()->setPalette(_palette, 0, 256); } } // End of namespace Mohawk diff --git a/engines/mohawk/myst_graphics.h b/engines/mohawk/myst_graphics.h index 1f70320bf6..6281c94cc8 100644 --- a/engines/mohawk/myst_graphics.h +++ b/engines/mohawk/myst_graphics.h @@ -40,7 +40,7 @@ enum RectState { class MystGraphics : public GraphicsManager { public: - MystGraphics(MohawkEngine_Myst*); + MystGraphics(MohawkEngine_Myst *vm); ~MystGraphics(); void copyImageSectionToScreen(uint16 image, Common::Rect src, Common::Rect dest); @@ -55,18 +55,15 @@ public: void fadeToBlack(); void fadeFromBlack(); + void clearScreenPalette(); + void setBasePalette(); + void setPaletteToScreen(); + const byte *getPalette() const { return _palette; } + protected: MohawkSurface *decodeImage(uint16 id); MohawkEngine *getVM() { return (MohawkEngine *)_vm; } - void simulatePreviousDrawDelay(const Common::Rect &dest); - void copyBackBufferToScreenWithSaturation(int16 saturation); - void transitionDissolve(Common::Rect rect, uint step); - void transitionSlideToLeft(Common::Rect rect, uint16 steps, uint16 delay); - void transitionSlideToRight(Common::Rect rect, uint16 steps, uint16 delay); - void transitionSlideToTop(Common::Rect rect, uint16 steps, uint16 delay); - 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); + private: MohawkEngine_Myst *_vm; MystBitmap *_bmpDecoder; @@ -74,11 +71,21 @@ private: Graphics::Surface *_backBuffer; Graphics::PixelFormat _pixelFormat; Common::Rect _viewport; + byte _palette[256 * 3]; int _enableDrawingTimeSimulation; uint32 _nextAllowedDrawTime; static const uint _constantDrawDelay = 10; // ms static const uint _proportionalDrawDelay = 500; // pixels per ms + + void simulatePreviousDrawDelay(const Common::Rect &dest); + void transitionDissolve(Common::Rect rect, uint step); + void transitionSlideToLeft(Common::Rect rect, uint16 steps, uint16 delay); + void transitionSlideToRight(Common::Rect rect, uint16 steps, uint16 delay); + void transitionSlideToTop(Common::Rect rect, uint16 steps, uint16 delay); + 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); }; } // End of namespace Mohawk diff --git a/engines/mohawk/myst_stacks/myst.cpp b/engines/mohawk/myst_stacks/myst.cpp index c500df5ad3..ca6e7c0ee5 100644 --- a/engines/mohawk/myst_stacks/myst.cpp +++ b/engines/mohawk/myst_stacks/myst.cpp @@ -3201,13 +3201,21 @@ Common::Point Myst::towerRotationMapComputeCoords(const Common::Point ¢er, u } void Myst::towerRotationMapDrawLine(const Common::Point ¢er, const Common::Point &end) { - Graphics::PixelFormat pf = _vm->_system->getScreenFormat(); - uint32 color = 0; + uint32 color; - if (!_towerRotationOverSpot) - color = pf.RGBToColor(0xFF, 0xFF, 0xFF); // White - else - color = pf.RGBToColor(0xFF, 0, 0); // Red + if (_vm->getFeatures() & GF_ME) { + Graphics::PixelFormat pf = _vm->_system->getScreenFormat(); + + if (!_towerRotationOverSpot) + color = pf.RGBToColor(0xFF, 0xFF, 0xFF); // White + else + color = pf.RGBToColor(0xFF, 0, 0); // Red + } else { + if (!_towerRotationOverSpot) + color = 0x00; // White + else + color = 0xF9; // Red + } const Common::Rect rect = Common::Rect(106, 42, 459, 273); diff --git a/engines/mohawk/video.cpp b/engines/mohawk/video.cpp index cebb72e24f..ca38f22b7a 100644 --- a/engines/mohawk/video.cpp +++ b/engines/mohawk/video.cpp @@ -53,6 +53,8 @@ bool VideoEntry::endOfVideo() { } VideoManager::VideoManager(MohawkEngine* vm) : _vm(vm) { + // Set dithering enabled, if required + _enableDither = _vm->getGameType() == GType_MYST && !(_vm->getFeatures() & GF_ME); } VideoManager::~VideoManager() { @@ -230,16 +232,23 @@ bool VideoManager::updateMovies() { Graphics::PixelFormat pixelFormat = _vm->_system->getScreenFormat(); if (frame->format != pixelFormat) { - // We don't support downconverting to 8bpp - if (pixelFormat.bytesPerPixel == 1) - error("Cannot convert high color video frame to 8bpp"); + // We don't support downconverting to 8bpp without having + // support in the codec. Set _enableDither if shows up. + if (pixelFormat.bytesPerPixel == 1) { + warning("Cannot convert high color video frame to 8bpp"); + delete _videoStreams[i].video; + _videoStreams[i].clear(); + continue; + } // Convert to the current screen format convertedFrame = frame->convertTo(pixelFormat, _videoStreams[i]->getPalette()); frame = convertedFrame; } else if (pixelFormat.bytesPerPixel == 1 && _videoStreams[i]->hasDirtyPalette()) { // Set the palette when running in 8bpp mode only - _vm->_system->getPaletteManager()->setPalette(_videoStreams[i]->getPalette(), 0, 256); + // Don't do this for Myst, which has its own per-stack handling + if (_vm->getGameType() != GType_MYST) + _vm->_system->getPaletteManager()->setPalette(_videoStreams[i]->getPalette(), 0, 256); } // Clip the width/height to make sure we stay on the screen (Myst does this a few times) @@ -394,6 +403,9 @@ VideoHandle VideoManager::createVideoHandle(uint16 id, uint16 x, uint16 y, bool entry.loop = loop; entry.enabled = true; + // Enable dither if necessary + checkEnableDither(entry); + entry->start(); // Search for any deleted videos so we can take a formerly used slot @@ -431,6 +443,10 @@ VideoHandle VideoManager::createVideoHandle(const Common::String &filename, uint } entry->loadStream(file); + + // Enable dither if necessary + checkEnableDither(entry); + entry->setVolume(volume); entry->start(); @@ -551,4 +567,26 @@ void VideoManager::pauseMovie(VideoHandle handle, bool pause) { _videoStreams[handle]->pauseVideo(pause); } +void VideoManager::checkEnableDither(VideoEntry &entry) { + // If we're not dithering, bail out + if (!_enableDither) + return; + + // Ignore any video which is already 8bpp + if (entry->getPixelFormat().bytesPerPixel == 1) + return; + + // Set the palette + byte palette[256 * 3]; + g_system->getPaletteManager()->grabPalette(palette, 0, 256); + entry->setDitheringPalette(palette); + + if (entry->getPixelFormat().bytesPerPixel != 1) { + if (entry.filename.empty()) + error("Failed to set dither for video %d", entry.id); + else + error("Failed to set dither for video %s", entry.filename.c_str()); + } +} + } // End of namespace Mohawk diff --git a/engines/mohawk/video.h b/engines/mohawk/video.h index 43181e3e6c..deb09afe6b 100644 --- a/engines/mohawk/video.h +++ b/engines/mohawk/video.h @@ -124,6 +124,10 @@ private: VideoHandle createVideoHandle(uint16 id, uint16 x, uint16 y, bool loop, uint16 volume = 0xff); VideoHandle createVideoHandle(const Common::String &filename, uint16 x, uint16 y, bool loop, byte volume = 0xff); + + // Dithering control + bool _enableDither; + void checkEnableDither(VideoEntry &entry); }; } // End of namespace Mohawk -- cgit v1.2.3