diff options
Diffstat (limited to 'engines/sci/graphics/frameout.cpp')
-rw-r--r-- | engines/sci/graphics/frameout.cpp | 2120 |
1 files changed, 746 insertions, 1374 deletions
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index ccce8ef046..fe43c75e5a 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -29,112 +29,82 @@ #include "common/system.h" #include "common/textconsole.h" #include "engines/engine.h" +#include "engines/util.h" #include "graphics/palette.h" #include "graphics/surface.h" #include "sci/sci.h" #include "sci/console.h" +#include "sci/engine/features.h" #include "sci/engine/kernel.h" #include "sci/engine/state.h" #include "sci/engine/selector.h" #include "sci/engine/vm.h" #include "sci/graphics/cache.h" -#include "sci/graphics/coordadjuster.h" #include "sci/graphics/compare.h" +#include "sci/graphics/cursor32.h" #include "sci/graphics/font.h" -#include "sci/graphics/view.h" -#include "sci/graphics/screen.h" +#include "sci/graphics/frameout.h" #include "sci/graphics/paint32.h" #include "sci/graphics/palette32.h" -#include "sci/graphics/picture.h" -#include "sci/graphics/text32.h" #include "sci/graphics/plane32.h" +#include "sci/graphics/remap32.h" +#include "sci/graphics/screen.h" #include "sci/graphics/screen_item32.h" +#include "sci/graphics/text32.h" #include "sci/graphics/frameout.h" -#include "sci/video/robot_decoder.h" +#include "sci/graphics/transitions32.h" +#include "sci/graphics/video32.h" namespace Sci { -// TODO/FIXME: This is partially guesswork - -static int dissolveSequences[2][20] = { - /* SCI2.1early- */ { 3, 6, 12, 20, 48, 96, 184, 272, 576, 1280, 3232, 6912, 13568, 24576, 46080 }, - /* SCI2.1mid+ */ { 0, 0, 3, 6, 12, 20, 48, 96, 184, 272, 576, 1280, 3232, 6912, 13568, 24576, 46080, 73728, 132096, 466944 } -}; -static int16 divisionsDefaults[2][16] = { - /* SCI2.1early- */ { 1, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 40, 40, 101, 101 }, - /* SCI2.1mid+ */ { 1, 20, 20, 20, 20, 10, 10, 10, 10, 20, 20, 6, 10, 101, 101, 2 } -}; -static int16 unknownCDefaults[2][16] = { - /* SCI2.1early- */ { 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 0, 0, 0, 0 }, - /* SCI2.1mid+ */ { 0, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 0, 0, 7, 7, 0 } -}; - -GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxCache *cache, GfxScreen *screen, GfxPalette32 *palette, GfxPaint32 *paint32) : - _isHiRes(false), - _cache(cache), +GfxFrameout::GfxFrameout(SegManager *segMan, GfxPalette32 *palette, GfxTransitions32 *transitions, GfxCursor32 *cursor) : + _isHiRes(gameIsHiRes()), _palette(palette), - _resMan(resMan), - _screen(screen), + _cursor(cursor), _segMan(segMan), - _paint32(paint32), - _showStyles(nullptr), - // TODO: Stop using _gfxScreen - _currentBuffer(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr), - _priorityMap(screen->getDisplayWidth(), screen->getDisplayHeight(), nullptr), + _transitions(transitions), + _benchmarkingFinished(false), + _throttleFrameOut(true), + _throttleState(0), _remapOccurred(false), _frameNowVisible(false), - _screenRect(screen->getDisplayWidth(), screen->getDisplayHeight()), _overdrawThreshold(0), _palMorphIsOn(false) { - _currentBuffer.setPixels(calloc(1, screen->getDisplayWidth() * screen->getDisplayHeight())); - - for (int i = 0; i < 236; i += 2) { - _styleRanges[i] = 0; - _styleRanges[i + 1] = -1; - } - for (int i = 236; i < ARRAYSIZE(_styleRanges); ++i) { - _styleRanges[i] = 0; - } - - // TODO: Make hires detection work uniformly across all SCI engine - // versions (this flag is normally passed by SCI::MakeGraphicsMgr - // to the GraphicsMgr constructor depending upon video configuration, - // so should be handled upstream based on game configuration instead - // of here) - if (getSciVersion() >= SCI_VERSION_2_1_EARLY && _resMan->detectHires()) { - _isHiRes = true; - } - - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - _dissolveSequenceSeeds = dissolveSequences[0]; - _defaultDivisions = divisionsDefaults[0]; - _defaultUnknownC = unknownCDefaults[0]; + if (g_sci->getGameId() == GID_PHANTASMAGORIA) { + _currentBuffer = Buffer(630, 450, nullptr); + } else if (_isHiRes) { + _currentBuffer = Buffer(640, 480, nullptr); } else { - _dissolveSequenceSeeds = dissolveSequences[1]; - _defaultDivisions = divisionsDefaults[1]; - _defaultUnknownC = unknownCDefaults[1]; - } - - // TODO: Nothing in the renderer really uses this. Currently, - // the cursor renderer does, and kLocalToGlobal/kGlobalToLocal - // do, but in the real engine (1) the cursor is handled in - // frameOut, and (2) functions do a very simple lookup of the - // plane and arithmetic with the plane's gameRect. In - // principle, CoordAdjuster could be reused for - // convertGameRectToPlaneRect, but it is not super clear yet - // what the benefit would be to do that. - _coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster; - - // TODO: Script resolution is hard-coded per game; - // also this must be set or else the engine will crash - _coordAdjuster->setScriptsResolution(_currentBuffer.scriptWidth, _currentBuffer.scriptHeight); + _currentBuffer = Buffer(320, 200, nullptr); + } + _currentBuffer.setPixels(calloc(1, _currentBuffer.screenWidth * _currentBuffer.screenHeight)); + _screenRect = Common::Rect(_currentBuffer.screenWidth, _currentBuffer.screenHeight); + initGraphics(_currentBuffer.screenWidth, _currentBuffer.screenHeight, _isHiRes); + + switch (g_sci->getGameId()) { + case GID_HOYLE5: + case GID_GK2: + case GID_LIGHTHOUSE: + case GID_LSL7: + case GID_PHANTASMAGORIA2: + case GID_PQSWAT: + case GID_TORIN: + case GID_RAMA: + _currentBuffer.scriptWidth = 640; + _currentBuffer.scriptHeight = 480; + break; + default: + // default script width for other games is 320x200 + break; + } } GfxFrameout::~GfxFrameout() { - CelObj::deinit(); clear(); + CelObj::deinit(); + free(_currentBuffer.getPixels()); } void GfxFrameout::run() { @@ -172,7 +142,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); @@ -239,22 +209,115 @@ 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 + +bool GfxFrameout::checkForFred(const reg_t object) { + const int16 viewId = readSelectorValue(_segMan, object, SELECTOR(view)); + const SciGameId gameId = g_sci->getGameId(); + + if (gameId == GID_QFG4 && viewId == 9999) { + return true; + } + + if (gameId != GID_QFG4 && viewId == -556) { + return true; + } + + if (Common::String(_segMan->getObjectName(object)) == "fred") { + return true; + } + + return false; +} + #pragma mark - #pragma mark Screen items +void GfxFrameout::addScreenItem(ScreenItem &screenItem) const { + Plane *plane = _planes.findByObject(screenItem._plane); + if (plane == nullptr) { + error("GfxFrameout::addScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x", PRINT_REG(screenItem._plane), PRINT_REG(screenItem._object)); + } + plane->_screenItemList.add(&screenItem); +} + +void GfxFrameout::updateScreenItem(ScreenItem &screenItem) const { + // TODO: In SCI3+ this will need to go through Plane +// Plane *plane = _planes.findByObject(screenItem._plane); +// if (plane == nullptr) { +// error("GfxFrameout::updateScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x", PRINT_REG(screenItem._plane), PRINT_REG(screenItem._object)); +// } + + screenItem.update(); +} + +void GfxFrameout::deleteScreenItem(ScreenItem &screenItem) { + Plane *plane = _planes.findByObject(screenItem._plane); + if (plane == nullptr) { + error("GfxFrameout::deleteScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x", PRINT_REG(screenItem._plane), PRINT_REG(screenItem._object)); + } + if (plane->_screenItemList.findByObject(screenItem._object) == nullptr) { + error("GfxFrameout::deleteScreenItem: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(screenItem._object), PRINT_REG(screenItem._plane)); + } + deleteScreenItem(screenItem, *plane); +} + +void GfxFrameout::deleteScreenItem(ScreenItem &screenItem, Plane &plane) { + if (screenItem._created == 0) { + screenItem._created = 0; + screenItem._updated = 0; + screenItem._deleted = getScreenCount(); + } else { + plane._screenItemList.erase(&screenItem); + plane._screenItemList.pack(); + } +} + +void GfxFrameout::deleteScreenItem(ScreenItem &screenItem, const reg_t planeObject) { + Plane *plane = _planes.findByObject(planeObject); + if (plane == nullptr) { + error("GfxFrameout::deleteScreenItem: Could not find plane %04x:%04x for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(screenItem._object)); + } + deleteScreenItem(screenItem, *plane); +} + void GfxFrameout::kernelAddScreenItem(const reg_t object) { - const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); + // The "fred" object is used to test graphics performance; + // it is impacted by framerate throttling, so disable the + // throttling when this item is on the screen for the + // performance check to pass. + if (!_benchmarkingFinished && _throttleFrameOut && checkForFred(object)) { + _throttleFrameOut = false; + } -// TODO: Remove -// debug("Adding screen item %04x:%04x to plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject)); + const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); _segMan->getObject(object)->setInfoSelectorFlag(kInfoFlagViewInserted); Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - warning("screen item %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid plane selector passed to kAddScreenItem"); + error("kAddScreenItem: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(object)); } ScreenItem *screenItem = plane->_screenItemList.findByObject(object); @@ -272,53 +335,44 @@ void GfxFrameout::kernelUpdateScreenItem(const reg_t object) { const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - warning("screen item %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid plane selector passed to kUpdateScreenItem"); + error("kUpdateScreenItem: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(object)); } ScreenItem *screenItem = plane->_screenItemList.findByObject(object); if (screenItem == nullptr) { - warning("screen item %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid screen item passed to kUpdateScreenItem"); + error("kUpdateScreenItem: Screen item %04x:%04x not found in plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject)); } screenItem->update(object); } else { - warning("TODO: Magnifier view not implemented yet!"); + error("Magnifier view is not known to be used by any game. Please submit a bug report with details about the game you were playing and what you were doing that triggered this error. Thanks!"); } } void GfxFrameout::kernelDeleteScreenItem(const reg_t object) { + // The "fred" object is used to test graphics performance; + // it is impacted by framerate throttling, so disable the + // throttling when this item is on the screen for the + // performance check to pass. + if (!_benchmarkingFinished && checkForFred(object)) { + _benchmarkingFinished = true; + _throttleFrameOut = true; + } + _segMan->getObject(object)->clearInfoSelectorFlag(kInfoFlagViewInserted); const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - // TODO: Remove -// warning("Invalid plane selector %04x:%04x passed to kDeleteScreenItem (real engine ignores this)", PRINT_REG(object)); return; } -// TODO: Remove -// debug("Deleting screen item %04x:%04x from plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject)); - ScreenItem *screenItem = plane->_screenItemList.findByObject(object); if (screenItem == nullptr) { -// TODO: Remove -// warning("Invalid screen item %04x:%04x passed to kDeleteScreenItem (real engine ignores this)", PRINT_REG(object)); return; } - if (screenItem->_created == 0) { - screenItem->_created = 0; - screenItem->_updated = 0; - screenItem->_deleted = getScreenCount(); - } else { - plane->_screenItemList.erase(screenItem); - plane->_screenItemList.pack(); - } + deleteScreenItem(*screenItem, *plane); } #pragma mark - @@ -338,8 +392,7 @@ void GfxFrameout::kernelAddPlane(const reg_t object) { void GfxFrameout::kernelUpdatePlane(const reg_t object) { Plane *plane = _planes.findByObject(object); if (plane == nullptr) { - warning("plane %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - error("Invalid plane selector passed to kUpdatePlane"); + error("kUpdatePlane: Plane %04x:%04x not found", PRINT_REG(object)); } plane->update(object); @@ -349,8 +402,7 @@ void GfxFrameout::kernelUpdatePlane(const reg_t object) { void GfxFrameout::kernelDeletePlane(const reg_t object) { Plane *plane = _planes.findByObject(object); if (plane == nullptr) { - warning("plane %x:%x (%s)", object.getSegment(), object.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(object)); - error("Invalid plane selector passed to kDeletePlane"); + error("kDeletePlane: Plane %04x:%04x not found", PRINT_REG(object)); } if (plane->_created) { @@ -358,13 +410,53 @@ void GfxFrameout::kernelDeletePlane(const reg_t object) { // just ends up doing this anyway so we skip the extra indirection _planes.erase(plane); } else { - // TODO: Remove -// debug("Deleting plane %04x:%04x", PRINT_REG(object)); plane->_created = 0; plane->_deleted = g_sci->_gfxFrameout->getScreenCount(); } } +void GfxFrameout::deletePlane(Plane &planeToFind) { + Plane *plane = _planes.findByObject(planeToFind._object); + if (plane == nullptr) { + error("deletePlane: Plane %04x:%04x not found", PRINT_REG(planeToFind._object)); + } + + if (plane->_created) { + _planes.erase(plane); + } else { + plane->_created = 0; + plane->_moved = 0; + plane->_deleted = getScreenCount(); + } +} + +void GfxFrameout::kernelMovePlaneItems(const reg_t object, const int16 deltaX, const int16 deltaY, const bool scrollPics) { + Plane *plane = _planes.findByObject(object); + if (plane == nullptr) { + error("kMovePlaneItems: Plane %04x:%04x not found", PRINT_REG(object)); + } + + plane->scrollScreenItems(deltaX, deltaY, scrollPics); + + for (ScreenItemList::iterator it = plane->_screenItemList.begin(); it != plane->_screenItemList.end(); ++it) { + ScreenItem &screenItem = **it; + + // If object is a number, the screen item from the + // engine, not a script, and should be ignored + if (screenItem._object.isNumber()) { + continue; + } + + if (deltaX != 0) { + writeSelectorValue(_segMan, screenItem._object, SELECTOR(x), readSelectorValue(_segMan, screenItem._object, SELECTOR(x)) + deltaX); + } + + if (deltaY != 0) { + writeSelectorValue(_segMan, screenItem._object, SELECTOR(y), readSelectorValue(_segMan, screenItem._object, SELECTOR(y)) + deltaY); + } + } +} + int16 GfxFrameout::kernelGetHighPlanePri() { return _planes.getTopSciPlanePriority(); } @@ -398,23 +490,24 @@ void GfxFrameout::updatePlane(Plane &plane) { #pragma mark - #pragma mark Pics -void GfxFrameout::kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 x, const int16 y, const bool mirrorX) { +void GfxFrameout::kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 x, const int16 y, const bool mirrorX, const bool deleteDuplicate) { Plane *plane = _planes.findByObject(planeObject); if (plane == nullptr) { - warning("plane %x:%x (%s)", planeObject.getSegment(), planeObject.getOffset(), g_sci->getEngineState()->_segMan->getObjectName(planeObject)); - error("Invalid plane selector passed to kAddPicAt"); + error("kAddPicAt: Plane %04x:%04x not found", PRINT_REG(planeObject)); } - plane->addPic(pictureId, Common::Point(x, y), mirrorX); + plane->addPic(pictureId, Common::Point(x, y), mirrorX, deleteDuplicate); } #pragma mark - #pragma mark Rendering -void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) { -// TODO: Robot -// if (_robot != nullptr) { -// _robot.doRobot(); -// } +void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &eraseRect) { + RobotDecoder &robotPlayer = g_sci->_video32->getRobotPlayer(); + const bool robotIsActive = robotPlayer.getStatus() != RobotDecoder::kRobotStatusUninitialized; + + if (robotIsActive) { + robotPlayer.doRobot(); + } // NOTE: The original engine allocated these as static arrays of 100 // pointers to ScreenItemList / RectList @@ -424,12 +517,11 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) screenItemLists.resize(_planes.size()); eraseLists.resize(_planes.size()); - // _numActiveRemaps was a global in SCI engine - if (/* TODO Remap::_numActiveRemaps > 0 */ false && _remapOccurred) { - // remapMarkRedraw(); + if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { + remapMarkRedraw(); } - calcLists(screenItemLists, eraseLists, rect); + calcLists(screenItemLists, eraseLists, eraseRect); for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { list->sort(); @@ -453,12 +545,11 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) drawScreenItemList(screenItemLists[i]); } -// TODO: Robot -// if (_robot != nullptr) { -// _robot->frameAlmostVisible(); -// } + if (robotIsActive) { + robotPlayer.frameAlmostVisible(); + } - _palette->updateHardware(); + _palette->updateHardware(!shouldShowBits); if (shouldShowBits) { showBits(); @@ -466,136 +557,335 @@ void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) _frameNowVisible = true; -// TODO: Robot -// if (_robot != nullptr) { -// robot->frameNowVisible(); -// } + if (robotIsActive) { + robotPlayer.frameNowVisible(); + } +} + +void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, PlaneShowStyle *showStyle) { + Palette sourcePalette(_palette->getNextPalette()); + alterVmap(sourcePalette, sourcePalette, -1, styleRanges); + + int16 prevRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][kGlobalVarPreviousRoomNo].toSint16(); + + Common::Rect rect(_currentBuffer.screenWidth, _currentBuffer.screenHeight); + _showList.add(rect); + showBits(); + + // NOTE: The original engine allocated these as static arrays of 100 + // pointers to ScreenItemList / RectList + ScreenItemListList screenItemLists; + EraseListList eraseLists; + + screenItemLists.resize(_planes.size()); + eraseLists.resize(_planes.size()); + + if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { + remapMarkRedraw(); + } + + calcLists(screenItemLists, eraseLists); + for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { + list->sort(); + } + + for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { + for (DrawList::iterator drawItem = list->begin(); drawItem != list->end(); ++drawItem) { + (*drawItem)->screenItem->getCelObj().submitPalette(); + } + } + + _remapOccurred = _palette->updateForFrame(); + _frameNowVisible = false; + + for (PlaneList::size_type i = 0; i < _planes.size(); ++i) { + drawEraseList(eraseLists[i], *_planes[i]); + drawScreenItemList(screenItemLists[i]); + } + + Palette nextPalette(_palette->getNextPalette()); + + if (prevRoom < 1000) { + for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) { + if (styleRanges[i] == -1 || styleRanges[i] == 0) { + sourcePalette.colors[i] = nextPalette.colors[i]; + sourcePalette.colors[i].used = true; + } + } + } else { + for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) { + if (styleRanges[i] == -1 || validZeroStyle(styleRanges[i], i)) { + sourcePalette.colors[i] = nextPalette.colors[i]; + sourcePalette.colors[i].used = true; + } + } + } + + _palette->submit(sourcePalette); + _palette->updateFFrame(); + _palette->updateHardware(); + alterVmap(nextPalette, sourcePalette, 1, _transitions->_styleRanges); + + if (showStyle && showStyle->type != kShowStyleMorph) { + _transitions->processEffects(*showStyle); + } else { + showBits(); + } + + _frameNowVisible = true; + + for (PlaneList::iterator plane = _planes.begin(); plane != _planes.end(); ++plane) { + (*plane)->_redrawAllCount = getScreenCount(); + } + + if (g_sci->_gfxRemap32->getRemapCount() > 0 && _remapOccurred) { + remapMarkRedraw(); + } + + calcLists(screenItemLists, eraseLists); + for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { + list->sort(); + } + + for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { + for (DrawList::iterator drawItem = list->begin(); drawItem != list->end(); ++drawItem) { + (*drawItem)->screenItem->getCelObj().submitPalette(); + } + } + + _remapOccurred = _palette->updateForFrame(); + // NOTE: During this second loop, `_frameNowVisible = false` is + // inside the next loop in SCI2.1mid + _frameNowVisible = false; + + for (PlaneList::size_type i = 0; i < _planes.size(); ++i) { + drawEraseList(eraseLists[i], *_planes[i]); + drawScreenItemList(screenItemLists[i]); + } + + _palette->submit(nextPalette); + _palette->updateFFrame(); + _palette->updateHardware(false); + showBits(); + + _frameNowVisible = true; } -// Determine the parts of 'r' that aren't overlapped by 'other'. -// Returns -1 if r and other have no intersection. -// Returns number of returned parts (in outRects) otherwise. -// (In particular, this returns 0 if r is contained in other.) +/** + * Determines the parts of `r` that aren't overlapped by `other`. + * Returns -1 if `r` and `other` have no intersection. + * Returns number of returned parts (in `outRects`) otherwise. + * (In particular, this returns 0 if `r` is contained in `other`.) + */ int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]) { if (!r.intersects(other)) { return -1; } - int count = 0; + int splitCount = 0; if (r.top < other.top) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.bottom = other.top; r.top = other.top; } if (r.bottom > other.bottom) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.top = other.bottom; r.bottom = other.bottom; } if (r.left < other.left) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.right = other.left; r.left = other.left; } if (r.right > other.right) { - Common::Rect &t = outRects[count++]; + Common::Rect &t = outRects[splitCount++]; t = r; t.left = other.right; } - return count; + return splitCount; } -void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &calcRect) { - RectList rectlist; - Common::Rect outRects[4]; +/** + * Determines the parts of `middleRect` that aren't overlapped + * by `showRect`, optimised for contiguous memory writes. + * Returns -1 if `middleRect` and `showRect` have no intersection. + * Returns number of returned parts (in `outRects`) otherwise. + * (In particular, this returns 0 if `middleRect` is contained + * in `other`.) + * + * `middleRect` is modified directly to extend into the upper + * and lower rects. + */ +int splitRectsForRender(Common::Rect &middleRect, const Common::Rect &showRect, Common::Rect(&outRects)[2]) { + if (!middleRect.intersects(showRect)) { + return -1; + } + + const int16 minLeft = MIN(middleRect.left, showRect.left); + const int16 maxRight = MAX(middleRect.right, showRect.right); + + int16 upperLeft, upperTop, upperRight, upperMaxTop; + if (middleRect.top < showRect.top) { + upperLeft = middleRect.left; + upperTop = middleRect.top; + upperRight = middleRect.right; + upperMaxTop = showRect.top; + } + else { + upperLeft = showRect.left; + upperTop = showRect.top; + upperRight = showRect.right; + upperMaxTop = middleRect.top; + } + + int16 lowerLeft, lowerRight, lowerBottom, lowerMinBottom; + if (middleRect.bottom > showRect.bottom) { + lowerLeft = middleRect.left; + lowerRight = middleRect.right; + lowerBottom = middleRect.bottom; + lowerMinBottom = showRect.bottom; + } else { + lowerLeft = showRect.left; + lowerRight = showRect.right; + lowerBottom = showRect.bottom; + lowerMinBottom = middleRect.bottom; + } + + int splitCount = 0; + middleRect.left = minLeft; + middleRect.top = upperMaxTop; + middleRect.right = maxRight; + middleRect.bottom = lowerMinBottom; + + if (upperTop != upperMaxTop) { + Common::Rect &upperRect = outRects[0]; + upperRect.left = upperLeft; + upperRect.top = upperTop; + upperRect.right = upperRight; + upperRect.bottom = upperMaxTop; + + // Merge upper rect into middle rect if possible + if (upperRect.left == middleRect.left && upperRect.right == middleRect.right) { + middleRect.top = upperRect.top; + } else { + ++splitCount; + } + } + + if (lowerBottom != lowerMinBottom) { + Common::Rect &lowerRect = outRects[splitCount]; + lowerRect.left = lowerLeft; + lowerRect.top = lowerMinBottom; + lowerRect.right = lowerRight; + lowerRect.bottom = lowerBottom; + + // Merge lower rect into middle rect if possible + if (lowerRect.left == middleRect.left && lowerRect.right == middleRect.right) { + middleRect.bottom = lowerRect.bottom; + } else { + ++splitCount; + } + } + + assert(splitCount <= 2); + return splitCount; +} +// NOTE: The third rectangle parameter is only ever given a non-empty rect +// by VMD code, via `frameOut` +void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &eraseRect) { + RectList eraseList; + Common::Rect outRects[4]; int deletedPlaneCount = 0; - bool addedToRectList = false; - int planeCount = _planes.size(); + bool addedToEraseList = false; bool foundTransparentPlane = false; - if (!calcRect.isEmpty()) { - addedToRectList = true; - rectlist.add(calcRect); + if (!eraseRect.isEmpty()) { + addedToEraseList = true; + eraseList.add(eraseRect); } - for (int outerPlaneIndex = 0; outerPlaneIndex < planeCount; ++outerPlaneIndex) { - Plane *outerPlane = _planes[outerPlaneIndex]; + PlaneList::size_type planeCount = _planes.size(); + for (PlaneList::size_type outerPlaneIndex = 0; outerPlaneIndex < planeCount; ++outerPlaneIndex) { + const Plane *outerPlane = _planes[outerPlaneIndex]; + const Plane *visiblePlane = _visiblePlanes.findByObject(outerPlane->_object); + // NOTE: SSCI only ever checks for kPlaneTypeTransparent here, even + // though kPlaneTypeTransparentPicture is also a transparent plane if (outerPlane->_type == kPlaneTypeTransparent) { foundTransparentPlane = true; } - Plane *visiblePlane = _visiblePlanes.findByObject(outerPlane->_object); - if (outerPlane->_deleted) { - if (visiblePlane != nullptr) { - if (!visiblePlane->_screenRect.isEmpty()) { - addedToRectList = true; - rectlist.add(visiblePlane->_screenRect); - } + if (visiblePlane != nullptr && !visiblePlane->_screenRect.isEmpty()) { + eraseList.add(visiblePlane->_screenRect); + addedToEraseList = true; } ++deletedPlaneCount; - } else if (visiblePlane != nullptr) { - if (outerPlane->_updated) { - --outerPlane->_updated; - - int splitcount = splitRects(visiblePlane->_screenRect, outerPlane->_screenRect, outRects); - if (splitcount) { - if (splitcount == -1) { - if (!visiblePlane->_screenRect.isEmpty()) { - rectlist.add(visiblePlane->_screenRect); - } + } else if (visiblePlane != nullptr && outerPlane->_moved) { + // _moved will be decremented in the final loop through the planes, + // at the end of this function + + { + const int splitCount = splitRects(visiblePlane->_screenRect, outerPlane->_screenRect, outRects); + if (splitCount) { + if (splitCount == -1 && !visiblePlane->_screenRect.isEmpty()) { + eraseList.add(visiblePlane->_screenRect); } else { - for (int i = 0; i < splitcount; ++i) { - rectlist.add(outRects[i]); + for (int i = 0; i < splitCount; ++i) { + eraseList.add(outRects[i]); } } - - addedToRectList = true; + addedToEraseList = true; } + } - if (!outerPlane->_redrawAllCount) { - int splitCount = splitRects(outerPlane->_screenRect, visiblePlane->_screenRect, outRects); - if (splitCount) { - for (int i = 0; i < splitCount; ++i) { - rectlist.add(outRects[i]); - } - addedToRectList = true; + if (!outerPlane->_redrawAllCount) { + const int splitCount = splitRects(outerPlane->_screenRect, visiblePlane->_screenRect, outRects); + if (splitCount) { + for (int i = 0; i < splitCount; ++i) { + eraseList.add(outRects[i]); } + addedToEraseList = true; } } } - if (addedToRectList) { - for (RectList::iterator rect = rectlist.begin(); rect != rectlist.end(); ++rect) { - for (int innerPlaneIndex = _planes.size() - 1; innerPlaneIndex >= 0; --innerPlaneIndex) { - Plane *innerPlane = _planes[innerPlaneIndex]; - - if (!innerPlane->_deleted && innerPlane->_type != kPlaneTypeTransparent && innerPlane->_screenRect.intersects(**rect)) { - if (innerPlane->_redrawAllCount == 0) { - eraseLists[innerPlaneIndex].add(innerPlane->_screenRect.findIntersectingRect(**rect)); + if (addedToEraseList) { + for (RectList::size_type rectIndex = 0; rectIndex < eraseList.size(); ++rectIndex) { + const Common::Rect &rect = *eraseList[rectIndex]; + for (int innerPlaneIndex = planeCount - 1; innerPlaneIndex >= 0; --innerPlaneIndex) { + const Plane &innerPlane = *_planes[innerPlaneIndex]; + + if ( + !innerPlane._deleted && + innerPlane._type != kPlaneTypeTransparent && + innerPlane._screenRect.intersects(rect) + ) { + if (!innerPlane._redrawAllCount) { + eraseLists[innerPlaneIndex].add(innerPlane._screenRect.findIntersectingRect(rect)); } - int splitCount = splitRects(**rect, innerPlane->_screenRect, outRects); + const int splitCount = splitRects(rect, innerPlane._screenRect, outRects); for (int i = 0; i < splitCount; ++i) { - rectlist.add(outRects[i]); + eraseList.add(outRects[i]); } - rectlist.erase(rect); + eraseList.erase_at(rectIndex); break; } } } - rectlist.pack(); + eraseList.pack(); } } @@ -607,9 +897,9 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL if (plane->_deleted) { --plane->_deleted; if (plane->_deleted <= 0) { - PlaneList::iterator visiblePlaneIt = Common::find_if(_visiblePlanes.begin(), _visiblePlanes.end(), FindByObject<Plane *>(plane->_object)); - if (visiblePlaneIt != _visiblePlanes.end()) { - _visiblePlanes.erase(visiblePlaneIt); + const int visiblePlaneIndex = _visiblePlanes.findIndexByObject(plane->_object); + if (visiblePlaneIndex != -1) { + _visiblePlanes.remove_at(visiblePlaneIndex); } _planes.remove_at(planeIndex); @@ -624,103 +914,118 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL } } + // Some planes may have been deleted, so re-retrieve count planeCount = _planes.size(); - for (int outerIndex = 0; outerIndex < planeCount; ++outerIndex) { - // "outer" just refers to the outer loop - Plane *outerPlane = _planes[outerIndex]; - if (outerPlane->_priorityChanged) { - --outerPlane->_priorityChanged; - Plane *visibleOuterPlane = _visiblePlanes.findByObject(outerPlane->_object); + for (PlaneList::size_type outerIndex = 0; outerIndex < planeCount; ++outerIndex) { + // "outer" just refers to the outer loop + Plane &outerPlane = *_planes[outerIndex]; + if (outerPlane._priorityChanged) { + --outerPlane._priorityChanged; + + const Plane *visibleOuterPlane = _visiblePlanes.findByObject(outerPlane._object); + if (visibleOuterPlane == nullptr) { + warning("calcLists could not find visible plane for %04x:%04x", PRINT_REG(outerPlane._object)); + continue; + } - rectlist.add(outerPlane->_screenRect.findIntersectingRect(visibleOuterPlane->_screenRect)); + eraseList.add(outerPlane._screenRect.findIntersectingRect(visibleOuterPlane->_screenRect)); - for (int innerIndex = planeCount - 1; innerIndex >= 0; --innerIndex) { + for (int innerIndex = (int)planeCount - 1; innerIndex >= 0; --innerIndex) { // "inner" just refers to the inner loop - Plane *innerPlane = _planes[innerIndex]; - Plane *visibleInnerPlane = _visiblePlanes.findByObject(innerPlane->_object); - - int rectCount = rectlist.size(); - for (int rectIndex = 0; rectIndex < rectCount; ++rectIndex) { - int splitCount = splitRects(*rectlist[rectIndex], _planes[innerIndex]->_screenRect, outRects); + const Plane &innerPlane = *_planes[innerIndex]; + const Plane *visibleInnerPlane = _visiblePlanes.findByObject(innerPlane._object); + const RectList::size_type rectCount = eraseList.size(); + for (RectList::size_type rectIndex = 0; rectIndex < rectCount; ++rectIndex) { + const int splitCount = splitRects(*eraseList[rectIndex], innerPlane._screenRect, outRects); if (splitCount == 0) { if (visibleInnerPlane != nullptr) { // same priority, or relative priority between inner/outer changed - if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane->_priority - innerPlane->_priority) <= 0) { - if (outerPlane->_priority <= innerPlane->_priority) { - eraseLists[innerIndex].add(*rectlist[rectIndex]); + if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane._priority - innerPlane._priority) <= 0) { + if (outerPlane._priority <= innerPlane._priority) { + eraseLists[innerIndex].add(*eraseList[rectIndex]); } else { - eraseLists[outerIndex].add(*rectlist[rectIndex]); + eraseLists[outerIndex].add(*eraseList[rectIndex]); } } } - rectlist.erase_at(rectIndex); + eraseList.erase_at(rectIndex); } else if (splitCount != -1) { for (int i = 0; i < splitCount; ++i) { - rectlist.add(outRects[i]); + eraseList.add(outRects[i]); } if (visibleInnerPlane != nullptr) { // same priority, or relative priority between inner/outer changed - if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane->_priority - innerPlane->_priority) <= 0) { - *rectlist[rectIndex] = outerPlane->_screenRect.findIntersectingRect(innerPlane->_screenRect); - if (outerPlane->_priority <= innerPlane->_priority) { - eraseLists[innerIndex].add(*rectlist[rectIndex]); - } - else { - eraseLists[outerIndex].add(*rectlist[rectIndex]); + if ((visibleOuterPlane->_priority - visibleInnerPlane->_priority) * (outerPlane._priority - innerPlane._priority) <= 0) { + *eraseList[rectIndex] = outerPlane._screenRect.findIntersectingRect(innerPlane._screenRect); + + if (outerPlane._priority <= innerPlane._priority) { + eraseLists[innerIndex].add(*eraseList[rectIndex]); + } else { + eraseLists[outerIndex].add(*eraseList[rectIndex]); } } } - rectlist.erase_at(rectIndex); + eraseList.erase_at(rectIndex); } } - rectlist.pack(); + eraseList.pack(); } } } - for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) { - Plane *plane = _planes[planeIndex]; - Plane *visiblePlane = nullptr; + for (PlaneList::size_type planeIndex = 0; planeIndex < planeCount; ++planeIndex) { + Plane &plane = *_planes[planeIndex]; + Plane *visiblePlane = _visiblePlanes.findByObject(plane._object); - PlaneList::iterator visiblePlaneIt = Common::find_if(_visiblePlanes.begin(), _visiblePlanes.end(), FindByObject<Plane *>(plane->_object)); - if (visiblePlaneIt != _visiblePlanes.end()) { - visiblePlane = *visiblePlaneIt; - } + if (!plane._screenRect.isEmpty()) { + if (plane._redrawAllCount) { + plane.redrawAll(visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); + } else { + if (visiblePlane == nullptr) { + error("Missing visible plane for source plane %04x:%04x", PRINT_REG(plane._object)); + } - if (plane->_redrawAllCount) { - plane->redrawAll(visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); - } else { - if (visiblePlane == nullptr) { - error("Missing visible plane for source plane %04x:%04x", PRINT_REG(plane->_object)); + plane.calcLists(*visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); } + } else { + plane.decrementScreenItemArrayCounts(visiblePlane, false); + } - plane->calcLists(*visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]); + if (plane._moved) { + // the work for handling moved/resized planes was already done + // earlier in the function, we are just cleaning up now + --plane._moved; } - if (plane->_created) { - _visiblePlanes.add(new Plane(*plane)); - --plane->_created; - } else if (plane->_moved) { - assert(visiblePlaneIt != _visiblePlanes.end()); - **visiblePlaneIt = *plane; - --plane->_moved; + if (plane._created) { + _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; } } + // NOTE: SSCI only looks for kPlaneTypeTransparent, not + // kPlaneTypeTransparentPicture if (foundTransparentPlane) { - for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) { - for (int i = planeIndex + 1; i < planeCount; ++i) { + for (PlaneList::size_type planeIndex = 0; planeIndex < planeCount; ++planeIndex) { + for (PlaneList::size_type i = planeIndex + 1; i < planeCount; ++i) { if (_planes[i]->_type == kPlaneTypeTransparent) { _planes[i]->filterUpEraseRects(drawLists[i], eraseLists[planeIndex]); } } if (_planes[planeIndex]->_type == kPlaneTypeTransparent) { - for (int i = planeIndex - 1; i >= 0; --i) { + for (int i = (int)planeIndex - 1; i >= 0; --i) { _planes[i]->filterDownEraseRects(drawLists[i], eraseLists[i], eraseLists[planeIndex]); } @@ -729,7 +1034,7 @@ void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseL } } - for (int i = planeIndex + 1; i < planeCount; ++i) { + for (PlaneList::size_type i = planeIndex + 1; i < planeCount; ++i) { if (_planes[i]->_type == kPlaneTypeTransparent) { _planes[i]->filterUpDrawRects(drawLists[i], drawLists[planeIndex]); } @@ -743,188 +1048,90 @@ void GfxFrameout::drawEraseList(const RectList &eraseList, const Plane &plane) { return; } - for (RectList::const_iterator it = eraseList.begin(); it != eraseList.end(); ++it) { - mergeToShowList(**it, _showList, _overdrawThreshold); - _currentBuffer.fillRect(**it, plane._back); + const RectList::size_type eraseListSize = eraseList.size(); + for (RectList::size_type i = 0; i < eraseListSize; ++i) { + mergeToShowList(*eraseList[i], _showList, _overdrawThreshold); + _currentBuffer.fillRect(*eraseList[i], plane._back); } } void GfxFrameout::drawScreenItemList(const DrawList &screenItemList) { - _hasRemappedScreenItem = false; - if (/* TODO: g_Remap_UnknownCounter2 */ false && !_priorityMap.isNull()) { - for (DrawList::const_iterator it = screenItemList.begin(); it != screenItemList.end(); ++it) { - if ((*it)->screenItem->getCelObj()._remap) { - _hasRemappedScreenItem = true; - break; - } - } - } - - for (DrawList::const_iterator it = screenItemList.begin(); it != screenItemList.end(); ++it) { - DrawItem &drawItem = **it; + const DrawList::size_type drawListSize = screenItemList.size(); + for (DrawList::size_type i = 0; i < drawListSize; ++i) { + const DrawItem &drawItem = *screenItemList[i]; mergeToShowList(drawItem.rect, _showList, _overdrawThreshold); - ScreenItem &screenItem = *drawItem.screenItem; + const ScreenItem &screenItem = *drawItem.screenItem; // TODO: Remove -// debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), drawItem.rect.left, drawItem.rect.top, drawItem.rect.right, drawItem.rect.bottom); +// debug("Drawing item %04x:%04x to %d %d %d %d", PRINT_REG(screenItem._object), PRINT_RECT(drawItem.rect)); CelObj &celObj = *screenItem._celObj; celObj.draw(_currentBuffer, screenItem, drawItem.rect, screenItem._mirrorX ^ celObj._mirrorX); } } void GfxFrameout::mergeToShowList(const Common::Rect &drawRect, RectList &showList, const int overdrawThreshold) { - Common::Rect merged(drawRect); - - bool didDelete = true; - RectList::size_type count = showList.size(); - while (didDelete && count) { - didDelete = false; - - for (RectList::size_type i = 0; i < count; ++i) { - Common::Rect existing = *showList[i]; - Common::Rect candidate; - candidate.left = MIN(merged.left, existing.left); - candidate.top = MIN(merged.top, existing.top); - candidate.right = MAX(merged.right, existing.right); - candidate.bottom = MAX(merged.bottom, existing.bottom); - - if (candidate.height() * candidate.width() - merged.width() * merged.height() - existing.width() * existing.height() <= overdrawThreshold) { - merged = candidate; - showList.erase_at(i); - didDelete = true; - } - } - - count = showList.pack(); - } - - showList.add(merged); -} - -void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry *showStyle) { - Palette sourcePalette(*_palette->getNextPalette()); - alterVmap(sourcePalette, sourcePalette, -1, styleRanges); - - // TODO: unsure if this is what this variable actually - // represents, but it is the correct variable number - int16 lastRoom = g_sci->getEngineState()->variables[VAR_GLOBAL][12].toSint16(); - - Common::Rect rect(_screen->getDisplayWidth(), _screen->getDisplayHeight()); - _showList.add(rect); - showBits(); - - Common::Rect calcRect(0, 0); - - // NOTE: The original engine allocated these as static arrays of 100 - // pointers to ScreenItemList / RectList - ScreenItemListList screenItemLists; - EraseListList eraseLists; - - screenItemLists.resize(_planes.size()); - eraseLists.resize(_planes.size()); - - // TODO: Remap - // _numActiveRemaps was a global in SCI engine - // if (Remap::_numActiveRemaps > 0 && _remapOccurred) { - // _screen->remapMarkRedraw(); - // } - - calcLists(screenItemLists, eraseLists, calcRect); - for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { - list->sort(); - } - - for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { - for (DrawList::iterator drawItem = list->begin(); drawItem != list->end(); ++drawItem) { - (*drawItem)->screenItem->getCelObj().submitPalette(); - } - } - - _remapOccurred = _palette->updateForFrame(); - _frameNowVisible = false; - - for (PlaneList::size_type i = 0; i < _planes.size(); ++i) { - drawEraseList(eraseLists[i], *_planes[i]); - drawScreenItemList(screenItemLists[i]); - } - - Palette nextPalette(*_palette->getNextPalette()); + RectList mergeList; + Common::Rect merged; + mergeList.add(drawRect); + + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + bool didMerge = false; + const Common::Rect &r1 = *mergeList[i]; + if (!r1.isEmpty()) { + for (RectList::size_type j = 0; j < showList.size(); ++j) { + const Common::Rect &r2 = *showList[j]; + if (!r2.isEmpty()) { + merged = r1; + merged.extend(r2); + + int difference = merged.width() * merged.height(); + difference -= r1.width() * r1.height(); + difference -= r2.width() * r2.height(); + if (r1.intersects(r2)) { + const Common::Rect overlap = r1.findIntersectingRect(r2); + difference += overlap.width() * overlap.height(); + } - if (lastRoom < 1000) { - for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) { - if (styleRanges[i] == -1 || styleRanges[i] == 0) { - sourcePalette.colors[i] = nextPalette.colors[i]; - sourcePalette.colors[i].used = true; - } - } - } else { - for (int i = 0; i < ARRAYSIZE(sourcePalette.colors); ++i) { - if (styleRanges[i] == -1 || (styleRanges[i] == 0 && i > 71 && i < 104)) { - sourcePalette.colors[i] = nextPalette.colors[i]; - sourcePalette.colors[i].used = true; + if (difference <= overdrawThreshold) { + mergeList.erase_at(i); + showList.erase_at(j); + mergeList.add(merged); + didMerge = true; + break; + } else { + Common::Rect outRects[2]; + int splitCount = splitRectsForRender(*mergeList[i], *showList[j], outRects); + if (splitCount != -1) { + mergeList.add(*mergeList[i]); + mergeList.erase_at(i); + showList.erase_at(j); + didMerge = true; + while (splitCount--) { + mergeList.add(outRects[splitCount]); + } + break; + } + } + } } - } - } - _palette->submit(sourcePalette); - _palette->updateFFrame(); - _palette->updateHardware(); - alterVmap(nextPalette, sourcePalette, 1, _styleRanges); - - if (showStyle && showStyle->type != kShowStyleUnknown) { -// TODO: SCI2.1mid transition effects -// processEffects(); - warning("Transition not implemented!"); - } else { - showBits(); - } - - _frameNowVisible = true; - - for (PlaneList::iterator plane = _planes.begin(); plane != _planes.end(); ++plane) { -// TODO: -// plane->updateRedrawAllCount(); - } - - // TODO: Remap - // _numActiveRemaps was a global in SCI engine - // if (Remap::_numActiveRemaps > 0 && _remapOccurred) { - // _screen->remapMarkRedraw(); - // } - - calcLists(screenItemLists, eraseLists, calcRect); - for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { - list->sort(); - } - - for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) { - for (DrawList::iterator drawItem = list->begin(); drawItem != list->end(); ++drawItem) { - (*drawItem)->screenItem->getCelObj().submitPalette(); + if (didMerge) { + showList.pack(); + } } } - _remapOccurred = _palette->updateForFrame(); - // NOTE: During this second loop, `_frameNowVisible = false` is - // inside the next loop in SCI2.1mid - _frameNowVisible = false; - - for (PlaneList::size_type i = 0; i < _planes.size(); ++i) { - drawEraseList(eraseLists[i], *_planes[i]); - drawScreenItemList(screenItemLists[i]); + mergeList.pack(); + for (RectList::size_type i = 0; i < mergeList.size(); ++i) { + showList.add(*mergeList[i]); } - - _palette->submit(nextPalette); - _palette->updateFFrame(); - _palette->updateHardware(); - showBits(); - - _frameNowVisible = true; } -// TODO: What does the bit masking for the show rects do, -// and does it cause an off-by-one error in rect calculations -// since SOL_Rect is BR inclusive and Common::Rect is BR -// exclusive? void GfxFrameout::showBits() { + if (!_showList.size()) { + g_system->updateScreen(); + return; + } + for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) { Common::Rect rounded(**rect); // NOTE: SCI engine used BR-inclusive rects so used slightly @@ -932,13 +1139,10 @@ void GfxFrameout::showBits() { // was always even. rounded.left &= ~1; rounded.right = (rounded.right + 1) & ~1; - - // TODO: - // _cursor->GonnaPaint(rounded); + _cursor->gonnaPaint(rounded); } - // TODO: - // _cursor->PaintStarting(); + _cursor->paintStarting(); for (RectList::const_iterator rect = _showList.begin(); rect != _showList.end(); ++rect) { Common::Rect rounded(**rect); @@ -950,13 +1154,20 @@ void GfxFrameout::showBits() { byte *sourceBuffer = (byte *)_currentBuffer.getPixels() + rounded.top * _currentBuffer.screenWidth + rounded.left; + // TODO: Sometimes transition screen items generate zero-dimension + // show rectangles. Is this a bug? + if (rounded.width() == 0 || rounded.height() == 0) { + warning("Zero-dimension show rectangle ignored"); + continue; + } + g_system->copyRectToScreen(sourceBuffer, _currentBuffer.screenWidth, rounded.left, rounded.top, rounded.width(), rounded.height()); } - // TODO: - // _cursor->DonePainting(); + _cursor->donePainting(); _showList.clear(); + g_system->updateScreen(); } void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, const int8 style, const int8 *const styleRanges) { @@ -969,7 +1180,7 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co if (styleRanges[paletteIndex] == style) { int minDiff = 262140; - int minDiffIndex; + int minDiffIndex = paletteIndex; for (int i = 0; i < 236; ++i) { if (styleRanges[i] != style) { @@ -989,7 +1200,7 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co if (style == 1 && styleRanges[paletteIndex] == 0) { int minDiff = 262140; - int minDiffIndex; + int minDiffIndex = paletteIndex; for (int i = 0; i < 236; ++i) { int r = palette2.colors[i].r; @@ -1007,17 +1218,17 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co } } - // NOTE: This is currBuffer->ptr in SCI engine byte *pixels = (byte *)_currentBuffer.getPixels(); - // TODO: Guessing that display width/height is the correct - // equivalent to screen width/height in SCI engine for (int pixelIndex = 0, numPixels = _currentBuffer.screenWidth * _currentBuffer.screenHeight; pixelIndex < numPixels; ++pixelIndex) { byte currentValue = pixels[pixelIndex]; int8 styleRangeValue = styleRanges[currentValue]; if (styleRangeValue == -1 && styleRangeValue == style) { currentValue = pixels[pixelIndex] = clut[currentValue]; - styleRangeValue = styleRanges[clut[currentValue]]; + // NOTE: In original engine this assignment happens outside of the + // condition, but if the branch is not followed the value is just + // going to be the same as it was before + styleRangeValue = styleRanges[currentValue]; } if ( @@ -1029,1009 +1240,155 @@ void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, co } } -void GfxFrameout::kernelSetPalStyleRange(const uint8 fromColor, const uint8 toColor) { - if (toColor > fromColor) { - return; - } - - for (int i = fromColor; i < toColor; ++i) { - _styleRanges[i] = 0; - } -} - -inline ShowStyleEntry * GfxFrameout::findShowStyleForPlane(const reg_t planeObj) const { - ShowStyleEntry *entry = _showStyles; - while (entry != nullptr) { - if (entry->plane == planeObj) { - break; - } - entry = entry->next; - } - - return entry; -} - -inline ShowStyleEntry *GfxFrameout::deleteShowStyleInternal(ShowStyleEntry *const showStyle) { - ShowStyleEntry *lastEntry = nullptr; - - for (ShowStyleEntry *testEntry = _showStyles; testEntry != nullptr; testEntry = testEntry->next) { - if (testEntry == showStyle) { - break; - } - lastEntry = testEntry; - } - - if (lastEntry == nullptr) { - _showStyles = showStyle->next; - lastEntry = _showStyles; +void GfxFrameout::kernelFrameOut(const bool shouldShowBits) { + if (_transitions->hasShowStyles()) { + _transitions->processShowStyles(); + } else if (_palMorphIsOn) { + palMorphFrameOut(_transitions->_styleRanges, nullptr); + _palMorphIsOn = false; } else { - lastEntry->next = showStyle->next; - } - - // NOTE: Differences from SCI2/2.1early engine: - // 1. Memory of ShowStyle-owned objects was freed before ShowStyle was - // removed from the linked list, but since this operation is position - // independent, it has been moved after removal from the list for - // consistency with SCI2.1mid+ - // 2. In SCI2, `screenItems` was a pointer to an array of pointers, so - // extra deletes were performed here; we use an owned container object - // instead, which is automatically freed when ShowStyle is freed -#if 0 - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - uint8 type = showStyle->type; - - if (type >= 1 && type <= 10) { - ScreenItemList &styleItems = showStyle->screenItems; - for (ScreenItemList::iterator it = styleItems.begin(); it != styleItems.end(); ++it) { - if (active) { - // TODO: _screen->deleteScreenItem(showStyle->plane, *it->id); - _screenItems.remove(*it); - } - delete *it; - } - } else if (type == 11 || type == 12) { - if (!showStyle->bitmapMemId.isNull()) { - _segMan->freeHunkEntry(showStyle->bitmapMemId); - } - if (showStyle->bitmapScreenItem != nullptr) { - // TODO: _screen->deleteScreenItem(showStyle->plane, showStyle->bitmapScreenItem->id); - _screenItems.remove(showStyle->bitmapScreenItem); - delete showStyle->bitmapScreenItem; - } + if (_transitions->hasScrolls()) { + _transitions->processScrolls(); } - } else { -#endif - delete[] showStyle->fadeColorRanges; -#if 0 - } -#endif - delete showStyle; - - // TODO: Verify that this is the correct entry to return - // for the loop in processShowStyles to work correctly - return lastEntry; -} - -// TODO: 10-argument version is only in SCI3; argc checks are currently wrong for this version -// and need to be fixed in future -// TODO: SQ6 does not use 'priority' (exists since SCI2) or 'blackScreen' (exists since SCI3); -// check to see if other versions use or if they are just always ignored -void GfxFrameout::kernelSetShowStyle(const uint16 argc, const reg_t &planeObj, const ShowStyleType type, const int16 seconds, const int16 back, const int16 priority, const int16 animate, const int16 frameOutNow, const reg_t &pFadeArray, const int16 divisions, const int16 blackScreen) { - - bool hasDivisions = false; - bool hasFadeArray = false; - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - hasDivisions = argc > 7; - hasFadeArray = false; - } else if (getSciVersion() < SCI_VERSION_3) { - hasDivisions = argc > 8; - hasFadeArray = argc > 7; - } else { - hasDivisions = argc > 9; - hasFadeArray = argc > 8; + frameOut(shouldShowBits); } - bool isFadeUp; - int16 color; - if (back != -1) { - isFadeUp = false; - color = back; - } else { - isFadeUp = true; - color = 0; - } - - if ((getSciVersion() < SCI_VERSION_2_1_MIDDLE && type == 15) || type > 15) { - error("Illegal show style %d for plane %04x:%04x", type, PRINT_REG(planeObj)); - } - - Plane *plane = _planes.findByObject(planeObj); - if (plane == nullptr) { - error("Plane %04x:%04x is not present in active planes list", PRINT_REG(planeObj)); - } - - // TODO: This is Plane.gameRect in SCI engine, not planeRect. Engine uses - // Plane::ConvGameRectToPlaneRect to convert from gameRect to planeRect. - // Also this never gets used by SQ6 so it is not clear what it does yet - // Common::Rect gameRect = plane.planeRect; - - bool createNewEntry = true; - ShowStyleEntry *entry = findShowStyleForPlane(planeObj); - if (entry != nullptr) { - bool useExisting = true; - - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - useExisting = plane->_planeRect.width() == entry->width && plane->_planeRect.height() == entry->height; - } - - if (useExisting) { - useExisting = entry->divisions == (hasDivisions ? divisions : _defaultDivisions[type]) && entry->unknownC == _defaultUnknownC[type]; - } - - if (useExisting) { - createNewEntry = false; - isFadeUp = true; - entry->currentStep = 0; - } else { - isFadeUp = true; - color = entry->color; - deleteShowStyleInternal(entry/*, true*/); - entry = nullptr; - } - } - - if (type > 0) { - if (createNewEntry) { - entry = new ShowStyleEntry; - // 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->unknownC = _defaultUnknownC[type]; - entry->processed = false; - entry->divisions = hasDivisions ? divisions : _defaultDivisions[type]; - entry->plane = planeObj; - -#if 0 - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - entry->bitmapMemId = NULL_REG; - entry->screenItems.empty(); - entry->width = plane->_planeRect.width(); - entry->height = plane->_planeRect.height(); - } else { -#endif - 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(); - } - } - } else { - entry->fadeColorRangesCount = 0; - } -#if 0 - } -#endif - } - - // 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; - - if (entry->delay == 0) { -#if 0 - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE && entry->fadeColorRanges != nullptr) { -#endif - if (entry->fadeColorRanges != nullptr) { - delete[] entry->fadeColorRanges; - } - delete entry; - error("ShowStyle has no duration"); - } - - if (frameOutNow) { - Common::Rect frameOutRect(0, 0); - frameOut(false, frameOutRect); - } - - if (createNewEntry) { - // TODO: Implement SCI3, which may or may not actually have - // the same transitions as SCI2/SCI2.1early, but implemented - // differently -#if 0 - if (getSciVersion() < SCI_VERSION_2_1_MIDDLE) { - switch (entry->type) { - case kShowStyleHShutterIn: - case kShowStyleHShutterOut: - prepareShowStyleWipe(entry, priority, 2, true); - break; - - case kShowStyleVShutterIn: - case kShowStyleVShutterOut: - prepareShowStyleWipe(entry, priority, 2, false); - break; - - case kShowStyleHWipe1: - case kShowStyleHWipe2: - prepareShowStyleWipe(entry, priority, 1, true); - break; - - case kShowStyleVWipe1: - case kShowStyleVWipe2: - prepareShowStyleWipe(entry, priority, 1, false); - break; - - case kShowStyleIrisIn: - case kShowStyleIrisOut: - prepareShowStyleIris(entry, priority); - break; - - case kShowStyle11: - case kShowStyle12: - prepareShowStylePixels(entry, priority, plane->planeRect); - break; - - default: - break; - } - } -#endif - - entry->next = _showStyles; - _showStyles = entry; - } - } + throttle(); } -#if 0 -void addFrameoutEntryInternal(ShowStyleEntry *const showStyle, const int16 priority, const CelInfo32 &celInfo, const Common::Rect &rect) { - ScreenItem *screenItem = new ScreenItem; - screenItem->plane = showStyle->plane; - screenItem->celInfo = celInfo; - screenItem->celRect = rect; - screenItem->isInList = true; - screenItem->priority = priority; - screenItem->visible = true; - showStyle->screenItems.push_back(screenItem); -} - -void GfxFrameout::prepareShowStyleWipe(ShowStyleEntry *const showStyle, const int16 priority, const int16 edgeCount, const bool horizontal) { - assert(edgeCount == 1 || edgeCount == 2); - - const int numScreenItems = showStyle->divisions * edgeCount; - const int extra = edgeCount > 1 ? 1 : 0; - - showStyle->edgeCount = edgeCount; - showStyle->screenItems.reserve(numScreenItems); - - CelInfo32 celInfo; - celInfo.bitmap = NULL_REG; - celInfo.type = kCelObjTypeView; - celInfo.color = showStyle->color; - - for (int i = 0; i < numScreenItems; ++i) { - Common::Rect rect; - - if (horizontal) { - rect.top = 0; - rect.bottom = showStyle->height - 1; - rect.left = (showStyle->width * i) / (showStyle->divisions * edgeCount); - rect.right = ((i + 1) * (showStyle->width + extra)) / (showStyle->divisions * edgeCount) - 1; +void GfxFrameout::throttle() { + if (_throttleFrameOut) { + uint8 throttleTime; + if (_throttleState == 2) { + throttleTime = 16; + _throttleState = 0; } else { - rect.left = 0; - rect.right = showStyle->width - 1; - rect.top = (showStyle->height * i) / (showStyle->divisions * edgeCount); - rect.bottom = ((i + 1) * (showStyle->height + extra)) / (showStyle->divisions * edgeCount) - 1; - } - - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); - - if (edgeCount == 2) { - if (horizontal) { - int temp = rect.left; - rect.left = showStyle->width - rect.right - 1; - rect.right = showStyle->width - temp - 1; - } else { - int temp = rect.top; - rect.top = showStyle->height - rect.bottom - 1; - rect.bottom = showStyle->height - temp - 1; - } - - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); - } - } -} - -void GfxFrameout::prepareShowStyleIris(ShowStyleEntry *const showStyle, const int16 priority) { - const int edgeCount = 4; - const int numScreenItems = showStyle->divisions * edgeCount; - - showStyle->edgeCount = edgeCount; - showStyle->screenItems.reserve(numScreenItems); - - CelInfo32 celInfo; - celInfo.bitmap = NULL_REG; - celInfo.type = kCelObjTypeView; - celInfo.color = showStyle->color; - - for (int i = 0; i < numScreenItems; ++i) { - Common::Rect rect; - - rect.right = showStyle->width - ((showStyle->width * i) / (showStyle->divisions * 2)) - 1; - rect.left = (showStyle->width * i) / (showStyle->divisions * 2); - rect.top = (showStyle->height * i) / (showStyle->divisions * 2); - rect.bottom = ((i + 1) * (showStyle->height + 1)) / (showStyle->divisions * 2) - 1; - - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); - - { - int temp = rect.top; - rect.top = showStyle->height - rect.bottom - 1; - rect.bottom = showStyle->height - temp - 1; + throttleTime = 17; + ++_throttleState; } - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); - - rect.top = ((i + 1) * (showStyle->height + 1)) / (showStyle->divisions * 2); - rect.right = ((i + 1) * (showStyle->width + 1)) / (showStyle->divisions * 2) - 1; - rect.bottom = ((i + 1) * (showStyle->height + 1)) / (showStyle->divisions * 2) - 1; - - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); - - { - int temp = rect.left; - rect.left = showStyle->width - rect.right - 1; - rect.right = showStyle->width - temp - 1; - } - - addFrameoutEntryInternal(showStyle, priority, celInfo, rect); + g_sci->getEngineState()->speedThrottler(throttleTime); + g_sci->getEngineState()->_throttleTrigger = true; } } -void GfxFrameout::prepareShowStylePixels(ShowStyleEntry *const showStyle, const int16 priority, const Common::Rect planeGameRect) { - const int bitmapSize = showStyle->width * showStyle->height; - - // TODO: Verify that memory type 0x200 (what GK1 engine uses) - // is Hunk type - reg_t bitmapMemId = _segMan->allocateHunkEntry("ShowStylePixels()", bitmapSize + sizeof(GfxBitmapHeader)); - showStyle->bitmapMemId = bitmapMemId; - - // TODO: SCI2 GK1 uses a Bitmap constructor function to - // do this work - byte *bitmap = _segMan->getHunkPointer(bitmapMemId); - GfxBitmapHeader *header = (GfxBitmapHeader *)bitmap; - byte *bitmapData = bitmap + sizeof(GfxBitmapHeader); - - // TODO: These are defaults from the Bitmap constructor in - // GK1, not specific values set by this function. - // TODO: This probably should not even be using a struct at - // all since this information is machine endian dependent - // and will be reversed for Mac versions or when running - // ScummVM on big-endian systems. GK1 used packed structs - // everywhere so this probably worked better there too. - header->field_18 = 36; - header->field_1c = 36; - memset(header, 0, sizeof(GfxBitmapHeader)); - - header->width = showStyle->width; - header->height = showStyle->height; - header->field_8 = 250; - header->size = bitmapSize; - - // TODO: Scaled dimensions in bitmap headers was not added - // until SCI2.1mid. It is not clear what the right thing to - // do here is. - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { - header->scaledWidth = _currentBuffer.scriptWidth; - header->scaledHeight = _currentBuffer.scriptHeight; - } - - Common::Rect copyRect; - // TODO: planeGameRect is supposedly in script coordinates, - // which are usually 320x200. If bitsSaveDisplayScreen is - // in native resolution then seemingly this function will - // not work properly since we will be not copy enough bits, - // or from the correct location. - copyRect.left = planeGameRect.left; - copyRect.top = planeGameRect.top; - copyRect.right = planeGameRect.left + showStyle->width; - copyRect.bottom = planeGameRect.top + showStyle->height; - _screen->bitsSaveDisplayScreen(copyRect, bitmapData); - - CelInfo32 celInfo; - celInfo.bitmap = bitmapMemId; - celInfo.type = kCelObjTypeMem; - - ScreenItem *screenItem = new ScreenItem; - - screenItem->position.x = 0; - screenItem->position.y = 0; - - showStyle->bitmapScreenItem = screenItem; - screenItem->priority = priority; - - // TODO: Have not seen/identified this particular flag yet in - // SCI2.1mid (SQ6) engine; maybe (1) a duplicate of `created`, - // or (2) does not exist any more, or (3) one of the other - // still-unidentified fields. Probably need to look at the - // GK1 source for its use in drawing algorithms to determine - // if/how this correlates to ScreenItem members in the - // SCI2.1mid engine. -// screenItem->isInList = true; - - Plane *plane = _planes.findByObject(showStyle.plane); - plane->_screenItemList.add(screenItem); -} -#endif - -// NOTE: Different version of SCI engine support different show styles -// SCI2 implements 0, 1/3/5/7/9, 2/4/6/8/10, 11, 12, 13, 14 -// SCI2.1 implements 0, 1/2/3/4/5/6/7/8/9/10/11/12/15, 13, 14 -// SCI3 implements 0, 1/3/5/7/9, 2/4/6/8/10, 11, 12/15, 13, 14 -// TODO: Sierra code needs to be replaced with code that uses the -// computed entry->delay property instead of just counting divisors, -// as the latter is machine-speed-dependent and leads to wrong -// transition speeds -void GfxFrameout::processShowStyles() { - uint32 now = g_sci->getTickCount(); - - bool continueProcessing; - - // TODO: Change to bool? Engine uses inc to set the value to true, - // but there does not seem to be any reason to actually count how - // many times it was set - int doFrameOut; - do { - continueProcessing = false; - doFrameOut = 0; - ShowStyleEntry *showStyle = _showStyles; - while (showStyle != nullptr) { - bool retval = false; - - if (!showStyle->animate) { - ++doFrameOut; - } - - if (showStyle->nextTick < now || !showStyle->animate) { - // TODO: Different versions of SCI use different processors! - // This is the SQ6/KQ7/SCI2.1mid table. - switch (showStyle->type) { - case kShowStyleNone: { - retval = processShowStyleNone(showStyle); - break; - } - case kShowStyleHShutterOut: - case kShowStyleVShutterOut: - case kShowStyleWipeLeft: - case kShowStyleWipeUp: - case kShowStyleIrisOut: -#if 0 - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { -#endif - retval = processShowStyleMorph(showStyle); -#if 0 - } else { - retval = processShowStyleWipe(-1, showStyle); - } -#endif - break; - case kShowStyleHShutterIn: - case kShowStyleVShutterIn: - case kShowStyleWipeRight: - case kShowStyleWipeDown: - case kShowStyleIrisIn: -#if 0 - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { -#endif - retval = processShowStyleMorph(showStyle); -#if 0 - } else { - retval = processShowStyleWipe(1, showStyle); - } -#endif - break; - case kShowStyle11: -#if 0 - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { -#endif - retval = processShowStyleMorph(showStyle); -#if 0 - } else { - retval = processShowStyle11(showStyle); - } -#endif - break; - case kShowStyle12: - case kShowStyleUnknown: { -#if 0 - if (getSciVersion() >= SCI_VERSION_2_1_MIDDLE) { -#endif - retval = processShowStyleMorph(showStyle); -#if 0 - } else { - retval = processShowStyle12(showStyle); - } -#endif - break; - } - case kShowStyleFadeOut: { - retval = processShowStyleFade(-1, showStyle); - break; - } - case kShowStyleFadeIn: { - retval = processShowStyleFade(1, showStyle); - break; - } - } - } - - if (!retval) { - continueProcessing = true; - } - - if (retval && showStyle->processed) { - showStyle = deleteShowStyleInternal(showStyle); - } else { - showStyle = showStyle->next; - } - } - - if (doFrameOut) { - Common::Rect frameOutRect(0, 0); - frameOut(true, frameOutRect); - - // TODO: It seems like transitions without the “animate” - // flag are too fast in in SCI2–2.1early, but the throttle - // value is arbitrary. Someone on real hardware probably - // needs to test what the actual speed of transitions - // should be - //state->speedThrottler(30); - //state->_throttleTrigger = true; - } - } while(continueProcessing && doFrameOut); -} - -bool GfxFrameout::processShowStyleNone(ShowStyleEntry *const showStyle) { - if (showStyle->fadeUp) { - _palette->setFade(100, 0, 255); - } else { - _palette->setFade(0, 0, 255); - } - - showStyle->processed = true; - return true; -} - -bool GfxFrameout::processShowStyleMorph(ShowStyleEntry *const showStyle) { - palMorphFrameOut(_styleRanges, showStyle); - showStyle->processed = true; - return true; -} - -// TODO: Normalise use of 'entry' vs 'showStyle' -bool GfxFrameout::processShowStyleFade(const int direction, ShowStyleEntry *const showStyle) { - bool unchanged = true; - if (showStyle->currentStep < showStyle->divisions) { - int percent; - if (direction <= 0) { - percent = showStyle->divisions - showStyle->currentStep - 1; - } else { - percent = showStyle->currentStep; - } - - percent *= 100; - percent /= showStyle->divisions - 1; - - if (showStyle->fadeColorRangesCount > 0) { - for (int i = 0, len = showStyle->fadeColorRangesCount; i < len; i += 2) { - _palette->setFade(percent, showStyle->fadeColorRanges[i], showStyle->fadeColorRanges[i + 1]); - } - } else { - _palette->setFade(percent, 0, 255); - } - - ++showStyle->currentStep; - showStyle->nextTick += showStyle->delay; - unchanged = false; +void GfxFrameout::shakeScreen(int16 numShakes, const ShakeDirection direction) { + if (direction & kShakeHorizontal) { + // Used by QFG4 room 750 + warning("TODO: Horizontal shake not implemented"); + return; } - if (showStyle->currentStep >= showStyle->divisions && unchanged) { - if (direction > 0) { - showStyle->processed = true; + while (numShakes--) { + if (direction & kShakeVertical) { + g_system->setShakePos(_isHiRes ? 8 : 4); } - return true; - } - - return false; -} - -// TODO: Rect sizes are wrong, rects in SCI are inclusive of bottom/right but -// in ScummVM are exclusive so extra ±1 operations here are wrong -#if 0 -bool GfxFrameout::processShowStyleWipe(const ShowStyleEntry *const style) { - const int16 divisions = style->divisions; - Common::Rect rect(divisions, divisions); - - const Plane *const plane = _visibleScreen->planeList->findByObject(style->plane); - - const int16 planeLeft = plane->field_4C.left; - const int16 planeTop = plane->field_4C.top; - const int16 planeRight = plane->field_4C.right; - const int16 planeBottom = plane->field_4C.bottom; - const int16 planeWidth = planeRight - planeLeft + 1; - const int16 planeHeight = planeBottom - planeTop + 1; - - const int16 divisionWidth = planeWidth / divisions - 1; - int16 shutterDivisionWidth = planeWidth / (2 * divisions); - if (shutterDivisionWidth >= 0) { - const int16 heightPerDivision = planeHeight / divisions; - int16 shutterMiddleX = divisions * shutterDivisionWidth; - for (int16 x = divisionWidth - shutterDivisionWidth; x <= divisionWidth; ++x) { - int16 divisionTop = 0; - for (int16 y = 0; y < heightPerDivision; ++y, divisionTop += divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + divisions - 1; - rect.left = planeLeft + shutterMiddleX; - rect.right = rect.left + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } - // number of divisions does not divide evenly into plane height, - // draw the remainder - if (planeHeight % divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + planeHeight % divisions - 1; - rect.left = planeLeft + shutterMiddleX; - rect.right = rect.left + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } + g_system->updateScreen(); + g_sci->getEngineState()->wait(3); - divisionTop = 0; - for (int16 y = 0; y < heightPerDivision; ++y, divisionTop += divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + divisions - 1; - rect.left = planeLeft + divisions * x; - rect.right = rect.left + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } - if (planeHeight % divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + planeHeight % divisions - 1; - rect.left = planeLeft + divisions * x; - rect.right = rect.left + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } - - shutterMiddleX -= divisions; - --shutterDivisionWidth; + if (direction & kShakeVertical) { + g_system->setShakePos(0); } - } - if (planeWidth % divisions) { - const int16 roundedPlaneWidth = divisions * divisionWidth; - int16 divisionTop = 0; - for (int16 y = 0; y < planeHeight / divisions; ++y, divisionTop += divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + divisions - 1; - rect.left = planeLeft + roundedPlaneWidth; - rect.right = rect.left + planeWidth % divisions + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } - if (planeHeight % divisions) { - rect.top = planeTop + divisionTop; - rect.bottom = rect.top + planeHeight % divisions - 1; - rect.left = planeLeft + roundedPlaneWidth; - rect.right = rect.left + planeWidth % divisions + divisions - 1; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); - } + g_system->updateScreen(); + g_sci->getEngineState()->wait(3); } - - rect.right = planeRight; - rect.left = planeLeft; - rect.top = planeTop; - rect.bottom = planeBottom; - // _screen->rectList.clear(); - // _screen->rectList.add(rect); - // showBits(); } -#endif -#if 0 -bool GfxFrameout::processShowStyleWipe(const int direction, ShowStyleEntry *const showStyle) { - if (showStyle->currentStep < showStyle->divisions) { - int index; - if (direction <= 0) { - index = showStyle->divisions - showStyle->currentStep - 1; - } else { - index = showStyle->currentStep; - } - index *= showStyle->edgeCount; - - if (showStyle->edgeCount > 0) { - for (int i = 0; i < showStyle->edgeCount; ++i) { - if (showStyle->fadeUp) { - ScreenItem *screenItem = showStyle->screenItems[index + i]; - if (screenItem != nullptr) { - // TODO: _screen->deleteScreenItem(screenItem); - _screenItems.remove(screenItem); - - delete screenItem; - showStyle->screenItems[index + i] = nullptr; - } - } else { - ScreenItem *screenItem = showStyle->screenItems[index + i]; - // TODO: _screen->addScreenItem(screenItem); - _screenItems.push_back(screenItem); - } - } +#pragma mark - +#pragma mark Mouse cursor - ++showStyle->currentStep; - showStyle->nextTick += showStyle->delay; - } +reg_t GfxFrameout::kernelIsOnMe(const reg_t object, const Common::Point &position, bool checkPixel) const { + const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane)); + Plane *plane = _visiblePlanes.findByObject(planeObject); + if (plane == nullptr) { + return make_reg(0, 0); } - if (showStyle->currentStep >= showStyle->divisions) { - if (showStyle->fadeUp) { - showStyle->processed = true; - } - - return true; + ScreenItem *screenItem = plane->_screenItemList.findByObject(object); + if (screenItem == nullptr) { + return make_reg(0, 0); } - return false; + // NOTE: The original engine passed a copy of the ScreenItem into isOnMe + // as a hack around the fact that the screen items in `_visiblePlanes` + // did not have their `_celObj` pointers cleared when their CelInfo was + // updated by `Plane::decrementScreenItemArrayCounts`. We handle this + // this more intelligently by clearing `_celObj` in the copy assignment + // operator, which is only ever called by `decrementScreenItemArrayCounts` + // anyway. + return make_reg(0, isOnMe(*screenItem, *plane, position, checkPixel)); } -void fillRect(byte *data, const Common::Rect &rect, const int16 color, const int16 stride) { - -} - -bool GfxFrameout::processShowStyle11(ShowStyleEntry *const showStyle) { - int divisions = showStyle->divisions * showStyle->divisions; - - byte *bitmapData = _segMan->getHunkPointer(showStyle->bitmapMemId) + sizeof(GfxBitmapHeader); +bool GfxFrameout::isOnMe(const ScreenItem &screenItem, const Plane &plane, const Common::Point &position, const bool checkPixel) const { - int ebx; + Common::Point scaledPosition(position); + mulru(scaledPosition, Ratio(_currentBuffer.screenWidth, _currentBuffer.scriptWidth), Ratio(_currentBuffer.screenHeight, _currentBuffer.scriptHeight)); + scaledPosition.x += plane._planeRect.left; + scaledPosition.y += plane._planeRect.top; - if (showStyle->currentStep == 0) { - int ctr = 0; - int bloot = divisions; - do { - bloot >>= 1; - ++ctr; - } while (bloot != 1); - - showStyle->dissolveSeed = _dissolveSequenceSeeds[ctr]; - ebx = 800; - showStyle->unknown3A = 800; - showStyle->dissolveInitial = 800; - } else { - int ebx = showStyle->unknown3A; - do { - int eax = ebx >> 1; - if (ebx & 1) { - ebx = showStyle->dissolveSeed ^ eax; - } else { - ebx = eax; - } - } while (ebx >= divisions); - - if (ebx == showStyle->dissolveInitial) { - ebx = 0; - } + if (!screenItem._screenRect.contains(scaledPosition)) { + return false; } - Common::Rect rect; - - rect.left = (showStyle->width + showStyle->divisions - 1) / showStyle->divisions; - rect.top = (showStyle->height + showStyle->divisions - 1) / showStyle->divisions; - - if (showStyle->currentStep <= showStyle->divisions) { - int ebp = 0; - do { - int ecx = ebx % showStyle->divisions; - - Common::Rect drawRect; - drawRect.left = rect.left * ecx; - drawRect.right = drawRect.left + rect.left - 1; - drawRect.top = rect.top * ebx; - drawRect.bottom = rect.top * ebx + rect.top - 1; - - bool doit; - if (drawRect.right >= 0 && drawRect.bottom >= 0 && drawRect.left <= rect.right && drawRect.top <= rect.bottom) { - doit = true; - } else { - doit = false; - } - - if (doit) { - if (drawRect.left < 0) { - drawRect.left = 0; - } - - if (drawRect.top < 0) { - drawRect.top = 0; - } - - if (drawRect.right > rect.right) { - drawRect.right = rect.right; - } - - if (drawRect.bottom > rect.bottom) { - drawRect.bottom = rect.bottom; - } - } else { - drawRect.right = 0; - drawRect.bottom = 0; - drawRect.left = 0; - drawRect.top = 0; - } - - fillRect(bitmapData, drawRect, showStyle->color, showStyle->width); - - int eax = ebx; - do { - eax >>= 1; - if (ebx & 1) { - ebx = showStyle->dissolveSeed; - ebx ^= eax; - } else { - ebx = eax; - } - } while (ebx >= divisions); + if (checkPixel) { + CelObj &celObj = screenItem.getCelObj(); - if (showStyle->currentStep != showStyle->divisions) { - ebp++; - } else { - drawRect.left = 0; - drawRect.top = 0; - drawRect.right = showStyle->width - 1; - drawRect.bottom = showStyle->height - 1; - fillRect(bitmapData, drawRect, showStyle->color, showStyle->width); - } + bool mirrorX = screenItem._mirrorX ^ celObj._mirrorX; - } while (ebp <= showStyle->divisions); + scaledPosition.x -= screenItem._scaledPosition.x; + scaledPosition.y -= screenItem._scaledPosition.y; - showStyle->unknown3A = ebx; - ++showStyle->currentStep; - showStyle->nextTick += showStyle->delay; - // _screen->updateScreenItem(showStyle->bitmapScreenItem); - } + mulru(scaledPosition, Ratio(celObj._xResolution, _currentBuffer.screenWidth), Ratio(celObj._yResolution, _currentBuffer.screenHeight)); - if (showStyle->currentStep >= showStyle->divisions) { - if (showStyle->fadeUp) { - showStyle->processed = true; + if (screenItem._scale.signal != kScaleSignalNone && screenItem._scale.x && screenItem._scale.y) { + scaledPosition.x = scaledPosition.x * 128 / screenItem._scale.x; + scaledPosition.y = scaledPosition.y * 128 / screenItem._scale.y; } - return true; + uint8 pixel = celObj.readPixel(scaledPosition.x, scaledPosition.y, mirrorX); + return pixel != celObj._skipColor; } - return false; -} - -bool GfxFrameout::processShowStyle12(ShowStyleEntry *const showStyle) { return true; } -#endif - -void GfxFrameout::kernelFrameout(const bool shouldShowBits) { - if (_showStyles != nullptr) { - processShowStyles(); - } else if (_palMorphIsOn) { - palMorphFrameOut(_styleRanges, nullptr); - _palMorphIsOn = false; - } else { -// TODO: Window scroll -// if (g_ScrollWindow) { -// doScroll(); -// } - - Common::Rect frameOutRect(0, 0); - frameOut(shouldShowBits, frameOutRect); - } -} -uint16 GfxFrameout::kernelIsOnMe(int16 x, int16 y, uint16 checkPixels, reg_t screenObject) { - reg_t planeObject = readSelector(_segMan, screenObject, SELECTOR(plane)); - Plane *screenItemPlane = _visiblePlanes.findByObject(planeObject); // Search for plane in visible planes - ScreenItem *screenItem = nullptr; +bool GfxFrameout::kernelSetNowSeen(const reg_t screenItemObject) const { + const reg_t planeObject = readSelector(_segMan, screenItemObject, SELECTOR(plane)); - if (!screenItemPlane) { - // Specified plane not found - return 0; + Plane *plane = _planes.findByObject(planeObject); + if (plane == nullptr) { + error("kSetNowSeen: Plane %04x:%04x not found for screen item %04x:%04x", PRINT_REG(planeObject), PRINT_REG(screenItemObject)); } - screenItem = screenItemPlane->_screenItemList.findByObject(screenObject); - if (!screenItem) { - // Specified screen object not in item list - return 0; + ScreenItem *screenItem = plane->_screenItemList.findByObject(screenItemObject); + if (screenItem == nullptr) { + return false; } - // Original SCI32 seems to have made a copy (?) of the screenitem? - // there is also a "or [ebp+arg_56], 1 - not sure what that did - return isOnMe(screenItemPlane, screenItem, x, y, checkPixels); -} - -uint16 GfxFrameout::isOnMe(Plane *screenItemPlane, ScreenItem *screenItem, int16 x, int16 y, uint16 checkPixels) { - // adjust coordinate according to resolution - int32 adjustedX = x * getCurrentBuffer().screenWidth / getCurrentBuffer().scriptWidth; - int32 adjustedY = y * getCurrentBuffer().screenHeight / getCurrentBuffer().scriptHeight; - - adjustedX += screenItemPlane->_planeRect.left; - adjustedY += screenItemPlane->_planeRect.top; - - //warning("kIsOnMe %s %d (%d, %d -> %d, %d) mouse %d, %d", _segMan->getObjectName(screenObject), checkPixels, screenItem->_screenRect.left, screenItem->_screenRect.top, screenItem->_screenRect.right, screenItem->_screenRect.bottom, adjustedX, adjustedY); + Common::Rect result = screenItem->getNowSeenRect(*plane); - if (!screenItem->_screenRect.contains(adjustedX, adjustedY)) { - // Specified coordinates are not within screen item - return 0; + if (g_sci->_features->usesAlternateSelectors()) { + writeSelectorValue(_segMan, screenItemObject, SELECTOR(left), result.left); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(top), result.top); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(right), result.right - 1); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(bottom), result.bottom - 1); + } else { + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsLeft), result.left); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsTop), result.top); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsRight), result.right - 1); + writeSelectorValue(_segMan, screenItemObject, SELECTOR(nsBottom), result.bottom - 1); } + return true; +} - //warning("HIT!"); - if (checkPixels) { - //warning("Check Pixels"); - CelObj &screenItemCelObject = screenItem->getCelObj(); - - Common::Point celAdjustedPoint(adjustedX, adjustedY); - bool celMirrored = screenItem->_mirrorX ^ screenItemCelObject._mirrorX; - - celAdjustedPoint.x -= screenItem->_scaledPosition.x; - celAdjustedPoint.y -= screenItem->_scaledPosition.y; - - Ratio celAdjustXRatio(screenItemCelObject._scaledWidth, getCurrentBuffer().screenWidth); - Ratio celAdjustYRatio(screenItemCelObject._scaledHeight, getCurrentBuffer().screenHeight); - mulru(celAdjustedPoint, celAdjustXRatio, celAdjustYRatio); - - if ((screenItem->_scale.signal) && (screenItem->_scale.x) && (screenItem->_scale.y)) { - // Apply scaling - celAdjustedPoint.x = celAdjustedPoint.x * 128 / screenItem->_scale.x; - celAdjustedPoint.y = celAdjustedPoint.y * 128 / screenItem->_scale.y; - } - - byte coordinateColor = screenItemCelObject.readPixel(celAdjustedPoint.x, celAdjustedPoint.y, celMirrored); - byte transparentColor = screenItemCelObject._transparentColor; - - if (coordinateColor == transparentColor) { - // Coordinate is transparent - //warning("TRANSPARENT!"); - return 0; - } +void GfxFrameout::remapMarkRedraw() { + for (PlaneList::const_iterator it = _planes.begin(); it != _planes.end(); ++it) { + Plane *p = *it; + p->remapMarkRedraw(); } - return 1; } #pragma mark - @@ -2052,6 +1409,15 @@ void GfxFrameout::printVisiblePlaneList(Console *con) const { printPlaneListInternal(con, _visiblePlanes); } +void GfxFrameout::printPlaneItemListInternal(Console *con, const ScreenItemList &screenItemList) const { + ScreenItemList::size_type i = 0; + for (ScreenItemList::const_iterator sit = screenItemList.begin(); sit != screenItemList.end(); sit++) { + ScreenItem *screenItem = *sit; + con->debugPrintf("%2d: ", i++); + screenItem->printDebugInfo(con); + } +} + void GfxFrameout::printPlaneItemList(Console *con, const reg_t planeObject) const { Plane *p = _planes.findByObject(planeObject); @@ -2060,12 +1426,18 @@ void GfxFrameout::printPlaneItemList(Console *con, const reg_t planeObject) cons return; } - ScreenItemList::size_type i = 0; - for (ScreenItemList::iterator sit = p->_screenItemList.begin(); sit != p->_screenItemList.end(); sit++) { - ScreenItem *screenItem = *sit; - con->debugPrintf("%2d: ", i++); - screenItem->printDebugInfo(con); + printPlaneItemListInternal(con, p->_screenItemList); +} + +void GfxFrameout::printVisiblePlaneItemList(Console *con, const reg_t planeObject) const { + Plane *p = _visiblePlanes.findByObject(planeObject); + + if (p == nullptr) { + con->debugPrintf("Plane does not exist"); + return; } + + printPlaneItemListInternal(con, p->_screenItemList); } } // End of namespace Sci |