aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics/screen_item32.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/screen_item32.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/screen_item32.cpp')
-rw-r--r--engines/sci/graphics/screen_item32.cpp534
1 files changed, 534 insertions, 0 deletions
diff --git a/engines/sci/graphics/screen_item32.cpp b/engines/sci/graphics/screen_item32.cpp
new file mode 100644
index 0000000000..300912f110
--- /dev/null
+++ b/engines/sci/graphics/screen_item32.cpp
@@ -0,0 +1,534 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "sci/console.h"
+#include "sci/resource.h"
+#include "sci/engine/kernel.h"
+#include "sci/engine/selector.h"
+#include "sci/engine/state.h"
+#include "sci/graphics/celobj32.h"
+#include "sci/graphics/frameout.h"
+#include "sci/graphics/screen_item32.h"
+#include "sci/graphics/view.h"
+
+namespace Sci {
+#pragma mark ScreenItem
+
+uint16 ScreenItem::_nextObjectId = 20000;
+
+ScreenItem::ScreenItem(const reg_t object) :
+_mirrorX(false),
+_pictureId(-1),
+_celObj(nullptr),
+_object(object),
+_deleted(0),
+_updated(0),
+_created(g_sci->_gfxFrameout->getScreenCount()) {
+ SegManager *segMan = g_sci->getEngineState()->_segMan;
+
+ setFromObject(segMan, object, true, true);
+ _plane = readSelector(segMan, object, SELECTOR(plane));
+}
+
+ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo) :
+_position(0, 0),
+_z(0),
+_object(make_reg(0, _nextObjectId++)),
+_celInfo(celInfo),
+_plane(plane),
+_celObj(nullptr),
+_useInsetRect(false),
+_fixPriority(false),
+_mirrorX(false),
+_pictureId(-1),
+_updated(0),
+_deleted(0),
+_created(g_sci->_gfxFrameout->getScreenCount()) {}
+
+ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect) :
+_position(rect.left, rect.top),
+_z(0),
+_object(make_reg(0, _nextObjectId++)),
+_celInfo(celInfo),
+_plane(plane),
+_celObj(nullptr),
+_useInsetRect(false),
+_fixPriority(false),
+_mirrorX(false),
+_pictureId(-1),
+_updated(0),
+_deleted(0),
+_created(g_sci->_gfxFrameout->getScreenCount()) {
+ if (celInfo.type == kCelTypeColor) {
+ _insetRect = rect;
+ }
+}
+
+ScreenItem::ScreenItem(const reg_t plane, const CelInfo32 &celInfo, const Common::Rect &rect, const ScaleInfo &scaleInfo) :
+_position(rect.left, rect.top),
+_z(0),
+_object(make_reg(0, _nextObjectId++)),
+_celInfo(celInfo),
+_plane(plane),
+_celObj(nullptr),
+_useInsetRect(false),
+_fixPriority(false),
+_mirrorX(false),
+_pictureId(-1),
+_updated(0),
+_deleted(0),
+_created(g_sci->_gfxFrameout->getScreenCount()),
+_scale(scaleInfo) {}
+
+ScreenItem::ScreenItem(const ScreenItem &other) :
+_object(other._object),
+_plane(other._plane),
+_celInfo(other._celInfo),
+_celObj(nullptr),
+_screenRect(other._screenRect),
+_mirrorX(other._mirrorX),
+_useInsetRect(other._useInsetRect),
+_scale(other._scale),
+_scaledPosition(other._scaledPosition) {
+ if (other._useInsetRect) {
+ _insetRect = other._insetRect;
+ }
+}
+
+void ScreenItem::operator=(const ScreenItem &other) {
+ _celInfo = other._celInfo;
+ _screenRect = other._screenRect;
+ _mirrorX = other._mirrorX;
+ _useInsetRect = other._useInsetRect;
+ if (other._useInsetRect) {
+ _insetRect = other._insetRect;
+ }
+ _scale = other._scale;
+ _scaledPosition = other._scaledPosition;
+}
+
+void ScreenItem::init() {
+ _nextObjectId = 20000;
+}
+
+void ScreenItem::setFromObject(SegManager *segMan, const reg_t object, const bool updateCel, const bool updateBitmap) {
+ _position.x = readSelectorValue(segMan, object, SELECTOR(x));
+ _position.y = readSelectorValue(segMan, object, SELECTOR(y));
+ _scale.x = readSelectorValue(segMan, object, SELECTOR(scaleX));
+ _scale.y = readSelectorValue(segMan, object, SELECTOR(scaleY));
+ _scale.max = readSelectorValue(segMan, object, SELECTOR(maxScale));
+ _scale.signal = (ScaleSignals32)(readSelectorValue(segMan, object, SELECTOR(scaleSignal)) & 3);
+
+ if (updateCel) {
+ _celInfo.resourceId = (GuiResourceId)readSelectorValue(segMan, object, SELECTOR(view));
+ _celInfo.loopNo = readSelectorValue(segMan, object, SELECTOR(loop));
+ _celInfo.celNo = readSelectorValue(segMan, object, SELECTOR(cel));
+
+ if (_celInfo.resourceId <= kPlanePic) {
+ // TODO: Enhance GfxView or ResourceManager to allow
+ // metadata for resources to be retrieved once, from a
+ // single location
+ Resource *view = g_sci->getResMan()->findResource(ResourceId(kResourceTypeView, _celInfo.resourceId), false);
+ if (!view) {
+ error("Failed to load resource %d", _celInfo.resourceId);
+ }
+
+ // NOTE: +2 because the header size field itself is excluded from
+ // the header size in the data
+ const uint16 headerSize = READ_SCI11ENDIAN_UINT16(view->data) + 2;
+ const uint8 loopCount = view->data[2];
+ const uint8 loopSize = view->data[12];
+
+ if (_celInfo.loopNo >= loopCount) {
+ const int maxLoopNo = loopCount - 1;
+ _celInfo.loopNo = maxLoopNo;
+ writeSelectorValue(segMan, object, SELECTOR(loop), maxLoopNo);
+ }
+
+ byte *loopData = view->data + headerSize + (_celInfo.loopNo * loopSize);
+ const int8 seekEntry = loopData[0];
+ if (seekEntry != -1) {
+ loopData = view->data + headerSize + (seekEntry * loopSize);
+ }
+ const uint8 celCount = loopData[2];
+ if (_celInfo.celNo >= celCount) {
+ const int maxCelNo = celCount - 1;
+ _celInfo.celNo = maxCelNo;
+ writeSelectorValue(segMan, object, SELECTOR(cel), maxCelNo);
+ }
+ }
+ }
+
+ if (updateBitmap) {
+ const reg_t bitmap = readSelector(segMan, object, SELECTOR(bitmap));
+ if (!bitmap.isNull()) {
+ _celInfo.bitmap = bitmap;
+ _celInfo.type = kCelTypeMem;
+ } else {
+ _celInfo.bitmap = NULL_REG;
+ _celInfo.type = kCelTypeView;
+ }
+ }
+
+ if (updateCel || updateBitmap) {
+ delete _celObj;
+ _celObj = nullptr;
+ }
+
+ if (readSelectorValue(segMan, object, SELECTOR(fixPriority))) {
+ _fixPriority = true;
+ _priority = readSelectorValue(segMan, object, SELECTOR(priority));
+ } else {
+ _fixPriority = false;
+ writeSelectorValue(segMan, object, SELECTOR(priority), _position.y);
+ }
+
+ _z = readSelectorValue(segMan, object, SELECTOR(z));
+ _position.y -= _z;
+
+ if (readSelectorValue(segMan, object, SELECTOR(useInsetRect))) {
+ _useInsetRect = true;
+ _insetRect.left = readSelectorValue(segMan, object, SELECTOR(inLeft));
+ _insetRect.top = readSelectorValue(segMan, object, SELECTOR(inTop));
+ _insetRect.right = readSelectorValue(segMan, object, SELECTOR(inRight));
+ _insetRect.bottom = readSelectorValue(segMan, object, SELECTOR(inBottom));
+ } else {
+ _useInsetRect = false;
+ }
+
+ // TODO: SCI2.1/SQ6 engine clears this flag any time ScreenItem::Update(MemID)
+ // or ScreenItem::ScreenItem(MemID) are called, but doing this breaks
+ // view cycling because the flag isn't being set again later. There are over
+ // 100 places in the engine code where this flag is set, so it is probably
+ // a matter of figuring out what all of those calls are that re-set it. For
+ // now, since these are the *only* calls that clear this flag, we can just
+ // leave it set all the time.
+ // segMan->getObject(object)->clearInfoSelectorFlag(kInfoFlagViewVisible);
+}
+
+void ScreenItem::calcRects(const Plane &plane) {
+ const int16 scriptWidth = g_sci->_gfxFrameout->getCurrentBuffer().scriptWidth;
+ const int16 scriptHeight = g_sci->_gfxFrameout->getCurrentBuffer().scriptHeight;
+ const int16 screenWidth = g_sci->_gfxFrameout->getCurrentBuffer().screenWidth;
+ const int16 screenHeight = g_sci->_gfxFrameout->getCurrentBuffer().screenHeight;
+
+ Common::Rect celRect(_celObj->_width, _celObj->_height);
+ if (_useInsetRect) {
+ if (_insetRect.intersects(celRect)) {
+ _insetRect.clip(celRect);
+ } else {
+ _insetRect = Common::Rect();
+ }
+ } else {
+ _insetRect = celRect;
+ }
+
+ Ratio newRatioX;
+ Ratio newRatioY;
+
+ if (_scale.signal & kScaleSignalDoScaling32) {
+ if (_scale.signal & kScaleSignalUseVanishingPoint) {
+ int num = _scale.max * (_position.y - plane._vanishingPoint.y) / (scriptWidth - plane._vanishingPoint.y);
+ newRatioX = Ratio(num, 128);
+ newRatioY = Ratio(num, 128);
+ } else {
+ newRatioX = Ratio(_scale.x, 128);
+ newRatioY = Ratio(_scale.y, 128);
+ }
+ }
+
+ if (newRatioX.getNumerator() && newRatioY.getNumerator()) {
+ _screenItemRect = _insetRect;
+
+ if (_celObj->_scaledWidth != scriptWidth || _celObj->_scaledHeight != scriptHeight) {
+ if (_useInsetRect) {
+ Ratio celScriptXRatio(_celObj->_scaledWidth, scriptWidth);
+ Ratio celScriptYRatio(_celObj->_scaledHeight, scriptHeight);
+ mulru(_screenItemRect, celScriptXRatio, celScriptYRatio);
+
+ if (_screenItemRect.intersects(celRect)) {
+ _screenItemRect.clip(celRect);
+ } else {
+ _screenItemRect = Common::Rect();
+ }
+ }
+
+ int displaceX = _celObj->_displace.x;
+ int displaceY = _celObj->_displace.y;
+
+ if (_mirrorX != _celObj->_mirrorX && _celInfo.type != kCelTypePic) {
+ displaceX = _celObj->_width - _celObj->_displace.x - 1;
+ }
+
+ if (!newRatioX.isOne() || !newRatioY.isOne()) {
+ mulru(_screenItemRect, newRatioX, newRatioY);
+ displaceX = (displaceX * newRatioX).toInt();
+ displaceY = (displaceY * newRatioY).toInt();
+ }
+
+ Ratio celXRatio(screenWidth, _celObj->_scaledWidth);
+ Ratio celYRatio(screenHeight, _celObj->_scaledHeight);
+
+ displaceX = (displaceX * celXRatio).toInt();
+ displaceY = (displaceY * celYRatio).toInt();
+
+ mulru(_screenItemRect, celXRatio, celYRatio);
+
+ if (/* TODO: dword_C6288 */ false && _celInfo.type == kCelTypePic) {
+ _scaledPosition.x = _position.x;
+ _scaledPosition.y = _position.y;
+ } else {
+ _scaledPosition.x = (_position.x * screenWidth / scriptWidth) - displaceX;
+ _scaledPosition.y = (_position.y * screenHeight / scriptHeight) - displaceY;
+ }
+
+ _screenItemRect.translate(_scaledPosition.x, _scaledPosition.y);
+
+ if (_mirrorX != _celObj->_mirrorX && _celInfo.type == kCelTypePic) {
+ Common::Rect temp(_insetRect);
+
+ if (!newRatioX.isOne()) {
+ mulru(temp, newRatioX, Ratio());
+ }
+
+ mulru(temp, celXRatio, Ratio());
+
+ CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj);
+
+ temp.translate(celObjPic->_relativePosition.x * screenWidth / scriptWidth - displaceX, 0);
+
+ // TODO: This is weird, and probably wrong calculation of widths
+ // due to BR-inclusion
+ int deltaX = plane._planeRect.right - plane._planeRect.left + 1 - temp.right - 1 - temp.left;
+
+ _scaledPosition.x += deltaX;
+ _screenItemRect.translate(deltaX, 0);
+ }
+
+ _scaledPosition.x += plane._planeRect.left;
+ _scaledPosition.y += plane._planeRect.top;
+ _screenItemRect.translate(plane._planeRect.left, plane._planeRect.top);
+
+ _ratioX = newRatioX * Ratio(screenWidth, _celObj->_scaledWidth);
+ _ratioY = newRatioY * Ratio(screenHeight, _celObj->_scaledHeight);
+ } else {
+ int displaceX = _celObj->_displace.x;
+ if (_mirrorX != _celObj->_mirrorX && _celInfo.type != kCelTypePic) {
+ displaceX = _celObj->_width - _celObj->_displace.x - 1;
+ }
+
+ if (!newRatioX.isOne() || !newRatioY.isOne()) {
+ mulru(_screenItemRect, newRatioX, newRatioY);
+ // TODO: This was in the original code, baked into the
+ // multiplication though it is not immediately clear
+ // why this is the only one that reduces the BR corner
+ _screenItemRect.right -= 1;
+ _screenItemRect.bottom -= 1;
+ }
+
+ _scaledPosition.x = _position.x - (displaceX * newRatioX).toInt();
+ _scaledPosition.y = _position.y - (_celObj->_displace.y * newRatioY).toInt();
+ _screenItemRect.translate(_scaledPosition.x, _scaledPosition.y);
+
+ if (_mirrorX != _celObj->_mirrorX && _celInfo.type == kCelTypePic) {
+ Common::Rect temp(_insetRect);
+
+ if (!newRatioX.isOne()) {
+ mulru(temp, newRatioX, Ratio());
+ temp.right -= 1;
+ }
+
+ CelObjPic *celObjPic = dynamic_cast<CelObjPic *>(_celObj);
+ temp.translate(celObjPic->_relativePosition.x - (displaceX * newRatioX).toInt(), celObjPic->_relativePosition.y - (_celObj->_displace.y * newRatioY).toInt());
+
+ // TODO: This is weird, and probably wrong calculation of widths
+ // due to BR-inclusion
+ int deltaX = plane._gameRect.right - plane._gameRect.left + 1 - temp.right - 1 - temp.left;
+
+ _scaledPosition.x += deltaX;
+ _screenItemRect.translate(deltaX, 0);
+ }
+
+ _scaledPosition.x += plane._gameRect.left;
+ _scaledPosition.y += plane._gameRect.top;
+ _screenItemRect.translate(plane._gameRect.left, plane._gameRect.top);
+
+ if (screenWidth != _celObj->_scaledWidth || _celObj->_scaledHeight != screenHeight) {
+ Ratio celXRatio(screenWidth, _celObj->_scaledWidth);
+ Ratio celYRatio(screenHeight, _celObj->_scaledHeight);
+ mulru(_scaledPosition, celXRatio, celYRatio);
+ mulru(_screenItemRect, celXRatio, celYRatio, 1);
+ }
+
+ _ratioX = newRatioX * Ratio(screenWidth, _celObj->_scaledWidth);
+ _ratioY = newRatioY * Ratio(screenHeight, _celObj->_scaledHeight);
+ }
+
+ _screenRect = _screenItemRect;
+
+ if (_screenRect.intersects(plane._screenRect)) {
+ _screenRect.clip(plane._screenRect);
+ } else {
+ _screenRect.right = 0;
+ _screenRect.bottom = 0;
+ _screenRect.left = 0;
+ _screenRect.top = 0;
+ }
+
+ if (!_fixPriority) {
+ _priority = _z + _position.y;
+ }
+ } else {
+ _screenRect.left = 0;
+ _screenRect.top = 0;
+ _screenRect.right = 0;
+ _screenRect.bottom = 0;
+ }
+}
+
+CelObj &ScreenItem::getCelObj() {
+ if (_celObj == nullptr) {
+ switch (_celInfo.type) {
+ case kCelTypeView:
+ _celObj = new CelObjView(_celInfo.resourceId, _celInfo.loopNo, _celInfo.celNo);
+ break;
+ case kCelTypePic:
+ error("Internal error, pic screen item with no cel.");
+ break;
+ case kCelTypeMem:
+ _celObj = new CelObjMem(_celInfo.bitmap);
+ break;
+ case kCelTypeColor:
+ _celObj = new CelObjColor(_celInfo.color, _insetRect.width(), _insetRect.height());
+ break;
+ }
+ }
+
+ return *_celObj;
+}
+
+void ScreenItem::printDebugInfo(Console *con) const {
+ con->debugPrintf("prio %d, x %d, y %d, z: %d, scaledX: %d, scaledY: %d flags: %d\n",
+ _priority,
+ _position.x,
+ _position.y,
+ _z,
+ _scaledPosition.x,
+ _scaledPosition.y,
+ _created | (_updated << 1) | (_deleted << 2)
+ );
+ con->debugPrintf(" screen rect (%d, %d, %d, %d)\n", PRINT_RECT(_screenRect));
+ if (_useInsetRect) {
+ con->debugPrintf(" inset rect: (%d, %d, %d, %d)\n", PRINT_RECT(_insetRect));
+ }
+
+ Common::String celType;
+ switch (_celInfo.type) {
+ case kCelTypePic:
+ celType = "pic";
+ break;
+ case kCelTypeView:
+ celType = "view";
+ break;
+ case kCelTypeColor:
+ celType = "color";
+ break;
+ case kCelTypeMem:
+ celType = "mem";
+ break;
+ }
+
+ con->debugPrintf(" type: %s, res %d, loop %d, cel %d, bitmap %04x:%04x, color: %d\n",
+ celType.c_str(),
+ _celInfo.resourceId,
+ _celInfo.loopNo,
+ _celInfo.celNo,
+ PRINT_REG(_celInfo.bitmap),
+ _celInfo.color
+ );
+ if (_celObj != nullptr) {
+ con->debugPrintf(" width %d, height %d, scaledWidth %d, scaledHeight %d\n",
+ _celObj->_width,
+ _celObj->_height,
+ _celObj->_scaledWidth,
+ _celObj->_scaledHeight
+ );
+ }
+}
+
+void ScreenItem::update(const reg_t object) {
+ SegManager *segMan = g_sci->getEngineState()->_segMan;
+
+ const GuiResourceId view = readSelectorValue(segMan, object, SELECTOR(view));
+ const int16 loopNo = readSelectorValue(segMan, object, SELECTOR(loop));
+ const int16 celNo = readSelectorValue(segMan, object, SELECTOR(cel));
+
+ const bool updateCel = (
+ _celInfo.resourceId != view ||
+ _celInfo.loopNo != loopNo ||
+ _celInfo.celNo != celNo
+ );
+
+ const bool updateBitmap = !readSelector(segMan, object, SELECTOR(bitmap)).isNull();
+
+ setFromObject(segMan, object, updateCel, updateBitmap);
+
+ if (!_created) {
+ _updated = g_sci->_gfxFrameout->getScreenCount();
+ }
+
+ _deleted = 0;
+}
+
+#pragma mark -
+#pragma mark ScreenItemList
+ScreenItem *ScreenItemList::findByObject(const reg_t object) const {
+ const_iterator screenItemIt = Common::find_if(begin(), end(), FindByObject<ScreenItem *>(object));
+
+ if (screenItemIt == end()) {
+ return nullptr;
+ }
+
+ return *screenItemIt;
+}
+void ScreenItemList::sort() {
+ // TODO: SCI engine used _unsorted as an array of indexes into the
+ // list itself and then performed the same swap operations on the
+ // _unsorted array as the _storage array during sorting, but the
+ // only reason to do this would be if some of the pointers in the
+ // list were replaced so the pointer values themselves couldn’t
+ // simply be recorded and then restored later. It is not yet
+ // verified whether this simplification of the sort/unsort is
+ // safe.
+ for (size_type i = 0; i < size(); ++i) {
+ _unsorted[i] = (*this)[i];
+ }
+
+ Common::sort(begin(), end(), sortHelper);
+}
+void ScreenItemList::unsort() {
+ for (size_type i = 0; i < size(); ++i) {
+ (*this)[i] = _unsorted[i];
+ }
+}
+
+}