diff options
Diffstat (limited to 'engines/sci/graphics')
25 files changed, 941 insertions, 785 deletions
diff --git a/engines/sci/graphics/animate.cpp b/engines/sci/graphics/animate.cpp index 98278397b7..8d92cb905f 100644 --- a/engines/sci/graphics/animate.cpp +++ b/engines/sci/graphics/animate.cpp @@ -145,7 +145,7 @@ bool GfxAnimate::invoke(List *list, int argc, reg_t *argv) { if (_fastCastEnabled) { // Check if the game has a fastCast object set // if we don't abort kAnimate processing, at least in kq5 there will be animation cels drawn into speech boxes. - if (!_s->variables[VAR_GLOBAL][84].isNull()) { + if (!_s->variables[VAR_GLOBAL][kGlobalVarFastCast].isNull()) { // This normally points to an object called "fastCast", // but for example in Eco Quest 1 it may also point to an object called "EventHandler" (see bug #5170) // Original SCI only checked, if this global was not 0. @@ -338,7 +338,7 @@ void GfxAnimate::applyGlobalScaling(AnimateList::iterator entry, GfxView *view) int16 maxScale = readSelectorValue(_s->_segMan, entry->object, SELECTOR(maxScale)); int16 celHeight = view->getHeight(entry->loopNo, entry->celNo); int16 maxCelHeight = (maxScale * celHeight) >> 7; - reg_t globalVar2 = _s->variables[VAR_GLOBAL][2]; // current room object + reg_t globalVar2 = _s->variables[VAR_GLOBAL][kGlobalVarCurrentRoom]; // current room object int16 vanishingY = readSelectorValue(_s->_segMan, globalVar2, SELECTOR(vanishingY)); int16 fixedPortY = _ports->getPort()->rect.bottom - vanishingY; diff --git a/engines/sci/graphics/celobj32.cpp b/engines/sci/graphics/celobj32.cpp index d67a4dc03c..430500ce1e 100644 --- a/engines/sci/graphics/celobj32.cpp +++ b/engines/sci/graphics/celobj32.cpp @@ -28,6 +28,7 @@ #include "sci/graphics/palette32.h" #include "sci/graphics/remap32.h" #include "sci/graphics/text32.h" +#include "sci/engine/workarounds.h" namespace Sci { #pragma mark CelScaler @@ -35,9 +36,6 @@ namespace Sci { CelScaler *CelObj::_scaler = nullptr; void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) { - const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth; - const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight; - for (int i = 0; i < ARRAYSIZE(_scaleTables); ++i) { if (_scaleTables[i].scaleX == scaleX && _scaleTables[i].scaleY == scaleY) { _activeIndex = i; @@ -50,14 +48,12 @@ void CelScaler::activateScaleTables(const Ratio &scaleX, const Ratio &scaleY) { CelScalerTable &table = _scaleTables[i]; if (table.scaleX != scaleX) { - assert(screenWidth <= ARRAYSIZE(table.valuesX)); - buildLookupTable(table.valuesX, scaleX, screenWidth); + buildLookupTable(table.valuesX, scaleX, kCelScalerTableSize); table.scaleX = scaleX; } if (table.scaleY != scaleY) { - assert(screenHeight <= ARRAYSIZE(table.valuesY)); - buildLookupTable(table.valuesY, scaleY, screenHeight); + buildLookupTable(table.valuesY, scaleY, kCelScalerTableSize); table.scaleY = scaleY; } } @@ -159,17 +155,19 @@ struct SCALER_NoScale { template<bool FLIP, typename READER> struct SCALER_Scale { #ifndef NDEBUG + int16 _minX; int16 _maxX; #endif const byte *_row; READER _reader; int16 _x; - static int16 _valuesX[4096]; - static int16 _valuesY[4096]; + static int16 _valuesX[kCelScalerTableSize]; + static int16 _valuesY[kCelScalerTableSize]; SCALER_Scale(const CelObj &celObj, const Common::Rect &targetRect, const Common::Point &scaledPosition, const Ratio scaleX, const Ratio scaleY) : _row(nullptr), #ifndef NDEBUG + _minX(targetRect.left), _maxX(targetRect.right - 1), #endif // The maximum width of the scaled object may not be as @@ -221,17 +219,17 @@ struct SCALER_Scale { } else { if (FLIP) { const int lastIndex = celObj._width - 1; - for (int16 x = 0; x < targetRect.width(); ++x) { - _valuesX[targetRect.left + x] = lastIndex - table->valuesX[x]; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = lastIndex - table->valuesX[x - scaledPosition.x]; } } else { - for (int16 x = 0; x < targetRect.width(); ++x) { - _valuesX[targetRect.left + x] = table->valuesX[x]; + for (int16 x = targetRect.left; x < targetRect.right; ++x) { + _valuesX[x] = table->valuesX[x - scaledPosition.x]; } } - for (int16 y = 0; y < targetRect.height(); ++y) { - _valuesY[targetRect.top + y] = table->valuesY[y]; + for (int16 y = targetRect.top; y < targetRect.bottom; ++y) { + _valuesY[y] = table->valuesY[y - scaledPosition.y]; } } } @@ -239,19 +237,19 @@ struct SCALER_Scale { inline void setTarget(const int16 x, const int16 y) { _row = _reader.getRow(_valuesY[y]); _x = x; - assert(_x >= 0 && _x <= _maxX); + assert(_x >= _minX && _x <= _maxX); } inline byte read() { - assert(_x >= 0 && _x <= _maxX); + assert(_x >= _minX && _x <= _maxX); return _row[_valuesX[_x++]]; } }; template<bool FLIP, typename READER> -int16 SCALER_Scale<FLIP, READER>::_valuesX[4096]; +int16 SCALER_Scale<FLIP, READER>::_valuesX[kCelScalerTableSize]; template<bool FLIP, typename READER> -int16 SCALER_Scale<FLIP, READER>::_valuesY[4096]; +int16 SCALER_Scale<FLIP, READER>::_valuesY[kCelScalerTableSize]; #pragma mark - #pragma mark CelObj - Resource readers @@ -283,13 +281,13 @@ public: struct READER_Compressed { private: const byte *const _resource; - byte _buffer[4096]; + byte _buffer[kCelScalerTableSize]; uint32 _controlOffset; uint32 _dataOffset; uint32 _uncompressedDataOffset; int16 _y; const int16 _sourceHeight; - const uint8 _transparentColor; + const uint8 _skipColor; const int16 _maxWidth; public: @@ -297,7 +295,7 @@ public: _resource(celObj.getResPointer()), _y(-1), _sourceHeight(celObj._height), - _transparentColor(celObj._transparentColor), + _skipColor(celObj._skipColor), _maxWidth(maxWidth) { assert(maxWidth <= celObj._width); @@ -328,7 +326,7 @@ public: // Fill with skip color if (controlByte & 0x40) { - memset(_buffer + i, _transparentColor, length); + memset(_buffer + i, _skipColor, length); // Next value is fill color } else { memset(_buffer + i, *literal, length); @@ -570,7 +568,7 @@ uint8 CelObj::readPixel(uint16 x, const uint16 y, bool mirrorX) const { void CelObj::submitPalette() const { if (_hunkPaletteOffset) { - HunkPalette palette(getResPointer() + _hunkPaletteOffset); + const HunkPalette palette(getResPointer() + _hunkPaletteOffset); g_sci->_gfxPalette32->submit(palette); } } @@ -667,7 +665,7 @@ void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common MAPPER mapper; SCALER scaler(*this, targetRect.left - scaledPosition.x + targetRect.width(), scaledPosition); - RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _transparentColor); + RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _skipColor); renderer.draw(target, targetRect, scaledPosition); } @@ -677,10 +675,10 @@ void CelObj::render(Buffer &target, const Common::Rect &targetRect, const Common MAPPER mapper; SCALER scaler(*this, targetRect, scaledPosition, scaleX, scaleY); if (_drawBlackLines) { - RENDERER<MAPPER, SCALER, true> renderer(mapper, scaler, _transparentColor); + RENDERER<MAPPER, SCALER, true> renderer(mapper, scaler, _skipColor); renderer.draw(target, targetRect, scaledPosition); } else { - RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _transparentColor); + RENDERER<MAPPER, SCALER, false> renderer(mapper, scaler, _skipColor); renderer.draw(target, targetRect, scaledPosition); } } @@ -812,7 +810,31 @@ int16 CelObjView::getNumCels(const GuiResourceId viewId, const int16 loopNo) { const byte *const data = resource->data; const uint16 loopCount = data[2]; - if (loopNo >= loopCount || loopNo < 0) { + + // Every version of SCI32 has a logic error in this function that causes + // random memory to be read if a script requests the cel count for one + // past the maximum loop index. At least GK1 room 800 does this, and gets + // stuck in an infinite loop because the game script expects this method + // to return a non-zero value. + // The scope of this bug means it is likely to pop up in other games, so we + // explicitly trap the bad condition here and report it so that any other + // game scripts relying on this broken behavior can be fixed as well + if (loopNo == loopCount) { + SciCallOrigin origin; + SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kNumCels_workarounds, &origin); + switch (solution.type) { + case WORKAROUND_NONE: + error("[CelObjView::getNumCels]: loop number %d is equal to loop count in view %u, %s", loopNo, viewId, origin.toString().c_str()); + case WORKAROUND_FAKE: + return (int16)solution.value; + case WORKAROUND_IGNORE: + return 0; + case WORKAROUND_STILLCALL: + break; + } + } + + if (loopNo > loopCount || loopNo < 0) { return 0; } @@ -869,20 +891,20 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int const byte *const data = resource->data; - _scaledWidth = READ_SCI11ENDIAN_UINT16(data + 14); - _scaledHeight = READ_SCI11ENDIAN_UINT16(data + 16); + _xResolution = READ_SCI11ENDIAN_UINT16(data + 14); + _yResolution = READ_SCI11ENDIAN_UINT16(data + 16); - if (_scaledWidth == 0 && _scaledHeight == 0) { + if (_xResolution == 0 && _yResolution == 0) { byte sizeFlag = data[5]; if (sizeFlag == 0) { - _scaledWidth = kLowResX; - _scaledHeight = kLowResY; + _xResolution = kLowResX; + _yResolution = kLowResY; } else if (sizeFlag == 1) { - _scaledWidth = 640; - _scaledHeight = 480; + _xResolution = 640; + _yResolution = 480; } else if (sizeFlag == 2) { - _scaledWidth = 640; - _scaledHeight = 400; + _xResolution = 640; + _yResolution = 400; } } @@ -936,9 +958,9 @@ CelObjView::CelObjView(const GuiResourceId viewId, const int16 loopNo, const int _width = READ_SCI11ENDIAN_UINT16(celHeader); _height = READ_SCI11ENDIAN_UINT16(celHeader + 2); - _displace.x = _width / 2 - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4); - _displace.y = _height - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6) - 1; - _transparentColor = celHeader[8]; + _origin.x = _width / 2 - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4); + _origin.y = _height - (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6) - 1; + _skipColor = celHeader[8]; _compressionType = (CelCompressionType)celHeader[9]; if (_compressionType != kCelCompressionNone && _compressionType != kCelCompressionRLE) { @@ -967,7 +989,7 @@ bool CelObjView::analyzeUncompressedForRemap() const { if ( pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && - pixel != _transparentColor + pixel != _skipColor ) { return true; } @@ -984,7 +1006,7 @@ bool CelObjView::analyzeForRemap() const { if ( pixel >= g_sci->_gfxRemap32->getStartColor() && pixel <= g_sci->_gfxRemap32->getEndColor() && - pixel != _transparentColor + pixel != _skipColor ) { return true; } @@ -1058,9 +1080,9 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { _width = READ_SCI11ENDIAN_UINT16(celHeader); _height = READ_SCI11ENDIAN_UINT16(celHeader + 2); - _displace.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4); - _displace.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6); - _transparentColor = celHeader[8]; + _origin.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 4); + _origin.y = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 6); + _skipColor = celHeader[8]; _compressionType = (CelCompressionType)celHeader[9]; _priority = READ_SCI11ENDIAN_UINT16(celHeader + 36); _relativePosition.x = (int16)READ_SCI11ENDIAN_UINT16(celHeader + 38); @@ -1070,17 +1092,17 @@ CelObjPic::CelObjPic(const GuiResourceId picId, const int16 celNo) { const uint16 sizeFlag2 = READ_SCI11ENDIAN_UINT16(data + 12); if (sizeFlag2) { - _scaledWidth = sizeFlag1; - _scaledHeight = sizeFlag2; + _xResolution = sizeFlag1; + _yResolution = sizeFlag2; } else if (sizeFlag1 == 0) { - _scaledWidth = kLowResX; - _scaledHeight = kLowResY; + _xResolution = kLowResX; + _yResolution = kLowResY; } else if (sizeFlag1 == 1) { - _scaledWidth = 640; - _scaledHeight = 480; + _xResolution = 640; + _yResolution = 480; } else if (sizeFlag1 == 2) { - _scaledWidth = 640; - _scaledHeight = 400; + _xResolution = 640; + _yResolution = 400; } if (celHeader[10] & 128) { @@ -1105,7 +1127,7 @@ bool CelObjPic::analyzeUncompressedForSkip() const { const byte *const pixels = resource + READ_SCI11ENDIAN_UINT32(resource + _celHeaderOffset + 24); for (int i = 0; i < _width * _height; ++i) { uint8 pixel = pixels[i]; - if (pixel == _transparentColor) { + if (pixel == _skipColor) { return true; } } @@ -1151,10 +1173,10 @@ CelObjMem::CelObjMem(const reg_t bitmapObject) { _width = bitmap->getWidth(); _height = bitmap->getHeight(); - _displace = bitmap->getDisplace(); - _transparentColor = bitmap->getSkipColor(); - _scaledWidth = bitmap->getScaledWidth(); - _scaledHeight = bitmap->getScaledHeight(); + _origin = bitmap->getOrigin(); + _skipColor = bitmap->getSkipColor(); + _xResolution = bitmap->getXResolution(); + _yResolution = bitmap->getYResolution(); _hunkPaletteOffset = bitmap->getHunkPaletteOffset(); _remap = bitmap->getRemap(); } @@ -1173,10 +1195,10 @@ byte *CelObjMem::getResPointer() const { CelObjColor::CelObjColor(const uint8 color, const int16 width, const int16 height) { _info.type = kCelTypeColor; _info.color = color; - _displace.x = 0; - _displace.y = 0; - _scaledWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - _scaledHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + _origin.x = 0; + _origin.y = 0; + _xResolution = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + _yResolution = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; _hunkPaletteOffset = 0; _mirrorX = false; _remap = false; diff --git a/engines/sci/graphics/celobj32.h b/engines/sci/graphics/celobj32.h index 21e86d03e0..03d950a3c3 100644 --- a/engines/sci/graphics/celobj32.h +++ b/engines/sci/graphics/celobj32.h @@ -141,13 +141,20 @@ typedef Common::Array<CelCacheEntry> CelCache; #pragma mark - #pragma mark CelScaler +enum { + /** + * The maximum size of a row/column of scaled pixel data. + */ + kCelScalerTableSize = 4096 +}; + struct CelScalerTable { /** * A lookup table of indexes that should be used to find * the correct column to read from the source bitmap * when drawing a scaled version of the source bitmap. */ - int valuesX[4096]; + int valuesX[kCelScalerTableSize]; /** * The ratio used to generate the x-values. @@ -159,7 +166,7 @@ struct CelScalerTable { * the correct row to read from a source bitmap when * drawing a scaled version of the source bitmap. */ - int valuesY[4096]; + int valuesY[kCelScalerTableSize]; /** * The ratio used to generate the y-values. @@ -279,7 +286,7 @@ public: /** * TODO: Documentation */ - Common::Point _displace; + Common::Point _origin; /** * The dimensions of the original coordinate system for @@ -294,21 +301,21 @@ public: * scriptWidth/Height but seems to typically be changed * to more closely match the native screen resolution. */ - uint16 _scaledWidth, _scaledHeight; + uint16 _xResolution, _yResolution; /** * The skip (transparent) color for the cel. When * compositing, any pixels matching this color will not * be copied to the buffer. */ - uint8 _transparentColor; + uint8 _skipColor; /** * Whether or not this cel has any transparent regions. * This is used for optimised drawing of non-transparent * cels. */ - bool _transparent; // TODO: probably "skip"? + bool _transparent; /** * The compression type for the pixel data for this cel. diff --git a/engines/sci/graphics/controls32.cpp b/engines/sci/graphics/controls32.cpp index 7689655d1d..0cd924b38d 100644 --- a/engines/sci/graphics/controls32.cpp +++ b/engines/sci/graphics/controls32.cpp @@ -160,7 +160,6 @@ reg_t GfxControls32::kernelEditText(const reg_t controlObject) { // Original engine did not have a QUIT event but we have to handle it if (event.type == SCI_EVENT_QUIT) { focused = false; - break; } else if (event.type == SCI_EVENT_MOUSE_PRESS && !editorPlaneRect.contains(event.mousePosSci)) { focused = false; } else if (event.type == SCI_EVENT_KEYBOARD) { @@ -313,8 +312,8 @@ reg_t GfxControls32::kernelEditText(const reg_t controlObject) { if (textChanged) { editor.text.trim(); - SciString *string = _segMan->lookupString(textObject); - string->fromString(editor.text); + SciArray &string = *_segMan->lookupArray(textObject); + string.fromString(editor.text); } return make_reg(0, textChanged); @@ -400,7 +399,7 @@ ScrollWindow::ScrollWindow(SegManager *segMan, const Common::Rect &gameRect, con const uint16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; Common::Rect bitmapRect(gameRect); - mulinc(bitmapRect, Ratio(_gfxText32._scaledWidth, scriptWidth), Ratio(_gfxText32._scaledHeight, scriptHeight)); + mulinc(bitmapRect, Ratio(_gfxText32._xResolution, scriptWidth), Ratio(_gfxText32._yResolution, scriptHeight)); _textRect.left = 2; _textRect.top = 2; @@ -441,6 +440,11 @@ void ScrollWindow::show() { } Plane *plane = g_sci->_gfxFrameout->getPlanes().findByObject(_plane); + + if (plane == nullptr) { + error("[ScrollWindow::show]: Plane %04x:%04x not found", PRINT_REG(_plane)); + } + plane->_screenItemList.add(_screenItem); _visible = true; diff --git a/engines/sci/graphics/cursor32.cpp b/engines/sci/graphics/cursor32.cpp index 88150db6e6..2f2611c769 100644 --- a/engines/sci/graphics/cursor32.cpp +++ b/engines/sci/graphics/cursor32.cpp @@ -122,6 +122,7 @@ void GfxCursor32::drawToHardware(const DrawRegion &source) { byte *sourcePixel = source.data + (sourceYOffset * source.rect.width()) + sourceXOffset; g_system->copyRectToScreen(sourcePixel, source.rect.width(), drawRect.left, drawRect.top, drawRect.width(), drawRect.height()); + g_system->updateScreen(); } void GfxCursor32::unhide() { @@ -181,7 +182,7 @@ void GfxCursor32::setView(const GuiResourceId viewId, const int16 loopNo, const if (_macCursorRemap.empty() && viewId != -1) { CelObjView view(viewId, loopNo, celNo); - _hotSpot = view._displace; + _hotSpot = view._origin; _width = view._width; _height = view._height; diff --git a/engines/sci/graphics/cursor32.h b/engines/sci/graphics/cursor32.h index 88a75beb7f..00a8b9baa4 100644 --- a/engines/sci/graphics/cursor32.h +++ b/engines/sci/graphics/cursor32.h @@ -31,7 +31,7 @@ namespace Sci { -class GfxCursor32 : Common::Serializable { +class GfxCursor32 : public Common::Serializable { public: GfxCursor32(); ~GfxCursor32(); diff --git a/engines/sci/graphics/font.h b/engines/sci/graphics/font.h index 451261f315..b79fb2f0ba 100644 --- a/engines/sci/graphics/font.h +++ b/engines/sci/graphics/font.h @@ -75,7 +75,7 @@ private: byte height; int16 offset; }; - + byte _fontHeight; uint16 _numChars; Charinfo *_chars; diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 4e0aa22669..843fe5ed86 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -58,7 +58,7 @@ namespace Sci { GfxFrameout::GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor) : - _isHiRes(ConfMan.getBool("enable_high_resolution_graphics")), + _isHiRes(gameIsHiRes()), _palette(palette), _cursor(cursor), _segMan(segMan), @@ -71,11 +71,6 @@ GfxFrameout::GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitio _overdrawThreshold(0), _palMorphIsOn(false) { - // QFG4 is the only SCI32 game that doesn't have a high-resolution version - if (g_sci->getGameId() == GID_QFG4) { - _isHiRes = false; - } - if (g_sci->getGameId() == GID_PHANTASMAGORIA) { _currentBuffer = Buffer(630, 450, nullptr); } else if (_isHiRes) { @@ -146,7 +141,7 @@ void GfxFrameout::syncWithScripts(bool addElements) { return; // Get planes list object - reg_t planesListObject = engineState->variables[VAR_GLOBAL][10]; + reg_t planesListObject = engineState->variables[VAR_GLOBAL][kGlobalVarPlanes]; reg_t planesListElements = readSelector(segMan, planesListObject, SELECTOR(elements)); List *planesList = segMan->lookupList(planesListElements); @@ -213,6 +208,26 @@ void GfxFrameout::syncWithScripts(bool addElements) { } } +bool GfxFrameout::gameIsHiRes() const { + // QFG4 is always low resolution + if (g_sci->getGameId() == GID_QFG4) { + return false; + } + + // GK1 DOS floppy is low resolution only, but GK1 Mac floppy is high + // resolution only + if (g_sci->getGameId() == GID_GK1 && + !g_sci->isCD() && + g_sci->getPlatform() != Common::kPlatformMacintosh) { + + return false; + } + + // All other games are either high resolution by default, or have a + // user-defined toggle + return ConfMan.getBool("enable_high_resolution_graphics"); +} + #pragma mark - #pragma mark Benchmarking @@ -550,7 +565,7 @@ void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *show Palette sourcePalette(_palette->getNextPalette()); alterVmap(sourcePalette, sourcePalette, -1, styleRanges); - int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16(); + int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][kGlobalVarPreviousRoomNo].toSint16(); Common::Rect rect(_currentBuffer.screenWidth, _currentBuffer.screenHeight); _showList.add(rect); @@ -989,6 +1004,10 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL _visiblePlanes.add(new Plane(plane)); --plane._created; } else if (plane._updated) { + if (visiblePlane == nullptr) { + error("[GfxFrameout::calcLists]: Attempt to update nonexistent visible plane"); + } + *visiblePlane = plane; --plane._updated; } @@ -1108,6 +1127,7 @@ void GfxFrameout::mergeToShowList(const Common::Rect &drawRect, RectList &showLi void GfxFrameout::showBits() { if (!_showList.size()) { + g_system->updateScreen(); return; } @@ -1146,6 +1166,7 @@ void GfxFrameout::showBits() { _cursor->donePainting(); _showList.clear(); + g_system->updateScreen(); } void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, const int8 style, const int8 *const styleRanges) { @@ -1327,7 +1348,7 @@ bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const scaledPosition.x -= screenItem._scaledPosition.x; scaledPosition.y -= screenItem._scaledPosition.y; - mulru(scaledPosition, Ratio(celObj._scaledWidth, _currentBuffer.screenWidth), Ratio(celObj._scaledHeight, _currentBuffer.screenHeight)); + mulru(scaledPosition, Ratio(celObj._xResolution, _currentBuffer.screenWidth), Ratio(celObj._yResolution, _currentBuffer.screenHeight)); if (screenItem._scale.signal != kScaleSignalNone && screenItem._scale.x && screenItem._scale.y) { scaledPosition.x = scaledPosition.x * 128 / screenItem._scale.x; @@ -1335,7 +1356,7 @@ bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const } uint8 pixel = celObj.readPixel(scaledPosition.x, scaledPosition.y, mirrorX); - return pixel != celObj._transparentColor; + return pixel != celObj._skipColor; } return true; diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index e4caffd9e5..9481b0ef5a 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -44,6 +44,12 @@ private: GfxPalette32 *_palette; SegManager *_segMan; + /** + * Determines whether the current game should be rendered in + * high resolution. + */ + bool gameIsHiRes() const; + public: GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor); ~GfxFrameout(); diff --git a/engines/sci/graphics/paint16.cpp b/engines/sci/graphics/paint16.cpp index 91817d4060..aac922d278 100644 --- a/engines/sci/graphics/paint16.cpp +++ b/engines/sci/graphics/paint16.cpp @@ -541,12 +541,10 @@ reg_t GfxPaint16::kernelDisplay(const char *text, uint16 languageSplitter, int a break; default: - SciTrackOriginReply originReply; + SciCallOrigin originReply; SciWorkaroundSolution solution = trackOriginAndFindWorkaround(0, kDisplay_workarounds, &originReply); if (solution.type == WORKAROUND_NONE) - error("Unknown kDisplay argument (%04x:%04x) from method %s::%s (script %d, localCall %x)", - PRINT_REG(displayArg), originReply.objectName.c_str(), originReply.methodName.c_str(), - originReply.scriptNr, originReply.localCallOffset); + error("Unknown kDisplay argument (%04x:%04x) from %s", PRINT_REG(displayArg), originReply.toString().c_str()); assert(solution.type == WORKAROUND_IGNORE); break; } diff --git a/engines/sci/graphics/paint32.cpp b/engines/sci/graphics/paint32.cpp index 338b70901e..bf7c73623e 100644 --- a/engines/sci/graphics/paint32.cpp +++ b/engines/sci/graphics/paint32.cpp @@ -38,7 +38,6 @@ reg_t GfxPaint32::kernelAddLine(const reg_t planeObject, const Common::Point &st Common::Rect gameRect; reg_t bitmapId = makeLineBitmap(startPoint, endPoint, priority, color, style, pattern, thickness, gameRect); - SciBitmap &bitmap = *_segMan->lookupBitmap(bitmapId); CelInfo32 celInfo; celInfo.type = kCelTypeMem; @@ -48,7 +47,7 @@ reg_t GfxPaint32::kernelAddLine(const reg_t planeObject, const Common::Point &st // `kUpdateLine` can get the originally used color celInfo.color = color; - ScreenItem *screenItem = new ScreenItem(planeObject, celInfo, Common::Rect(startPoint.x, startPoint.y, startPoint.x + bitmap.getWidth(), startPoint.y + bitmap.getHeight())); + ScreenItem *screenItem = new ScreenItem(planeObject, celInfo, gameRect); screenItem->_priority = priority; screenItem->_fixedPriority = true; @@ -89,9 +88,13 @@ void GfxPaint32::plotter(int x, int y, int color, void *data) { LineProperties &properties = *static_cast<LineProperties *>(data); byte *pixels = properties.bitmap->getPixels(); - const uint32 index = properties.bitmap->getWidth() * y + x; + const uint16 bitmapWidth = properties.bitmap->getWidth(); + const uint16 bitmapHeight = properties.bitmap->getHeight(); + const uint32 index = bitmapWidth * y + x; - if (index < properties.bitmap->getDataSize()) { + // Only draw the points in the bitmap, and ignore the rest. SSCI scripts + // can draw lines ending outside the visible area (e.g. negative coordinates) + if (x >= 0 && x < bitmapWidth && y >= 0 && y < bitmapHeight) { if (properties.solid) { pixels[index] = (uint8)color; return; @@ -112,25 +115,29 @@ void GfxPaint32::plotter(int x, int y, int color, void *data) { if (properties.patternIndex == ARRAYSIZE(properties.pattern)) { properties.patternIndex = 0; } - } else { - warning("GfxPaint32::plotter: Attempted to write out of bounds (%u >= %u)", index, properties.bitmap->getDataSize()); } } reg_t GfxPaint32::makeLineBitmap(const Common::Point &startPoint, const Common::Point &endPoint, const int16 priority, const uint8 color, const LineStyle style, uint16 pattern, uint8 thickness, Common::Rect &outRect) { const uint8 skipColor = color != kDefaultSkipColor ? kDefaultSkipColor : 0; - // Thickness is expected to be 2n+1 - thickness = ((MAX((uint8)1, thickness) - 1) | 1); + // Line thickness is expected to be 2 * thickness + 1 + thickness = (MAX<uint8>(1, thickness) - 1) | 1; const uint8 halfThickness = thickness >> 1; - outRect.left = (startPoint.x < endPoint.x ? startPoint.x : endPoint.x) - halfThickness; - outRect.top = (startPoint.y < endPoint.y ? startPoint.y : endPoint.y) - halfThickness; - outRect.right = (startPoint.x > endPoint.x ? startPoint.x : endPoint.x) + halfThickness + 1; - outRect.bottom = (startPoint.y > endPoint.y ? startPoint.y : endPoint.y) + halfThickness + 1; + const uint16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + const uint16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + + outRect.left = MIN<int16>(startPoint.x, endPoint.x); + outRect.top = MIN<int16>(startPoint.y, endPoint.y); + outRect.right = MAX<int16>(startPoint.x, endPoint.x) + 1 + 1; // rect lower edge + thickness offset + outRect.bottom = MAX<int16>(startPoint.y, endPoint.y) + 1 + 1; // rect lower edge + thickness offset + + outRect.grow(halfThickness); + outRect.clip(Common::Rect(scriptWidth, scriptHeight)); reg_t bitmapId; - SciBitmap &bitmap = *_segMan->allocateBitmap(&bitmapId, outRect.width(), outRect.height(), skipColor, 0, 0, g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth, g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight, 0, false, true); + SciBitmap &bitmap = *_segMan->allocateBitmap(&bitmapId, outRect.width(), outRect.height(), skipColor, 0, 0, scriptWidth, scriptHeight, 0, false, true); byte *pixels = bitmap.getPixels(); memset(pixels, skipColor, bitmap.getWidth() * bitmap.getHeight()); @@ -152,12 +159,11 @@ reg_t GfxPaint32::makeLineBitmap(const Common::Point &startPoint, const Common:: break; } - const Common::Rect drawRect( - startPoint.x - outRect.left, - startPoint.y - outRect.top, - endPoint.x - outRect.left, - endPoint.y - outRect.top - ); + // Change coordinates to be relative to the bitmap + const int16 x1 = startPoint.x - outRect.left; + const int16 y1 = startPoint.y - outRect.top; + const int16 x2 = endPoint.x - outRect.left; + const int16 y2 = endPoint.y - outRect.top; if (!properties.solid) { for (int i = 0; i < ARRAYSIZE(properties.pattern); ++i) { @@ -166,14 +172,14 @@ reg_t GfxPaint32::makeLineBitmap(const Common::Point &startPoint, const Common:: } properties.patternIndex = 0; - properties.horizontal = ABS(drawRect.right - drawRect.left) > ABS(drawRect.bottom - drawRect.top); - properties.lastAddress = properties.horizontal ? drawRect.left : drawRect.top; + properties.horizontal = ABS(x2 - x1) > ABS(y2 - y1); + properties.lastAddress = properties.horizontal ? x1 : y1; } if (thickness <= 1) { - Graphics::drawLine(drawRect.left, drawRect.top, drawRect.right, drawRect.bottom, color, plotter, &properties); + Graphics::drawLine(x1, y1, x2, y2, color, plotter, &properties); } else { - Graphics::drawThickLine2(drawRect.left, drawRect.top, drawRect.right, drawRect.bottom, thickness, color, plotter, &properties); + Graphics::drawThickLine2(x1, y1, x2, y2, thickness, color, plotter, &properties); } return bitmapId; diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp index c7098bc3e4..ec3d912365 100644 --- a/engines/sci/graphics/palette32.cpp +++ b/engines/sci/graphics/palette32.cpp @@ -52,7 +52,7 @@ HunkPalette::HunkPalette(byte *rawPalette) : } } -void HunkPalette::setVersion(const uint32 version) { +void HunkPalette::setVersion(const uint32 version) const { if (_numPalettes != _data[10]) { error("Invalid HunkPalette"); } @@ -93,9 +93,9 @@ const Palette HunkPalette::toPalette() const { if (_numPalettes) { const EntryHeader header = getEntryHeader(); - byte *data = getPalPointer() + kEntryHeaderSize; + const byte *data = getPalPointer() + kEntryHeaderSize; - int16 end = header.startColor + header.numColors; + const int16 end = header.startColor + header.numColors; assert(end <= 256); if (header.sharedUsed) { @@ -118,20 +118,19 @@ const Palette HunkPalette::toPalette() const { return outPalette; } - #pragma mark - #pragma mark GfxPalette32 GfxPalette32::GfxPalette32(ResourceManager *resMan) : _resMan(resMan), + // Palette versioning _version(1), _needsUpdate(false), _currentPalette(), _sourcePalette(), _nextPalette(), - // Clut - _clutTable(nullptr), + // Palette varying _varyStartPalette(nullptr), _varyTargetPalette(nullptr), @@ -142,6 +141,7 @@ GfxPalette32::GfxPalette32(ResourceManager *resMan) _varyDirection(0), _varyTargetPercent(0), _varyNumTimesPaused(0), + // Palette cycling _cyclers(), _cycleMap() { @@ -161,44 +161,6 @@ GfxPalette32::~GfxPalette32() { cycleAllOff(); } -inline void mergePaletteInternal(Palette *const to, const Palette *const from) { - // The last color is always white, so it is not copied. - // (Some palettes try to set the last color, which causes - // churning in the palettes when they are merged) - for (int i = 0, len = ARRAYSIZE(to->colors) - 1; i < len; ++i) { - if (from->colors[i].used) { - to->colors[i] = from->colors[i]; - } - } -} - -void GfxPalette32::submit(const Palette &palette) { - const Palette oldSourcePalette(_sourcePalette); - mergePaletteInternal(&_sourcePalette, &palette); - - if (!_needsUpdate && _sourcePalette != oldSourcePalette) { - ++_version; - _needsUpdate = true; - } -} - -void GfxPalette32::submit(HunkPalette &hunkPalette) { - if (hunkPalette.getVersion() == _version) { - return; - } - - const Palette oldSourcePalette(_sourcePalette); - const Palette palette = hunkPalette.toPalette(); - mergePaletteInternal(&_sourcePalette, &palette); - - if (!_needsUpdate && oldSourcePalette != _sourcePalette) { - ++_version; - _needsUpdate = true; - } - - hunkPalette.setVersion(_version); -} - bool GfxPalette32::loadPalette(const GuiResourceId resourceId) { Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false); @@ -206,7 +168,7 @@ bool GfxPalette32::loadPalette(const GuiResourceId resourceId) { return false; } - HunkPalette palette(palResource->data); + const HunkPalette palette(palResource->data); submit(palette); return true; } @@ -241,6 +203,33 @@ int16 GfxPalette32::matchColor(const uint8 r, const uint8 g, const uint8 b) { return bestIndex; } +void GfxPalette32::submit(const Palette &palette) { + const Palette oldSourcePalette(_sourcePalette); + mergePalette(_sourcePalette, palette); + + if (!_needsUpdate && _sourcePalette != oldSourcePalette) { + ++_version; + _needsUpdate = true; + } +} + +void GfxPalette32::submit(const HunkPalette &hunkPalette) { + if (hunkPalette.getVersion() == _version) { + return; + } + + const Palette oldSourcePalette(_sourcePalette); + const Palette palette = hunkPalette.toPalette(); + mergePalette(_sourcePalette, palette); + + if (!_needsUpdate && oldSourcePalette != _sourcePalette) { + ++_version; + _needsUpdate = true; + } + + hunkPalette.setVersion(_version); +} + bool GfxPalette32::updateForFrame() { applyAll(); _needsUpdate = false; @@ -266,10 +255,10 @@ void GfxPalette32::updateHardware(const bool updateScreen) { _currentPalette.colors[i] = _nextPalette.colors[i]; // NOTE: If the brightness option in the user configuration file is set, - // SCI engine adjusts palette brightnesses here by mapping RGB values to values - // in some hard-coded brightness tables. There is no reason on modern hardware - // to implement this, unless it is discovered that some game uses a non-standard - // brightness setting by default + // SCI engine adjusts palette brightnesses here by mapping RGB values to + // values in some hard-coded brightness tables. There is no reason on + // modern hardware to implement this, unless it is discovered that some + // game uses a non-standard brightness setting by default // All color entries MUST be copied, not just "used" entries, otherwise // uninitialised memory from bpal makes its way into the system palette. @@ -295,7 +284,30 @@ void GfxPalette32::updateHardware(const bool updateScreen) { g_system->getPaletteManager()->setPalette(bpal, 0, 256); if (updateScreen) { - g_sci->getEventManager()->updateScreen(); + g_system->updateScreen(); + } +} + +Palette GfxPalette32::getPaletteFromResource(const GuiResourceId resourceId) const { + Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false); + + if (!palResource) { + error("Could not load vary palette %d", resourceId); + } + + const HunkPalette rawPalette(palResource->data); + return rawPalette.toPalette(); +} + +void GfxPalette32::mergePalette(Palette &to, const Palette &from) { + // The last color is always white in SCI, so it is not copied. (Some + // palettes, particularly in KQ7, try to set the last color, which causes + // unnecessary palette updates since the last color is forced by SSCI to a + // specific value) + for (int i = 0; i < ARRAYSIZE(to.colors) - 1; ++i) { + if (from.colors[i].used) { + to.colors[i] = from.colors[i]; + } } } @@ -306,81 +318,43 @@ void GfxPalette32::applyAll() { } #pragma mark - -#pragma mark Colour look-up - -#ifdef ENABLE_SCI3_GAMES -bool GfxPalette32::loadClut(uint16 clutId) { - // loadClut() will load a color lookup table from a clu file and set - // the palette found in the file. This is to be used with Phantasmagoria 2. - - unloadClut(); - - Common::String filename = Common::String::format("%d.clu", clutId); - Common::File clut; - - if (!clut.open(filename) || clut.size() != 0x10000 + 236 * 3) - return false; - - // Read in the lookup table - // It maps each RGB565 color to a palette index - _clutTable = new byte[0x10000]; - clut.read(_clutTable, 0x10000); +#pragma mark Varying - Palette pal; - memset(&pal, 0, sizeof(Palette)); +void GfxPalette32::setVary(const Palette &target, const int16 percent, const int32 ticks, const int16 fromColor, const int16 toColor) { + setTarget(target); + setVaryTime(percent, ticks); - // Setup 1:1 mapping - for (int i = 0; i < 256; i++) { - pal.mapping[i] = i; + if (fromColor > -1) { + _varyFromColor = fromColor; } - - // Now load in the palette - for (int i = 1; i <= 236; i++) { - pal.colors[i].used = 1; - pal.colors[i].r = clut.readByte(); - pal.colors[i].g = clut.readByte(); - pal.colors[i].b = clut.readByte(); + if (toColor > -1) { + assert(toColor < 256); + _varyToColor = toColor; } - - set(&pal, true); - setOnScreen(); - return true; } -byte GfxPalette32::matchClutColor(uint16 color) { - // Match a color in RGB565 format to a palette index based on the loaded CLUT - assert(_clutTable); - return _clutTable[color]; -} +void GfxPalette32::setVaryPercent(const int16 percent, const int32 ticks) { + if (_varyTargetPalette != nullptr) { + setVaryTime(percent, ticks); + } -void GfxPalette32::unloadClut() { - // This will only unload the actual table, but not reset any palette - delete[] _clutTable; - _clutTable = nullptr; + // NOTE: SSCI had two additional parameters for this function to change the + // `_varyFromColor`, but they were always hardcoded to be ignored } -#endif - -#pragma mark - -#pragma mark Varying - -inline Palette GfxPalette32::getPaletteFromResourceInternal(const GuiResourceId resourceId) const { - Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false); - if (!palResource) { - error("Could not load vary palette %d", resourceId); +void GfxPalette32::setVaryTime(const int32 time) { + if (_varyTargetPalette != nullptr) { + setVaryTime(_varyTargetPercent, time); } - - HunkPalette rawPalette(palResource->data); - return rawPalette.toPalette(); } -inline void GfxPalette32::setVaryTimeInternal(const int16 percent, const int time) { +void GfxPalette32::setVaryTime(const int16 percent, const int32 ticks) { _varyLastTick = g_sci->getTickCount(); - if (!time || _varyPercent == percent) { + if (!ticks || _varyPercent == percent) { _varyDirection = 0; _varyTargetPercent = _varyPercent = percent; } else { - _varyTime = time / (percent - _varyPercent); + _varyTime = ticks / (percent - _varyPercent); _varyTargetPercent = percent; if (_varyTime > 0) { @@ -395,72 +369,6 @@ inline void GfxPalette32::setVaryTimeInternal(const int16 percent, const int tim } } -void GfxPalette32::kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int time, const int16 fromColor, const int16 toColor) { - Palette palette = getPaletteFromResourceInternal(paletteId); - setVary(&palette, percent, time, fromColor, toColor); -} - -void GfxPalette32::kernelPalVaryMergeTarget(GuiResourceId paletteId) { - Palette palette = getPaletteFromResourceInternal(paletteId); - mergeTarget(&palette); -} - -void GfxPalette32::kernelPalVarySetTarget(GuiResourceId paletteId) { - Palette palette = getPaletteFromResourceInternal(paletteId); - setTarget(&palette); -} - -void GfxPalette32::kernelPalVarySetStart(GuiResourceId paletteId) { - Palette palette = getPaletteFromResourceInternal(paletteId); - setStart(&palette); -} - -void GfxPalette32::kernelPalVaryMergeStart(GuiResourceId paletteId) { - Palette palette = getPaletteFromResourceInternal(paletteId); - mergeStart(&palette); -} - -void GfxPalette32::kernelPalVaryPause(bool pause) { - if (pause) { - varyPause(); - } else { - varyOn(); - } -} - -void GfxPalette32::setVary(const Palette *const target, const int16 percent, const int time, const int16 fromColor, const int16 toColor) { - setTarget(target); - setVaryTimeInternal(percent, time); - - if (fromColor > -1) { - _varyFromColor = fromColor; - } - if (toColor > -1) { - assert(toColor < 256); - _varyToColor = toColor; - } -} - -void GfxPalette32::setVaryPercent(const int16 percent, const int time, const int16 fromColor, const int16 fromColorAlternate) { - if (_varyTargetPalette != nullptr) { - setVaryTimeInternal(percent, time); - } - - // This looks like a mistake in the actual SCI engine (both SQ6 and Lighthouse); - // the values are always hardcoded to -1 in kPalVary, so this code can never - // actually be executed - if (fromColor > -1) { - _varyFromColor = fromColor; - } - if (fromColorAlternate > -1) { - _varyFromColor = fromColorAlternate; - } -} - -int16 GfxPalette32::getVaryPercent() const { - return ABS(_varyPercent); -} - void GfxPalette32::varyOff() { _varyNumTimesPaused = 0; _varyPercent = _varyTargetPercent = 0; @@ -479,14 +387,6 @@ void GfxPalette32::varyOff() { } } -void GfxPalette32::mergeTarget(const Palette *const palette) { - if (_varyTargetPalette != nullptr) { - mergePaletteInternal(_varyTargetPalette, palette); - } else { - _varyTargetPalette = new Palette(*palette); - } -} - void GfxPalette32::varyPause() { _varyDirection = 0; ++_varyNumTimesPaused; @@ -497,51 +397,44 @@ void GfxPalette32::varyOn() { --_varyNumTimesPaused; } - if (_varyTargetPalette != nullptr && _varyNumTimesPaused == 0 && _varyPercent != _varyTargetPercent) { - if (_varyTime == 0) { - _varyPercent = _varyTargetPercent; - } else if (_varyTargetPercent < _varyPercent) { - _varyDirection = -1; + if (_varyTargetPalette != nullptr && _varyNumTimesPaused == 0) { + if (_varyPercent != _varyTargetPercent && _varyTime != 0) { + _varyDirection = (_varyTargetPercent - _varyPercent > 0) ? 1 : -1; } else { - _varyDirection = 1; + _varyPercent = _varyTargetPercent; } } } -void GfxPalette32::setVaryTime(const int time) { - if (_varyTargetPalette == nullptr) { - return; - } - - setVaryTimeInternal(_varyTargetPercent, time); +void GfxPalette32::setTarget(const Palette &palette) { + delete _varyTargetPalette; + _varyTargetPalette = new Palette(palette); } -void GfxPalette32::setTarget(const Palette *const palette) { - if (_varyTargetPalette != nullptr) { - delete _varyTargetPalette; - } - - _varyTargetPalette = new Palette(*palette); +void GfxPalette32::setStart(const Palette &palette) { + delete _varyStartPalette; + _varyStartPalette = new Palette(palette); } -void GfxPalette32::setStart(const Palette *const palette) { +void GfxPalette32::mergeStart(const Palette &palette) { if (_varyStartPalette != nullptr) { - delete _varyStartPalette; + mergePalette(*_varyStartPalette, palette); + } else { + _varyStartPalette = new Palette(palette); } - - _varyStartPalette = new Palette(*palette); } -void GfxPalette32::mergeStart(const Palette *const palette) { - if (_varyStartPalette != nullptr) { - mergePaletteInternal(_varyStartPalette, palette); +void GfxPalette32::mergeTarget(const Palette &palette) { + if (_varyTargetPalette != nullptr) { + mergePalette(*_varyTargetPalette, palette); } else { - _varyStartPalette = new Palette(*palette); + _varyTargetPalette = new Palette(palette); } } void GfxPalette32::applyVary() { - while (g_sci->getTickCount() - _varyLastTick > (uint32)_varyTime && _varyDirection != 0) { + const uint32 now = g_sci->getTickCount(); + while ((int32)(now - _varyLastTick) > _varyTime && _varyDirection != 0) { _varyLastTick += _varyTime; if (_varyPercent == _varyTargetPercent) { @@ -552,7 +445,7 @@ void GfxPalette32::applyVary() { } if (_varyPercent == 0 || _varyTargetPalette == nullptr) { - for (int i = 0, len = ARRAYSIZE(_nextPalette.colors); i < len; ++i) { + for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) { if (_varyStartPalette != nullptr && i >= _varyFromColor && i <= _varyToColor) { _nextPalette.colors[i] = _varyStartPalette->colors[i]; } else { @@ -560,7 +453,7 @@ void GfxPalette32::applyVary() { } } } else { - for (int i = 0, len = ARRAYSIZE(_nextPalette.colors); i < len; ++i) { + for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) { if (i >= _varyFromColor && i <= _varyToColor) { Color targetColor = _varyTargetPalette->colors[i]; Color sourceColor; @@ -591,82 +484,67 @@ void GfxPalette32::applyVary() { } } -#pragma mark - -#pragma mark Cycling - -inline void GfxPalette32::clearCycleMap(const uint16 fromColor, const uint16 numColorsToClear) { - bool *mapEntry = _cycleMap + fromColor; - const bool *lastEntry = _cycleMap + numColorsToClear; - while (mapEntry < lastEntry) { - *mapEntry++ = false; - } +void GfxPalette32::kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int32 ticks, const int16 fromColor, const int16 toColor) { + const Palette palette = getPaletteFromResource(paletteId); + setVary(palette, percent, ticks, fromColor, toColor); } -inline void GfxPalette32::setCycleMap(const uint16 fromColor, const uint16 numColorsToSet) { - bool *mapEntry = _cycleMap + fromColor; - const bool *lastEntry = _cycleMap + numColorsToSet; - while (mapEntry < lastEntry) { - if (*mapEntry != false) { - error("Cycles intersect"); - } - *mapEntry++ = true; - } +void GfxPalette32::kernelPalVaryMergeTarget(const GuiResourceId paletteId) { + const Palette palette = getPaletteFromResource(paletteId); + mergeTarget(palette); } -inline PalCycler *GfxPalette32::getCycler(const uint16 fromColor) { - const int numCyclers = ARRAYSIZE(_cyclers); - - for (int cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) { - PalCycler *cycler = _cyclers[cyclerIndex]; - if (cycler != nullptr && cycler->fromColor == fromColor) { - return cycler; - } - } +void GfxPalette32::kernelPalVarySetTarget(const GuiResourceId paletteId) { + const Palette palette = getPaletteFromResource(paletteId); + setTarget(palette); +} - return nullptr; +void GfxPalette32::kernelPalVarySetStart(const GuiResourceId paletteId) { + const Palette palette = getPaletteFromResource(paletteId); + setStart(palette); } -inline void doCycleInternal(PalCycler *cycler, const int16 speed) { - int16 currentCycle = cycler->currentCycle; - const uint16 numColorsToCycle = cycler->numColorsToCycle; +void GfxPalette32::kernelPalVaryMergeStart(const GuiResourceId paletteId) { + const Palette palette = getPaletteFromResource(paletteId); + mergeStart(palette); +} - if (cycler->direction == 0) { - currentCycle = (currentCycle - (speed % numColorsToCycle)) + numColorsToCycle; +void GfxPalette32::kernelPalVaryPause(const bool pause) { + if (pause) { + varyPause(); } else { - currentCycle = currentCycle + speed; + varyOn(); } - - cycler->currentCycle = (uint8) (currentCycle % numColorsToCycle); } +#pragma mark - +#pragma mark Cycling + void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay) { assert(fromColor < toColor); - int cyclerIndex; - const int numCyclers = ARRAYSIZE(_cyclers); - PalCycler *cycler = getCycler(fromColor); if (cycler != nullptr) { clearCycleMap(fromColor, cycler->numColorsToCycle); } else { - for (cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) { - if (_cyclers[cyclerIndex] == nullptr) { - cycler = new PalCycler; - _cyclers[cyclerIndex] = cycler; + for (int i = 0; i < kNumCyclers; ++i) { + if (_cyclers[i] == nullptr) { + _cyclers[i] = cycler = new PalCycler; break; } } } - // SCI engine overrides the first oldest cycler that it finds where - // “oldest” is determined by the difference between the tick and now + // If there are no free cycler slots, SCI engine overrides the first oldest + // cycler that it finds, where "oldest" is determined by the difference + // between the tick and now if (cycler == nullptr) { const uint32 now = g_sci->getTickCount(); uint32 minUpdateDelta = 0xFFFFFFFF; - for (cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) { - PalCycler *candidate = _cyclers[cyclerIndex]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const candidate = _cyclers[i]; const uint32 updateDelta = now - candidate->lastUpdateTick; if (updateDelta < minUpdateDelta) { @@ -678,11 +556,14 @@ void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const in clearCycleMap(cycler->fromColor, cycler->numColorsToCycle); } - const uint16 numColorsToCycle = 1 + ((uint8) toColor) - fromColor; - cycler->fromColor = (uint8) fromColor; - cycler->numColorsToCycle = (uint8) numColorsToCycle; - cycler->currentCycle = (uint8) fromColor; - cycler->direction = direction < 0 ? PalCycleBackward : PalCycleForward; + uint16 numColorsToCycle = toColor - fromColor; + if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE || g_sci->getGameId() == GID_KQ7) { + numColorsToCycle += 1; + } + cycler->fromColor = fromColor; + cycler->numColorsToCycle = numColorsToCycle; + cycler->currentCycle = fromColor; + cycler->direction = direction < 0 ? kPalCycleBackward : kPalCycleForward; cycler->delay = delay; cycler->lastUpdateTick = g_sci->getTickCount(); cycler->numTimesPaused = 0; @@ -691,30 +572,30 @@ void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const in } void GfxPalette32::doCycle(const uint8 fromColor, const int16 speed) { - PalCycler *cycler = getCycler(fromColor); + PalCycler *const cycler = getCycler(fromColor); if (cycler != nullptr) { cycler->lastUpdateTick = g_sci->getTickCount(); - doCycleInternal(cycler, speed); + updateCycler(*cycler, speed); } } void GfxPalette32::cycleOn(const uint8 fromColor) { - PalCycler *cycler = getCycler(fromColor); + PalCycler *const cycler = getCycler(fromColor); if (cycler != nullptr && cycler->numTimesPaused > 0) { --cycler->numTimesPaused; } } void GfxPalette32::cyclePause(const uint8 fromColor) { - PalCycler *cycler = getCycler(fromColor); + PalCycler *const cycler = getCycler(fromColor); if (cycler != nullptr) { ++cycler->numTimesPaused; } } void GfxPalette32::cycleAllOn() { - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr && cycler->numTimesPaused > 0) { --cycler->numTimesPaused; } @@ -724,8 +605,8 @@ void GfxPalette32::cycleAllOn() { void GfxPalette32::cycleAllPause() { // NOTE: The original engine did not check for null pointers in the // palette cyclers pointer array. - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr) { // This seems odd, because currentCycle is 0..numColorsPerCycle, // but fromColor is 0..255. When applyAllCycles runs, the values @@ -736,8 +617,8 @@ void GfxPalette32::cycleAllPause() { applyAllCycles(); - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr) { ++cycler->numTimesPaused; } @@ -745,8 +626,8 @@ void GfxPalette32::cycleAllPause() { } void GfxPalette32::cycleOff(const uint8 fromColor) { - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr && cycler->fromColor == fromColor) { clearCycleMap(fromColor, cycler->numColorsToCycle); delete cycler; @@ -757,8 +638,8 @@ void GfxPalette32::cycleOff(const uint8 fromColor) { } void GfxPalette32::cycleAllOff() { - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr) { clearCycleMap(cycler->fromColor, cycler->numColorsToCycle); delete cycler; @@ -767,16 +648,57 @@ void GfxPalette32::cycleAllOff() { } } +void GfxPalette32::updateCycler(PalCycler &cycler, const int16 speed) { + int16 currentCycle = cycler.currentCycle; + const uint16 numColorsToCycle = cycler.numColorsToCycle; + + if (cycler.direction == kPalCycleBackward) { + currentCycle = (currentCycle - (speed % numColorsToCycle)) + numColorsToCycle; + } else { + currentCycle = currentCycle + speed; + } + + cycler.currentCycle = currentCycle % numColorsToCycle; +} + +void GfxPalette32::clearCycleMap(const uint16 fromColor, const uint16 numColorsToClear) { + bool *mapEntry = _cycleMap + fromColor; + const bool *const lastEntry = _cycleMap + numColorsToClear; + while (mapEntry < lastEntry) { + *mapEntry++ = false; + } +} + +void GfxPalette32::setCycleMap(const uint16 fromColor, const uint16 numColorsToSet) { + bool *mapEntry = _cycleMap + fromColor; + const bool *const lastEntry = _cycleMap + numColorsToSet; + while (mapEntry < lastEntry) { + if (*mapEntry != false) { + error("Cycles intersect"); + } + *mapEntry++ = true; + } +} + +PalCycler *GfxPalette32::getCycler(const uint16 fromColor) { + for (int cyclerIndex = 0; cyclerIndex < kNumCyclers; ++cyclerIndex) { + PalCycler *cycler = _cyclers[cyclerIndex]; + if (cycler != nullptr && cycler->fromColor == fromColor) { + return cycler; + } + } + + return nullptr; +} + void GfxPalette32::applyAllCycles() { Color paletteCopy[256]; memcpy(paletteCopy, _nextPalette.colors, sizeof(Color) * 256); - for (int cyclerIndex = 0, numCyclers = ARRAYSIZE(_cyclers); cyclerIndex < numCyclers; ++cyclerIndex) { - PalCycler *cycler = _cyclers[cyclerIndex]; + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler != nullptr) { - cycler->currentCycle = (uint8) ((((int) cycler->currentCycle) + 1) % cycler->numColorsToCycle); - // Disassembly was not fully evaluated to verify this is exactly the same - // as the code from applyCycles, but it appeared to be at a glance + cycler->currentCycle = (((int) cycler->currentCycle) + 1) % cycler->numColorsToCycle; for (int j = 0; j < cycler->numColorsToCycle; j++) { _nextPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle]; } @@ -788,15 +710,16 @@ void GfxPalette32::applyCycles() { Color paletteCopy[256]; memcpy(paletteCopy, _nextPalette.colors, sizeof(Color) * 256); - for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) { - PalCycler *cycler = _cyclers[i]; + const uint32 now = g_sci->getTickCount(); + for (int i = 0; i < kNumCyclers; ++i) { + PalCycler *const cycler = _cyclers[i]; if (cycler == nullptr) { continue; } if (cycler->delay != 0 && cycler->numTimesPaused == 0) { - while ((cycler->delay + cycler->lastUpdateTick) < g_sci->getTickCount()) { - doCycleInternal(cycler, 1); + while ((cycler->delay + cycler->lastUpdateTick) < now) { + updateCycler(*cycler, 1); cycler->lastUpdateTick += cycler->delay; } } @@ -810,31 +733,31 @@ void GfxPalette32::applyCycles() { #pragma mark - #pragma mark Fading -// NOTE: There are some game scripts (like SQ6 Sierra logo and main menu) that call -// setFade with numColorsToFade set to 256, but other parts of the engine like -// processShowStyleNone use 255 instead of 256. It is not clear if this is because -// the last palette entry is intentionally left unmodified, or if this is a bug -// in the engine. It certainly seems confused because all other places that accept -// color ranges typically receive values in the range of 0–255. -void GfxPalette32::setFade(uint16 percent, uint8 fromColor, uint16 numColorsToFade) { - if (fromColor > numColorsToFade) { +void GfxPalette32::setFade(const uint16 percent, const uint8 fromColor, uint16 toColor) { + if (fromColor > toColor) { return; } - assert(numColorsToFade <= ARRAYSIZE(_fadeTable)); + // Some game scripts (like SQ6 Sierra logo and main menu) incorrectly call + // setFade with toColor set to 256 + if (toColor > 255) { + toColor = 255; + } - for (int i = fromColor; i < numColorsToFade; i++) + for (int i = fromColor; i <= toColor; i++) { _fadeTable[i] = percent; + } } void GfxPalette32::fadeOff() { - setFade(100, 0, 256); + setFade(100, 0, 255); } void GfxPalette32::applyFade() { for (int i = 0; i < ARRAYSIZE(_fadeTable); ++i) { - if (_fadeTable[i] == 100) + if (_fadeTable[i] == 100) { continue; + } Color &color = _nextPalette.colors[i]; diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h index dc2158022f..81e9bbbfd3 100644 --- a/engines/sci/graphics/palette32.h +++ b/engines/sci/graphics/palette32.h @@ -24,30 +24,33 @@ #define SCI_GRAPHICS_PALETTE32_H #include "sci/graphics/palette.h" - namespace Sci { +#pragma mark HunkPalette + /** - * HunkPalette represents a raw palette resource - * read from disk. + * HunkPalette represents a raw palette resource read from disk. The data + * structure of a HunkPalette allows palettes to be smaller than 256 colors. It + * also allows multiple palettes to be stored in one HunkPalette, though in + * SCI32 games there seems to only ever be one palette per HunkPalette. */ class HunkPalette { public: HunkPalette(byte *rawPalette); /** - * Gets the version of the palette. + * Gets the version of the palette. Used to avoid resubmitting a HunkPalette + * which has already been submitted for the next frame. */ uint32 getVersion() const { return _version; } /** * Sets the version of the palette. */ - void setVersion(const uint32 version); + void setVersion(const uint32 version) const; /** - * Converts the hunk palette to a standard - * palette. + * Converts the hunk palette to a standard Palette. */ const Palette toPalette() const; @@ -64,15 +67,14 @@ private: kEntryHeaderSize = 22, /** - * The offset of the hunk palette version - * within the palette entry header. + * The offset of the hunk palette version within the palette entry + * header. */ kEntryVersionOffset = 18 }; /** - * The header for a palette inside the - * HunkPalette. + * The header for a palette inside the HunkPalette. */ struct EntryHeader { /** @@ -81,8 +83,7 @@ private: uint8 startColor; /** - * The number of palette colors in this - * entry. + * The number of palette colors in this entry. */ uint16 numColors; @@ -92,8 +93,7 @@ private: bool used; /** - * Whether or not all palette entries - * share the same `used` value in + * Whether or not all palette entries share the same `used` value in * `defaultFlag`. */ bool sharedUsed; @@ -105,14 +105,14 @@ private: }; /** - * The version number from the last time this - * palette was submitted to GfxPalette32. + * The version number from the last time this palette was submitted to + * GfxPalette32. */ - uint32 _version; + mutable uint32 _version; /** - * The number of palettes stored in the hunk - * palette. In SCI32 games this is always 1. + * The number of palettes stored in the hunk palette. In SCI32 games this is + * always 1. */ uint8 _numPalettes; @@ -122,41 +122,44 @@ private: byte *_data; /** - * Returns a struct that describes the palette - * held by this HunkPalette. The entry header - * is reconstructed on every call from the raw - * palette data. + * Returns a struct that describes the palette held by this HunkPalette. The + * entry header is reconstructed on every call from the raw palette data. */ const EntryHeader getEntryHeader() const; /** - * Returns a pointer to the palette data within - * the hunk palette. + * Returns a pointer to the palette data within the hunk palette. */ byte *getPalPointer() const { return _data + kHunkPaletteHeaderSize + (2 * _numPalettes); } }; +#pragma mark - +#pragma mark PalCycler + enum PalCyclerDirection { - PalCycleBackward = 0, - PalCycleForward = 1 + kPalCycleBackward = 0, + kPalCycleForward = 1 }; +/** + * PalCycler represents a range of palette entries that are rotated on a timer. + */ struct PalCycler { /** - * The color index of the palette cycler. This value is effectively used as the ID for the - * cycler. + * The color index of this palette cycler. This value is used as the unique + * key for this PalCycler object. */ uint8 fromColor; /** - * The number of palette slots which are cycled by the palette cycler. + * The number of palette slots which are to be cycled by this cycler. */ uint16 numColorsToCycle; /** - * The position of the cursor in its cycle. + * The current position of the first palette entry. */ uint8 currentCycle; @@ -166,15 +169,15 @@ struct PalCycler { PalCyclerDirection direction; /** - * The cycle tick at the last time the cycler’s currentCycle was updated. - * 795 days of game time ought to be enough for everyone? :) + * The last tick the cycler cycled. */ uint32 lastUpdateTick; /** - * The amount of time in ticks each cycle should take to complete. In other words, - * the higher the delay, the slower the cycle animation. If delay is 0, the cycler - * does not automatically cycle and needs to be pumped manually with DoCycle. + * The amount of time in ticks each cycle should take to complete. In other + * words, the higher the delay, the slower the cycle animation. If delay is + * 0, the cycler does not automatically cycle and needs to be cycled + * manually by calling `doCycle`. */ int16 delay; @@ -184,17 +187,72 @@ struct PalCycler { uint16 numTimesPaused; }; +#pragma mark - +#pragma mark GfxPalette32 + class GfxPalette32 { public: GfxPalette32(ResourceManager *resMan); ~GfxPalette32(); + void saveLoadWithSerializer(Common::Serializer &s); + + /** + * Gets the palette that will be use for the next frame. + */ + inline const Palette &getNextPalette() const { return _nextPalette; }; + + /** + * Gets the palette that is used for the current frame. + */ + inline const Palette &getCurrentPalette() const { return _currentPalette; }; + + /** + * Loads a palette into GfxPalette32 with the given resource ID. + */ + bool loadPalette(const GuiResourceId resourceId); + + /** + * Finds the nearest color in the current palette matching the given RGB + * value. + */ + int16 matchColor(const uint8 r, const uint8 g, const uint8 b); + + /** + * Submits a palette to display. Entries marked as "used" in the submitted + * palette are merged into `_sourcePalette`. + */ + void submit(const Palette &palette); + void submit(const HunkPalette &palette); + + /** + * Applies all fades, cycles, remaps, and varies for the current frame to + * `nextPalette`. + */ + bool updateForFrame(); + + /** + * Copies all palette entries from `sourcePalette` to `nextPalette` and + * applies remaps. Unlike `updateForFrame`, this call does not apply fades, + * cycles, or varies. + */ + void updateFFrame(); + + /** + * Copies all entries from `nextPalette` to `currentPalette` and updates the + * backend's raw palette. + * + * @param updateScreen If true, this call will also tell the backend to draw + * to the screen. + */ + void updateHardware(const bool updateScreen = true); + private: ResourceManager *_resMan; /** - * The palette revision version. Increments once per game - * loop that changes the source palette. + * The palette revision version. Increments once per game loop that changes + * the source palette. */ uint32 _version; @@ -209,90 +267,140 @@ private: Palette _currentPalette; /** - * The unmodified source palette loaded by kPalette. Additional - * palette entries may be mixed into the source palette by - * CelObj objects, which contain their own palettes. + * The unmodified source palette loaded by kPalette. Additional palette + * entries may be mixed into the source palette by CelObj objects, which + * contain their own palettes. */ Palette _sourcePalette; /** * The palette to be used when the hardware is next updated. - * On update, _nextPalette is transferred to _currentPalette. + * On update, `_nextPalette` is transferred to `_currentPalette`. */ Palette _nextPalette; - bool createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const; - Palette getPaletteFromResourceInternal(const GuiResourceId paletteId) const; + /** + * Creates and returns a new Palette object with data from the given + * resource ID. + */ + Palette getPaletteFromResource(const GuiResourceId paletteId) const; + + /** + * Merges used colors in the `from` palette into the `to` palette. + */ + void mergePalette(Palette &to, const Palette &from); + + /** + * Applies all varies, cycles, and fades to `_nextPalette`. + */ + void applyAll(); +#pragma mark - +#pragma mark Varying public: - void saveLoadWithSerializer(Common::Serializer &s); - inline const Palette &getNextPalette() const { return _nextPalette; }; - inline const Palette &getCurrentPalette() const { return _currentPalette; }; + /** + * Blends the `target` palette into the current palette over `time` ticks. + * + * @param target The target palette. + * @param percent The amount that the target palette should be blended into + * the source palette by the end of the vary. + * @param ticks The number of ticks that it should take for the blend to be + * completed. + * @param fromColor The first palette entry that should be blended. + * @param toColor The last palette entry that should be blended. + */ + void setVary(const Palette &target, const int16 percent, const int32 ticks, const int16 fromColor, const int16 toColor); /** - * Loads a palette into GfxPalette32 with the given resource - * ID. + * Gets the current vary blend amount. */ - bool loadPalette(const GuiResourceId resourceId); + inline int16 getVaryPercent() const { return ABS(_varyPercent); } /** - * Finds the nearest color in the current palette matching the - * given RGB value. + * Changes the percentage of the current vary to `percent`, to be completed + * over `time` ticks, if there is a currently active vary target palette. */ - int16 matchColor(const uint8 r, const uint8 g, const uint8 b); + void setVaryPercent(const int16 percent, const int32 time); /** - * Submits a palette to display. Entries marked as “used” in the - * submitted palette are merged into the existing entries of - * _sourcePalette. + * Changes the amount of time, in ticks, an in-progress palette vary should + * take to finish. */ - void submit(const Palette &palette); - void submit(HunkPalette &palette); + void setVaryTime(const int32 ticks); - bool updateForFrame(); - void updateFFrame(); - void updateHardware(const bool updateScreen = true); - void applyAll(); + /** + * Changes the vary percent and time to perform the vary. + */ + void setVaryTime(const int16 percent, const int32 ticks); -#pragma mark - -#pragma mark Color look-up -private: /** - * An optional lookup table used to remap RGB565 colors to a palette - * index. Used by Phantasmagoria 2 in 8-bit color environments. + * Removes the active palette vary. */ - byte *_clutTable; + void varyOff(); -public: - bool loadClut(uint16 clutId); - byte matchClutColor(uint16 color); - void unloadClut(); + /** + * Pauses any active palette vary. + */ + void varyPause(); + + /** + * Unpauses any paused palette vary. + */ + void varyOn(); + + /** + * Sets the target palette for the blend. + */ + void setTarget(const Palette &palette); + + /** + * Sets the start palette for the blend. + */ + void setStart(const Palette &palette); + + /** + * Merges a new start palette into the existing start palette. + */ + void mergeStart(const Palette &palette); + + /** + * Merges a new target palette into the existing target palette. + */ + void mergeTarget(const Palette &palette); + + /** + * Applies any active palette vary to `_nextPalette`. + */ + void applyVary(); + + void kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int32 ticks, const int16 fromColor, const int16 toColor); + void kernelPalVaryMergeTarget(const GuiResourceId paletteId); + void kernelPalVarySetTarget(const GuiResourceId paletteId); + void kernelPalVarySetStart(const GuiResourceId paletteId); + void kernelPalVaryMergeStart(const GuiResourceId paletteId); + void kernelPalVaryPause(const bool pause); -#pragma mark - -#pragma mark Varying private: /** - * An optional palette used to describe the source colors used - * in a palette vary operation. If this palette is not specified, - * sourcePalette is used instead. + * An optional palette used to provide source colors for a palette vary + * operation. If this palette is not specified, `_sourcePalette` is used + * instead. */ Palette *_varyStartPalette; /** - * An optional palette used to describe the target colors used - * in a palette vary operation. + * An optional palette used to provide target colors for a palette vary + * operation. */ Palette *_varyTargetPalette; /** - * The minimum palette index that has been varied from the - * source palette. 0–255 + * The minimum palette index that has been varied from the source palette. */ uint8 _varyFromColor; /** - * The maximum palette index that is has been varied from the - * source palette. 0-255 + * The maximum palette index that has been varied from the source palette. */ uint8 _varyToColor; @@ -302,10 +410,10 @@ private: uint32 _varyLastTick; /** - * The amount of time to elapse, in ticks, between each cycle - * of a palette vary animation. + * The amount of time that should elapse, in ticks, between each cycle of a + * palette vary animation. */ - int _varyTime; + int32 _varyTime; /** * The direction of change: -1, 0, or 1. @@ -313,97 +421,158 @@ private: int16 _varyDirection; /** - * The amount, in percent, that the vary color is currently - * blended into the source color. + * The amount, in percent, that the vary color is currently blended into the + * source color. */ int16 _varyPercent; /** - * The target amount that a vary color will be blended into - * the source color. + * The target amount that a vary color will be blended into the source + * color. */ int16 _varyTargetPercent; /** - * The number of time palette varying has been paused. + * The number of times palette varying has been paused. */ uint16 _varyNumTimesPaused; -public: - void kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int time, const int16 fromColor, const int16 toColor); - void kernelPalVaryMergeTarget(const GuiResourceId paletteId); - void kernelPalVarySetTarget(const GuiResourceId paletteId); - void kernelPalVarySetStart(const GuiResourceId paletteId); - void kernelPalVaryMergeStart(const GuiResourceId paletteId); - void kernelPalVaryPause(bool pause); - - void setVary(const Palette *const targetPalette, const int16 percent, const int time, const int16 fromColor, const int16 toColor); - void setVaryPercent(const int16 percent, const int time, const int16 fromColor, const int16 fromColorAlternate); - int16 getVaryPercent() const; - void varyOff(); - void mergeTarget(const Palette *const palette); - void varyPause(); - void varyOn(); - void setVaryTime(const int time); - void setTarget(const Palette *const palette); - void setStart(const Palette *const palette); - void mergeStart(const Palette *const palette); - void setVaryTimeInternal(const int16 percent, const int time); - void applyVary(); - #pragma mark - #pragma mark Cycling -private: - // SQ6 defines 10 cyclers - PalCycler *_cyclers[10]; +public: + inline const bool *getCycleMap() const { return _cycleMap; } /** - * The cycle map is used to detect overlapping cyclers. - * According to SCI engine code, when two cyclers overlap, - * a fatal error has occurred and the engine will display - * an error and then exit. + * Cycle palette entries between `fromColor` and `toColor`, inclusive. + * Palette cyclers may not overlap. `fromColor` is used in other methods as + * the key for looking up a cycler. * - * The cycle map is also by the color remapping system to - * avoid attempting to remap to palette entries that are - * cycling (so won't be the expected color once the cycler - * runs again). + * @param fromColor The first color in the cycle. + * @param toColor The last color in the cycle. + * @param delay The number of ticks that should elapse between cycles. + * @param direction A negative `direction` will cycle backwards instead of + * forwards. The numeric value of this argument is ignored; + * only its sign is used to determine direction. */ - bool _cycleMap[256]; - inline void clearCycleMap(uint16 fromColor, uint16 numColorsToClear); - inline void setCycleMap(uint16 fromColor, uint16 numColorsToClear); - inline PalCycler *getCycler(uint16 fromColor); - -public: void setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay); + + /** + * Performs a round of palette cycling. + * + * @param fromColor The color key for the cycler. + * @param speed The number of entries that should be cycled this round. + */ void doCycle(const uint8 fromColor, const int16 speed); + + /** + * Unpauses the cycler starting at `fromColor`. + */ void cycleOn(const uint8 fromColor); + + /** + * Pauses the cycler starting at `fromColor`. + */ void cyclePause(const uint8 fromColor); + + /** + * Unpauses all cyclers. + */ void cycleAllOn(); + + /** + * Pauses all cyclers. + */ void cycleAllPause(); + + /** + * Removes the cycler starting at `fromColor`. + */ void cycleOff(const uint8 fromColor); + + /** + * Removes all cyclers. + */ void cycleAllOff(); + +private: + enum { + kNumCyclers = 10 + }; + + PalCycler *_cyclers[kNumCyclers]; + + /** + * Updates the `currentCycle` of the given `cycler` by `speed` entries. + */ + void updateCycler(PalCycler &cycler, const int16 speed); + + /** + * The cycle map is used to detect overlapping cyclers, and to avoid + * remapping to palette entries that are being cycled. + * + * According to SCI engine code, when two cyclers overlap, a fatal error has + * occurred and the engine will display an error and then exit. + * + * The color remapping system avoids attempts to remap to palette entries + * that are cycling because they won't be the expected color once the cycler + * updates the palette entries. + */ + bool _cycleMap[256]; + + /** + * Marks `numColorsToClear` colors starting at `fromColor` in the cycle + * map as inactive. + */ + void clearCycleMap(const uint16 fromColor, const uint16 numColorsToClear); + + /** + * Marks `numColorsToClear` colors starting at `fromColor` in the cycle + * map as active. + */ + void setCycleMap(const uint16 fromColor, const uint16 numColorsToClear); + + /** + * Gets the cycler object that starts at the given `fromColor`, or NULL if + * there is no cycler for that color. + */ + PalCycler *getCycler(const uint16 fromColor); + + /** + * Advances all cyclers by one step, regardless of whether or not it is time + * to perform another cycle. + */ void applyAllCycles(); + + /** + * Advances, by one step, only the cyclers whose time has come to cycle. + */ void applyCycles(); - inline const bool *getCycleMap() const { return _cycleMap; } #pragma mark - #pragma mark Fading -private: +public: /** - * The fade table records the expected intensity level of each pixel - * in the palette that will be displayed on the next frame. + * Sets the intensity level for a range of palette entries. An intensity of + * zero indicates total darkness. Intensity may also be set above 100 + * percent to increase the intensity of a palette entry. */ - uint16 _fadeTable[256]; + void setFade(const uint16 percent, const uint8 fromColor, const uint16 toColor); -public: /** - * Sets the intensity level for a range of palette - * entries. An intensity of zero indicates total - * darkness. Intensity may be set to over 100 percent. + * Resets the intensity of all palette entries to 100%. */ - void setFade(const uint16 percent, const uint8 fromColor, const uint16 toColor); void fadeOff(); + + /** + * Applies intensity values to the palette entries in `_nextPalette`. + */ void applyFade(); + +private: + /** + * The intensity levels of each palette entry, in percent. Defaults to 100. + */ + uint16 _fadeTable[256]; }; } // End of namespace Sci diff --git a/engines/sci/graphics/picture.cpp b/engines/sci/graphics/picture.cpp index 0025b24476..75a885da57 100644 --- a/engines/sci/graphics/picture.cpp +++ b/engines/sci/graphics/picture.cpp @@ -1004,14 +1004,14 @@ void GfxPicture::vectorFloodFill(int16 x, int16 y, byte color, byte priority, by int16 borderRight = curPort->rect.right + curPort->left - 1; int16 borderBottom = curPort->rect.bottom + curPort->top - 1; int16 curToLeft, curToRight, a_set, b_set; - + // Translate coordinates, if required (needed for Macintosh 480x300) _screen->vectorAdjustCoordinate(&borderLeft, &borderTop); _screen->vectorAdjustCoordinate(&borderRight, &borderBottom); //return; stack.push(p); - + while (stack.size()) { p = stack.pop(); if ((matchedMask = _screen->vectorIsFillMatch(p.x, p.y, matchMask, searchColor, searchPriority, searchControl, isEGA)) == 0) // already filled @@ -1239,7 +1239,7 @@ void GfxPicture::vectorPatternTexturedCircle(Common::Rect box, byte size, byte c byte bitNo = 0; const bool *textureData = &vectorPatternTextures[vectorPatternTextureOffset[texture]]; int y, x; - + for (y = box.top; y < box.bottom; y++) { for (x = box.left; x < box.right; x++) { if (bitmap & 1) { diff --git a/engines/sci/graphics/plane32.cpp b/engines/sci/graphics/plane32.cpp index 1cd88d667b..e7da8b815d 100644 --- a/engines/sci/graphics/plane32.cpp +++ b/engines/sci/graphics/plane32.cpp @@ -527,14 +527,8 @@ void Plane::decrementScreenItemArrayCounts(Plane *visiblePlane, const bool force if (item != nullptr) { // update item in visiblePlane if item is updated - if ( - item->_updated || - ( - forceUpdate && - visiblePlane != nullptr && - visiblePlane->_screenItemList.findByObject(item->_object) != nullptr - ) - ) { + if (visiblePlane != nullptr && ( + item->_updated || (forceUpdate && visiblePlane->_screenItemList.findByObject(item->_object) != nullptr))) { *visiblePlane->_screenItemList[i] = *item; } diff --git a/engines/sci/graphics/screen.cpp b/engines/sci/graphics/screen.cpp index 601ab9f09f..23e92ef6a9 100644 --- a/engines/sci/graphics/screen.cpp +++ b/engines/sci/graphics/screen.cpp @@ -37,7 +37,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { // Scale the screen, if needed _upscaledHires = GFX_SCREEN_UPSCALED_DISABLED; - + // we default to scripts running at 320x200 _scriptWidth = 320; _scriptHeight = 200; @@ -45,7 +45,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { _height = 0; _displayWidth = 0; _displayHeight = 0; - + // King's Quest 6 and Gabriel Knight 1 have hires content, gk1/cd was able // to provide that under DOS as well, but as gk1/floppy does support // upscaled hires scriptswise, but doesn't actually have the hires content @@ -58,7 +58,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { // Japanese versions of games use hi-res font on upscaled version of the game. if ((g_sci->getLanguage() == Common::JA_JPN) && (getSciVersion() <= SCI_VERSION_1_1)) _upscaledHires = GFX_SCREEN_UPSCALED_640x400; - + // Macintosh SCI0 games used 480x300, while the scripts were running at 320x200 if (g_sci->getPlatform() == Common::kPlatformMacintosh) { if (getSciVersion() <= SCI_VERSION_01) { @@ -66,7 +66,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { _width = 480; _height = 300; // regular visual, priority and control map are 480x300 (this is different than other upscaled SCI games) } - + // Some Mac SCI1/1.1 games only take up 190 rows and do not // have the menu bar. // TODO: Verify that LSL1 and LSL5 use height 190 @@ -145,7 +145,7 @@ GfxScreen::GfxScreen(ResourceManager *resMan) : _resMan(resMan) { } _displayPixels = _displayWidth * _displayHeight; - + // Allocate visual, priority, control and display screen _visualScreen = (byte *)calloc(_pixels, 1); _priorityScreen = (byte *)calloc(_pixels, 1); @@ -314,7 +314,7 @@ void GfxScreen::vectorAdjustLineCoordinates(int16 *left, int16 *top, int16 *righ void GfxScreen::vectorPutLinePixel(int16 x, int16 y, byte drawMask, byte color, byte priority, byte control) { if (_upscaledHires == GFX_SCREEN_UPSCALED_480x300) { vectorPutLinePixel480x300(x, y, drawMask, color, priority, control); - return; + return; } // For anything else forward to the regular putPixel @@ -628,7 +628,7 @@ void GfxScreen::dither(bool addToFlag) { byte color, ditheredColor; byte *visualPtr = _visualScreen; byte *displayPtr = _displayScreen; - + if (!_unditheringEnabled) { // Do dithering on visual and display-screen for (y = 0; y < _height; y++) { diff --git a/engines/sci/graphics/screen.h b/engines/sci/graphics/screen.h index 65416252f6..63ee4ed09e 100644 --- a/engines/sci/graphics/screen.h +++ b/engines/sci/graphics/screen.h @@ -289,7 +289,7 @@ public: default: break; } - + // For non-upscaled mode and 480x300 Mac put pixels directly int offset = y * _width + x; diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp index f4ed269265..9b0d390cc3 100644 --- a/engines/sci/graphics/screen_item32.cpp +++ b/engines/sci/graphics/screen_item32.cpp @@ -263,33 +263,30 @@ void ScreenItem::calcRects(const Plane &plane) { } Ratio scaleX, scaleY; - - if (_scale.signal & kScaleSignalDoScaling32) { - if (_scale.signal & kScaleSignalUseVanishingPoint) { - int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y); - scaleX = Ratio(num, 128); - scaleY = Ratio(num, 128); - } else { - scaleX = Ratio(_scale.x, 128); - scaleY = Ratio(_scale.y, 128); - } + if (_scale.signal == kScaleSignalManual) { + scaleX = Ratio(_scale.x, 128); + scaleY = Ratio(_scale.y, 128); + } else if (_scale.signal == kScaleSignalVanishingPoint) { + int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y); + scaleX = Ratio(num, 128); + scaleY = Ratio(num, 128); } if (scaleX.getNumerator() && scaleY.getNumerator()) { _screenItemRect = _insetRect; - const Ratio celToScreenX(screenWidth, celObj._scaledWidth); - const Ratio celToScreenY(screenHeight, celObj._scaledHeight); + const Ratio celToScreenX(screenWidth, celObj._xResolution); + const Ratio celToScreenY(screenHeight, celObj._yResolution); // Cel may use a coordinate system that is not the same size as the // script coordinate system (usually this means high-resolution // pictures with low-resolution scripts) - if (celObj._scaledWidth != kLowResX || celObj._scaledHeight != kLowResY) { + if (celObj._xResolution != kLowResX || celObj._yResolution != kLowResY) { // high resolution coordinates if (_useInsetRect) { - const Ratio scriptToCelX(celObj._scaledWidth, scriptWidth); - const Ratio scriptToCelY(celObj._scaledHeight, scriptHeight); + const Ratio scriptToCelX(celObj._xResolution, scriptWidth); + const Ratio scriptToCelY(celObj._yResolution, scriptHeight); mulru(_screenItemRect, scriptToCelX, scriptToCelY, 0); if (_screenItemRect.intersects(celRect)) { @@ -299,11 +296,11 @@ void ScreenItem::calcRects(const Plane &plane) { } } - int displaceX = celObj._displace.x; - int displaceY = celObj._displace.y; + int originX = celObj._origin.x; + int originY = celObj._origin.y; if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { - displaceX = celObj._width - celObj._displace.x - 1; + originX = celObj._width - celObj._origin.x - 1; } if (!scaleX.isOne() || !scaleY.isOne()) { @@ -331,13 +328,13 @@ void ScreenItem::calcRects(const Plane &plane) { } } - displaceX = (displaceX * scaleX).toInt(); - displaceY = (displaceY * scaleY).toInt(); + originX = (originX * scaleX).toInt(); + originY = (originY * scaleY).toInt(); } mulinc(_screenItemRect, celToScreenX, celToScreenY); - displaceX = (displaceX * celToScreenX).toInt(); - displaceY = (displaceY * celToScreenY).toInt(); + originX = (originX * celToScreenX).toInt(); + originY = (originY * celToScreenY).toInt(); const Ratio scriptToScreenX = Ratio(screenWidth, scriptWidth); const Ratio scriptToScreenY = Ratio(screenHeight, scriptHeight); @@ -346,8 +343,8 @@ void ScreenItem::calcRects(const Plane &plane) { _scaledPosition.x = _position.x; _scaledPosition.y = _position.y; } else { - _scaledPosition.x = (_position.x * scriptToScreenX).toInt() - displaceX; - _scaledPosition.y = (_position.y * scriptToScreenY).toInt() - displaceY; + _scaledPosition.x = (_position.x * scriptToScreenX).toInt() - originX; + _scaledPosition.y = (_position.y * scriptToScreenY).toInt() - originY; } _screenItemRect.translate(_scaledPosition.x, _scaledPosition.y); @@ -365,7 +362,7 @@ void ScreenItem::calcRects(const Plane &plane) { if (celObjPic == nullptr) { error("Expected a CelObjPic"); } - temp.translate((celObjPic->_relativePosition.x * scriptToScreenX).toInt() - displaceX, 0); + temp.translate((celObjPic->_relativePosition.x * scriptToScreenX).toInt() - originX, 0); // TODO: This is weird. int deltaX = plane._planeRect.width() - temp.right - 1 - temp.left; @@ -383,9 +380,9 @@ void ScreenItem::calcRects(const Plane &plane) { } else { // low resolution coordinates - int displaceX = celObj._displace.x; + int originX = celObj._origin.x; if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { - displaceX = celObj._width - celObj._displace.x - 1; + originX = celObj._width - celObj._origin.x - 1; } if (!scaleX.isOne() || !scaleY.isOne()) { @@ -397,8 +394,8 @@ void ScreenItem::calcRects(const Plane &plane) { _screenItemRect.bottom -= 1; } - _scaledPosition.x = _position.x - (displaceX * scaleX).toInt(); - _scaledPosition.y = _position.y - (celObj._displace.y * scaleY).toInt(); + _scaledPosition.x = _position.x - (originX * scaleX).toInt(); + _scaledPosition.y = _position.y - (celObj._origin.y * scaleY).toInt(); _screenItemRect.translate(_scaledPosition.x, _scaledPosition.y); if (_mirrorX != celObj._mirrorX && _celInfo.type == kCelTypePic) { @@ -413,7 +410,7 @@ void ScreenItem::calcRects(const Plane &plane) { if (celObjPic == nullptr) { error("Expected a CelObjPic"); } - temp.translate(celObjPic->_relativePosition.x - (displaceX * scaleX).toInt(), celObjPic->_relativePosition.y - (celObj._displace.y * scaleY).toInt()); + temp.translate(celObjPic->_relativePosition.x - (originX * scaleX).toInt(), celObjPic->_relativePosition.y - (celObj._origin.y * scaleY).toInt()); // TODO: This is weird. int deltaX = plane._gameRect.width() - temp.right - 1 - temp.left; @@ -426,7 +423,7 @@ void ScreenItem::calcRects(const Plane &plane) { _scaledPosition.y += plane._gameRect.top; _screenItemRect.translate(plane._gameRect.left, plane._gameRect.top); - if (celObj._scaledWidth != screenWidth || celObj._scaledHeight != screenHeight) { + if (celObj._xResolution != screenWidth || celObj._yResolution != screenHeight) { mulru(_scaledPosition, celToScreenX, celToScreenY); mulru(_screenItemRect, celToScreenX, celToScreenY, 1); } @@ -520,11 +517,11 @@ void ScreenItem::printDebugInfo(Console *con) const { _celInfo.color ); if (_celObj != nullptr) { - con->debugPrintf(" width %d, height %d, scaledWidth %d, scaledHeight %d\n", + con->debugPrintf(" width %d, height %d, x-resolution %d, y-resolution %d\n", _celObj->_width, _celObj->_height, - _celObj->_scaledWidth, - _celObj->_scaledHeight + _celObj->_xResolution, + _celObj->_yResolution ); } } @@ -593,34 +590,32 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { const uint16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; Ratio scaleX, scaleY; - if (_scale.signal & kScaleSignalDoScaling32) { - if (_scale.signal & kScaleSignalUseVanishingPoint) { - int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y); - scaleX = Ratio(num, 128); - scaleY = Ratio(num, 128); - } else { - scaleX = Ratio(_scale.x, 128); - scaleY = Ratio(_scale.y, 128); - } + if (_scale.signal == kScaleSignalManual) { + scaleX = Ratio(_scale.x, 128); + scaleY = Ratio(_scale.y, 128); + } else if (_scale.signal == kScaleSignalVanishingPoint) { + int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y); + scaleX = Ratio(num, 128); + scaleY = Ratio(num, 128); } if (scaleX.getNumerator() == 0 || scaleY.getNumerator() == 0) { return Common::Rect(); } - int16 displaceX = celObj._displace.x; - int16 displaceY = celObj._displace.y; + int16 originX = celObj._origin.x; + int16 originY = celObj._origin.y; if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { - displaceX = celObj._width - displaceX - 1; + originX = celObj._width - originX - 1; } - if (celObj._scaledWidth != kLowResX || celObj._scaledHeight != kLowResY) { + if (celObj._xResolution != kLowResX || celObj._yResolution != kLowResY) { // high resolution coordinates if (_useInsetRect) { - Ratio scriptToCelX(celObj._scaledWidth, scriptWidth); - Ratio scriptToCelY(celObj._scaledHeight, scriptHeight); + Ratio scriptToCelX(celObj._xResolution, scriptWidth); + Ratio scriptToCelY(celObj._yResolution, scriptHeight); mulru(nsRect, scriptToCelX, scriptToCelY, 0); if (nsRect.intersects(celObjRect)) { @@ -661,14 +656,14 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { } } - Ratio celToScriptX(scriptWidth, celObj._scaledWidth); - Ratio celToScriptY(scriptHeight, celObj._scaledHeight); + Ratio celToScriptX(scriptWidth, celObj._xResolution); + Ratio celToScriptY(scriptHeight, celObj._yResolution); - displaceX = (displaceX * scaleX * celToScriptX).toInt(); - displaceY = (displaceY * scaleY * celToScriptY).toInt(); + originX = (originX * scaleX * celToScriptX).toInt(); + originY = (originY * scaleY * celToScriptY).toInt(); mulinc(nsRect, celToScriptX, celToScriptY); - nsRect.translate(_position.x - displaceX, _position.y - displaceY); + nsRect.translate(_position.x - originX, _position.y - originY); } else { // low resolution coordinates @@ -681,9 +676,9 @@ Common::Rect ScreenItem::getNowSeenRect(const Plane &plane) const { nsRect.bottom -= 1; } - displaceX = (displaceX * scaleX).toInt(); - displaceY = (displaceY * scaleY).toInt(); - nsRect.translate(_position.x - displaceX, _position.y - displaceY); + originX = (originX * scaleX).toInt(); + originY = (originY * scaleY).toInt(); + nsRect.translate(_position.x - originX, _position.y - originY); if (_mirrorX != celObj._mirrorX && _celInfo.type != kCelTypePic) { nsRect.translate(plane._gameRect.width() - nsRect.width(), 0); diff --git a/engines/sci/graphics/screen_item32.h b/engines/sci/graphics/screen_item32.h index 4221c0ea52..009b608f18 100644 --- a/engines/sci/graphics/screen_item32.h +++ b/engines/sci/graphics/screen_item32.h @@ -30,13 +30,9 @@ namespace Sci { enum ScaleSignals32 { - kScaleSignalNone = 0, - // TODO: rename to 'manual' - kScaleSignalDoScaling32 = 1, // enables scaling when drawing that cel (involves scaleX and scaleY) - kScaleSignalUseVanishingPoint = 2, - // TODO: Is this actually a thing? I have not seen it and - // the original engine masks &3 where it uses scale signals. - kScaleSignalDisableGlobalScaling32 = 4 + kScaleSignalNone = 0, + kScaleSignalManual = 1, + kScaleSignalVanishingPoint = 2 }; struct ScaleInfo { diff --git a/engines/sci/graphics/text16.cpp b/engines/sci/graphics/text16.cpp index cb6e614657..b5dd9aee0b 100644 --- a/engines/sci/graphics/text16.cpp +++ b/engines/sci/graphics/text16.cpp @@ -83,7 +83,7 @@ void GfxText16::ClearChar(int16 chr) { } // This internal function gets called as soon as a '|' is found in a text. It -// will process the encountered code and set new font/set color. +// will process the encountered code and set new font/set color. // Returns textcode character count. int16 GfxText16::CodeProcessing(const char *&text, GuiResourceId orgFontId, int16 orgPenColor, bool doingDrawing) { const char *textCode = text; @@ -179,7 +179,7 @@ static const uint16 text16_shiftJIS_punctuation_SCI01[] = { // return max # of chars to fit maxwidth with full words, does not include // breaking space // Also adjusts text pointer to the new position for the caller -// +// // Special cases in games: // Laura Bow 2 - Credits in the game menu - all the text lines start with spaces (bug #5159) // Act 6 Coroner questionaire - the text of all control buttons has trailing spaces @@ -245,7 +245,7 @@ int16 GfxText16::GetLongest(const char *&textPtr, int16 maxWidth, GuiResourceId break; } tempWidth += _font->getCharWidth(curChar); - + // Width is too large? -> break out if (tempWidth > maxWidth) break; diff --git a/engines/sci/graphics/text16.h b/engines/sci/graphics/text16.h index 2724d97347..eb39fb2513 100644 --- a/engines/sci/graphics/text16.h +++ b/engines/sci/graphics/text16.h @@ -64,7 +64,7 @@ public: void Box(const char *text, bool show, const Common::Rect &rect, TextAlignment alignment, GuiResourceId fontId) { Box(text, 0, show, rect, alignment, fontId); } - + void DrawString(const char *text); void DrawStatus(const char *text); diff --git a/engines/sci/graphics/text32.cpp b/engines/sci/graphics/text32.cpp index 11572581ff..d142ff75c3 100644 --- a/engines/sci/graphics/text32.cpp +++ b/engines/sci/graphics/text32.cpp @@ -39,8 +39,8 @@ namespace Sci { int16 GfxText32::_defaultFontId = 0; -int16 GfxText32::_scaledWidth = 0; -int16 GfxText32::_scaledHeight = 0; +int16 GfxText32::_xResolution = 0; +int16 GfxText32::_yResolution = 0; GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts) : _segMan(segMan), @@ -52,10 +52,10 @@ GfxText32::GfxText32(SegManager *segMan, GfxCache *fonts) : _fontId = _defaultFontId; _font = _cache->getFont(_defaultFontId); - if (_scaledWidth == 0) { + if (_xResolution == 0) { // initialize the statics - _scaledWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - _scaledHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; + _xResolution = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + _yResolution = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; } } @@ -78,8 +78,8 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - Ratio scaleX(_scaledWidth, scriptWidth); - Ratio scaleY(_scaledHeight, scriptHeight); + Ratio scaleX(_xResolution, scriptWidth); + Ratio scaleY(_yResolution, scriptHeight); _width = (_width * scaleX).toInt(); _height = (_height * scaleY).toInt(); @@ -96,7 +96,7 @@ reg_t GfxText32::createFontBitmap(int16 width, int16 height, const Common::Rect _textRect = Common::Rect(); } - _segMan->allocateBitmap(&_bitmap, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false, gc); + _segMan->allocateBitmap(&_bitmap, _width, _height, _skipColor, 0, 0, _xResolution, _yResolution, 0, false, gc); erase(bitmapRect, false); @@ -120,12 +120,12 @@ reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect & int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - mulinc(_textRect, Ratio(_scaledWidth, scriptWidth), Ratio(_scaledHeight, scriptHeight)); + mulinc(_textRect, Ratio(_xResolution, scriptWidth), Ratio(_yResolution, scriptHeight)); CelObjView view(celInfo.resourceId, celInfo.loopNo, celInfo.celNo); - _skipColor = view._transparentColor; - _width = view._width * _scaledWidth / view._scaledWidth; - _height = view._height * _scaledHeight / view._scaledHeight; + _skipColor = view._skipColor; + _width = view._width * _xResolution / view._xResolution; + _height = view._height * _yResolution / view._yResolution; Common::Rect bitmapRect(_width, _height); if (_textRect.intersects(bitmapRect)) { @@ -134,7 +134,7 @@ reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect & _textRect = Common::Rect(); } - SciBitmap &bitmap = *_segMan->allocateBitmap(&_bitmap, _width, _height, _skipColor, 0, 0, _scaledWidth, _scaledHeight, 0, false, gc); + SciBitmap &bitmap = *_segMan->allocateBitmap(&_bitmap, _width, _height, _skipColor, 0, 0, _xResolution, _yResolution, 0, false, gc); // NOTE: The engine filled the bitmap pixels with 11 here, which is silly // because then it just erased the bitmap using the skip color. So we don't @@ -144,7 +144,7 @@ reg_t GfxText32::createFontBitmap(const CelInfo32 &celInfo, const Common::Rect & erase(bitmapRect, false); _backColor = backColor; - view.draw(bitmap.getBuffer(), bitmapRect, Common::Point(0, 0), false, Ratio(_scaledWidth, view._scaledWidth), Ratio(_scaledHeight, view._scaledHeight)); + view.draw(bitmap.getBuffer(), bitmapRect, Common::Point(0, 0), false, Ratio(_xResolution, view._xResolution), Ratio(_yResolution, view._yResolution)); if (_backColor != skipColor && _foreColor != skipColor) { erase(_textRect, false); @@ -183,13 +183,15 @@ void GfxText32::drawFrame(const Common::Rect &rect, const int16 size, const uint // NOTE: Not fully disassembled, but this should be right int16 rectWidth = targetRect.width(); - int16 sidesHeight = targetRect.height() - size * 2; + int16 heightRemaining = targetRect.height(); + int16 sidesHeight = heightRemaining - size * 2; int16 centerWidth = rectWidth - size * 2; int16 stride = _width - rectWidth; - for (int16 y = 0; y < size; ++y) { + for (int16 y = 0; y < size && y < heightRemaining; ++y) { memset(pixels, color, rectWidth); pixels += _width; + --heightRemaining; } for (int16 y = 0; y < sidesHeight; ++y) { for (int16 x = 0; x < size; ++x) { @@ -201,9 +203,10 @@ void GfxText32::drawFrame(const Common::Rect &rect, const int16 size, const uint } pixels += stride; } - for (int16 y = 0; y < size; ++y) { + for (int16 y = 0; y < size && y < heightRemaining; ++y) { memset(pixels, color, rectWidth); pixels += _width; + --heightRemaining; } } @@ -329,7 +332,7 @@ void GfxText32::drawText(const uint index, uint length) { void GfxText32::invertRect(const reg_t bitmapId, int16 bitmapStride, const Common::Rect &rect, const uint8 foreColor, const uint8 backColor, const bool doScaling) { Common::Rect targetRect = rect; if (doScaling) { - bitmapStride = bitmapStride * _scaledWidth / g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; + bitmapStride = bitmapStride * _xResolution / g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; targetRect = scaleRect(rect); } @@ -554,13 +557,13 @@ Common::Rect GfxText32::getTextSize(const Common::String &text, int16 maxWidth, int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - maxWidth = maxWidth * _scaledWidth / scriptWidth; + maxWidth = maxWidth * _xResolution / scriptWidth; _text = text; if (maxWidth >= 0) { if (maxWidth == 0) { - maxWidth = _scaledWidth * 3 / 5; + maxWidth = _xResolution * 3 / 5; } result.right = maxWidth; @@ -603,8 +606,8 @@ Common::Rect GfxText32::getTextSize(const Common::String &text, int16 maxWidth, if (doScaling) { // NOTE: The original engine code also scaled top/left but these are // always zero so there is no reason to do that. - result.right = ((result.right - 1) * scriptWidth + _scaledWidth - 1) / _scaledWidth + 1; - result.bottom = ((result.bottom - 1) * scriptHeight + _scaledHeight - 1) / _scaledHeight + 1; + result.right = ((result.right - 1) * scriptWidth + _xResolution - 1) / _xResolution + 1; + result.bottom = ((result.bottom - 1) * scriptHeight + _yResolution - 1) / _yResolution + 1; } return result; @@ -627,7 +630,7 @@ int16 GfxText32::getTextCount(const Common::String &text, const uint index, cons Common::Rect scaledRect(textRect); if (doScaling) { - mulinc(scaledRect, Ratio(_scaledWidth, scriptWidth), Ratio(_scaledHeight, scriptHeight)); + mulinc(scaledRect, Ratio(_xResolution, scriptWidth), Ratio(_yResolution, scriptHeight)); } Common::String oldText = _text; diff --git a/engines/sci/graphics/text32.h b/engines/sci/graphics/text32.h index 44bd48afd5..c4521a4f56 100644 --- a/engines/sci/graphics/text32.h +++ b/engines/sci/graphics/text32.h @@ -151,8 +151,8 @@ private: Common::Rect scaledRect(rect); int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - Ratio scaleX(_scaledWidth, scriptWidth); - Ratio scaleY(_scaledHeight, scriptHeight); + Ratio scaleX(_xResolution, scriptWidth); + Ratio scaleY(_yResolution, scriptHeight); mulinc(scaledRect, scaleX, scaleY); return scaledRect; } @@ -169,13 +169,13 @@ public: * The size of the x-dimension of the coordinate system * used by the text renderer. Static since it was global in SSCI. */ - static int16 _scaledWidth; + static int16 _xResolution; /** * The size of the y-dimension of the coordinate system * used by the text renderer. Static since it was global in SSCI. */ - static int16 _scaledHeight; + static int16 _yResolution; /** * The currently active font resource used to write text @@ -199,12 +199,12 @@ public: inline int scaleUpWidth(int value) const { const int scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth; - return (value * scriptWidth + _scaledWidth - 1) / _scaledWidth; + return (value * scriptWidth + _xResolution - 1) / _xResolution; } inline int scaleUpHeight(int value) const { const int scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight; - return (value * scriptHeight + _scaledHeight - 1) / _scaledHeight; + return (value * scriptHeight + _yResolution - 1) / _yResolution; } /** diff --git a/engines/sci/graphics/transitions32.cpp b/engines/sci/graphics/transitions32.cpp index 37f608da85..ee230f50a8 100644 --- a/engines/sci/graphics/transitions32.cpp +++ b/engines/sci/graphics/transitions32.cpp @@ -20,6 +20,7 @@ * */ +#include "sci/console.h" #include "sci/engine/segment.h" #include "sci/engine/seg_manager.h" #include "sci/engine/state.h" @@ -118,6 +119,7 @@ void GfxTransitions32::processShowStyles() { if (doFrameOut) { g_sci->_gfxFrameout->frameOut(true); + g_sci->getSciDebugger()->onFrame(); throttle(); } } while(continueProcessing && doFrameOut); @@ -233,92 +235,98 @@ void GfxTransitions32::kernelSetShowStyle(const uint16 argc, const reg_t planeOb } } - if (type > 0) { - if (createNewEntry) { - entry = new PlaneShowStyle; - // NOTE: SCI2.1 engine tests if allocation returned a null pointer - // but then only avoids setting currentStep if this is so. Since - // this is a nonsensical approach, we do not do that here - entry->currentStep = 0; - entry->processed = false; - entry->divisions = hasDivisions ? divisions : _defaultDivisions[type]; - entry->plane = planeObj; - entry->fadeColorRangesCount = 0; - - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - // for pixel dissolve - entry->bitmap = NULL_REG; - entry->bitmapScreenItem = nullptr; - - // for wipe - entry->screenItems.clear(); - entry->width = plane->_gameRect.width(); - entry->height = plane->_gameRect.height(); - } else { - entry->fadeColorRanges = nullptr; - if (hasFadeArray) { - // NOTE: SCI2.1mid engine does no check to verify that an array is - // successfully retrieved, and SegMan will cause a fatal error - // if we try to use a memory segment that is not an array - SciArray<reg_t> *table = _segMan->lookupArray(pFadeArray); - - uint32 rangeCount = table->getSize(); - entry->fadeColorRangesCount = rangeCount; - - // NOTE: SCI engine code always allocates memory even if the range - // table has no entries, but this does not really make sense, so - // we avoid the allocation call in this case - if (rangeCount > 0) { - entry->fadeColorRanges = new uint16[rangeCount]; - for (size_t i = 0; i < rangeCount; ++i) { - entry->fadeColorRanges[i] = table->getValue(i).toUint16(); - } + if (type == kShowStyleNone) { + if (createNewEntry == false) { + deleteShowStyle(findIteratorForPlane(planeObj)); + } + + return; + } + + if (createNewEntry) { + entry = new PlaneShowStyle; + // NOTE: SCI2.1 engine tests if allocation returned a null pointer + // but then only avoids setting currentStep if this is so. Since + // this is a nonsensical approach, we do not do that here + entry->currentStep = 0; + entry->processed = false; + entry->divisions = hasDivisions ? divisions : _defaultDivisions[type]; + entry->plane = planeObj; + entry->fadeColorRangesCount = 0; + + if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { + // for pixel dissolve + entry->bitmap = NULL_REG; + entry->bitmapScreenItem = nullptr; + + // for wipe + entry->screenItems.clear(); + entry->width = plane->_gameRect.width(); + entry->height = plane->_gameRect.height(); + } else { + entry->fadeColorRanges = nullptr; + if (hasFadeArray) { + // NOTE: SCI2.1mid engine does no check to verify that an array is + // successfully retrieved, and SegMan will cause a fatal error + // if we try to use a memory segment that is not an array + SciArray &table = *_segMan->lookupArray(pFadeArray); + + uint32 rangeCount = table.size(); + entry->fadeColorRangesCount = rangeCount; + + // NOTE: SCI engine code always allocates memory even if the range + // table has no entries, but this does not really make sense, so + // we avoid the allocation call in this case + if (rangeCount > 0) { + entry->fadeColorRanges = new uint16[rangeCount]; + for (size_t i = 0; i < rangeCount; ++i) { + entry->fadeColorRanges[i] = table.getAsInt16(i); } } } } + } - // NOTE: The original engine had no nullptr check and would just crash - // if it got to here - if (entry == nullptr) { - error("Cannot edit non-existing ShowStyle entry"); - } + // NOTE: The original engine had no nullptr check and would just crash + // if it got to here + if (entry == nullptr) { + error("Cannot edit non-existing ShowStyle entry"); + } - entry->fadeUp = isFadeUp; - entry->color = color; - entry->nextTick = g_sci->getTickCount(); - entry->type = type; - entry->animate = animate; - entry->delay = (seconds * 60 + entry->divisions - 1) / entry->divisions; + entry->fadeUp = isFadeUp; + entry->color = color; + entry->nextTick = g_sci->getTickCount(); + entry->type = type; + entry->animate = animate; + entry->delay = (seconds * 60 + entry->divisions - 1) / entry->divisions; - if (entry->delay == 0) { - error("ShowStyle has no duration"); - } + if (entry->delay == 0) { + error("ShowStyle has no duration"); + } - if (frameOutNow) { - // Creates a reference frame for the pixel dissolves to use - g_sci->_gfxFrameout->frameOut(false); - } + if (frameOutNow) { + // Creates a reference frame for the pixel dissolves to use + g_sci->_gfxFrameout->frameOut(false); + } - if (createNewEntry) { - if (getSciVersion() <= SCI_VERSION_2_1_EARLY) { - switch (entry->type) { - case kShowStyleIrisOut: - case kShowStyleIrisIn: - configure21EarlyIris(*entry, priority); - break; - case kShowStyleDissolve: - configure21EarlyDissolve(*entry, priority, plane->_gameRect); - break; - default: - // do nothing - break; - } + if (createNewEntry) { + if (getSciVersion() <= SCI_VERSION_2_1_EARLY) { + switch (entry->type) { + case kShowStyleIrisOut: + case kShowStyleIrisIn: + configure21EarlyIris(*entry, priority); + break; + case kShowStyleDissolve: + configure21EarlyDissolve(*entry, priority, plane->_gameRect); + break; + default: + // do nothing + break; } - - _showStyles.push_back(*entry); - delete entry; } + + _showStyles.push_back(*entry); + delete entry; } } @@ -940,8 +948,9 @@ void GfxTransitions32::kernelSetScroll(const reg_t planeId, const int16 deltaX, g_sci->_gfxFrameout->frameOut(true); throttle(); } - delete scroll; } + + delete scroll; } bool GfxTransitions32::processScroll(PlaneScroll &scroll) { @@ -953,7 +962,7 @@ bool GfxTransitions32::processScroll(PlaneScroll &scroll) { int deltaX = scroll.deltaX; int deltaY = scroll.deltaY; - if (((scroll.x + deltaX) * scroll.y) <= 0) { + if (((scroll.x + deltaX) * scroll.x) <= 0) { deltaX = -scroll.x; } if (((scroll.y + deltaY) * scroll.y) <= 0) { @@ -965,6 +974,10 @@ bool GfxTransitions32::processScroll(PlaneScroll &scroll) { Plane *plane = g_sci->_gfxFrameout->getPlanes().findByObject(scroll.plane); + if (plane == nullptr) { + error("[GfxTransitions32::processScroll]: Plane %04x:%04x not found", PRINT_REG(scroll.plane)); + } + if ((scroll.x == 0) && (scroll.y == 0)) { plane->deletePic(scroll.oldPictureId, scroll.newPictureId); finished = true; diff --git a/engines/sci/graphics/video32.cpp b/engines/sci/graphics/video32.cpp index 8b1d4ef32b..1db66644c8 100644 --- a/engines/sci/graphics/video32.cpp +++ b/engines/sci/graphics/video32.cpp @@ -58,7 +58,10 @@ SEQPlayer::SEQPlayer(SegManager *segMan) : void SEQPlayer::play(const Common::String &fileName, const int16 numTicks, const int16 x, const int16 y) { delete _decoder; _decoder = new SEQDecoder(numTicks); - _decoder->loadFile(fileName); + if (!_decoder->loadFile(fileName)) { + warning("[SEQPlayer::play]: Failed to load %s", fileName.c_str()); + return; + } // NOTE: In the original engine, video was output directly to the hardware, // bypassing the game's rendering engine. Instead of doing this, we use a @@ -86,9 +89,8 @@ void SEQPlayer::play(const Common::String &fileName, const int16 numTicks, const _decoder->start(); while (!g_engine->shouldQuit() && !_decoder->endOfVideo()) { + g_sci->sleep(_decoder->getTimeToNextFrame()); renderFrame(); - g_sci->getEngineState()->speedThrottler(_decoder->getTimeToNextFrame()); - g_sci->getEngineState()->_throttleTrigger = true; } _segMan->freeBitmap(_screenItem->_celInfo.bitmap); @@ -119,8 +121,8 @@ void SEQPlayer::renderFrame() const { } g_sci->_gfxFrameout->updateScreenItem(*_screenItem); - g_sci->getSciDebugger()->onFrame(); g_sci->_gfxFrameout->frameOut(true); + g_sci->getSciDebugger()->onFrame(); } #pragma mark - @@ -311,9 +313,8 @@ AVIPlayer::IOStatus AVIPlayer::play(const int16 from, const int16 to, const int1 void AVIPlayer::renderVideo() const { _decoder->start(); while (!g_engine->shouldQuit() && !_decoder->endOfVideo()) { - g_sci->getEngineState()->speedThrottler(_decoder->getTimeToNextFrame()); - g_sci->getEngineState()->_throttleTrigger = true; - if (_decoder->needsUpdate()) { + g_sci->sleep(_decoder->getTimeToNextFrame()); + while (_decoder->needsUpdate()) { renderFrame(); } } @@ -408,8 +409,8 @@ void AVIPlayer::renderFrame() const { } g_sci->_gfxFrameout->updateScreenItem(*_screenItem); - g_sci->getSciDebugger()->onFrame(); g_sci->_gfxFrameout->frameOut(true); + g_sci->getSciDebugger()->onFrame(); } else { assert(surface->format.bytesPerPixel == 4); @@ -457,9 +458,8 @@ AVIPlayer::EventFlags AVIPlayer::playUntilEvent(EventFlags flags) { break; } - g_sci->getEngineState()->speedThrottler(_decoder->getTimeToNextFrame()); - g_sci->getEngineState()->_throttleTrigger = true; - if (_decoder->needsUpdate()) { + g_sci->sleep(_decoder->getTimeToNextFrame()); + while (_decoder->needsUpdate()) { renderFrame(); } @@ -485,13 +485,6 @@ AVIPlayer::EventFlags AVIPlayer::playUntilEvent(EventFlags flags) { break; } } - - // TODO: Hot rectangles - if ((flags & kEventFlagHotRectangle) /* && event.type == SCI_EVENT_HOT_RECTANGLE */) { - warning("Hot rectangles not implemented in VMD player"); - stopFlag = kEventFlagHotRectangle; - break; - } } return stopFlag; @@ -635,7 +628,7 @@ VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, co const int32 maxFrameNo = (int32)(_decoder->getFrameCount() - 1); if ((flags & kEventFlagToFrame) && lastFrameNo > 0) { - _decoder->setEndFrame(MIN((int32)lastFrameNo, maxFrameNo)); + _decoder->setEndFrame(MIN<int32>(lastFrameNo, maxFrameNo)); } else { _decoder->setEndFrame(maxFrameNo); } @@ -645,7 +638,7 @@ VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, co if (yieldInterval == -1 && !(flags & kEventFlagToFrame)) { _yieldInterval = lastFrameNo; } else if (yieldInterval != -1) { - _yieldInterval = MIN((int32)yieldInterval, maxFrameNo); + _yieldInterval = MIN<int32>(yieldInterval, maxFrameNo); } } else { _yieldInterval = maxFrameNo; @@ -655,11 +648,11 @@ VMDPlayer::EventFlags VMDPlayer::kernelPlayUntilEvent(const EventFlags flags, co } VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { - // Flushing all the keyboard and mouse events out of the event manager to - // avoid letting any events queued from before the video started from - // accidentally activating an event callback + // Flushing all the keyboard and mouse events out of the event manager + // keeps events queued from before the start of playback from accidentally + // activating a video stop flag for (;;) { - const SciEvent event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_MOUSE_PRESS | SCI_EVENT_MOUSE_RELEASE | SCI_EVENT_QUIT); + const SciEvent event = _eventMan->getSciEvent(SCI_EVENT_KEYBOARD | SCI_EVENT_MOUSE_PRESS | SCI_EVENT_MOUSE_RELEASE | SCI_EVENT_HOT_RECTANGLE | SCI_EVENT_QUIT); if (event.type == SCI_EVENT_NONE) { break; } else if (event.type == SCI_EVENT_QUIT) { @@ -667,8 +660,6 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { } } - _decoder->pauseVideo(false); - if (flags & kEventFlagReverse) { // NOTE: This flag may not work properly since SSCI does not care // if a video has audio, but the VMD decoder does. @@ -698,12 +689,12 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { if (_doublePixels) { vmdScaleInfo.x = 256; vmdScaleInfo.y = 256; - vmdScaleInfo.signal = kScaleSignalDoScaling32; + vmdScaleInfo.signal = kScaleSignalManual; vmdRect.right += vmdRect.width(); vmdRect.bottom += vmdRect.height(); } else if (_stretchVertical) { vmdScaleInfo.y = 256; - vmdScaleInfo.signal = kScaleSignalDoScaling32; + vmdScaleInfo.signal = kScaleSignalManual; vmdRect.bottom += vmdRect.height(); } @@ -748,6 +739,14 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { g_sci->_gfxFrameout->addScreenItem(*_screenItem); + // HACK: When VMD playback is allowed to yield back to the VM, this + // causes the VMD decoder to freak out for some reason and video output + // starts jittering horribly. This problem does not happen if audio sync + // is disabled, but this causes some audible clicking between frames + // of audio (and, presumably, will cause some AV sync problems). Still, + // that's better than really bad jitter. + _decoder->setAudioSync(!(flags & kEventFlagYieldToVM)); + _decoder->start(); } @@ -758,9 +757,11 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { break; } - g_sci->getEngineState()->speedThrottler(_decoder->getTimeToNextFrame()); - g_sci->getEngineState()->_throttleTrigger = true; - if (_decoder->needsUpdate()) { + // Sleeping any more than 1/60th of a second will make the mouse feel + // very sluggish during VMD action sequences because the frame rate of + // VMDs is usually only 15fps + g_sci->sleep(MIN<uint32>(10, _decoder->getTimeToNextFrame())); + while (_decoder->needsUpdate()) { renderFrame(); } @@ -802,15 +803,13 @@ VMDPlayer::EventFlags VMDPlayer::playUntilEvent(const EventFlags flags) { } } - // TODO: Hot rectangles - if ((flags & kEventFlagHotRectangle) /* && event.type == SCI_EVENT_HOT_RECTANGLE */) { - warning("Hot rectangles not implemented in VMD player"); + event = _eventMan->getSciEvent(SCI_EVENT_HOT_RECTANGLE | SCI_EVENT_PEEK); + if ((flags & kEventFlagHotRectangle) && event.type == SCI_EVENT_HOT_RECTANGLE) { stopFlag = kEventFlagHotRectangle; break; } } - _decoder->pauseVideo(true); return stopFlag; } @@ -857,9 +856,8 @@ void VMDPlayer::renderFrame() const { } } else { g_sci->_gfxFrameout->updateScreenItem(*_screenItem); - g_sci->getSciDebugger()->onFrame(); g_sci->_gfxFrameout->frameOut(true); - g_sci->_gfxFrameout->throttle(); + g_sci->getSciDebugger()->onFrame(); } } @@ -891,7 +889,7 @@ void VMDPlayer::restrictPalette(const uint8 startColor, const int16 endColor) { // At least GK2 sends 256 as the end color, which is wrong, // but works in the original engine as the storage size is 4 bytes // and used values are clamped to 0-255 - _endColor = MIN((int16)255, endColor); + _endColor = MIN<int16>(255, endColor); } } // End of namespace Sci |