diff options
Diffstat (limited to 'engines/sci')
-rw-r--r-- | engines/sci/detection_tables.h | 50 | ||||
-rw-r--r-- | engines/sci/engine/segment.h | 25 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.cpp | 68 | ||||
-rw-r--r-- | engines/sci/graphics/frameout.h | 13 | ||||
-rw-r--r-- | engines/sci/graphics/helpers.h | 42 | ||||
-rw-r--r-- | engines/sci/graphics/palette32.cpp | 40 | ||||
-rw-r--r-- | engines/sci/graphics/palette32.h | 27 | ||||
-rw-r--r-- | engines/sci/graphics/plane32.cpp | 3 | ||||
-rw-r--r-- | engines/sci/graphics/video32.cpp | 367 | ||||
-rw-r--r-- | engines/sci/graphics/video32.h | 70 |
10 files changed, 505 insertions, 200 deletions
diff --git a/engines/sci/detection_tables.h b/engines/sci/detection_tables.h index b5c426d836..9378f2b1ec 100644 --- a/engines/sci/detection_tables.h +++ b/engines/sci/detection_tables.h @@ -846,13 +846,14 @@ static const struct ADGameDescription SciGameDescriptions[] = { GUIO_NOLAUNCHLOAD, \ GUIO_NOASPECT, \ GAMEOPTION_HQ_VIDEO) -#define GUIO_GK2 GUIO7(GUIO_NOSUBTITLES, \ +#define GUIO_GK2 GUIO8(GUIO_NOSUBTITLES, \ GUIO_NOSFX, \ GUIO_NOSPEECHVOLUME, \ GUIO_NOMIDI, \ GUIO_NOASPECT, \ GAMEOPTION_ORIGINAL_SAVELOAD, \ - GAMEOPTION_ENABLE_BLACK_LINED_VIDEO) + GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ + GAMEOPTION_HQ_VIDEO) #define GUIO_GK2_MAC GUIO_GK2 // Gabriel Knight 2 - English Windows Non-Interactive Demo @@ -2695,10 +2696,11 @@ static const struct ADGameDescription SciGameDescriptions[] = { GUIO_NOMIDI, \ GUIO_NOLAUNCHLOAD, \ GAMEOPTION_ORIGINAL_SAVELOAD) -#define GUIO_LSL7 GUIO4(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ +#define GUIO_LSL7 GUIO5(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ GUIO_NOASPECT, \ GUIO_NOMIDI, \ - GAMEOPTION_ORIGINAL_SAVELOAD) + GAMEOPTION_ORIGINAL_SAVELOAD, \ + GAMEOPTION_HQ_VIDEO) // Larry 7 - English DOS Demo (provided by richiefs in bug report #2670691) // SCI interpreter version 2.100.002 @@ -2758,9 +2760,10 @@ static const struct ADGameDescription SciGameDescriptions[] = { #define GUIO_LIGHTHOUSE_DEMO GUIO3(GUIO_NOSPEECH, \ GUIO_NOASPECT, \ GAMEOPTION_ORIGINAL_SAVELOAD) -#define GUIO_LIGHTHOUSE GUIO3(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ +#define GUIO_LIGHTHOUSE GUIO4(GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ GUIO_NOASPECT, \ - GAMEOPTION_ORIGINAL_SAVELOAD) + GAMEOPTION_ORIGINAL_SAVELOAD, \ + GAMEOPTION_HQ_VIDEO) // Lighthouse - English Windows Demo (from jvprat) // Executable scanning reports "2.100.002", VERSION file reports "1.00" @@ -2979,11 +2982,12 @@ static const struct ADGameDescription SciGameDescriptions[] = { #ifdef ENABLE_SCI32 -#define GUIO_PHANTASMAGORIA_DEMO GUIO5(GUIO_NOSUBTITLES, \ +#define GUIO_PHANTASMAGORIA_DEMO GUIO6(GUIO_NOSUBTITLES, \ GUIO_NOASPECT, \ GUIO_NOLAUNCHLOAD, \ GUIO_LINKSPEECHTOSFX, \ - GAMEOPTION_ENABLE_BLACK_LINED_VIDEO) + GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ + GAMEOPTION_HQ_VIDEO) #define GUIO_PHANTASMAGORIA GUIO_PHANTASMAGORIA_DEMO #define GUIO_PHANTASMAGORIA_MAC GUIO_PHANTASMAGORIA_DEMO @@ -3509,12 +3513,13 @@ static const struct ADGameDescription SciGameDescriptions[] = { GUIO_LINKSPEECHTOSFX, \ GUIO_NOASPECT, \ GUIO_NOLAUNCHLOAD) -#define GUIO_PQSWAT GUIO6(GUIO_NOSUBTITLES, \ +#define GUIO_PQSWAT GUIO7(GUIO_NOSUBTITLES, \ GUIO_NOMIDI, \ GUIO_LINKMUSICTOSFX, \ GUIO_LINKSPEECHTOSFX, \ GUIO_NOASPECT, \ - GAMEOPTION_ENABLE_BLACK_LINED_VIDEO) + GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ + GAMEOPTION_HQ_VIDEO) // Police Quest: SWAT - English DOS/Windows Demo (from jvprat) // Executable scanning reports "2.100.002", VERSION file reports "0.001.200" @@ -3957,14 +3962,16 @@ static const struct ADGameDescription SciGameDescriptions[] = { #undef GUIO_QFG4_CD // TODO: Correct GUIOs -#define GUIO_RAMA_DEMO GUIO4(GUIO_NOMIDI, \ +#define GUIO_RAMA_DEMO GUIO5(GUIO_NOMIDI, \ GUIO_NOLAUNCHLOAD, \ GUIO_NOASPECT, \ - GAMEOPTION_ENABLE_BLACK_LINED_VIDEO) -#define GUIO_RAMA GUIO4(GUIO_NOMIDI, \ + GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ + GAMEOPTION_HQ_VIDEO) +#define GUIO_RAMA GUIO5(GUIO_NOMIDI, \ GUIO_NOASPECT, \ GAMEOPTION_ORIGINAL_SAVELOAD, \ - GAMEOPTION_ENABLE_BLACK_LINED_VIDEO) + GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ + GAMEOPTION_HQ_VIDEO) // RAMA - English DOS/Windows Demo // Executable scanning reports "2.100.002", VERSION file reports "000.000.008" @@ -4040,11 +4047,12 @@ static const struct ADGameDescription SciGameDescriptions[] = { GUIO_LINKSPEECHTOSFX, \ GUIO_LINKMUSICTOSFX, \ GUIO_NOASPECT) -#define GUIO_SHIVERS GUIO5(GUIO_NOMIDI, \ +#define GUIO_SHIVERS GUIO6(GUIO_NOMIDI, \ GUIO_LINKSPEECHTOSFX, \ GUIO_LINKMUSICTOSFX, \ GUIO_NOASPECT, \ - GAMEOPTION_ENABLE_BLACK_LINED_VIDEO) + GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ + GAMEOPTION_HQ_VIDEO) // Shivers - English Windows (from jvprat) // Executable scanning reports "2.100.002", VERSION file reports "1.02" @@ -4661,10 +4669,11 @@ static const struct ADGameDescription SciGameDescriptions[] = { #define GUIO_SQ6_DEMO GUIO3(GUIO_NOLAUNCHLOAD, \ GUIO_LINKSPEECHTOSFX, \ GUIO_NOASPECT) -#define GUIO_SQ6 GUIO4(GUIO_LINKSPEECHTOSFX, \ +#define GUIO_SQ6 GUIO5(GUIO_LINKSPEECHTOSFX, \ GUIO_NOASPECT, \ GAMEOPTION_ORIGINAL_SAVELOAD, \ - GAMEOPTION_ENABLE_BLACK_LINED_VIDEO) + GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ + GAMEOPTION_HQ_VIDEO) // Space Quest 6 - English DOS/Win3.11 CD (from the Space Quest Collection) // Executable scanning reports "2.100.002", VERSION file reports "1.0" @@ -4740,10 +4749,11 @@ static const struct ADGameDescription SciGameDescriptions[] = { #define GUIO_TORIN_DEMO GUIO3(GUIO_NOMIDI, \ GUIO_NOLAUNCHLOAD, \ GUIO_NOASPECT) -#define GUIO_TORIN GUIO4(GUIO_NOMIDI, \ +#define GUIO_TORIN GUIO5(GUIO_NOMIDI, \ GUIO_NOASPECT, \ GAMEOPTION_ORIGINAL_SAVELOAD, \ - GAMEOPTION_ENABLE_BLACK_LINED_VIDEO) + GAMEOPTION_ENABLE_BLACK_LINED_VIDEO, \ + GAMEOPTION_HQ_VIDEO) #define GUIO_TORIN_MAC GUIO_TORIN // Torin's Passage - English Windows Interactive Demo diff --git a/engines/sci/engine/segment.h b/engines/sci/engine/segment.h index adc8f138be..8eca6713b3 100644 --- a/engines/sci/engine/segment.h +++ b/engines/sci/engine/segment.h @@ -29,6 +29,9 @@ #include "sci/engine/vm.h" #include "sci/engine/vm_types.h" // for reg_t #include "sci/util.h" +#ifdef ENABLE_SCI32 +#include "sci/graphics/palette32.h" +#endif namespace Sci { @@ -1026,7 +1029,7 @@ public: setRemap(remap); setDataSize(width * height); WRITE_SCI11ENDIAN_UINT32(_data + 16, 0); - setHunkPaletteOffset(paletteSize > 0 ? (width * height) : 0); + setHunkPaletteOffset(paletteSize > 0 ? (bitmapHeaderSize + width * height) : 0); setDataOffset(bitmapHeaderSize); setUncompressedDataOffset(bitmapHeaderSize); setControlOffset(0); @@ -1099,17 +1102,7 @@ public: BITMAP_PROPERTY(32, DataSize, 12); - inline uint32 getHunkPaletteOffset() const { - return READ_SCI11ENDIAN_UINT32(_data + 20); - } - - inline void setHunkPaletteOffset(uint32 hunkPaletteOffset) { - if (hunkPaletteOffset) { - hunkPaletteOffset += getBitmapHeaderSize(); - } - - WRITE_SCI11ENDIAN_UINT32(_data + 20, hunkPaletteOffset); - } + BITMAP_PROPERTY(32, HunkPaletteOffset, 20); BITMAP_PROPERTY(32, DataOffset, 24); @@ -1162,6 +1155,14 @@ public: return _data + getHunkPaletteOffset(); } + inline void setPalette(const Palette &palette) { + byte *paletteData = getHunkPalette(); + if (paletteData != nullptr) { + SciSpan<byte> paletteSpan(paletteData, getRawSize() - getHunkPaletteOffset()); + HunkPalette::write(paletteSpan, palette); + } + } + virtual void saveLoadWithSerializer(Common::Serializer &ser); void applyRemap(SciArray &clut) { diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 80800da074..7415979f5c 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -46,6 +46,7 @@ #include "sci/graphics/cursor32.h" #include "sci/graphics/font.h" #include "sci/graphics/frameout.h" +#include "sci/graphics/helpers.h" #include "sci/graphics/paint32.h" #include "sci/graphics/palette32.h" #include "sci/graphics/plane32.h" @@ -548,47 +549,20 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *show showBits(); } -/** - * Determines the parts of `r` that aren't overlapped by `other`. - * Returns -1 if `r` and `other` have no intersection. - * Returns number of returned parts (in `outRects`) otherwise. - * (In particular, this returns 0 if `r` is contained in `other`.) - */ -int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]) { - if (!r.intersects(other)) { - return -1; - } - - int splitCount = 0; - if (r.top < other.top) { - Common::Rect &t = outRects[splitCount++]; - t = r; - t.bottom = other.top; - r.top = other.top; - } - - if (r.bottom > other.bottom) { - Common::Rect &t = outRects[splitCount++]; - t = r; - t.top = other.bottom; - r.bottom = other.bottom; - } - - if (r.left < other.left) { - Common::Rect &t = outRects[splitCount++]; - t = r; - t.right = other.left; - r.left = other.left; - } - - if (r.right > other.right) { - Common::Rect &t = outRects[splitCount++]; - t = r; - t.left = other.right; - } +void GfxFrameout::directFrameOut(const Common::Rect &showRect) { + updateMousePositionForRendering(); + _showList.add(showRect); + showBits(); +} - return splitCount; +#ifdef USE_RGB_COLOR +void GfxFrameout::resetHardware() { + updateMousePositionForRendering(); + _showList.add(Common::Rect(getCurrentBuffer().screenWidth, getCurrentBuffer().screenHeight)); + g_system->getPaletteManager()->setPalette(_palette->getHardwarePalette(), 0, 256); + showBits(); } +#endif /** * Determines the parts of `middleRect` that aren't overlapped @@ -1041,7 +1015,21 @@ void GfxFrameout::showBits() { continue; } - g_system->copyRectToScreen(sourceBuffer, _currentBuffer.screenWidth, rounded.left, rounded.top, rounded.width(), rounded.height()); +#ifdef USE_RGB_COLOR + if (g_system->getScreenFormat() != _currentBuffer.format) { + // This happens (at least) when playing a video in Shivers with + // HQ video on & subtitles on + Graphics::Surface *screenSurface = _currentBuffer.getSubArea(rounded).convertTo(g_system->getScreenFormat(), _palette->getHardwarePalette()); + assert(screenSurface); + g_system->copyRectToScreen(screenSurface->getPixels(), screenSurface->pitch, rounded.left, rounded.top, screenSurface->w, screenSurface->h); + screenSurface->free(); + delete screenSurface; + } else { +#else + { +#endif + g_system->copyRectToScreen(sourceBuffer, _currentBuffer.screenWidth, rounded.left, rounded.top, rounded.width(), rounded.height()); + } } _cursor->donePainting(); diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index 4bdb172e5e..26732d5c01 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -340,6 +340,19 @@ public: void palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *showStyle); /** + * Draws the given rect from the internal screen buffer to hardware without + * processing any other graphics updates except for cursor changes. + */ + void directFrameOut(const Common::Rect &showRect); + +#ifdef USE_RGB_COLOR + /** + * Sends the entire internal screen buffer and palette to hardware. + */ + void resetHardware(); +#endif + + /** * Modifies the raw pixel data for the next frame with * new palette indexes based on matched style ranges. */ diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h index 1da3749c90..52699c6242 100644 --- a/engines/sci/graphics/helpers.h +++ b/engines/sci/graphics/helpers.h @@ -187,6 +187,48 @@ inline void mulru(Common::Rect &rect, const Common::Rational &ratioX, const Comm rect.bottom = mulru(rect.bottom - 1, ratioY, extra) + 1; } +/** + * Determines the parts of `r` that aren't overlapped by `other`. + * Returns -1 if `r` and `other` have no intersection. + * Returns number of returned parts (in `outRects`) otherwise. + * (In particular, this returns 0 if `r` is contained in `other`.) + */ +inline int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]) { + if (!r.intersects(other)) { + return -1; + } + + int splitCount = 0; + if (r.top < other.top) { + Common::Rect &t = outRects[splitCount++]; + t = r; + t.bottom = other.top; + r.top = other.top; + } + + if (r.bottom > other.bottom) { + Common::Rect &t = outRects[splitCount++]; + t = r; + t.top = other.bottom; + r.bottom = other.bottom; + } + + if (r.left < other.left) { + Common::Rect &t = outRects[splitCount++]; + t = r; + t.right = other.left; + r.left = other.left; + } + + if (r.right > other.right) { + Common::Rect &t = outRects[splitCount++]; + t = r; + t.left = other.right; + } + + return splitCount; +} + struct Buffer : public Graphics::Surface { uint16 screenWidth; uint16 screenHeight; diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp index acf63bc396..339461d157 100644 --- a/engines/sci/graphics/palette32.cpp +++ b/engines/sci/graphics/palette32.cpp @@ -53,6 +53,29 @@ HunkPalette::HunkPalette(const SciSpan<const byte> &rawPalette) : } } +void HunkPalette::write(SciSpan<byte> &out, const Palette &palette) { + const uint8 numPalettes = 1; + const uint16 paletteOffset = kHunkPaletteHeaderSize + 2 * numPalettes; + + out[kNumPaletteEntriesOffset] = numPalettes; + out[kHunkPaletteHeaderSize + 2] = paletteOffset; + + SciSpan<byte> entry = out.subspan(paletteOffset); + entry[kEntryStartColorOffset] = 0; + entry.setUint16SEAt(kEntryNumColorsOffset, ARRAYSIZE(palette.colors)); + entry[kEntryUsedOffset] = 1; + entry[kEntrySharedUsedOffset] = 0; + entry.setUint32SEAt(kEntryVersionOffset, 1); + + SciSpan<byte> paletteData = entry.subspan(kEntryHeaderSize); + for (uint i = 0; i < ARRAYSIZE(palette.colors); ++i) { + *paletteData++ = palette.colors[i].used; + *paletteData++ = palette.colors[i].r; + *paletteData++ = palette.colors[i].g; + *paletteData++ = palette.colors[i].b; + } +} + void HunkPalette::setVersion(const uint32 version) const { if (_numPalettes != _data.getUint8At(kNumPaletteEntriesOffset)) { error("Invalid HunkPalette"); @@ -333,6 +356,9 @@ static const uint8 gammaTables[GfxPalette32::numGammaTables][256] = { // Palette versioning _version(1), _needsUpdate(false), +#ifdef USE_RGB_COLOR + _hardwarePalette(), +#endif _currentPalette(), _sourcePalette(), _nextPalette(), @@ -459,7 +485,11 @@ void GfxPalette32::updateHardware() { return; } - byte bpal[3 * 256]; +#ifdef USE_RGB_COLOR + uint8 *bpal = _hardwarePalette; +#else + uint8 bpal[256 * 3]; +#endif // HACK: There are resources in a couple of Windows-only games that seem to // include bogus palette entries above 236. SSCI does a lot of extra work @@ -508,7 +538,13 @@ void GfxPalette32::updateHardware() { bpal[255 * 3 + 2] = 0; } - g_system->getPaletteManager()->setPalette(bpal, 0, 256); + // If the system is in a high color mode, which can happen during video + // playback, attempting to send the palette to OSystem is illegal and will + // result in a crash + if (g_system->getScreenFormat().bytesPerPixel == 1) { + g_system->getPaletteManager()->setPalette(bpal, 0, 256); + } + _gammaChanged = false; } diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h index fb0b226130..b8ba85eb4a 100644 --- a/engines/sci/graphics/palette32.h +++ b/engines/sci/graphics/palette32.h @@ -37,6 +37,16 @@ class HunkPalette { public: HunkPalette(const SciSpan<const byte> &rawPalette); + static void write(SciSpan<byte> &out, const Palette &palette); + + static uint32 calculateHunkPaletteSize(const uint16 numIndexes = 256, const bool sharedUsed = true) { + const int numPalettes = 1; + return kHunkPaletteHeaderSize + + /* slack bytes between hunk header & palette offset table */ 2 + + /* palette offset table */ 2 * numPalettes + + /* palette data */ (kEntryHeaderSize + numIndexes * (/* RGB */ 3 + !sharedUsed)) * numPalettes; + } + /** * Gets the version of the palette. Used to avoid resubmitting a HunkPalette * which has already been submitted for the next frame. @@ -237,6 +247,14 @@ public: */ inline const Palette &getCurrentPalette() const { return _currentPalette; }; +#ifdef USE_RGB_COLOR + /** + * Gets the raw hardware palette in RGB format. This should be used instead + * of `::PaletteManager::grabPalette` when the OSystem screen is >8bpp. + */ + inline const uint8 *getHardwarePalette() const { return _hardwarePalette; }; +#endif + /** * Loads a palette into GfxPalette32 with the given resource ID. */ @@ -288,6 +306,15 @@ private: */ bool _needsUpdate; +#ifdef USE_RGB_COLOR + /** + * A local copy of the hardware palette. Used when the backend is in a true + * color mode and a change to the game's internal framebuffer occurs that + * needs to be reconverted from 8bpp to the backend's bit depth. + */ + uint8 _hardwarePalette[256 * 3]; +#endif + /** * The currently displayed palette. */ diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp index 0a69ab2a98..47d41bc752 100644 --- a/engines/sci/graphics/plane32.cpp +++ b/engines/sci/graphics/plane32.cpp @@ -26,6 +26,7 @@ #include "sci/engine/selector.h" #include "sci/engine/state.h" #include "sci/graphics/frameout.h" +#include "sci/graphics/helpers.h" #include "sci/graphics/lists32.h" #include "sci/graphics/plane32.h" #include "sci/graphics/remap32.h" @@ -262,8 +263,6 @@ void Plane::deleteAllPics() { #pragma mark - #pragma mark Plane - Rendering -extern int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]); - void Plane::breakDrawListByPlanes(DrawList &drawList, const PlaneList &planeList) const { const int nextPlaneIndex = planeList.findIndexByObject(_object) + 1; const PlaneList::size_type planeCount = planeList.size(); diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp index ecde85b622..b05b9e6868 100644 --- a/engines/sci/graphics/video32.cpp +++ b/engines/sci/graphics/video32.cpp @@ -562,9 +562,10 @@ VMDPlayer::IOStatus VMDPlayer::open(const Common::String &fileName, const OpenFl return kIOError; } -void VMDPlayer::init(const int16 x, const int16 y, const PlayFlags flags, const int16 boostPercent, const int16 boostStartColor, const int16 boostEndColor) { - _x = getSciVersion() >= SCI_VERSION_3 ? x : (x & ~1); - _y = y; +void VMDPlayer::init(int16 x, const int16 y, const PlayFlags flags, const int16 boostPercent, const int16 boostStartColor, const int16 boostEndColor) { + if (getSciVersion() < SCI_VERSION_3) { + x &= ~1; + } _doublePixels = flags & kPlayFlagDoublePixels; _blackLines = ConfMan.getBool("enable_black_lined_video") && (flags & kPlayFlagBlackLines); // If ScummVM has been configured to disable black lines on video playback, @@ -579,6 +580,11 @@ void VMDPlayer::init(const int16 x, const int16 y, const PlayFlags flags, const _blackPalette = flags & kPlayFlagBlackPalette; #endif _stretchVertical = flags & kPlayFlagStretchVertical; + + _drawRect = Common::Rect(x, + y, + x + (_decoder->getWidth() << _doublePixels), + y + (_decoder->getHeight() << (_doublePixels || _stretchVertical))); } VMDPlayer::IOStatus VMDPlayer::close() { @@ -586,32 +592,10 @@ VMDPlayer::IOStatus VMDPlayer::close() { return kIOSuccess; } - _decoder->close(); - _isOpen = false; - _isInitialized = false; - _ignorePalettes = false; - - if (_bundledVmd) { - g_sci->getResMan()->unlockResource(_bundledVmd); - _bundledVmd = nullptr; - } - - if (_bitmapId != NULL_REG) { - _segMan->freeBitmap(_bitmapId); - _bitmapId = NULL_REG; - } - - if (!_planeIsOwned && _screenItem != nullptr) { - g_sci->_gfxFrameout->deleteScreenItem(*_screenItem); - _screenItem = nullptr; - } else if (_plane != nullptr) { - g_sci->_gfxFrameout->deletePlane(*_plane); - _plane = nullptr; - } - - if (!_leaveLastFrame && _leaveScreenBlack) { - // This call *actually* deletes the plane/screen item - g_sci->_gfxFrameout->frameOut(true); + if (_isComposited) { + closeComposited(); + } else { + closeOverlay(); } if (_blackoutPlane != nullptr) { @@ -624,13 +608,24 @@ VMDPlayer::IOStatus VMDPlayer::close() { g_sci->_gfxFrameout->frameOut(true); } + _decoder->close(); + + if (_bundledVmd) { + g_sci->getResMan()->unlockResource(_bundledVmd); + _bundledVmd = nullptr; + } + if (!_showCursor) { g_sci->_gfxCursor32->unhide(); } + _isOpen = false; + _isInitialized = false; + _ignorePalettes = false; _lastYieldedFrameNo = 0; _planeIsOwned = true; _priority = 0; + _drawRect = Common::Rect(); return kIOSuccess; } @@ -695,70 +690,21 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { g_sci->_gfxCursor32->hide(); } - Common::Rect vmdRect(_x, - _y, - _x + _decoder->getWidth(), - _y + _decoder->getHeight()); - ScaleInfo vmdScaleInfo; - if (!_blackoutRect.isEmpty() && _planeIsOwned) { _blackoutPlane = new Plane(_blackoutRect); g_sci->_gfxFrameout->addPlane(*_blackoutPlane); } - if (_doublePixels) { - vmdScaleInfo.x = 256; - vmdScaleInfo.y = 256; - vmdScaleInfo.signal = kScaleSignalManual; - vmdRect.right += vmdRect.width(); - vmdRect.bottom += vmdRect.height(); - } else if (_stretchVertical) { - vmdScaleInfo.y = 256; - vmdScaleInfo.signal = kScaleSignalManual; - vmdRect.bottom += vmdRect.height(); - } - - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; - const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - - SciBitmap &vmdBitmap = *_segMan->allocateBitmap(&_bitmapId, vmdRect.width(), vmdRect.height(), 255, 0, 0, screenWidth, screenHeight, 0, false, false); - vmdBitmap.getBuffer().fillRect(Common::Rect(vmdRect.width(), vmdRect.height()), 0); - - if (screenWidth != scriptWidth || screenHeight != scriptHeight) { - mulru(vmdRect, Ratio(scriptWidth, screenWidth), Ratio(scriptHeight, screenHeight), 1); - } - - CelInfo32 vmdCelInfo; - vmdCelInfo.bitmap = _bitmapId; - _decoder->setSurfaceMemory(vmdBitmap.getPixels(), vmdBitmap.getWidth(), vmdBitmap.getHeight(), 1); - - if (_planeIsOwned) { - _x = 0; - _y = 0; - _plane = new Plane(vmdRect, kPlanePicColored); - if (_priority) { - _plane->_priority = _priority; - } - g_sci->_gfxFrameout->addPlane(*_plane); - _screenItem = new ScreenItem(_plane->_object, vmdCelInfo, Common::Point(), vmdScaleInfo); + if (shouldUseCompositing()) { + _isComposited = true; + _usingHighColor = false; + initComposited(); } else { - _screenItem = new ScreenItem(_plane->_object, vmdCelInfo, Common::Point(_x, _y), vmdScaleInfo); - if (_priority) { - _screenItem->_priority = _priority; - } - } - - if (_blackLines) { - _screenItem->_drawBlackLines = true; + _isComposited = false; + _usingHighColor = shouldUseHighColor(); + initOverlay(); } - // NOTE: There was code for positioning the screen item using insetRect - // here, but none of the game scripts seem to use this functionality. - - g_sci->_gfxFrameout->addScreenItem(*_screenItem); - _decoder->start(); } @@ -830,66 +776,245 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { return stopFlag; } -#pragma mark - -#pragma mark VMDPlayer - Rendering +void VMDPlayer::initOverlay() { + g_sci->_gfxFrameout->frameOut(true); -void VMDPlayer::renderFrame() const { - // This writes directly to the CelObjMem we already created, - // so no need to take its return value - _decoder->decodeNextFrame(); +#ifdef USE_RGB_COLOR + // TODO: Allow interpolation for videos where the cursor is drawn, either by + // writing to an intermediate 4bpp surface and using that surface during + // cursor drawing, or by promoting the cursor code to use CursorMan, if + // possible + if (_usingHighColor) { + // TODO: 8888 is used here because 4bpp is the only format currently + // supported by the common scaling code + const Graphics::PixelFormat format = Graphics::createPixelFormat<8888>(); + g_sci->_gfxFrameout->setPixelFormat(format); + redrawGameScreen(); + } +#endif +} - // NOTE: Normally this would write a hunk palette at the end of the - // video bitmap that CelObjMem would read out and submit, but instead - // we are just submitting it directly here because the decoder exposes - // this information a little bit differently than the one in SSCI - const bool dirtyPalette = _decoder->hasDirtyPalette(); - if (dirtyPalette && !_ignorePalettes) { - Palette palette; - for (uint16 i = 0; i < _startColor; ++i) { - palette.colors[i].used = false; +#ifdef USE_RGB_COLOR +void VMDPlayer::redrawGameScreen() const { + Graphics::Surface *game = g_sci->_gfxFrameout->getCurrentBuffer().convertTo(g_system->getScreenFormat(), g_sci->_gfxPalette32->getHardwarePalette()); + + Common::Rect rects[4]; + int splitCount = splitRects(Common::Rect(game->w, game->h), _drawRect, rects); + if (splitCount != -1) { + while (splitCount--) { + const Common::Rect &drawRect = rects[splitCount]; + g_system->copyRectToScreen(game->getBasePtr(drawRect.left, drawRect.top), game->pitch, drawRect.left, drawRect.top, drawRect.width(), drawRect.height()); } - for (uint16 i = _endColor; i < 256; ++i) { - palette.colors[i].used = false; + } + + game->free(); + delete game; +} +#endif + +void VMDPlayer::renderOverlay() const { + const Graphics::Surface *nextFrame = _decoder->decodeNextFrame(); + +#ifdef USE_RGB_COLOR + if (_usingHighColor) { + if (updatePalette()) { + redrawGameScreen(); } -#if SCI_VMD_BLACK_PALETTE - if (_blackPalette) { - for (uint16 i = _startColor; i <= _endColor; ++i) { - palette.colors[i].r = palette.colors[i].g = palette.colors[i].b = 0; - palette.colors[i].used = true; + + writeFrameToSystem<Graphics::FILTER_BILINEAR>(nextFrame, _decoder, _drawRect); + } else { +#else + { +#endif + updatePalette(); + + Graphics::Surface out = g_sci->_gfxFrameout->getCurrentBuffer().getSubArea(_drawRect); + + const int lineCount = _blackLines ? 2 : 1; + if (_doublePixels) { + for (int16 y = 0; y < _drawRect.height(); y += lineCount) { + const uint8 *source = (uint8 *)nextFrame->getBasePtr(0, y >> 1); + uint8 *target = (uint8 *)out.getBasePtr(0, y); + for (int16 x = 0; x < _decoder->getWidth(); ++x) { + *target++ = *source; + *target++ = *source++; + } + } + } else if (_blackLines) { + for (int16 y = 0; y < _drawRect.height(); y += lineCount) { + const uint8 *source = (uint8 *)nextFrame->getBasePtr(0, y); + uint8 *target = (uint8 *)out.getBasePtr(0, y); + memcpy(target, source, _drawRect.width()); } - } else + } else { + out.copyRectToSurface(nextFrame->getPixels(), nextFrame->pitch, 0, 0, nextFrame->w, nextFrame->h); + } + + g_sci->_gfxFrameout->directFrameOut(_drawRect); + } +} + +bool VMDPlayer::updatePalette() const { + if (_ignorePalettes || !_decoder->hasDirtyPalette()) { + return false; + } + + Palette palette; + for (uint16 i = 0; i < _startColor; ++i) { + palette.colors[i].used = false; + } + for (uint16 i = _endColor + 1; i < ARRAYSIZE(palette.colors); ++i) { + palette.colors[i].used = false; + } +#if SCI_VMD_BLACK_PALETTE + if (_blackPalette) { + for (uint16 i = _startColor; i <= _endColor; ++i) { + palette.colors[i].r = palette.colors[i].g = palette.colors[i].b = 0; + palette.colors[i].used = true; + } + } else #endif - fillPalette(palette); + fillPalette(palette); - g_sci->_gfxPalette32->submit(palette); + if (_isComposited) { + SciBitmap *bitmap = _segMan->lookupBitmap(_bitmapId); + bitmap->setPalette(palette); g_sci->_gfxFrameout->updateScreenItem(*_screenItem); g_sci->_gfxFrameout->frameOut(true); + } else { + g_sci->_gfxPalette32->submit(palette); + g_sci->_gfxPalette32->updateForFrame(); + g_sci->_gfxPalette32->updateHardware(); + } #if SCI_VMD_BLACK_PALETTE - if (_blackPalette) { - fillPalette(palette); - g_sci->_gfxPalette32->submit(palette); - g_sci->_gfxPalette32->updateForFrame(); - g_sci->_gfxPalette32->updateHardware(); + if (_blackPalette) { + fillPalette(palette); + if (_isComposited) { + SciBitmap *bitmap = _segMan->lookupBitmap(_bitmapId); + bitmap->setPalette(palette); } + g_sci->_gfxPalette32->submit(palette); + g_sci->_gfxPalette32->updateForFrame(); + g_sci->_gfxPalette32->updateHardware(); + } #endif + + return true; +} + +void VMDPlayer::closeOverlay() { +#ifdef USE_RGB_COLOR + if (_usingHighColor) { + g_sci->_gfxFrameout->setPixelFormat(Graphics::PixelFormat::createFormatCLUT8()); + g_sci->_gfxFrameout->resetHardware(); } else { +#else + { +#endif + g_sci->_gfxFrameout->frameOut(true, _drawRect); + } +} + +void VMDPlayer::initComposited() { + ScaleInfo vmdScaleInfo; + + if (_doublePixels) { + vmdScaleInfo.x *= 2; + vmdScaleInfo.y *= 2; + vmdScaleInfo.signal = kScaleSignalManual; + } else if (_stretchVertical) { + vmdScaleInfo.y *= 2; + vmdScaleInfo.signal = kScaleSignalManual; + } + + const uint32 hunkPaletteSize = HunkPalette::calculateHunkPaletteSize(256, false); + const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; + const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; + + SciBitmap &vmdBitmap = *_segMan->allocateBitmap(&_bitmapId, _drawRect.width(), _drawRect.height(), 255, 0, 0, screenWidth, screenHeight, hunkPaletteSize, false, false); + vmdBitmap.getBuffer().fillRect(Common::Rect(_drawRect.width(), _drawRect.height()), 0); + + CelInfo32 vmdCelInfo; + vmdCelInfo.bitmap = _bitmapId; + _decoder->setSurfaceMemory(vmdBitmap.getPixels(), vmdBitmap.getWidth(), vmdBitmap.getHeight(), 1); + + if (_planeIsOwned) { + _plane = new Plane(_drawRect, kPlanePicColored); + if (_priority) { + _plane->_priority = _priority; + } + g_sci->_gfxFrameout->addPlane(*_plane); + _screenItem = new ScreenItem(_plane->_object, vmdCelInfo, Common::Point(), vmdScaleInfo); + } else { + _screenItem = new ScreenItem(_plane->_object, vmdCelInfo, Common::Point(_drawRect.left, _drawRect.top), vmdScaleInfo); + if (_priority) { + _screenItem->_priority = _priority; + } + } + + if (_blackLines) { + _screenItem->_drawBlackLines = true; + } + + // NOTE: There was code for positioning the screen item using insetRect + // here, but none of the game scripts seem to use this functionality. + + g_sci->_gfxFrameout->addScreenItem(*_screenItem); +} + +void VMDPlayer::renderComposited() const { + // This writes directly to the CelObjMem we already created, + // so no need to take its return value + _decoder->decodeNextFrame(); + if (!updatePalette()) { g_sci->_gfxFrameout->updateScreenItem(*_screenItem); g_sci->_gfxFrameout->frameOut(true); } } +void VMDPlayer::closeComposited() { + if (_bitmapId != NULL_REG) { + _segMan->freeBitmap(_bitmapId); + _bitmapId = NULL_REG; + } + + if (!_planeIsOwned && _screenItem != nullptr) { + g_sci->_gfxFrameout->deleteScreenItem(*_screenItem); + _screenItem = nullptr; + } else if (_plane != nullptr) { + g_sci->_gfxFrameout->deletePlane(*_plane); + _plane = nullptr; + } + + if (!_leaveLastFrame && _leaveScreenBlack) { + // This call *actually* deletes the plane/screen item + g_sci->_gfxFrameout->frameOut(true); + } +} + +#pragma mark - +#pragma mark VMDPlayer - Rendering + +void VMDPlayer::renderFrame() const { + if (_isComposited) { + renderComposited(); + } else { + renderOverlay(); + } +} + void VMDPlayer::fillPalette(Palette &palette) const { const byte *vmdPalette = _decoder->getPalette() + _startColor * 3; for (uint16 i = _startColor; i <= _endColor; ++i) { - int16 r = *vmdPalette++; - int16 g = *vmdPalette++; - int16 b = *vmdPalette++; + uint8 r = *vmdPalette++; + uint8 g = *vmdPalette++; + uint8 b = *vmdPalette++; if (_boostPercent != 100 && i >= _boostStartColor && i <= _boostEndColor) { - r = CLIP<int16>(r * _boostPercent / 100, 0, 255); - g = CLIP<int16>(g * _boostPercent / 100, 0, 255); - b = CLIP<int16>(b * _boostPercent / 100, 0, 255); + r = CLIP(r * _boostPercent / 100, 0, 255); + g = CLIP(g * _boostPercent / 100, 0, 255); + b = CLIP(b * _boostPercent / 100, 0, 255); } palette.colors[i].r = r; diff --git a/engines/sci/graphics/video32.h b/engines/sci/graphics/video32.h index a023fd4cd9..0f63996040 100644 --- a/engines/sci/graphics/video32.h +++ b/engines/sci/graphics/video32.h @@ -308,6 +308,14 @@ private: */ int _lastYieldedFrameNo; + void initOverlay(); + void renderOverlay() const; + void closeOverlay(); + + void initComposited(); + void renderComposited() const; + void closeComposited(); + /** * Plays the VMD until an event occurs (e.g. user * presses escape, clicks, etc.). @@ -330,10 +338,9 @@ public: private: /** - * The location of the VMD plane, in game script - * coordinates. + * The rectangle where the video will be drawn, in screen coordinates. */ - int16 _x, _y; + Common::Rect _drawRect; /** * The plane where the VMD will be drawn. @@ -399,17 +406,74 @@ private: bool _ignorePalettes; /** + * Whether or not rendering mode is composited. + */ + bool _isComposited; + + /** + * Whether or not rendering of the video is being performed in high color or + * better. + */ + bool _usingHighColor; + + /** * Renders a frame of video to the output bitmap. */ void renderFrame() const; /** + * Updates the system with palette data from the video. + */ + bool updatePalette() const; + + /** * Fills the given palette with RGB values from * the VMD palette, applying brightness boost if * it is enabled. */ void fillPalette(Palette &palette) const; +#ifdef USE_RGB_COLOR + /** + * Redraws areas of the screen outside of the video to the system buffer. + * This is used when + */ + void redrawGameScreen() const; +#endif + + /** + * Determines whether or not the VMD player should upgrade the renderer to + * high color depth when rendering the video. + * + * @TODO It should be possible in the future to allow high color composited + * video, but this will require additional work in GfxFrameout and + * GfxCursor32 since the internal buffer and cursor code are 8bpp only. + */ + bool shouldUseHighColor() const { +#ifdef USE_RGB_COLOR + return ConfMan.getBool("enable_hq_video") && + _priority == 0 && + (_doublePixels || _stretchVertical) && + !_leaveLastFrame && + !_showCursor && + !_blackLines; +#else + return false; +#endif + } + + /** + * Determines whether or not the video should use the compositing renderer + * instead of the overlay renderer. + */ + bool shouldUseCompositing() const { +#ifdef USE_RGB_COLOR + return getSciVersion() == SCI_VERSION_3 && !shouldUseHighColor(); +#else + return getSciVersion() == SCI_VERSION_3; +#endif + } + #pragma mark - #pragma mark VMDPlayer - Blackout public: |