aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics/frameout.cpp
diff options
context:
space:
mode:
authorColin Snover2016-01-18 00:12:47 -0600
committerColin Snover2016-02-18 13:18:02 -0600
commit75ccabc325d56876dd34d4a55e2034ee66d33d0b (patch)
tree01961ae148804e0206aa8c7858ac879471da04dd /engines/sci/graphics/frameout.cpp
parent4ba0ff8deb57aba3b034462c6c00ecf13ee281c9 (diff)
downloadscummvm-rg350-75ccabc325d56876dd34d4a55e2034ee66d33d0b.tar.gz
scummvm-rg350-75ccabc325d56876dd34d4a55e2034ee66d33d0b.tar.bz2
scummvm-rg350-75ccabc325d56876dd34d4a55e2034ee66d33d0b.zip
SCI: Implement accurate renderer architecture for SCI32
Diffstat (limited to 'engines/sci/graphics/frameout.cpp')
-rw-r--r--engines/sci/graphics/frameout.cpp2355
1 files changed, 1625 insertions, 730 deletions
diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp
index cb81fe8d61..d748cc05de 100644
--- a/engines/sci/graphics/frameout.cpp
+++ b/engines/sci/graphics/frameout.cpp
@@ -47,962 +47,1857 @@
#include "sci/graphics/palette32.h"
#include "sci/graphics/picture.h"
#include "sci/graphics/text32.h"
+#include "sci/graphics/plane32.h"
+#include "sci/graphics/screen_item32.h"
#include "sci/graphics/frameout.h"
#include "sci/video/robot_decoder.h"
namespace Sci {
-// TODO/FIXME: This is all guesswork
+// TODO/FIXME: This is partially guesswork
-enum SciSpeciaPlanelPictureCodes {
- kPlaneTranslucent = 0xfffe, // -2
- kPlanePlainColored = 0xffff // -1
+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)
- : _segMan(segMan), _resMan(resMan), _cache(cache), _screen(screen), _palette(palette), _paint32(paint32), _isHiRes(false) {
-
- _coordAdjuster = (GfxCoordAdjuster32 *)coordAdjuster;
- _curScrollText = -1;
- _showScrollText = false;
- _maxScrollTexts = 0;
+GfxFrameout::GfxFrameout(SegManager *segMan, ResourceManager *resMan, GfxCoordAdjuster *coordAdjuster, GfxCache *cache, GfxScreen *screen, GfxPalette32 *palette, GfxPaint32 *paint32) :
+ _segMan(segMan),
+ _resMan(resMan),
+ _cache(cache),
+ _screen(screen),
+ _palette(palette),
+ _paint32(paint32),
+ _isHiRes(false),
+ _palMorphIsOn(false),
+ _showStyles(nullptr),
+ _remapOccurred(false),
+ _frameNowVisible(false),
+ // TODO: Stop using _gfxScreen
+ _currentBuffer(screen->getWidth(), screen->getHeight(), nullptr),
+ _priorityMap(screen->getWidth(), screen->getHeight(), nullptr),
+ _screenRect(screen->getWidth(), screen->getHeight()),
+ _overdrawThreshold(0) {
+
+ _currentBuffer.setPixels(calloc(1, screen->getWidth() * screen->getHeight()));
+
+ 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)
+ // 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];
+ } 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);
}
GfxFrameout::~GfxFrameout() {
+ CelObj::deinit();
clear();
}
+void GfxFrameout::run() {
+ CelObj::init();
+ Plane::init();
+ ScreenItem::init();
+
+ // NOTE: This happens in SCI::InitPlane in the actual engine,
+ // and is a background fill plane to ensure hidden planes
+ // (planes with a priority of -1) are never drawn
+ Plane *initPlane = new Plane(Common::Rect(_currentBuffer.scriptWidth - 1, _currentBuffer.scriptHeight - 1));
+ initPlane->_priority = 0;
+ _planes.add(initPlane);
+}
+
void GfxFrameout::clear() {
- deletePlaneItems(NULL_REG);
_planes.clear();
- deletePlanePictures(NULL_REG);
- clearScrollTexts();
-}
-
-void GfxFrameout::clearScrollTexts() {
- _scrollTexts.clear();
- _curScrollText = -1;
-}
-
-void GfxFrameout::addScrollTextEntry(Common::String &text, reg_t kWindow, uint16 x, uint16 y, bool replace) {
- //reg_t bitmapHandle = g_sci->_gfxText32->createScrollTextBitmap(text, kWindow);
- // HACK: We set the container dimensions manually
- reg_t bitmapHandle = g_sci->_gfxText32->createScrollTextBitmap(text, kWindow, 480, 70);
- ScrollTextEntry textEntry;
- textEntry.bitmapHandle = bitmapHandle;
- textEntry.kWindow = kWindow;
- textEntry.x = x;
- textEntry.y = y;
- if (!replace || _scrollTexts.size() == 0) {
- if (_scrollTexts.size() > _maxScrollTexts) {
- _scrollTexts.remove_at(0);
- _curScrollText--;
- }
- _scrollTexts.push_back(textEntry);
- _curScrollText++;
+ _showList.clear();
+}
+
+#pragma mark -
+#pragma mark Screen items
+
+void GfxFrameout::kernelAddScreenItem(const reg_t object) {
+ const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane));
+
+// TODO: Remove
+// debug("Adding screen item %04x:%04x to plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject));
+
+ _segMan->getObject(object)->setInfoSelectorFlag(kInfoFlagViewInserted);
+
+ Plane *plane = _planes.findByObject(planeObject);
+ if (plane == nullptr) {
+ error("Invalid plane selector passed to kAddScreenItem");
+ }
+
+ ScreenItem *screenItem = plane->_screenItemList.findByObject(object);
+ if (screenItem != nullptr) {
+ screenItem->update(object);
} else {
- _scrollTexts.pop_back();
- _scrollTexts.push_back(textEntry);
+ screenItem = new ScreenItem(object);
+ plane->_screenItemList.add(screenItem);
}
}
-void GfxFrameout::showCurrentScrollText() {
- if (!_showScrollText || _curScrollText < 0)
- return;
+void GfxFrameout::kernelUpdateScreenItem(const reg_t object) {
+ const reg_t magnifierObject = readSelector(_segMan, object, SELECTOR(magnifier));
+ if (magnifierObject.isNull()) {
+ const reg_t planeObject = readSelector(_segMan, object, SELECTOR(plane));
+ Plane *plane = _planes.findByObject(planeObject);
+ if (plane == nullptr) {
+ error("Invalid plane selector passed to kUpdateScreenItem");
+ }
- uint16 size = (uint16)_scrollTexts.size();
- if (size > 0) {
- assert(_curScrollText < size);
- ScrollTextEntry textEntry = _scrollTexts[_curScrollText];
- g_sci->_gfxText32->drawScrollTextBitmap(textEntry.kWindow, textEntry.bitmapHandle, textEntry.x, textEntry.y);
- }
-}
-
-extern void showScummVMDialog(const Common::String &message);
-
-void GfxFrameout::kernelAddPlane(reg_t object) {
- PlaneEntry newPlane;
-
- if (_planes.empty()) {
- // There has to be another way for sierra sci to do this or maybe script resolution is compiled into
- // interpreter (TODO)
- uint16 scriptWidth = readSelectorValue(_segMan, object, SELECTOR(resX));
- uint16 scriptHeight = readSelectorValue(_segMan, object, SELECTOR(resY));
-
- // Phantasmagoria 2 doesn't specify a script width/height
- if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
- scriptWidth = 640;
- scriptHeight = 480;
- }
-
- assert(scriptWidth > 0 && scriptHeight > 0);
- _coordAdjuster->setScriptsResolution(scriptWidth, scriptHeight);
- }
-
- // Import of QfG character files dialog is shown in QFG4.
- // Display additional popup information before letting user use it.
- // For the SCI0-SCI1.1 version of this, check kDrawControl().
- if (g_sci->inQfGImportRoom() && !strcmp(_segMan->getObjectName(object), "DSPlane")) {
- showScummVMDialog("Characters saved inside ScummVM are shown "
- "automatically. Character files saved in the original "
- "interpreter need to be put inside ScummVM's saved games "
- "directory and a prefix needs to be added depending on which "
- "game it was saved in: 'qfg1-' for Quest for Glory 1, 'qfg2-' "
- "for Quest for Glory 2, 'qfg3-' for Quest for Glory 3. "
- "Example: 'qfg2-thief.sav'.");
- }
-
- newPlane.object = object;
- newPlane.priority = readSelectorValue(_segMan, object, SELECTOR(priority));
- newPlane.lastPriority = -1; // hidden
- newPlane.planeOffsetX = 0;
- newPlane.planeOffsetY = 0;
- newPlane.pictureId = kPlanePlainColored;
- newPlane.planePictureMirrored = false;
- newPlane.planeBack = 0;
- _planes.push_back(newPlane);
-
- kernelUpdatePlane(object);
-}
-
-void GfxFrameout::kernelUpdatePlane(reg_t object) {
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
- if (it->object == object) {
- // Read some information
- it->priority = readSelectorValue(_segMan, object, SELECTOR(priority));
- GuiResourceId lastPictureId = it->pictureId;
- it->pictureId = readSelectorValue(_segMan, object, SELECTOR(picture));
- if (lastPictureId != it->pictureId) {
- // picture got changed, load new picture
- deletePlanePictures(object);
- // Draw the plane's picture if it's not a translucent/plane colored frame
- if ((it->pictureId != kPlanePlainColored) && (it->pictureId != kPlaneTranslucent)) {
- // SQ6 gives us a bad picture number for the control menu
- if (_resMan->testResource(ResourceId(kResourceTypePic, it->pictureId)))
- addPlanePicture(object, it->pictureId, 0);
- }
- }
- it->planeRect.top = readSelectorValue(_segMan, object, SELECTOR(top));
- it->planeRect.left = readSelectorValue(_segMan, object, SELECTOR(left));
- it->planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom));
- it->planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right));
+ ScreenItem *screenItem = plane->_screenItemList.findByObject(object);
+ if (screenItem == nullptr) {
+ error("Invalid screen item passed to kUpdateScreenItem");
+ }
- _coordAdjuster->fromScriptToDisplay(it->planeRect.top, it->planeRect.left);
- _coordAdjuster->fromScriptToDisplay(it->planeRect.bottom, it->planeRect.right);
+ screenItem->update(object);
+ } else {
+ warning("TODO: Magnifier view not implemented yet!");
+ }
+}
- // We get negative left in kq7 in scrolling rooms
- if (it->planeRect.left < 0) {
- it->planeOffsetX = -it->planeRect.left;
- it->planeRect.left = 0;
- } else {
- it->planeOffsetX = 0;
- }
+void GfxFrameout::kernelDeleteScreenItem(const reg_t object) {
+ _segMan->getObject(object)->clearInfoSelectorFlag(kInfoFlagViewInserted);
- if (it->planeRect.top < 0) {
- it->planeOffsetY = -it->planeRect.top;
- it->planeRect.top = 0;
- } else {
- it->planeOffsetY = 0;
- }
+ 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;
+ }
- // We get bad plane-bottom in sq6
- if (it->planeRect.right > _screen->getWidth())
- it->planeRect.right = _screen->getWidth();
- if (it->planeRect.bottom > _screen->getHeight())
- it->planeRect.bottom = _screen->getHeight();
+// TODO: Remove
+// debug("Deleting screen item %04x:%04x from plane %04x:%04x", PRINT_REG(object), PRINT_REG(planeObject));
- it->planeClipRect = Common::Rect(it->planeRect.width(), it->planeRect.height());
- it->upscaledPlaneRect = it->planeRect;
- it->upscaledPlaneClipRect = it->planeClipRect;
- if (_screen->getUpscaledHires()) {
- _screen->adjustToUpscaledCoordinates(it->upscaledPlaneRect.top, it->upscaledPlaneRect.left);
- _screen->adjustToUpscaledCoordinates(it->upscaledPlaneRect.bottom, it->upscaledPlaneRect.right);
- _screen->adjustToUpscaledCoordinates(it->upscaledPlaneClipRect.top, it->upscaledPlaneClipRect.left);
- _screen->adjustToUpscaledCoordinates(it->upscaledPlaneClipRect.bottom, it->upscaledPlaneClipRect.right);
- }
+ 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;
+ }
- it->planePictureMirrored = readSelectorValue(_segMan, object, SELECTOR(mirrored));
- it->planeBack = readSelectorValue(_segMan, object, SELECTOR(back));
+ if (screenItem->_created == 0) {
+ screenItem->_created = 0;
+ screenItem->_updated = 0;
+ screenItem->_deleted = getScreenCount();
+ } else {
+ plane->_screenItemList.erase(screenItem);
+ plane->_screenItemList.pack();
+ }
+}
- sortPlanes();
+#pragma mark -
+#pragma mark Planes
- // Update the items in the plane
- for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
- reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane));
- if (object == itemPlane) {
- kernelUpdateScreenItem((*listIterator)->object);
- }
- }
+void GfxFrameout::kernelAddPlane(const reg_t object) {
+ Plane *plane = _planes.findByObject(object);
+ if (plane != nullptr) {
+ plane->update(object);
+ updatePlane(*plane);
+ } else {
+ plane = new Plane(object);
+ addPlane(*plane);
+ }
+}
- return;
- }
+void GfxFrameout::kernelUpdatePlane(const reg_t object) {
+ Plane *plane = _planes.findByObject(object);
+ if (plane == nullptr) {
+ error("Invalid plane selector passed to kUpdatePlane");
}
- error("kUpdatePlane called on plane that wasn't added before");
+
+ plane->update(object);
+ updatePlane(*plane);
}
-void GfxFrameout::kernelDeletePlane(reg_t object) {
- deletePlaneItems(object);
- deletePlanePictures(object);
+void GfxFrameout::kernelDeletePlane(const reg_t object) {
+ Plane *plane = _planes.findByObject(object);
+ if (plane == nullptr) {
+ error("Invalid plane selector passed to kDeletePlane");
+ }
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
- if (it->object == object) {
- _planes.erase(it);
- Common::Rect planeRect;
- planeRect.top = readSelectorValue(_segMan, object, SELECTOR(top));
- planeRect.left = readSelectorValue(_segMan, object, SELECTOR(left));
- planeRect.bottom = readSelectorValue(_segMan, object, SELECTOR(bottom));
- planeRect.right = readSelectorValue(_segMan, object, SELECTOR(right));
+ if (plane->_created) {
+ // NOTE: The original engine calls some `AbortPlane` function that
+ // 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();
+ }
+}
- _coordAdjuster->fromScriptToDisplay(planeRect.top, planeRect.left);
- _coordAdjuster->fromScriptToDisplay(planeRect.bottom, planeRect.right);
+int16 GfxFrameout::kernelGetHighPlanePri() {
+ return _planes.getTopSciPlanePriority();
+}
- // Blackout removed plane rect
- _paint32->fillRect(planeRect, 0);
- return;
+void GfxFrameout::addPlane(Plane &plane) {
+ if (_planes.findByObject(plane._object) == nullptr) {
+ plane.clipScreenRect(_screenRect);
+ _planes.add(&plane);
+ } else {
+ plane._deleted = 0;
+ if (plane._created == 0) {
+ plane._moved = g_sci->_gfxFrameout->getScreenCount();
}
+ _planes.sort();
}
}
-void GfxFrameout::addPlanePicture(reg_t object, GuiResourceId pictureId, uint16 startX, uint16 startY) {
- if (pictureId == kPlanePlainColored || pictureId == kPlaneTranslucent) // sanity check
- return;
+void GfxFrameout::updatePlane(Plane &plane) {
+ // NOTE: This assertion comes from SCI engine code.
+ assert(_planes.findByObject(plane._object) == &plane);
- PlanePictureEntry newPicture;
- newPicture.object = object;
- newPicture.pictureId = pictureId;
- newPicture.picture = new GfxPicture(_resMan, _coordAdjuster, 0, _screen, _palette, pictureId, false);
- newPicture.startX = startX;
- newPicture.startY = startY;
- newPicture.pictureCels = 0;
- _planePictures.push_back(newPicture);
+ Plane *visiblePlane = _visiblePlanes.findByObject(plane._object);
+ plane.sync(visiblePlane, _screenRect);
+ // NOTE: updateScreenRect was originally called a second time here,
+ // but it is already called at the end of the Plane::Update call
+ // in the original engine anyway.
+
+ _planes.sort();
}
-void GfxFrameout::deletePlanePictures(reg_t object) {
- PlanePictureList::iterator it = _planePictures.begin();
+#pragma mark -
+#pragma mark Pics
- while (it != _planePictures.end()) {
- if (it->object == object || object.isNull()) {
- delete it->pictureCels;
- delete it->picture;
- it = _planePictures.erase(it);
- } else {
- ++it;
- }
+void GfxFrameout::kernelAddPicAt(const reg_t planeObject, const GuiResourceId pictureId, const int16 x, const int16 y, const bool mirrorX) {
+ Plane *plane = _planes.findByObject(planeObject);
+ if (plane == nullptr) {
+ error("Invalid plane selector passed to kAddPicAt");
}
+ plane->addPic(pictureId, Common::Point(x, y), mirrorX);
}
-// Provides the same functionality as kGraph(DrawLine)
-reg_t GfxFrameout::addPlaneLine(reg_t object, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control) {
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
- if (it->object == object) {
- PlaneLineEntry line;
- line.hunkId = _segMan->allocateHunkEntry("PlaneLine()", 1); // we basically use this for a unique ID
- line.startPoint = startPoint;
- line.endPoint = endPoint;
- line.color = color;
- line.priority = priority;
- line.control = control;
- it->lines.push_back(line);
- return line.hunkId;
+#pragma mark -
+#pragma mark Rendering
+
+void GfxFrameout::frameOut(const bool shouldShowBits, const Common::Rect &rect) {
+// TODO: Robot
+// if (_robot != nullptr) {
+// _robot.doRobot();
+// }
+
+ // 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());
+
+ // _numActiveRemaps was a global in SCI engine
+ if (/* TODO Remap::_numActiveRemaps > 0 */ false && _remapOccurred) {
+ // remapMarkRedraw();
+ }
+
+ calcLists(screenItemLists, eraseLists, rect);
+
+ 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();
}
}
- return NULL_REG;
+ _remapOccurred = _palette->updateForFrame();
+
+ // NOTE: SCI engine set this to false on each loop through the
+ // planelist iterator below. Since that is a waste, we only set
+ // it once.
+ _frameNowVisible = false;
+
+ for (PlaneList::size_type i = 0; i < _planes.size(); ++i) {
+ drawEraseList(eraseLists[i], *_planes[i]);
+ drawScreenItemList(screenItemLists[i]);
+ }
+
+// TODO: Robot
+// if (_robot != nullptr) {
+// _robot->frameAlmostVisible();
+// }
+
+ _palette->updateHardware();
+
+ if (shouldShowBits) {
+ showBits();
+ }
+
+ _frameNowVisible = true;
+
+// TODO: Robot
+// if (_robot != nullptr) {
+// robot->frameNowVisible();
+// }
}
-void GfxFrameout::updatePlaneLine(reg_t object, reg_t hunkId, Common::Point startPoint, Common::Point endPoint, byte color, byte priority, byte control) {
- // Check if we're asked to update a line that was never added
- if (hunkId.isNull())
- return;
+int splitRects(Common::Rect r, const Common::Rect &other, Common::Rect(&outRects)[4]) {
+ if (!r.intersects(other)) {
+ return -1;
+ }
+
+ int count = 0;
+ if (r.top < other.top) {
+ Common::Rect &t = outRects[count++];
+ t = r;
+ t.bottom = other.top;
+ r.top = other.top;
+ }
+
+ if (r.bottom > other.bottom) {
+ Common::Rect &t = outRects[count++];
+ t = r;
+ t.top = other.bottom;
+ r.bottom = other.bottom;
+ }
+
+ if (r.left < other.left) {
+ Common::Rect &t = outRects[count++];
+ t = r;
+ t.right = other.left;
+ r.left = other.left;
+ }
+
+ if (r.right > other.right) {
+ Common::Rect &t = outRects[count++];
+ t = r;
+ t.left = other.right;
+ }
+
+ return count;
+}
+
+void GfxFrameout::calcLists(ScreenItemListList &drawLists, EraseListList &eraseLists, const Common::Rect &calcRect) {
+ RectList rectlist;
+ Common::Rect outRects[4];
+
+ int deletedPlaneCount = 0;
+ bool addedToRectList = false;
+ int planeCount = _planes.size();
+ bool foundTransparentPlane = false;
+
+ if (!calcRect.isEmpty()) {
+ addedToRectList = true;
+ rectlist.add(calcRect);
+ }
+
+ for (int outerPlaneIndex = 0; outerPlaneIndex < planeCount; ++outerPlaneIndex) {
+ Plane *outerPlane = _planes[outerPlaneIndex];
+
+ if (outerPlane->_type == kPlaneTypeTransparent) {
+ foundTransparentPlane = true;
+ }
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
- if (it->object == object) {
- for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) {
- if (it2->hunkId == hunkId) {
- it2->startPoint = startPoint;
- it2->endPoint = endPoint;
- it2->color = color;
- it2->priority = priority;
- it2->control = control;
- return;
+ Plane *visiblePlane = _visiblePlanes.findByObject(outerPlane->_object);
+
+ if (outerPlane->_deleted) {
+ if (visiblePlane != nullptr) {
+ if (!visiblePlane->_screenRect.isEmpty()) {
+ addedToRectList = true;
+ rectlist.add(visiblePlane->_screenRect);
+ }
+ }
+ ++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 {
+ for (int i = 0; i < splitcount; ++i) {
+ rectlist.add(outRects[i]);
+ }
+ }
+
+ addedToRectList = 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;
+ }
}
}
}
- }
-}
-void GfxFrameout::deletePlaneLine(reg_t object, reg_t hunkId) {
- // Check if we're asked to delete a line that was never added (happens during the intro of LSL6)
- if (hunkId.isNull())
- return;
+ 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));
+ }
+
+ int splitCount = splitRects(**rect, innerPlane->_screenRect, outRects);
+ for (int i = 0; i < splitCount; ++i) {
+ rectlist.add(outRects[i]);
+ }
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); ++it) {
- if (it->object == object) {
- for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) {
- if (it2->hunkId == hunkId) {
- _segMan->freeHunkEntry(hunkId);
- it2 = it->lines.erase(it2);
- return;
+ rectlist.erase(rect);
+ break;
+ }
}
}
+
+ rectlist.pack();
}
}
-}
-// Adapted from GfxAnimate::applyGlobalScaling()
-void GfxFrameout::applyGlobalScaling(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 celHeight) {
- // Global scaling uses global var 2 and some other stuff to calculate scaleX/scaleY
- int16 maxScale = readSelectorValue(_segMan, itemEntry->object, SELECTOR(maxScale));
- int16 maxCelHeight = (maxScale * celHeight) >> 7;
- reg_t globalVar2 = g_sci->getEngineState()->variables[VAR_GLOBAL][2]; // current room object
- int16 vanishingY = readSelectorValue(_segMan, globalVar2, SELECTOR(vanishingY));
+ // clean up deleted planes
+ if (deletedPlaneCount) {
+ for (int planeIndex = planeCount - 1; planeIndex >= 0; --planeIndex) {
+ Plane *plane = _planes[planeIndex];
+
+ 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);
+ }
- int16 fixedPortY = planeRect.bottom - vanishingY;
- int16 fixedEntryY = itemEntry->y - vanishingY;
- if (!fixedEntryY)
- fixedEntryY = 1;
+ _planes.remove_at(planeIndex);
+ eraseLists.remove_at(planeIndex);
+ drawLists.remove_at(planeIndex);
+ }
- if ((celHeight == 0) || (fixedPortY == 0))
- error("global scaling panic");
+ if (--deletedPlaneCount <= 0) {
+ break;
+ }
+ }
+ }
+ }
- itemEntry->scaleY = (maxCelHeight * fixedEntryY) / fixedPortY;
- itemEntry->scaleY = (itemEntry->scaleY * maxScale) / celHeight;
+ 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);
+
+ rectlist.add(outerPlane->_screenRect.findIntersectingRect(visibleOuterPlane->_screenRect));
+
+ for (int innerIndex = 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);
+
+ 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]);
+ } else {
+ eraseLists[outerIndex].add(*rectlist[rectIndex]);
+ }
+ }
+ }
+
+ rectlist.erase_at(rectIndex);
+ } else if (splitCount != -1) {
+ for (int i = 0; i < splitCount; ++i) {
+ rectlist.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]);
+ }
+ }
+ }
+ rectlist.erase_at(rectIndex);
+ }
+ }
+ rectlist.pack();
+ }
+ }
+ }
- // Make sure that the calculated value is sane
- if (itemEntry->scaleY < 1 /*|| itemEntry->scaleY > 128*/)
- itemEntry->scaleY = 128;
+ for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) {
+ Plane *plane = _planes[planeIndex];
+ Plane *visiblePlane = nullptr;
- itemEntry->scaleX = itemEntry->scaleY;
+ PlaneList::iterator visiblePlaneIt = Common::find_if(_visiblePlanes.begin(), _visiblePlanes.end(), FindByObject<Plane *>(plane->_object));
+ if (visiblePlaneIt != _visiblePlanes.end()) {
+ visiblePlane = *visiblePlaneIt;
+ }
- // and set objects scale selectors
- //writeSelectorValue(_segMan, itemEntry->object, SELECTOR(scaleX), itemEntry->scaleX);
- //writeSelectorValue(_segMan, itemEntry->object, SELECTOR(scaleY), itemEntry->scaleY);
-}
+ 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));
+ }
-void GfxFrameout::kernelAddScreenItem(reg_t object) {
- // Ignore invalid items
- if (!_segMan->isObject(object)) {
- warning("kernelAddScreenItem: Attempt to add an invalid object (%04x:%04x)", PRINT_REG(object));
- return;
+ plane->calcLists(*visiblePlane, _planes, drawLists[planeIndex], eraseLists[planeIndex]);
+ }
+
+ if (plane->_created) {
+ _visiblePlanes.add(new Plane(*plane));
+ --plane->_created;
+ } else if (plane->_moved) {
+ assert(visiblePlaneIt != _visiblePlanes.end());
+ **visiblePlaneIt = *plane;
+ --plane->_moved;
+ }
}
- FrameoutEntry *itemEntry = new FrameoutEntry();
- memset(itemEntry, 0, sizeof(FrameoutEntry));
- itemEntry->object = object;
- itemEntry->givenOrderNr = _screenItems.size();
- itemEntry->visible = true;
- _screenItems.push_back(itemEntry);
+ if (foundTransparentPlane) {
+ for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) {
+ for (int 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) {
+ _planes[i]->filterDownEraseRects(drawLists[i], eraseLists[i], eraseLists[planeIndex]);
+ }
+
+ if (eraseLists[planeIndex].size() > 0) {
+ error("Transparent plane's erase list not absorbed");
+ }
+ }
- kernelUpdateScreenItem(object);
+ for (int i = planeIndex + 1; i < planeCount; ++i) {
+ if (_planes[i]->_type == kPlaneTypeTransparent) {
+ _planes[i]->filterUpDrawRects(drawLists[i], drawLists[planeIndex]);
+ }
+ }
+ }
+ }
}
-void GfxFrameout::kernelUpdateScreenItem(reg_t object) {
- // Ignore invalid items
- if (!_segMan->isObject(object)) {
- warning("kernelUpdateScreenItem: Attempt to update an invalid object (%04x:%04x)", PRINT_REG(object));
+void GfxFrameout::drawEraseList(const RectList &eraseList, const Plane &plane) {
+ if (plane._type != kPlaneTypeColored) {
return;
}
- FrameoutEntry *itemEntry = findScreenItem(object);
- if (!itemEntry) {
- warning("kernelUpdateScreenItem: invalid object %04x:%04x", PRINT_REG(object));
- return;
+ for (RectList::const_iterator it = eraseList.begin(); it != eraseList.end(); ++it) {
+ mergeToShowList(**it, _showList, _overdrawThreshold);
+ _currentBuffer.fillRect(**it, 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;
+ }
+ }
}
- itemEntry->viewId = readSelectorValue(_segMan, object, SELECTOR(view));
- itemEntry->loopNo = readSelectorValue(_segMan, object, SELECTOR(loop));
- itemEntry->celNo = readSelectorValue(_segMan, object, SELECTOR(cel));
- itemEntry->x = readSelectorValue(_segMan, object, SELECTOR(x));
- itemEntry->y = readSelectorValue(_segMan, object, SELECTOR(y));
- itemEntry->z = readSelectorValue(_segMan, object, SELECTOR(z));
- itemEntry->priority = readSelectorValue(_segMan, object, SELECTOR(priority));
- if (readSelectorValue(_segMan, object, SELECTOR(fixPriority)) == 0)
- itemEntry->priority = itemEntry->y;
+ for (DrawList::const_iterator it = screenItemList.begin(); it != screenItemList.end(); ++it) {
+ DrawItem &drawItem = **it;
+ mergeToShowList(drawItem.rect, _showList, _overdrawThreshold);
+ 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);
+ CelObj &celObj = *screenItem._celObj;
+ celObj.draw(_currentBuffer, screenItem, drawItem.rect, screenItem._mirrorX ^ celObj._mirrorX);
+ }
+}
- itemEntry->signal = readSelectorValue(_segMan, object, SELECTOR(signal));
- itemEntry->scaleSignal = readSelectorValue(_segMan, object, SELECTOR(scaleSignal));
+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;
+ }
+ }
- if (itemEntry->scaleSignal & kScaleSignalDoScaling32) {
- itemEntry->scaleX = readSelectorValue(_segMan, object, SELECTOR(scaleX));
- itemEntry->scaleY = readSelectorValue(_segMan, object, SELECTOR(scaleY));
- } else {
- itemEntry->scaleX = 128;
- itemEntry->scaleY = 128;
+ count = showList.pack();
}
- itemEntry->visible = true;
- // Check if the entry can be hidden
- if (lookupSelector(_segMan, object, SELECTOR(visible), NULL, NULL) != kSelectorNone)
- itemEntry->visible = readSelectorValue(_segMan, object, SELECTOR(visible));
+ showList.add(merged);
}
-void GfxFrameout::kernelDeleteScreenItem(reg_t object) {
- FrameoutEntry *itemEntry = findScreenItem(object);
- // If the item could not be found, it may already have been deleted
- if (!itemEntry)
- return;
+void GfxFrameout::palMorphFrameOut(const int8 *styleRanges, const ShowStyleEntry *showStyle) {
+ Palette sourcePalette(*_palette->getNextPalette());
+ alterVmap(sourcePalette, sourcePalette, -1, styleRanges);
- _screenItems.remove(itemEntry);
- delete itemEntry;
-}
+ // 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();
-void GfxFrameout::deletePlaneItems(reg_t planeObject) {
- FrameoutList::iterator listIterator = _screenItems.begin();
+ Common::Rect rect(_screen->getDisplayWidth(), _screen->getDisplayHeight());
+ _showList.add(rect);
+ showBits();
- while (listIterator != _screenItems.end()) {
- bool objectMatches = false;
- if (!planeObject.isNull()) {
- reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane));
- objectMatches = (planeObject == itemPlane);
- } else {
- objectMatches = true;
+ 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();
}
+ }
- if (objectMatches) {
- FrameoutEntry *itemEntry = *listIterator;
- listIterator = _screenItems.erase(listIterator);
- delete itemEntry;
- } else {
- ++listIterator;
+ _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 (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;
+ }
}
}
-}
-FrameoutEntry *GfxFrameout::findScreenItem(reg_t object) {
- for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
- FrameoutEntry *itemEntry = *listIterator;
- if (itemEntry->object == object)
- return itemEntry;
+ _palette->submit(sourcePalette);
+ _palette->updateFFrame();
+ _palette->updateHardware();
+ alterVmap(nextPalette, sourcePalette, 1, _styleRanges);
+
+ if (showStyle->type > 0 && showStyle->type < 15) {
+// TODO: SCI2.1mid transition effects
+// processEffects();
+ warning("Transition not implemented!");
+ } else {
+ showBits();
}
- return NULL;
-}
+ _frameNowVisible = true;
-int16 GfxFrameout::kernelGetHighPlanePri() {
- sortPlanes();
- return readSelectorValue(g_sci->getEngineState()->_segMan, _planes.back().object, SELECTOR(priority));
-}
+ for (PlaneList::iterator plane = _planes.begin(); plane != _planes.end(); ++plane) {
+// TODO:
+// plane->updateRedrawAllCount();
+ }
-void GfxFrameout::kernelAddPicAt(reg_t planeObj, GuiResourceId pictureId, int16 pictureX, int16 pictureY) {
- addPlanePicture(planeObj, pictureId, pictureX, pictureY);
-}
+ // TODO: Remap
+ // _numActiveRemaps was a global in SCI engine
+ // if (Remap::_numActiveRemaps > 0 && _remapOccurred) {
+ // _screen->remapMarkRedraw();
+ // }
-bool sortHelper(const FrameoutEntry* entry1, const FrameoutEntry* entry2) {
- if (entry1->priority == entry2->priority) {
- if (entry1->y == entry2->y)
- return (entry1->givenOrderNr < entry2->givenOrderNr);
- return (entry1->y < entry2->y);
+ calcLists(screenItemLists, eraseLists, calcRect);
+ for (ScreenItemListList::iterator list = screenItemLists.begin(); list != screenItemLists.end(); ++list) {
+ list->sort();
}
- return (entry1->priority < entry2->priority);
-}
-bool planeSortHelper(const PlaneEntry &entry1, const PlaneEntry &entry2) {
- if (entry1.priority < 0)
- return true;
+ 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]);
+ }
- if (entry2.priority < 0)
- return false;
+ _palette->submit(nextPalette);
+ _palette->updateFFrame();
+ _palette->updateHardware();
+ showBits();
- return entry1.priority < entry2.priority;
+ _frameNowVisible = true;
}
-void GfxFrameout::sortPlanes() {
- // First, remove any invalid planes
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end();) {
- if (!_segMan->isObject(it->object))
- it = _planes.erase(it);
- else
- it++;
+// 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() {
+ 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
+ // different masking here to ensure that the width of rects
+ // was always even.
+ rounded.left &= ~1;
+ rounded.right = (rounded.right + 1) & ~1;
+
+ // TODO:
+ // _cursor->GonnaPaint(rounded);
}
- // Sort the rest of them
- Common::sort(_planes.begin(), _planes.end(), planeSortHelper);
+ // TODO:
+ // _cursor->PaintStarting();
+
+ 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
+ // different masking here to ensure that the width of rects
+ // was always even.
+ rounded.left &= ~1;
+ rounded.right = (rounded.right + 1) & ~1;
+
+ byte *sourceBuffer = (byte *)_currentBuffer.getPixels() + rounded.top * _currentBuffer.screenWidth + rounded.left;
+
+ g_system->copyRectToScreen(sourceBuffer, _currentBuffer.screenWidth, rounded.left, rounded.top, rounded.width(), rounded.height());
+ }
+
+ // TODO:
+ // _cursor->DonePainting();
+
+ _showList.clear();
}
-void GfxFrameout::showVideo() {
- bool skipVideo = false;
- RobotDecoder *videoDecoder = g_sci->_robotDecoder;
- uint16 x = videoDecoder->getPos().x;
- uint16 y = videoDecoder->getPos().y;
- uint16 screenWidth = _screen->getWidth();
- uint16 screenHeight = _screen->getHeight();
- uint16 outputWidth;
- uint16 outputHeight;
+void GfxFrameout::alterVmap(const Palette &palette1, const Palette &palette2, const int8 style, const int8 *const styleRanges) {
+ uint8 clut[256];
+
+ for (int paletteIndex = 0; paletteIndex < ARRAYSIZE(palette1.colors); ++paletteIndex) {
+ int outerR = palette1.colors[paletteIndex].r;
+ int outerG = palette1.colors[paletteIndex].g;
+ int outerB = palette1.colors[paletteIndex].b;
+
+ if (styleRanges[paletteIndex] == style) {
+ int minDiff = 262140;
+ int minDiffIndex;
+
+ for (int i = 0; i < 236; ++i) {
+ if (styleRanges[i] != style) {
+ int r = palette1.colors[i].r;
+ int g = palette1.colors[i].g;
+ int b = palette1.colors[i].b;
+ int diffSquared = (outerR - r) * (outerR - r) + (outerG - g) * (outerG - g) + (outerB - b) * (outerB - b);
+ if (diffSquared < minDiff) {
+ minDiff = diffSquared;
+ minDiffIndex = i;
+ }
+ }
+ }
- if (videoDecoder->hasDirtyPalette())
- g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256);
+ clut[paletteIndex] = minDiffIndex;
+ }
- while (!g_engine->shouldQuit() && !videoDecoder->endOfVideo() && !skipVideo) {
- if (videoDecoder->needsUpdate()) {
- const Graphics::Surface *frame = videoDecoder->decodeNextFrame();
- if (frame) {
- // We need to clip here
- // At least Phantasmagoria shows a 640x390 video on a 630x450 screen during the intro
- outputWidth = frame->w > screenWidth ? screenWidth : frame->w;
- outputHeight = frame->h > screenHeight ? screenHeight : frame->h;
- g_system->copyRectToScreen(frame->getPixels(), frame->pitch, x, y, outputWidth, outputHeight);
+ if (style == 1 && styleRanges[paletteIndex] == 0) {
+ int minDiff = 262140;
+ int minDiffIndex;
- if (videoDecoder->hasDirtyPalette())
- g_system->getPaletteManager()->setPalette(videoDecoder->getPalette(), 0, 256);
+ for (int i = 0; i < 236; ++i) {
+ int r = palette2.colors[i].r;
+ int g = palette2.colors[i].g;
+ int b = palette2.colors[i].b;
- g_system->updateScreen();
+ int diffSquared = (outerR - r) * (outerR - r) + (outerG - g) * (outerG - g) + (outerB - b) * (outerB - b);
+ if (diffSquared < minDiff) {
+ minDiff = diffSquared;
+ minDiffIndex = i;
+ }
}
+
+ clut[paletteIndex] = minDiffIndex;
}
+ }
- Common::Event event;
- while (g_system->getEventManager()->pollEvent(event)) {
- if ((event.type == Common::EVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_ESCAPE) || event.type == Common::EVENT_LBUTTONUP)
- skipVideo = true;
+ // 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]];
}
- g_system->delayMillis(10);
+ if (
+ (styleRangeValue == 1 && styleRangeValue == style) ||
+ (styleRangeValue == 0 && style == 1)
+ ) {
+ pixels[pixelIndex] = clut[currentValue];
+ }
}
}
-void GfxFrameout::createPlaneItemList(reg_t planeObject, FrameoutList &itemList) {
- // Copy screen items of the current frame to the list of items to be drawn
- for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
- reg_t itemPlane = readSelector(_segMan, (*listIterator)->object, SELECTOR(plane));
- if (planeObject == itemPlane) {
- kernelUpdateScreenItem((*listIterator)->object); // TODO: Why is this necessary?
- itemList.push_back(*listIterator);
+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;
}
- for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) {
- if (pictureIt->object == planeObject) {
- GfxPicture *planePicture = pictureIt->picture;
- // Allocate memory for picture cels
- pictureIt->pictureCels = new FrameoutEntry[planePicture->getSci32celCount()];
+ return entry;
+}
+
+inline ShowStyleEntry *GfxFrameout::deleteShowStyleInternal(ShowStyleEntry *const showStyle) {
+ ShowStyleEntry *lastEntry = nullptr;
- // Add following cels to the itemlist
- FrameoutEntry *picEntry = pictureIt->pictureCels;
- int planePictureCels = planePicture->getSci32celCount();
- for (int pictureCelNr = 0; pictureCelNr < planePictureCels; pictureCelNr++) {
- picEntry->celNo = pictureCelNr;
- picEntry->object = NULL_REG;
- picEntry->picture = planePicture;
- picEntry->y = planePicture->getSci32celY(pictureCelNr);
- picEntry->x = planePicture->getSci32celX(pictureCelNr);
- picEntry->picStartX = pictureIt->startX;
- picEntry->picStartY = pictureIt->startY;
- picEntry->visible = true;
+ for (ShowStyleEntry *testEntry = _showStyles; testEntry != nullptr; testEntry = testEntry->next) {
+ if (testEntry == showStyle) {
+ break;
+ }
+ lastEntry = testEntry;
+ }
- picEntry->priority = planePicture->getSci32celPriority(pictureCelNr);
+ if (lastEntry == nullptr) {
+ _showStyles = showStyle->next;
+ lastEntry = _showStyles;
+ } else {
+ lastEntry->next = showStyle->next;
+ }
- itemList.push_back(picEntry);
- picEntry++;
+ // 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;
}
}
+ } else {
+#endif
+ delete[] showStyle->fadeColorRanges;
+#if 0
}
+#endif
+
+ delete showStyle;
- // Now sort our itemlist
- Common::sort(itemList.begin(), itemList.end(), sortHelper);
+ // TODO: Verify that this is the correct entry to return
+ // for the loop in processShowStyles to work correctly
+ return lastEntry;
}
-bool GfxFrameout::isPictureOutOfView(FrameoutEntry *itemEntry, Common::Rect planeRect, int16 planeOffsetX, int16 planeOffsetY) {
- // Out of view horizontally (sanity checks)
- int16 pictureCelStartX = itemEntry->picStartX + itemEntry->x;
- int16 pictureCelEndX = pictureCelStartX + itemEntry->picture->getSci32celWidth(itemEntry->celNo);
- int16 planeStartX = planeOffsetX;
- int16 planeEndX = planeStartX + planeRect.width();
- if (pictureCelEndX < planeStartX)
- return true;
- if (pictureCelStartX > planeEndX)
- return true;
+// 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;
+ }
- // Out of view vertically (sanity checks)
- int16 pictureCelStartY = itemEntry->picStartY + itemEntry->y;
- int16 pictureCelEndY = pictureCelStartY + itemEntry->picture->getSci32celHeight(itemEntry->celNo);
- int16 planeStartY = planeOffsetY;
- int16 planeEndY = planeStartY + planeRect.height();
- if (pictureCelEndY < planeStartY)
- return true;
- if (pictureCelStartY > planeEndY)
- return true;
+ bool isFadeUp;
+ int16 color;
+ if (back != -1) {
+ isFadeUp = false;
+ color = back;
+ } else {
+ isFadeUp = true;
+ color = 0;
+ }
- return false;
-}
+ 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];
+ }
-void GfxFrameout::drawPicture(FrameoutEntry *itemEntry, int16 planeOffsetX, int16 planeOffsetY, bool planePictureMirrored) {
- int16 pictureOffsetX = planeOffsetX;
- int16 pictureX = itemEntry->x;
- if ((planeOffsetX) || (itemEntry->picStartX)) {
- if (planeOffsetX <= itemEntry->picStartX) {
- pictureX += itemEntry->picStartX - planeOffsetX;
- pictureOffsetX = 0;
+ if (useExisting) {
+ createNewEntry = false;
+ isFadeUp = true;
+ entry->currentStep = 0;
} else {
- pictureOffsetX = planeOffsetX - itemEntry->picStartX;
+ isFadeUp = true;
+ color = entry->color;
+ deleteShowStyleInternal(entry/*, true*/);
+ entry = nullptr;
}
}
- int16 pictureOffsetY = planeOffsetY;
- int16 pictureY = itemEntry->y;
- if ((planeOffsetY) || (itemEntry->picStartY)) {
- if (planeOffsetY <= itemEntry->picStartY) {
- pictureY += itemEntry->picStartY - planeOffsetY;
- pictureOffsetY = 0;
- } else {
- pictureOffsetY = planeOffsetY - itemEntry->picStartY;
+ 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;
}
}
+}
- itemEntry->picture->drawSci32Vga(itemEntry->celNo, pictureX, itemEntry->y, pictureOffsetX, pictureOffsetY, planePictureMirrored);
- // warning("picture cel %d %d", itemEntry->celNo, itemEntry->priority);
+#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);
}
-/* TODO: This is the proper implementation of GraphicsMgr::FrameOut transcribed from SQ6 SCI engine disassembly.
-static DrawList* g_drawLists[100];
-static RectList* g_rectLists[100];
-void GfxFrameout::FrameOut(bool shouldShowBits, SOL_Rect *rect) {
- if (robot) {
- robot.doRobot();
+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;
+ } 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);
+ }
}
+}
- auto planeCount = screen.planeList.planeCount;
- if (planeCount > 0) {
- for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) {
- Plane plane = *screen.planeList[planeIndex];
+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;
+ }
+
+ 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;
- DrawList* drawList = new DrawList();
- g_drawLists[planeIndex] = drawList;
- RectList* rectList = new RectList();
- g_rectLists[planeIndex] = rectList;
+ 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);
}
-
- if (g_Remap_numActiveRemaps > 0 && remapNeeded) {
- screen.RemapMarkRedraw();
+}
+
+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;
}
-
- CalcLists(&g_drawLists, &g_rectLists, rect);
- // SCI engine stores reference *after* CalcLists
- planeCount = screen.planeList.planeCount;
- if (planeCount > 0) {
- for (int drawListIndex = 0; drawListIndex < planeCount; ++i) {
- DrawList* drawList = g_drawLists[drawListIndex];
- drawList->Sort();
- }
+ 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;
+ }
+ }
+ }
- for (int drawListIndex = 0; drawListIndex < planeCount; ++i) {
- DrawList* drawList = g_drawLists[drawListIndex];
- if (drawList == nullptr || drawList->count == 0) {
- continue;
+ if (!retval) {
+ continueProcessing = true;
}
- for (int screenItemIndex = 0, screenItemCount = drawList->count; screenItemIndex < screenItemCount; ++screenItemIndex) {
- ScreenItem* screenItem = drawList->items[screenItemIndex];
- screenItem->GetCelObj()->SubmitPalette();
+ 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);
}
- // UpdateForFrame is where all palette mutations occur (cycles, varies, etc.)
- bool remapNeeded = GPalette().UpdateForFrame();
- if (planeCount > 0) {
- frameNowVisible = false;
+ showStyle->processed = true;
+ return true;
+}
- for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) {
- Plane* plane = screen.planeList[planeIndex];
+bool GfxFrameout::processShowStyleMorph(ShowStyleEntry *const showStyle) {
+ palMorphFrameOut(_styleRanges, showStyle);
+ showStyle->processed = true;
+ return true;
+}
- DrawEraseList(g_rectLists[planeIndex], plane);
- DrawScreenItemsList(g_drawLists[planeIndex]);
+// 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;
}
- }
- if (robot) {
- robot.FrameAlmostVisible();
- }
+ percent *= 100;
+ percent /= showStyle->divisions - 1;
- GPalette().UpdateHardware();
+ 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);
+ }
- if (shouldShowBits) {
- ShowBits();
+ ++showStyle->currentStep;
+ showStyle->nextTick += showStyle->delay;
+ unchanged = false;
}
- frameNowVisible = true;
+ if (showStyle->currentStep >= showStyle->divisions && unchanged) {
+ if (direction > 0) {
+ showStyle->processed = true;
+ }
- if (robot) {
- robot.FrameNowVisible();
+ return true;
}
- if (planeCount > 0) {
- for (int planeIndex = 0; planeIndex < planeCount; ++planeIndex) {
- if (g_rectLists[planeIndex] != nullptr) {
- delete g_rectLists[planeIndex];
+ 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();
}
- if (g_drawLists[planeIndex] != nullptr) {
- delete g_drawLists[planeIndex];
+
+ 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;
}
}
-}
-void GfxFrameout::CalcLists(DrawList **drawLists, RectList **rectLists, SOL_Rect *rect) {
- screen.CalcLists(&visibleScreen, drawLists, rectLists, rect);
-}
-*/
-void GfxFrameout::kernelFrameout() {
- if (g_sci->_robotDecoder->isVideoLoaded()) {
- showVideo();
- return;
+
+ 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();
+ }
}
- _palette->updateForFrame();
+ 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;
+ }
- // TODO: Tons of drawing stuff should be here, see commented out implementation above
+ index *= showStyle->edgeCount;
- _palette->updateHardware();
+ 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);
- for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) {
- reg_t planeObject = it->object;
+ delete screenItem;
+ showStyle->screenItems[index + i] = nullptr;
+ }
+ } else {
+ ScreenItem *screenItem = showStyle->screenItems[index + i];
+ // TODO: _screen->addScreenItem(screenItem);
+ _screenItems.push_back(screenItem);
+ }
+ }
- // Draw any plane lines, if they exist
- // These are drawn on invisible planes as well. (e.g. "invisiblePlane" in LSL6 hires)
- // FIXME: Lines aren't always drawn (e.g. when the narrator speaks in LSL6 hires).
- // Perhaps something is painted over them?
- for (PlaneLineList::iterator it2 = it->lines.begin(); it2 != it->lines.end(); ++it2) {
- Common::Point startPoint = it2->startPoint;
- Common::Point endPoint = it2->endPoint;
- _coordAdjuster->kernelLocalToGlobal(startPoint.x, startPoint.y, it->object);
- _coordAdjuster->kernelLocalToGlobal(endPoint.x, endPoint.y, it->object);
- _screen->drawLine(startPoint, endPoint, it2->color, it2->priority, it2->control);
+ ++showStyle->currentStep;
+ showStyle->nextTick += showStyle->delay;
}
+ }
- int16 planeLastPriority = it->lastPriority;
-
- // Update priority here, sq6 sets it w/o UpdatePlane
- int16 planePriority = it->priority = readSelectorValue(_segMan, planeObject, SELECTOR(priority));
-
- it->lastPriority = planePriority;
- if (planePriority < 0) { // Plane currently not meant to be shown
- // If plane was shown before, delete plane rect
- if (planePriority != planeLastPriority)
- _paint32->fillRect(it->planeRect, 0);
- continue;
+ if (showStyle->currentStep >= showStyle->divisions) {
+ if (showStyle->fadeUp) {
+ showStyle->processed = true;
}
- // There is a race condition lurking in SQ6, which causes the game to hang in the intro, when teleporting to Polysorbate LX.
- // Since I first wrote the patch, the race has stopped occurring for me though.
- // I'll leave this for investigation later, when someone can reproduce.
- //if (it->pictureId == kPlanePlainColored) // FIXME: This is what SSCI does, and fixes the intro of LSL7, but breaks the dialogs in GK1 (adds black boxes)
- if (it->pictureId == kPlanePlainColored && (it->planeBack || g_sci->getGameId() != GID_GK1))
- _paint32->fillRect(it->planeRect, it->planeBack);
+ return true;
+ }
+
+ return false;
+}
- _coordAdjuster->pictureSetDisplayArea(it->planeRect);
- // Invoking drewPicture() with an invalid picture ID in SCI32 results in
- // invalidating the palVary palette when a palVary effect is active. This
- // is quite obvious in QFG4, where the day time palette is incorrectly
- // shown when exiting the caves, and the correct night time palette
- // flashes briefly each time that kPalVaryInit is called.
- if (it->pictureId != 0xFFFF)
- _palette->drewPicture(it->pictureId);
+void fillRect(byte *data, const Common::Rect &rect, const int16 color, const int16 stride) {
- FrameoutList itemList;
+}
- createPlaneItemList(planeObject, itemList);
+bool GfxFrameout::processShowStyle11(ShowStyleEntry *const showStyle) {
+ int divisions = showStyle->divisions * showStyle->divisions;
- for (FrameoutList::iterator listIterator = itemList.begin(); listIterator != itemList.end(); listIterator++) {
- FrameoutEntry *itemEntry = *listIterator;
+ byte *bitmapData = _segMan->getHunkPointer(showStyle->bitmapMemId) + sizeof(GfxBitmapHeader);
- if (!itemEntry->visible)
- continue;
+ int ebx;
- if (itemEntry->object.isNull()) {
- // Picture cel data
- _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x);
- _coordAdjuster->fromScriptToDisplay(itemEntry->picStartY, itemEntry->picStartX);
+ if (showStyle->currentStep == 0) {
+ int ctr = 0;
+ int bloot = divisions;
+ do {
+ bloot >>= 1;
+ ++ctr;
+ } while (bloot != 1);
- if (!isPictureOutOfView(itemEntry, it->planeRect, it->planeOffsetX, it->planeOffsetY))
- drawPicture(itemEntry, it->planeOffsetX, it->planeOffsetY, it->planePictureMirrored);
+ 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 {
- GfxView *view = (itemEntry->viewId != 0xFFFF) ? _cache->getView(itemEntry->viewId) : NULL;
- int16 dummyX = 0;
-
- if (view && view->isSci2Hires()) {
- view->adjustToUpscaledCoordinates(itemEntry->y, itemEntry->x);
- view->adjustToUpscaledCoordinates(itemEntry->z, dummyX);
- } else if (getSciVersion() >= SCI_VERSION_2_1_EARLY) {
- _coordAdjuster->fromScriptToDisplay(itemEntry->y, itemEntry->x);
- _coordAdjuster->fromScriptToDisplay(itemEntry->z, dummyX);
- }
+ ebx = eax;
+ }
+ } while (ebx >= divisions);
- // Adjust according to current scroll position
- itemEntry->x -= it->planeOffsetX;
- itemEntry->y -= it->planeOffsetY;
-
- uint16 useInsetRect = readSelectorValue(_segMan, itemEntry->object, SELECTOR(useInsetRect));
- if (useInsetRect) {
- itemEntry->celRect.top = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inTop));
- itemEntry->celRect.left = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inLeft));
- itemEntry->celRect.bottom = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inBottom));
- itemEntry->celRect.right = readSelectorValue(_segMan, itemEntry->object, SELECTOR(inRight));
- if (view && view->isSci2Hires()) {
- view->adjustToUpscaledCoordinates(itemEntry->celRect.top, itemEntry->celRect.left);
- view->adjustToUpscaledCoordinates(itemEntry->celRect.bottom, itemEntry->celRect.right);
- }
- itemEntry->celRect.translate(itemEntry->x, itemEntry->y);
- // TODO: maybe we should clip the cels rect with this, i'm not sure
- // the only currently known usage is game menu of gk1
- } else if (view) {
- // Process global scaling, if needed.
- // TODO: Seems like SCI32 always processes global scaling for scaled objects
- // TODO: We can only process symmetrical scaling for now (i.e. same value for scaleX/scaleY)
- if ((itemEntry->scaleSignal & kScaleSignalDoScaling32) &&
- !(itemEntry->scaleSignal & kScaleSignalDisableGlobalScaling32) &&
- (itemEntry->scaleX == itemEntry->scaleY) &&
- itemEntry->scaleX != 128)
- applyGlobalScaling(itemEntry, it->planeRect, view->getHeight(itemEntry->loopNo, itemEntry->celNo));
-
- if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
- view->getCelRect(itemEntry->loopNo, itemEntry->celNo,
- itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->celRect);
- else
- view->getCelScaledRect(itemEntry->loopNo, itemEntry->celNo,
- itemEntry->x, itemEntry->y, itemEntry->z, itemEntry->scaleX,
- itemEntry->scaleY, itemEntry->celRect);
-
- Common::Rect nsRect = itemEntry->celRect;
- // Translate back to actual coordinate within scrollable plane
- nsRect.translate(it->planeOffsetX, it->planeOffsetY);
-
- if (g_sci->getGameId() == GID_PHANTASMAGORIA2) {
- // HACK: Some (?) objects in Phantasmagoria 2 have no NS rect. Skip them for now.
- // TODO: Remove once we figure out how Phantasmagoria 2 draws objects on screen.
- if (lookupSelector(_segMan, itemEntry->object, SELECTOR(nsLeft), NULL, NULL) != kSelectorVariable)
- continue;
- }
+ if (ebx == showStyle->dissolveInitial) {
+ ebx = 0;
+ }
+ }
- if (view && view->isSci2Hires()) {
- view->adjustBackUpscaledCoordinates(nsRect.top, nsRect.left);
- view->adjustBackUpscaledCoordinates(nsRect.bottom, nsRect.right);
- g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect);
- } else if (getSciVersion() >= SCI_VERSION_2_1_EARLY && _isHiRes) {
- _coordAdjuster->fromDisplayToScript(nsRect.top, nsRect.left);
- _coordAdjuster->fromDisplayToScript(nsRect.bottom, nsRect.right);
- g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect);
- }
+ Common::Rect rect;
- // TODO: For some reason, the top left nsRect coordinates get
- // swapped in the GK1 inventory screen, investigate why.
- // This is also needed for GK1 rooms 710 and 720 (catacombs, inner and
- // outer circle), for handling the tiles and talking to Wolfgang.
- // HACK: Fix the coordinates by explicitly setting them here for GK1.
- // Also check bug #6729, for another case where this is needed.
- if (g_sci->getGameId() == GID_GK1)
- g_sci->_gfxCompare->setNSRect(itemEntry->object, nsRect);
- }
+ rect.left = (showStyle->width + showStyle->divisions - 1) / showStyle->divisions;
+ rect.top = (showStyle->height + showStyle->divisions - 1) / showStyle->divisions;
- // Don't attempt to draw sprites that are outside the visible
- // screen area. An example is the random people walking in
- // Jackson Square in GK1.
- if (itemEntry->celRect.bottom < 0 || itemEntry->celRect.top >= _screen->getDisplayHeight() ||
- itemEntry->celRect.right < 0 || itemEntry->celRect.left >= _screen->getDisplayWidth())
- continue;
+ if (showStyle->currentStep <= showStyle->divisions) {
+ int ebp = 0;
+ do {
+ int ecx = ebx % showStyle->divisions;
- Common::Rect clipRect, translatedClipRect;
- clipRect = itemEntry->celRect;
+ 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;
- if (view && view->isSci2Hires()) {
- clipRect.clip(it->upscaledPlaneClipRect);
- translatedClipRect = clipRect;
- translatedClipRect.translate(it->upscaledPlaneRect.left, it->upscaledPlaneRect.top);
- } else {
- // QFG4 passes invalid rectangles when a battle is starting
- if (!clipRect.isValidRect())
- continue;
- clipRect.clip(it->planeClipRect);
- translatedClipRect = clipRect;
- translatedClipRect.translate(it->planeRect.left, it->planeRect.top);
+ 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 (view) {
- if (!clipRect.isEmpty()) {
- if ((itemEntry->scaleX == 128) && (itemEntry->scaleY == 128))
- view->draw(itemEntry->celRect, clipRect, translatedClipRect,
- itemEntry->loopNo, itemEntry->celNo, 255, 0, view->isSci2Hires());
- else
- view->drawScaled(itemEntry->celRect, clipRect, translatedClipRect,
- itemEntry->loopNo, itemEntry->celNo, 255, itemEntry->scaleX, itemEntry->scaleY);
- }
+ if (drawRect.top < 0) {
+ drawRect.top = 0;
}
- // Draw text, if it exists
- if (lookupSelector(_segMan, itemEntry->object, SELECTOR(text), NULL, NULL) == kSelectorVariable) {
- g_sci->_gfxText32->drawTextBitmap(itemEntry->x, itemEntry->y, it->planeRect, itemEntry->object);
+ 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;
}
- }
- for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) {
- if (pictureIt->object == planeObject) {
- delete[] pictureIt->pictureCels;
- pictureIt->pictureCels = 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 (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);
}
- }
+
+ } while (ebp <= showStyle->divisions);
+
+ showStyle->unknown3A = ebx;
+ ++showStyle->currentStep;
+ showStyle->nextTick += showStyle->delay;
+ // _screen->updateScreenItem(showStyle->bitmapScreenItem);
}
- showCurrentScrollText();
+ if (showStyle->currentStep >= showStyle->divisions) {
+ if (showStyle->fadeUp) {
+ showStyle->processed = true;
+ }
- _screen->copyToScreen();
+ return true;
+ }
- g_sci->getEngineState()->_throttleTrigger = true;
+ return false;
}
-void GfxFrameout::printPlaneList(Console *con) {
- for (PlaneList::const_iterator it = _planes.begin(); it != _planes.end(); ++it) {
- PlaneEntry p = *it;
- Common::String curPlaneName = _segMan->getObjectName(p.object);
- Common::Rect r = p.upscaledPlaneRect;
- Common::Rect cr = p.upscaledPlaneClipRect;
+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();
+// }
- con->debugPrintf("%04x:%04x (%s): prio %d, lastprio %d, offsetX %d, offsetY %d, pic %d, mirror %d, back %d\n",
- PRINT_REG(p.object), curPlaneName.c_str(),
- (int16)p.priority, (int16)p.lastPriority,
- p.planeOffsetX, p.planeOffsetY, p.pictureId,
- p.planePictureMirrored, p.planeBack);
- con->debugPrintf(" rect: (%d, %d, %d, %d), clip rect: (%d, %d, %d, %d)\n",
- r.left, r.top, r.right, r.bottom,
- cr.left, cr.top, cr.right, cr.bottom);
+ Common::Rect frameOutRect(0, 0);
+ frameOut(shouldShowBits, frameOutRect);
+ }
+}
- if (p.pictureId != 0xffff && p.pictureId != 0xfffe) {
- con->debugPrintf("Pictures:\n");
+#pragma mark -
+#pragma mark Debugging
- for (PlanePictureList::iterator pictureIt = _planePictures.begin(); pictureIt != _planePictures.end(); pictureIt++) {
- if (pictureIt->object == p.object) {
- con->debugPrintf(" Picture %d: x %d, y %d\n", pictureIt->pictureId, pictureIt->startX, pictureIt->startY);
- }
- }
- }
+void GfxFrameout::printPlaneListInternal(Console *con, const PlaneList &planeList) const {
+ for (PlaneList::const_iterator it = planeList.begin(); it != planeList.end(); ++it) {
+ Plane *p = *it;
+ p->printDebugInfo(con);
}
}
-void GfxFrameout::printPlaneItemList(Console *con, reg_t planeObject) {
- for (FrameoutList::iterator listIterator = _screenItems.begin(); listIterator != _screenItems.end(); listIterator++) {
- FrameoutEntry *e = *listIterator;
- reg_t itemPlane = readSelector(_segMan, e->object, SELECTOR(plane));
+void GfxFrameout::printPlaneList(Console *con) const {
+ printPlaneListInternal(con, _planes);
+}
+
+void GfxFrameout::printVisiblePlaneList(Console *con) const {
+ printPlaneListInternal(con, _visiblePlanes);
+}
- if (planeObject == itemPlane) {
- Common::String curItemName = _segMan->getObjectName(e->object);
- Common::Rect icr = e->celRect;
- GuiResourceId picId = e->picture ? e->picture->getResourceId() : 0;
+void GfxFrameout::printPlaneItemList(Console *con, const reg_t planeObject) const {
+ Plane *p = _planes.findByObject(planeObject);
- con->debugPrintf("%d: %04x:%04x (%s), view %d, loop %d, cel %d, x %d, y %d, z %d, "
- "signal %d, scale signal %d, scaleX %d, scaleY %d, rect (%d, %d, %d, %d), "
- "pic %d, picX %d, picY %d, visible %d\n",
- e->givenOrderNr, PRINT_REG(e->object), curItemName.c_str(),
- e->viewId, e->loopNo, e->celNo, e->x, e->y, e->z,
- e->signal, e->scaleSignal, e->scaleX, e->scaleY,
- icr.left, icr.top, icr.right, icr.bottom,
- picId, e->picStartX, e->picStartY, e->visible);
- }
+ if (p == nullptr) {
+ con->debugPrintf("Plane does not exist");
+ 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);
}
}