From fb891e4c081d5f0678cdda3855daa82728dc0ac0 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 14 Jan 2016 10:50:41 -0600 Subject: SCI: Implement SCI32 kPalVary and kPalette setFade This also fixes kPalCycle signatures to be more accurate. --- engines/sci/graphics/frameout.cpp | 11 +- engines/sci/graphics/helpers.h | 24 ++ engines/sci/graphics/palette.cpp | 4 +- engines/sci/graphics/palette.h | 10 +- engines/sci/graphics/palette32.cpp | 508 ++++++++++++++++++++++++++++++++----- engines/sci/graphics/palette32.h | 166 ++++++++++-- 6 files changed, 631 insertions(+), 92 deletions(-) (limited to 'engines/sci/graphics') diff --git a/engines/sci/graphics/frameout.cpp b/engines/sci/graphics/frameout.cpp index 9512ec2ed1..90bd798a1b 100644 --- a/engines/sci/graphics/frameout.cpp +++ b/engines/sci/graphics/frameout.cpp @@ -748,12 +748,11 @@ void GfxFrameout::kernelFrameout() { return; } - _palette->palVaryUpdate(); - _palette->applyCycles(); - _palette->applyFade(); - // TODO: This should probably not require screen pic invalidation - _screen->_picNotValid = 1; - _palette->setOnScreen(); + _palette->updateForFrame(); + + // TODO: Tons of drawing stuff should be here, see commented out implementation above + + _palette->updateHardware(); for (PlaneList::iterator it = _planes.begin(); it != _planes.end(); it++) { reg_t planeObject = it->object; diff --git a/engines/sci/graphics/helpers.h b/engines/sci/graphics/helpers.h index 4889f12bd2..e5b9f2aaed 100644 --- a/engines/sci/graphics/helpers.h +++ b/engines/sci/graphics/helpers.h @@ -121,6 +121,15 @@ struct Window : public Port, public Common::Serializable { struct Color { byte used; byte r, g, b; + +#ifdef ENABLE_SCI32 + bool operator==(const Color &other) const { + return used == other.used && r == other.r && g == other.g && b == other.b; + } + inline bool operator!=(const Color &other) const { + return !(*this == other); + } +#endif }; struct Palette { @@ -128,6 +137,21 @@ struct Palette { uint32 timestamp; Color colors[256]; byte intensity[256]; + +#ifdef ENABLE_SCI32 + bool operator==(const Palette &other) const { + for (int i = 0; i < ARRAYSIZE(colors); ++i) { + if (colors[i] != other.colors[i]) { + return false; + } + } + + return false; + } + inline bool operator!=(const Palette &other) const { + return !(*this == other); + } +#endif }; struct PalSchedule { diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index 3742a8d2d8..698ba632fe 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -136,7 +136,7 @@ void GfxPalette::setDefault() { #define SCI_PAL_FORMAT_CONSTANT 1 #define SCI_PAL_FORMAT_VARIABLE 0 -void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) { +void GfxPalette::createFromData(byte *data, int bytesLeft, Palette *paletteOut) const { int palFormat = 0; int palOffset = 0; int palColorStart = 0; @@ -811,8 +811,6 @@ bool GfxPalette::palVaryLoadTargetPalette(GuiResourceId resourceId) { return false; } -// TODO: This code should not set up an independent timer in SCI32. SCI32 engine palette varies happen in -// Palette::UpdateForFrame which is called by GraphicsMgr::FrameOut. void GfxPalette::palVaryInstallTimer() { // Remove any possible leftover palVary timer callbacks. // This happens for example in QFG1VGA, when sleeping at Erana's place diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index 82fd20a432..dfc276fa59 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -53,11 +53,11 @@ public: bool isUsing16bitColorMatch(); void setDefault(); - void createFromData(byte *data, int bytesLeft, Palette *paletteOut); + void createFromData(byte *data, int bytesLeft, Palette *paletteOut) const; bool setAmiga(); void modifyAmigaPalette(byte *data); void setEGA(); - void set(Palette *sciPal, bool force, bool forceRealMerge = false); + virtual void set(Palette *sciPal, bool force, bool forceRealMerge = false); bool insert(Palette *newPalette, Palette *destPalette); bool merge(Palette *pFrom, bool force, bool forceRealMerge); uint16 matchColor(byte r, byte g, byte b); @@ -78,7 +78,7 @@ public: void drewPicture(GuiResourceId pictureId); - bool kernelSetFromResource(GuiResourceId resourceId, bool force); + virtual bool kernelSetFromResource(GuiResourceId resourceId, bool force); void kernelSetFlag(uint16 fromColor, uint16 toColor, uint16 flag); void kernelUnsetFlag(uint16 fromColor, uint16 toColor, uint16 flag); void kernelSetIntensity(uint16 fromColor, uint16 toColor, uint16 intensity, bool setPalette); @@ -96,7 +96,7 @@ public: int16 kernelPalVaryGetCurrentStep(); int16 kernelPalVaryChangeTarget(GuiResourceId resourceId); void kernelPalVaryChangeTicks(uint16 ticks); - void kernelPalVaryPause(bool pause); + virtual void kernelPalVaryPause(bool pause); void kernelPalVaryDeinit(); void palVaryUpdate(); void palVaryPrepareForTransition(); @@ -110,7 +110,7 @@ public: byte findMacIconBarColor(byte r, byte g, byte b); bool colorIsFromMacClut(byte index); -private: +protected: void palVaryInit(); void palVaryInstallTimer(); void palVaryRemoveTimer(); 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; + } +} } diff --git a/engines/sci/graphics/palette32.h b/engines/sci/graphics/palette32.h index 3432e4bfd2..2e3f6274fa 100644 --- a/engines/sci/graphics/palette32.h +++ b/engines/sci/graphics/palette32.h @@ -77,12 +77,42 @@ namespace Sci { GfxPalette32(ResourceManager *resMan, GfxScreen *screen); ~GfxPalette32(); - private: - // SCI2 (SQ6) defines 10 cyclers + protected: + /** + * The palette revision version. Increments once per game + * loop that changes the source palette. TODO: Possibly + * other areas also change _version, double-check once it + * is all implemented. + */ + uint32 _version; + + /** + * Whether or not the palette manager version was updated + * during this loop. + */ + bool _versionUpdated; + + /** + * 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 _sysPalette. + */ + Palette _nextPalette; + + // SQ6 defines 10 cyclers PalCycler *_cyclers[10]; + /** - * 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. + * 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. */ bool _cycleMap[256]; inline void clearCycleMap(uint16 fromColor, uint16 numColorsToClear); @@ -90,32 +120,138 @@ namespace Sci { inline PalCycler *getCycler(uint16 fromColor); /** - * The fade table records the expected intensity level of each pixel in the palette that will be displayed on - * the next frame. + * The fade table records the expected intensity level of each pixel + * in the palette that will be displayed on the next frame. */ byte _fadeTable[256]; + + /** + * An optional lookup table used to remap RGB565 colors to a palette + * index. Used by Phantasmagoria 2 in 8-bit color environments. + */ byte *_clutTable; + /** + * 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. + */ + Palette *_varyStartPalette; + + /** + * An optional palette used to describe the target colors used + * in a palette vary operation. + */ + Palette *_varyTargetPalette; + + /** + * The minimum palette index that has been varied from the + * source palette. 0–255 + */ + uint8 _varyFromColor; + + /** + * The maximum palette index that is has been varied from the + * source palette. 0-255 + */ + uint8 _varyToColor; + + /** + * The tick at the last time the palette vary was updated. + */ + uint32 _varyLastTick; + + /** + * TODO: Document + * The velocity of change in percent? + */ + int _varyTime; + + /** + * TODO: Better documentation + * The direction of change, -1, 0, or 1. + */ + int16 _varyDirection; + + /** + * 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. + */ + int16 _varyTargetPercent; + + /** + * The number of time palette varying has been paused. + */ + uint16 _varyNumTimesPaused; + + /** + * Submits a palette to display. Entries marked as “used” in the + * submitted palette are merged into the existing entries of + * _sourcePalette. + */ + void submit(Palette &palette); + public: - void applyAllCycles(); - void applyCycles(); - void applyFade(); + override virtual void saveLoadWithSerializer(Common::Serializer &s); + + override virtual bool kernelSetFromResource(GuiResourceId resourceId, bool force); + override virtual int16 kernelFindColor(const uint16 r, const uint16 g, const uint16 b); + override virtual void set(Palette *newPalette, bool force, bool forceRealMerge = false); + int16 matchColor(const byte matchRed, const byte matchGreen, const byte matchBlue, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable); + + void updateForFrame(); + void updateHardware(); + void applyAll(); bool loadClut(uint16 clutId); byte matchClutColor(uint16 color); void unloadClut(); - int16 setCycle(uint16 fromColor, uint16 toColor, int16 direction, int16 delay); - void doCycle(uint16 fromColor, int16 speed); - void cycleOn(uint16 fromColor); - void cyclePause(uint16 fromColor); + 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); + override virtual void kernelPalVaryPause(const 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); + private: + bool createPaletteFromResourceInternal(const GuiResourceId paletteId, Palette *const out) const; + Palette getPaletteFromResourceInternal(const GuiResourceId paletteId) const; + void setVaryTimeInternal(const int16 percent, const int time); + public: + void applyVary(); + + void setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay); + void doCycle(const uint8 fromColor, const int16 speed); + void cycleOn(const uint8 fromColor); + void cyclePause(const uint8 fromColor); void cycleAllOn(); void cycleAllPause(); - void cycleOff(uint16 fromColor); + void cycleOff(const uint8 fromColor); void cycleAllOff(); + void applyAllCycles(); + void applyCycles(); - void setFade(uint8 percent, uint16 fromColor, uint16 toColor); + void setFade(const uint8 percent, const uint8 fromColor, const uint16 toColor); void fadeOff(); + void applyFade(); }; } -- cgit v1.2.3 From 76bd2eeb2e580a6a5d0b883dfc3e6b3c6e84e6b3 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 14 Jan 2016 10:54:27 -0600 Subject: SCI: Use tick-based timing more consistently This means tick-based times are saved to save games, as in SCI32 engine, instead of seconds, which are not accurate enough. It also means places in SCI engine that need to access game ticks should do so through g_sci instead of g_system or g_engine. --- engines/sci/graphics/palette.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'engines/sci/graphics') diff --git a/engines/sci/graphics/palette.cpp b/engines/sci/graphics/palette.cpp index 698ba632fe..106924ecd3 100644 --- a/engines/sci/graphics/palette.cpp +++ b/engines/sci/graphics/palette.cpp @@ -650,7 +650,7 @@ bool GfxPalette::kernelAnimate(byte fromColor, byte toColor, int speed) { Color col; //byte colorNr; int16 colorCount; - uint32 now = g_system->getMillis() * 60 / 1000; + uint32 now = g_sci->getTickCount(); // search for sheduled animations with the same 'from' value // schedule animation... -- cgit v1.2.3 From 8224d321226793c6d7499c1d1548269ef3912327 Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 14 Jan 2016 10:56:58 -0600 Subject: SCI: Implement SCI32 kPalette findColor and matchColor It seems that findColor is used only by kPalette, and matchColor is used only by Remap. --- engines/sci/graphics/palette.h | 2 +- engines/sci/graphics/palette32.cpp | 82 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) (limited to 'engines/sci/graphics') diff --git a/engines/sci/graphics/palette.h b/engines/sci/graphics/palette.h index dfc276fa59..61a8d36cb9 100644 --- a/engines/sci/graphics/palette.h +++ b/engines/sci/graphics/palette.h @@ -82,7 +82,7 @@ public: void kernelSetFlag(uint16 fromColor, uint16 toColor, uint16 flag); void kernelUnsetFlag(uint16 fromColor, uint16 toColor, uint16 flag); void kernelSetIntensity(uint16 fromColor, uint16 toColor, uint16 intensity, bool setPalette); - int16 kernelFindColor(uint16 r, uint16 g, uint16 b); + virtual int16 kernelFindColor(uint16 r, uint16 g, uint16 b); bool kernelAnimate(byte fromColor, byte toColor, int speed); void kernelAnimateSet(); reg_t kernelSave(); diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp index 00e502c696..9aa08782a5 100644 --- a/engines/sci/graphics/palette32.cpp +++ b/engines/sci/graphics/palette32.cpp @@ -111,6 +111,45 @@ override bool GfxPalette32::kernelSetFromResource(GuiResourceId resourceId, bool return false; } +// In SCI32 engine this method is SOLPalette::Match(Rgb24 *) +// and is called as PaletteMgr.Current().Match(color) +override int16 GfxPalette32::kernelFindColor(uint16 r, uint16 g, uint16 b) { + // SQ6 SCI32 engine takes the 16-bit r, g, b arguments from the + // VM and puts them into al, ah, dl. For compatibility, make sure + // to discard any high bits here too + r = r & 0xFF; + g = g & 0xFF; + b = b & 0xFF; + int16 bestIndex = 0; + int bestDifference = 0xFFFFF; + int difference; + + // SQ6 DOS really does check only the first 236 entries + for (int i = 0, channelDifference; i < 236; ++i) { + difference = _sysPalette.colors[i].r - r; + difference *= difference; + if (bestDifference <= difference) { + continue; + } + + channelDifference = _sysPalette.colors[i].g - g; + difference += channelDifference * channelDifference; + if (bestDifference <= difference) { + continue; + } + + channelDifference = _sysPalette.colors[i].b - b; + difference += channelDifference * channelDifference; + if (bestDifference <= difference) { + continue; + } + bestDifference = difference; + bestIndex = i; + } + + return bestIndex; +} + // 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 @@ -123,6 +162,49 @@ override void GfxPalette32::set(Palette *newPalette, bool force, bool forceRealM submit(*newPalette); } +// In SCI32 engine this method is SOLPalette::Match(Rgb24 *, int, int *, int *) +// and is used by Remap +// TODO: Anything that calls GfxPalette::matchColor(int, int, int) is going to +// match using an algorithm from SCI16 engine right now. This needs to be +// corrected in the future so either nothing calls +// GfxPalette::matchColor(int, int, int), or it is fixed to match the other +// SCI32 algorithms. +int16 GfxPalette32::matchColor(const byte r, const byte g, const byte b, const int defaultDifference, int &lastCalculatedDifference, const bool *const matchTable) { + int16 bestIndex = -1; + int bestDifference = 0xFFFFF; + int difference = defaultDifference; + + // SQ6 DOS really does check only the first 236 entries + for (int i = 0, channelDifference; i < 236; ++i) { + if (matchTable[i] == 0) { + continue; + } + + difference = _sysPalette.colors[i].r - r; + difference *= difference; + if (bestDifference <= difference) { + continue; + } + channelDifference = _sysPalette.colors[i].g - g; + difference += channelDifference * channelDifference; + if (bestDifference <= difference) { + continue; + } + channelDifference = _sysPalette.colors[i].b - b; + difference += channelDifference * channelDifference; + if (bestDifference <= difference) { + continue; + } + bestDifference = difference; + bestIndex = i; + } + + // NOTE: This value is only valid if the last index to + // perform a difference calculation was the best index + lastCalculatedDifference = difference; + return bestIndex; +} + void GfxPalette32::updateForFrame() { applyAll(); _versionUpdated = false; -- cgit v1.2.3 From 40f6d74d97618adc80d10244a77c444bf7e613eb Mon Sep 17 00:00:00 2001 From: Colin Snover Date: Thu, 14 Jan 2016 10:58:37 -0600 Subject: SCI: Minor clean-ups to comments and null pointer values --- engines/sci/graphics/frameout.h | 3 ++- engines/sci/graphics/palette32.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'engines/sci/graphics') diff --git a/engines/sci/graphics/frameout.h b/engines/sci/graphics/frameout.h index a422572ef4..0017fb19e8 100644 --- a/engines/sci/graphics/frameout.h +++ b/engines/sci/graphics/frameout.h @@ -110,7 +110,8 @@ class GfxPalette; class GfxScreen; /** - * Frameout class, kFrameout and relevant functions for SCI32 games + * Frameout class, kFrameout and relevant functions for SCI32 games. + * Roughly equivalent to GraphicsMgr in the actual SCI engine. */ class GfxFrameout { public: diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp index 9aa08782a5..8ce8b85678 100644 --- a/engines/sci/graphics/palette32.cpp +++ b/engines/sci/graphics/palette32.cpp @@ -297,7 +297,7 @@ byte GfxPalette32::matchClutColor(uint16 color) { void GfxPalette32::unloadClut() { // This will only unload the actual table, but not reset any palette delete[] _clutTable; - _clutTable = 0; + _clutTable = nullptr; } // -- cgit v1.2.3