aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics/palette32.cpp
diff options
context:
space:
mode:
authorColin Snover2016-01-06 20:00:28 -0600
committerColin Snover2016-01-07 16:35:09 -0600
commitaeee621e4413fe71ffada6c4bc096ae2a5a8c204 (patch)
tree41817ee3e96fcc279988bb5e604553694e2737ba /engines/sci/graphics/palette32.cpp
parent453afd7bbc7b83506638bf87b830384078f729e1 (diff)
downloadscummvm-rg350-aeee621e4413fe71ffada6c4bc096ae2a5a8c204.tar.gz
scummvm-rg350-aeee621e4413fe71ffada6c4bc096ae2a5a8c204.tar.bz2
scummvm-rg350-aeee621e4413fe71ffada6c4bc096ae2a5a8c204.zip
SCI32: Add initial support for palette cycling (kPalCycle) and fading (kPalFade)
Graphics palette code was rewritten between SCI1 and SCI2, so SCI32 palette engine code has been moved to a separate GfxPalette32 class.
Diffstat (limited to 'engines/sci/graphics/palette32.cpp')
-rw-r--r--engines/sci/graphics/palette32.cpp350
1 files changed, 350 insertions, 0 deletions
diff --git a/engines/sci/graphics/palette32.cpp b/engines/sci/graphics/palette32.cpp
new file mode 100644
index 0000000000..61ca1b99c2
--- /dev/null
+++ b/engines/sci/graphics/palette32.cpp
@@ -0,0 +1,350 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "common/file.h"
+
+#include "sci/sci.h"
+#include "sci/graphics/palette32.h"
+
+namespace Sci {
+
+GfxPalette32::GfxPalette32(ResourceManager *resMan, GfxScreen *screen)
+ : GfxPalette(resMan, screen), _clutTable(0), _cyclers(), _cycleMap() {}
+
+GfxPalette32::~GfxPalette32() {
+ unloadClut();
+ cycleAllOff();
+}
+
+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);
+
+ Palette pal;
+ memset(&pal, 0, sizeof(Palette));
+
+ // Setup 1:1 mapping
+ for (int i = 0; i < 256; i++) {
+ pal.mapping[i] = i;
+ }
+
+ // 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();
+ }
+
+ 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::unloadClut() {
+ // This will only unload the actual table, but not reset any palette
+ delete[] _clutTable;
+ _clutTable = 0;
+}
+
+inline void GfxPalette32::_clearCycleMap(const uint16 fromColor, const uint16 numColorsToClear) {
+ bool *mapEntry = _cycleMap + fromColor;
+ const bool *lastEntry = _cycleMap + numColorsToClear;
+ while (mapEntry < lastEntry) {
+ *mapEntry++ = false;
+ }
+}
+
+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;
+ }
+}
+
+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;
+ }
+ }
+
+ return nullptr;
+}
+
+inline void _doCycle(PalCycler *cycler, const int16 speed) {
+ int16 currentCycle = cycler->currentCycle;
+ const uint16 numColorsToCycle = cycler->numColorsToCycle;
+
+ if (cycler->direction == 0) {
+ currentCycle = (currentCycle - (speed % numColorsToCycle)) + numColorsToCycle;
+ } else {
+ currentCycle = currentCycle + speed;
+ }
+
+ cycler->currentCycle = (uint8) (currentCycle % numColorsToCycle);
+}
+
+inline void _applyCycleToPalette(PalCycler *cycler, Palette *palette) {
+ const int16 currentCycle = cycler->currentCycle;
+ const uint16 numColorsToCycle = cycler->numColorsToCycle;
+
+ Sci::Color tempPalette[numColorsToCycle];
+ Sci::Color *sourceColor = palette->colors + cycler->fromColor;
+ memcpy(tempPalette, sourceColor, sizeof(tempPalette));
+
+ Sci::Color *targetColor = sourceColor;
+ for (int numColorsCycled = 0; numColorsCycled < numColorsToCycle; ++numColorsCycled) {
+ Sci::Color sourceColor = *(tempPalette + ((currentCycle + numColorsCycled) % numColorsToCycle));
+ *(targetColor + numColorsCycled) = sourceColor;
+ }
+}
+
+void GfxPalette32::applyAllCycles() {
+ 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
+ _applyCycleToPalette(cycler, &_sysPalette);
+ }
+ }
+}
+
+void GfxPalette32::applyCycles() {
+ 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()) {
+ _doCycle(cycler, 1);
+ cycler->lastUpdateTick += cycler->delay;
+ }
+ }
+
+ _applyCycleToPalette(cycler, &_sysPalette);
+ }
+}
+
+int16 GfxPalette32::setCycle(const uint16 fromColor, const uint16 toColor, const int16 direction, const int16 delay) {
+ assert(fromColor <= UINT8_MAX);
+ assert(toColor <= UINT8_MAX);
+ assert(fromColor < toColor);
+
+ int cyclerIndex;
+ const int numCyclers = ARRAYSIZE(_cyclers);
+
+ PalCycler *cycler = _getCycler(fromColor);
+
+ if (cycler != nullptr) {
+ debug("Resetting existing cycler");
+ _clearCycleMap(fromColor, cycler->numColorsToCycle);
+ } else {
+ for (cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) {
+ if (_cyclers[cyclerIndex] == nullptr) {
+ cycler = new PalCycler;
+ _cyclers[cyclerIndex] = cycler;
+ break;
+ }
+ }
+ }
+
+ // 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) {
+ int maxUpdateDelta = -1;
+ // Optimization: Unlike actual SCI (SQ6) engine, we call
+ // getTickCount only once and store it, instead of calling it
+ // twice on each iteration through the loop
+ const uint32 now = g_sci->getTickCount();
+
+ for (cyclerIndex = 0; cyclerIndex < numCyclers; ++cyclerIndex) {
+ PalCycler *candidate = _cyclers[cyclerIndex];
+
+ const int32 updateDelta = now - candidate->lastUpdateTick;
+ if (updateDelta >= maxUpdateDelta) {
+ maxUpdateDelta = updateDelta;
+ cycler = candidate;
+ }
+ }
+
+ _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;
+ cycler->delay = delay;
+ cycler->lastUpdateTick = g_sci->getTickCount();
+ 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) {
+ PalCycler *cycler = _getCycler(fromColor);
+ if (cycler != nullptr) {
+ cycler->lastUpdateTick = g_sci->getTickCount();
+ _doCycle(cycler, speed);
+ }
+}
+
+void GfxPalette32::cycleOn(const uint16 fromColor) {
+ PalCycler *cycler = _getCycler(fromColor);
+ if (cycler != nullptr && cycler->numTimesPaused > 0) {
+ --cycler->numTimesPaused;
+ }
+}
+
+void GfxPalette32::cyclePause(const uint16 fromColor) {
+ PalCycler *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];
+ if (cycler != nullptr && cycler->numTimesPaused > 0) {
+ --cycler->numTimesPaused;
+ }
+ }
+}
+
+void GfxPalette32::cycleAllPause() {
+ // TODO: The SCI SQ6 cycleAllPause function does not seem to perform
+ // nullptr checking?? This would definitely cause null pointer
+ // dereference in SCI code. I have not seen anything actually call
+ // this function yet, so it is possible it is just unused and broken
+ // in SCI SQ6. This assert exists so that if this function is called,
+ // it is noticed and can be rechecked in the actual engine.
+ // Obviously this code *does* do nullptr checks instead of crashing. :)
+ assert(0);
+ for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
+ PalCycler *cycler = _cyclers[i];
+ if (cycler != nullptr) {
+ // This seems odd, because currentCycle is 0..numColorsPerCycle,
+ // but fromColor is 0..255. When applyAllCycles runs, the values
+ // end up back in range
+ cycler->currentCycle = cycler->fromColor;
+ }
+ }
+
+ applyAllCycles();
+
+ for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
+ PalCycler *cycler = _cyclers[i];
+ if (cycler != nullptr) {
+ ++cycler->numTimesPaused;
+ }
+ }
+}
+
+void GfxPalette32::cycleOff(const uint16 fromColor) {
+ for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
+ PalCycler *cycler = _cyclers[i];
+ if (cycler != nullptr && cycler->fromColor == fromColor) {
+ _clearCycleMap(fromColor, cycler->numColorsToCycle);
+ delete cycler;
+ _cyclers[i] = nullptr;
+ break;
+ }
+ }
+}
+
+void GfxPalette32::cycleAllOff() {
+ for (int i = 0, len = ARRAYSIZE(_cyclers); i < len; ++i) {
+ PalCycler *cycler = _cyclers[i];
+ if (cycler != nullptr) {
+ _clearCycleMap(cycler->fromColor, cycler->numColorsToCycle);
+ delete cycler;
+ _cyclers[i] = nullptr;
+ }
+ }
+}
+
+void GfxPalette32::applyFade() {
+ // 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)
+ Sci::Color *color = _sysPalette.colors;
+ uint8 *fadeAmount = _fadeTable;
+ for (int i = 0; i < 256; ++i) {
+ if (*fadeAmount == 100) {
+ continue;
+ }
+
+ color->r = ((int) color->r * ((int) *fadeAmount)) / 100;
+ color->g = ((int) color->r * ((int) *fadeAmount)) / 100;
+ color->b = ((int) color->r * ((int) *fadeAmount)) / 100;
+ }
+}
+
+void GfxPalette32::setFade(uint8 percent, uint16 fromColor, uint16 toColor) {
+ uint8 *fadeAmount = _fadeTable;
+ for (int i = fromColor, len = toColor - fromColor + 1; i < len; ++i) {
+ *fadeAmount++ = percent;
+ }
+}
+
+void GfxPalette32::fadeOff() {
+ setFade(100, 0, 255);
+}
+
+}