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