aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics/palette32.cpp
diff options
context:
space:
mode:
authorColin Snover2016-01-14 10:50:41 -0600
committerColin Snover2016-01-14 16:13:22 -0600
commitfb891e4c081d5f0678cdda3855daa82728dc0ac0 (patch)
treeb12fc60daad3bb20d63be8c850023a8a40ec5f84 /engines/sci/graphics/palette32.cpp
parentf1cf07eded09f5f66a0915ddb79a890a3a11a362 (diff)
downloadscummvm-rg350-fb891e4c081d5f0678cdda3855daa82728dc0ac0.tar.gz
scummvm-rg350-fb891e4c081d5f0678cdda3855daa82728dc0ac0.tar.bz2
scummvm-rg350-fb891e4c081d5f0678cdda3855daa82728dc0ac0.zip
SCI: Implement SCI32 kPalVary and kPalette setFade
This also fixes kPalCycle signatures to be more accurate.
Diffstat (limited to 'engines/sci/graphics/palette32.cpp')
-rw-r--r--engines/sci/graphics/palette32.cpp508
1 files changed, 445 insertions, 63 deletions
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
index 95c4f99974..00e502c696 100644
--- a/engines/sci/graphics/palette32.cpp
+++ b/engines/sci/graphics/palette32.cpp
@@ -21,20 +21,153 @@
*/
#include "common/file.h"
+#include "common/system.h"
+#include "graphics/palette.h"
#include "sci/sci.h"
+#include "sci/event.h"
+#include "sci/resource.h"
#include "sci/graphics/palette32.h"
+#include "sci/graphics/screen.h"
namespace Sci {
GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen)
- : GfxPalette(resMan, screen), _clutTable(0), _cyclers(), _cycleMap() {}
+ : GfxPalette(resMan, screen),
+ _clutTable(nullptr),
+ // Palette cycling
+ _cyclers(), _cycleMap(),
+ // Palette varying
+ _sourcePalette(_sysPalette), _nextPalette(_sysPalette),
+ _varyTime(0), _varyDirection(0), _varyTargetPercent(0),
+ _varyTargetPalette(nullptr), _varyStartPalette(nullptr),
+ _varyFromColor(0), _varyToColor(255), _varyNumTimesPaused(0),
+ _varyPercent(_varyTargetPercent), _varyLastTick(0),
+ // Palette versioning
+ _version(1), _versionUpdated(false) {
+ memset(_fadeTable, 100, sizeof(_fadeTable));
+
+ // NOTE: In SCI engine, the palette manager constructor loads
+ // the default palette, but in ScummVM this initialisation
+ // is performed by SciEngine::run; see r49523 for details
+ }
GfxPalette32::~GfxPalette32() {
unloadClut();
+ varyOff();
cycleAllOff();
}
+inline void mergePaletteInternal(Palette *const to, const Palette *const from) {
+ for (int i = 0; i < ARRAYSIZE(to->colors); ++i) {
+ if (from->colors[i].used) {
+ to->colors[i] = from->colors[i];
+ }
+ }
+}
+
+void GfxPalette32::submit(Palette &palette) {
+ // TODO: The resource manager in SCI32 retains raw data of palettes from
+ // the ResourceManager (ResourceMgr) through SegManager (MemoryMgr), and
+ // the version number for submitted palettes is set in the raw palette
+ // data in memory as an int at an offset
+ // `rawData + *rawData[0x0a] * 2 + 31`. However, ScummVM does not retain
+ // resource data like this, so this versioning code, while accurate to
+ // the original engine, does not do much.
+ // (Hopefully this was an optimisation mechanism in SCI engine and not a
+ // clever thing to keep the same palette submitted many times from
+ // overwriting other palette entries.)
+ if (palette.timestamp == _version) {
+ return;
+ }
+
+ Palette oldSourcePalette(_sourcePalette);
+ mergePaletteInternal(&_sourcePalette, &palette);
+
+ if (!_versionUpdated && _sourcePalette != oldSourcePalette) {
+ ++_version;
+ _versionUpdated = true;
+ }
+
+ // Technically this information is supposed to be persisted through a
+ // HunkPalette object; right now it would just be lost once the temporary
+ // palette was destroyed.
+ palette.timestamp = _version;
+}
+
+override bool GfxPalette32::kernelSetFromResource(GuiResourceId resourceId, bool force) {
+ // TODO: In SCI32, palettes that come from resources come in as
+ // HunkPalette objects, not SOLPalette objects. The HunkPalettes
+ // have some extra persistence stuff associated with them, such that
+ // when they are passed to GfxPalette32::submit, they would get the
+ // version number of GfxPalette32 assigned to them.
+ Palette palette;
+
+ if (createPaletteFromResourceInternal(resourceId, &palette)) {
+ submit(palette);
+ return true;
+ }
+
+ return false;
+}
+
+// TODO: set is overridden for the time being to send palettes coming from
+// various draw methods like GfxPicture::drawSci32Vga and GfxView::draw to
+// _nextPalette instead of _sysPalette. In the SCI32 engine, CelObj palettes
+// (which are stored as Hunk palettes) are submitted by GraphicsMgr::FrameOut
+// to PaletteMgr::Submit by way of calls to CelObj::SubmitPalette.
+// GfxPalette::set is very similar to GfxPalette32::submit, except that SCI32
+// does not do any fancy best-fit merging and so does not accept arguments
+// like `force` and `forceRealMerge`.
+override void GfxPalette32::set(Palette *newPalette, bool force, bool forceRealMerge) {
+ submit(*newPalette);
+}
+
+void GfxPalette32::updateForFrame() {
+ applyAll();
+ _versionUpdated = false;
+ // TODO: Implement remapping
+ // g_sci->_gfxFrameout->remapAllTables(_nextPalette != _sysPalette);
+}
+
+void GfxPalette32::updateHardware() {
+ if (_sysPalette == _nextPalette) {
+ // TODO: This condition has never been encountered yet
+ debug("Skipping hardware update because palettes are identical");
+ return;
+ }
+
+ byte bpal[3 * 256];
+
+ for (int i = 0; i < ARRAYSIZE(_sysPalette.colors); ++i) {
+ _sysPalette.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
+ if (_sysPalette.colors[i].used) {
+ bpal[i * 3 ] = _sysPalette.colors[i].r;
+ bpal[i * 3 + 1] = _sysPalette.colors[i].g;
+ bpal[i * 3 + 2] = _sysPalette.colors[i].b;
+ }
+ }
+
+ g_system->getPaletteManager()->setPalette(bpal, 0, 256);
+ g_sci->getEventManager()->updateScreen();
+}
+
+void GfxPalette32::applyAll() {
+ applyVary();
+ applyCycles();
+ applyFade();
+}
+
+//
+// Clut
+//
+
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.
@@ -85,6 +218,252 @@ void GfxPalette32::unloadClut() {
_clutTable = 0;
}
+//
+// Palette vary
+//
+
+inline bool GfxPalette32::createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const {
+ Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, paletteId), false);
+
+ if (!palResource) {
+ return false;
+ }
+
+ createFromData(palResource->data, palResource->size, out);
+ return true;
+}
+
+inline Palette GfxPalette32::getPaletteFromResourceInternal(const GuiResourceId paletteId) const {
+ Palette palette;
+ if (!createPaletteFromResourceInternal(paletteId, &palette)) {
+ error("Could not load vary target %d", paletteId);
+ }
+ return palette;
+}
+
+inline void GfxPalette32::setVaryTimeInternal(const int16 percent, const int time) {
+ _varyLastTick = g_sci->getTickCount();
+ if (!time || _varyPercent == percent) {
+ _varyDirection = 0;
+ _varyTargetPercent = _varyPercent = percent;
+ } else {
+ _varyTime = time / (percent - _varyPercent);
+ _varyTargetPercent = percent;
+
+ if (_varyTime > 0) {
+ _varyDirection = 1;
+ } else if (_varyTime < 0) {
+ _varyDirection = -1;
+ _varyTime = -_varyTime;
+ } else {
+ _varyDirection = 0;
+ _varyTargetPercent = _varyPercent = percent;
+ }
+ }
+}
+
+// TODO: This gets called *a lot* in at least the first scene
+// of SQ6. Optimisation would not be the worst idea in the world.
+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);
+}
+
+override 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;
+ _varyFromColor = 0;
+ _varyToColor = 255;
+ _varyDirection = 0;
+
+ if (_varyTargetPalette != nullptr) {
+ delete _varyTargetPalette;
+ _varyTargetPalette = nullptr;
+ }
+
+ if (_varyStartPalette != nullptr) {
+ delete _varyStartPalette;
+ _varyStartPalette = nullptr;
+ }
+}
+
+void GfxPalette32::mergeTarget(const Palette *const palette) {
+ if (_varyTargetPalette != nullptr) {
+ mergePaletteInternal(_varyTargetPalette, palette);
+ } else {
+ _varyTargetPalette = new Palette(*palette);
+ }
+}
+
+void GfxPalette32::varyPause() {
+ _varyDirection = 0;
+ ++_varyNumTimesPaused;
+}
+
+void GfxPalette32::varyOn() {
+ if (_varyNumTimesPaused > 0) {
+ --_varyNumTimesPaused;
+ }
+
+ if (_varyTargetPalette != nullptr && _varyNumTimesPaused == 0 && _varyPercent != _varyTargetPercent) {
+ if (_varyTime == 0) {
+ _varyPercent = _varyTargetPercent;
+ } else if (_varyTargetPercent < _varyPercent) {
+ _varyDirection = -1;
+ } else {
+ _varyDirection = 1;
+ }
+ }
+}
+
+void GfxPalette32::setVaryTime(const int time) {
+ if (_varyTargetPalette == nullptr) {
+ return;
+ }
+
+ setVaryTimeInternal(_varyTargetPercent, time);
+}
+
+void GfxPalette32::setTarget(const Palette *const palette) {
+ if (_varyTargetPalette != nullptr) {
+ delete _varyTargetPalette;
+ }
+
+ _varyTargetPalette = new Palette(*palette);
+}
+
+void GfxPalette32::setStart(const Palette *const palette) {
+ if (_varyStartPalette != nullptr) {
+ delete _varyStartPalette;
+ }
+
+ _varyStartPalette = new Palette(*palette);
+}
+
+void GfxPalette32::mergeStart(const Palette *const palette) {
+ if (_varyStartPalette != nullptr) {
+ mergePaletteInternal(_varyStartPalette, palette);
+ } else {
+ _varyStartPalette = new Palette(*palette);
+ }
+}
+
+void GfxPalette32::applyVary() {
+ while (g_sci->getTickCount() - _varyLastTick > _varyTime && _varyDirection != 0) {
+ _varyLastTick += _varyTime;
+
+ if (_varyPercent == _varyTargetPercent) {
+ _varyDirection = 0;
+ }
+
+ _varyPercent += _varyDirection;
+ }
+
+ if (_varyPercent == 0 || _varyTargetPalette == nullptr) {
+ for (int i = 0, len = ARRAYSIZE(_nextPalette.colors); i < len; ++i) {
+ if (_varyStartPalette != nullptr && i >= _varyFromColor && i <= _varyToColor) {
+ _nextPalette.colors[i] = _varyStartPalette->colors[i];
+ } else {
+ _nextPalette.colors[i] = _sourcePalette.colors[i];
+ }
+ }
+ } else {
+ for (int i = 0, len = ARRAYSIZE(_nextPalette.colors); i < len; ++i) {
+ if (i >= _varyFromColor && i <= _varyToColor) {
+ Color targetColor = _varyTargetPalette->colors[i];
+ Color sourceColor;
+
+ if (_varyStartPalette != nullptr) {
+ sourceColor = _varyStartPalette->colors[i];
+ } else {
+ sourceColor = _sourcePalette.colors[i];
+ }
+
+ Color computedColor;
+
+ int color;
+ color = targetColor.r - sourceColor.r;
+ computedColor.r = ((color * _varyPercent) / 100) + sourceColor.r;
+ color = targetColor.g - sourceColor.g;
+ computedColor.g = ((color * _varyPercent) / 100) + sourceColor.g;
+ color = targetColor.b - sourceColor.b;
+ computedColor.b = ((color * _varyPercent) / 100) + sourceColor.b;
+ computedColor.used = sourceColor.used;
+
+ _nextPalette.colors[i] = computedColor;
+ }
+ else {
+ _nextPalette.colors[i] = _sourcePalette.colors[i];
+ }
+ }
+ }
+}
+
+//
+// Palette cycling
+//
+
inline void GfxPalette32::clearCycleMap(const uint16 fromColor, const uint16 numColorsToClear) {
bool *mapEntry = _cycleMap + fromColor;
const bool *lastEntry = _cycleMap + numColorsToClear;
@@ -130,47 +509,7 @@ inline void doCycleInternal(PalCycler *cycler, const int16 speed) {
cycler->currentCycle = (uint8) (currentCycle % numColorsToCycle);
}
-void GfxPalette32::applyAllCycles() {
- Color paletteCopy[256];
- memcpy(paletteCopy, _sysPalette.colors, sizeof(Color) * 256);
-
- for (int cyclerIndex = 0, numCyclers = ARRAYSIZE(_cyclers); cyclerIndex < numCyclers; ++cyclerIndex) {
- PalCycler *cycler = _cyclers[cyclerIndex];
- 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
- for (int j = 0; j < cycler->numColorsToCycle; j++) {
- _sysPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
- }
- }
- }
-}
-
-void GfxPalette32::applyCycles() {
- Color paletteCopy[256];
- memcpy(paletteCopy, _sysPalette.colors, sizeof(Color) * 256);
-
- for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
- PalCycler *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);
- cycler->lastUpdateTick += cycler->delay;
- }
- }
-
- for (int j = 0; j < cycler->numColorsToCycle; j++) {
- _sysPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
- }
- }
-}
-
-int16 GfxPalette32::setCycle(const uint16 fromColor, const uint16 toColor, const int16 direction, const int16 delay) {
+void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay) {
assert(fromColor < toColor);
int cyclerIndex;
@@ -179,7 +518,6 @@ int16 GfxPalette32::setCycle(const uint16 fromColor, const uint16 toColor, const
PalCycler *cycler = getCycler(fromColor);
if (cycler != nullptr) {
- //debug("Resetting existing cycler");
clearCycleMap(fromColor, cycler->numColorsToCycle);
} else {
for (cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) {
@@ -223,13 +561,9 @@ int16 GfxPalette32::setCycle(const uint16 fromColor, const uint16 toColor, const
cycler->numTimesPaused = 0;
setCycleMap(fromColor, numColorsToCycle);
-
- // TODO: Validate that this is the correct return value according
- // to disassembly
- return 0;
}
-void GfxPalette32::doCycle(const uint16 fromColor, const int16 speed) {
+void GfxPalette32::doCycle(const uint8 fromColor, const int16 speed) {
PalCycler *cycler = getCycler(fromColor);
if (cycler != nullptr) {
cycler->lastUpdateTick = g_sci->getTickCount();
@@ -237,14 +571,14 @@ void GfxPalette32::doCycle(const uint16 fromColor, const int16 speed) {
}
}
-void GfxPalette32::cycleOn(const uint16 fromColor) {
+void GfxPalette32::cycleOn(const uint8 fromColor) {
PalCycler *cycler = getCycler(fromColor);
if (cycler != nullptr && cycler->numTimesPaused > 0) {
--cycler->numTimesPaused;
}
}
-void GfxPalette32::cyclePause(const uint16 fromColor) {
+void GfxPalette32::cyclePause(const uint8 fromColor) {
PalCycler *cycler = getCycler(fromColor);
if (cycler != nullptr) {
++cycler->numTimesPaused;
@@ -289,7 +623,7 @@ void GfxPalette32::cycleAllPause() {
}
}
-void GfxPalette32::cycleOff(const uint16 fromColor) {
+void GfxPalette32::cycleOff(const uint8 fromColor) {
for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
PalCycler *cycler = _cyclers[i];
if (cycler != nullptr && cycler->fromColor == fromColor) {
@@ -312,27 +646,75 @@ void GfxPalette32::cycleAllOff() {
}
}
-void GfxPalette32::applyFade() {
- for (int i = 0; i < 256; ++i) {
- if (_fadeTable[i] == 100)
+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];
+ 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
+ for (int j = 0; j < cycler->numColorsToCycle; j++) {
+ _nextPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
+ }
+ }
+ }
+}
+
+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];
+ if (cycler == nullptr) {
continue;
+ }
- // TODO: Create and update a _nextPalette, not a single _sysPalette, to
- // conform to the way the actual SCI32 engine works (writes to a
- // next-frame-palette and then copies to the current palette on frameout)
- _sysPalette.colors[i].r = (_sysPalette.colors[i].r * _fadeTable[i]) / 100;
- _sysPalette.colors[i].g = (_sysPalette.colors[i].g * _fadeTable[i]) / 100;
- _sysPalette.colors[i].b = (_sysPalette.colors[i].b * _fadeTable[i]) / 100;
+ if (cycler->delay != 0 && cycler->numTimesPaused == 0) {
+ while ((cycler->delay + cycler->lastUpdateTick) < g_sci->getTickCount()) {
+ doCycleInternal(cycler, 1);
+ cycler->lastUpdateTick += cycler->delay;
+ }
+ }
+
+ for (int j = 0; j < cycler->numColorsToCycle; j++) {
+ _nextPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
+ }
}
}
-void GfxPalette32::setFade(uint8 percent, uint16 fromColor, uint16 toColor) {
- for (int i = fromColor; i <= toColor; i++)
+//
+// Palette fading
+//
+
+void GfxPalette32::setFade(uint8 percent, uint8 fromColor, uint16 numColorsToFade) {
+ if (fromColor > numColorsToFade) {
+ return;
+ }
+
+ assert(numColorsToFade <= ARRAYSIZE(_fadeTable));
+
+ for (int i = fromColor; i < numColorsToFade; i++)
_fadeTable[i] = percent;
}
void GfxPalette32::fadeOff() {
- setFade(100, 0, 255);
+ setFade(100, 0, 256);
}
+void GfxPalette32::applyFade() {
+ for (int i = 0; i < ARRAYSIZE(_fadeTable); ++i) {
+ if (_fadeTable[i] == 100)
+ continue;
+
+ Color &color = _nextPalette.colors[i];
+
+ color.r = (int16)color.r * _fadeTable[i] / 100;
+ color.g = (int16)color.g * _fadeTable[i] / 100;
+ color.b = (int16)color.b * _fadeTable[i] / 100;
+ }
+}
}