scummvm/engines/sci/graphics/palette32.cpp
Colin Snover 5cffa3891f SCI32: Clean up GfxPalette32
* Replace raw pointers with smart pointers
* Use references instead of const pointers where appropriate
* Tweak initialisation
* Tweak palette copies to the stack
2017-10-06 22:10:50 -05:00

1038 lines
31 KiB
C++

/* 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 "common/system.h"
#include "graphics/palette.h"
#include "sci/sci.h"
#include "sci/event.h"
#include "sci/resource.h"
#include "sci/util.h"
#include "sci/engine/features.h"
#include "sci/graphics/palette32.h"
#include "sci/graphics/remap32.h"
#include "sci/graphics/screen.h"
namespace Sci {
#pragma mark HunkPalette
HunkPalette::HunkPalette(const SciSpan<const byte> &rawPalette) :
_version(0),
// The header size in palettes is garbage. In at least KQ7 2.00b and Phant1,
// the 999.pal sets this value to 0. In most other palettes it is set to 14,
// but the *actual* size of the header structure used in SSCI is 13, which
// is reflected by `kHunkPaletteHeaderSize`.
_numPalettes(rawPalette.getUint8At(kNumPaletteEntriesOffset)),
_data() {
assert(_numPalettes == 0 || _numPalettes == 1);
if (_numPalettes) {
_data = rawPalette;
_version = getEntryHeader().version;
}
}
void HunkPalette::write(SciSpan<byte> &out, const Palette &palette) {
const uint8 numPalettes = 1;
const uint16 paletteOffset = kHunkPaletteHeaderSize + 2 * numPalettes;
out[kNumPaletteEntriesOffset] = numPalettes;
out[kHunkPaletteHeaderSize + 2] = paletteOffset;
SciSpan<byte> entry = out.subspan(paletteOffset);
entry[kEntryStartColorOffset] = 0;
entry.setUint16SEAt(kEntryNumColorsOffset, ARRAYSIZE(palette.colors));
entry[kEntryUsedOffset] = 1;
entry[kEntrySharedUsedOffset] = 0;
entry.setUint32SEAt(kEntryVersionOffset, 1);
SciSpan<byte> paletteData = entry.subspan(kEntryHeaderSize);
for (uint i = 0; i < ARRAYSIZE(palette.colors); ++i) {
*paletteData++ = palette.colors[i].used;
*paletteData++ = palette.colors[i].r;
*paletteData++ = palette.colors[i].g;
*paletteData++ = palette.colors[i].b;
}
}
void HunkPalette::setVersion(const uint32 version) const {
if (_numPalettes != _data.getUint8At(kNumPaletteEntriesOffset)) {
error("Invalid HunkPalette");
}
if (_numPalettes) {
const EntryHeader header = getEntryHeader();
if (header.version != _version) {
error("Invalid HunkPalette");
}
byte *palette = const_cast<byte *>(getPalPointer().getUnsafeDataAt(kEntryVersionOffset, sizeof(uint32)));
WRITE_SCI11ENDIAN_UINT32(palette, version);
_version = version;
}
}
const HunkPalette::EntryHeader HunkPalette::getEntryHeader() const {
const SciSpan<const byte> data(getPalPointer());
EntryHeader header;
header.startColor = data.getUint8At(kEntryStartColorOffset);
header.numColors = data.getUint16SEAt(kEntryNumColorsOffset);
header.used = data.getUint8At(kEntryUsedOffset);
header.sharedUsed = data.getUint8At(kEntrySharedUsedOffset);
header.version = data.getUint32SEAt(kEntryVersionOffset);
return header;
}
const Palette HunkPalette::toPalette() const {
Palette outPalette;
for (int16 i = 0; i < ARRAYSIZE(outPalette.colors); ++i) {
outPalette.colors[i].used = false;
outPalette.colors[i].r = 0;
outPalette.colors[i].g = 0;
outPalette.colors[i].b = 0;
}
if (_numPalettes) {
const EntryHeader header = getEntryHeader();
const uint32 dataSize = header.numColors * (/* RGB */ 3 + (header.sharedUsed ? 0 : 1));
const byte *data = getPalPointer().getUnsafeDataAt(kEntryHeaderSize, dataSize);
const int16 end = header.startColor + header.numColors;
assert(end <= 256);
if (header.sharedUsed) {
for (int16 i = header.startColor; i < end; ++i) {
outPalette.colors[i].used = header.used;
outPalette.colors[i].r = *data++;
outPalette.colors[i].g = *data++;
outPalette.colors[i].b = *data++;
}
} else {
for (int16 i = header.startColor; i < end; ++i) {
outPalette.colors[i].used = *data++;
outPalette.colors[i].r = *data++;
outPalette.colors[i].g = *data++;
outPalette.colors[i].b = *data++;
}
}
}
return outPalette;
}
#pragma mark -
#pragma mark Gamma correction tables
static const uint8 gammaTables[GfxPalette32::numGammaTables][256] = {
{ 0, 2, 3, 5, 6, 7, 9, 10,
11, 13, 14, 15, 16, 18, 19, 20,
21, 22, 23, 25, 26, 27, 28, 29,
30, 32, 33, 34, 35, 36, 37, 38,
39, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 54, 55, 56,
57, 58, 59, 60, 61, 62, 63, 64,
65, 66, 67, 68, 69, 70, 71, 72,
74, 75, 76, 77, 78, 79, 80, 81,
82, 83, 84, 85, 86, 87, 88, 89,
90, 91, 92, 93, 94, 95, 96, 97,
98, 99, 100, 101, 102, 103, 104, 105,
106, 107, 108, 109, 110, 111, 112, 113,
114, 115, 116, 117, 118, 119, 120, 121,
122, 123, 124, 125, 126, 127, 128, 128,
129, 130, 131, 132, 133, 134, 135, 136,
137, 138, 139, 140, 141, 142, 143, 144,
145, 146, 147, 148, 149, 150, 151, 152,
153, 153, 154, 155, 156, 157, 158, 159,
160, 161, 162, 163, 164, 165, 166, 167,
168, 169, 170, 171, 171, 172, 173, 174,
175, 176, 177, 178, 179, 180, 181, 182,
183, 184, 185, 186, 186, 187, 188, 189,
190, 191, 192, 193, 194, 195, 196, 197,
198, 199, 199, 200, 201, 202, 203, 204,
205, 206, 207, 208, 209, 210, 211, 211,
212, 213, 214, 215, 216, 217, 218, 219,
220, 221, 222, 222, 223, 224, 225, 226,
227, 228, 229, 230, 231, 232, 232, 233,
234, 235, 236, 237, 238, 239, 240, 241,
242, 242, 243, 244, 245, 246, 247, 248,
249, 250, 251, 251, 252, 253, 254, 255 },
{ 0, 3, 5, 6, 8, 10, 11, 13,
14, 16, 17, 19, 20, 22, 23, 24,
26, 27, 28, 30, 31, 32, 33, 35,
36, 37, 38, 40, 41, 42, 43, 44,
46, 47, 48, 49, 50, 51, 53, 54,
55, 56, 57, 58, 59, 60, 62, 63,
64, 65, 66, 67, 68, 69, 70, 71,
73, 74, 75, 76, 77, 78, 79, 80,
81, 82, 83, 84, 85, 86, 87, 88,
89, 90, 91, 92, 93, 94, 95, 96,
97, 99, 100, 101, 102, 103, 104, 105,
106, 107, 108, 108, 109, 110, 111, 112,
113, 114, 115, 116, 117, 118, 119, 120,
121, 122, 123, 124, 125, 126, 127, 128,
129, 130, 131, 132, 133, 134, 135, 136,
136, 137, 138, 139, 140, 141, 142, 143,
144, 145, 146, 147, 148, 149, 150, 151,
151, 152, 153, 154, 155, 156, 157, 158,
159, 160, 161, 162, 162, 163, 164, 165,
166, 167, 168, 169, 170, 171, 172, 172,
173, 174, 175, 176, 177, 178, 179, 180,
180, 181, 182, 183, 184, 185, 186, 187,
188, 188, 189, 190, 191, 192, 193, 194,
195, 196, 196, 197, 198, 199, 200, 201,
202, 202, 203, 204, 205, 206, 207, 208,
209, 209, 210, 211, 212, 213, 214, 215,
215, 216, 217, 218, 219, 220, 221, 221,
222, 223, 224, 225, 226, 227, 227, 228,
229, 230, 231, 232, 233, 233, 234, 235,
236, 237, 238, 238, 239, 240, 241, 242,
243, 243, 244, 245, 246, 247, 248, 249,
249, 250, 251, 252, 253, 254, 254, 255 },
{ 0, 4, 6, 8, 10, 12, 14, 16,
18, 19, 21, 23, 24, 26, 27, 29,
30, 32, 33, 35, 36, 37, 39, 40,
41, 43, 44, 45, 47, 48, 49, 50,
52, 53, 54, 55, 57, 58, 59, 60,
61, 62, 64, 65, 66, 67, 68, 69,
71, 72, 73, 74, 75, 76, 77, 78,
79, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
96, 97, 98, 99, 100, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112,
112, 113, 114, 115, 116, 117, 118, 119,
120, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135,
135, 136, 137, 138, 139, 140, 141, 142,
143, 144, 145, 146, 146, 147, 148, 149,
150, 151, 152, 153, 154, 155, 156, 156,
157, 158, 159, 160, 161, 162, 163, 163,
164, 165, 166, 167, 168, 169, 170, 170,
171, 172, 173, 174, 175, 176, 177, 177,
178, 179, 180, 181, 182, 183, 183, 184,
185, 186, 187, 188, 188, 189, 190, 191,
192, 193, 194, 194, 195, 196, 197, 198,
199, 199, 200, 201, 202, 203, 203, 204,
205, 206, 207, 208, 208, 209, 210, 211,
212, 212, 213, 214, 215, 216, 217, 217,
218, 219, 220, 221, 221, 222, 223, 224,
225, 225, 226, 227, 228, 229, 229, 230,
231, 232, 233, 233, 234, 235, 236, 237,
237, 238, 239, 240, 240, 241, 242, 243,
244, 244, 245, 246, 247, 247, 248, 249,
250, 251, 251, 252, 253, 254, 254, 255 },
{ 0, 5, 9, 11, 14, 16, 19, 21,
23, 25, 26, 28, 30, 32, 33, 35,
37, 38, 40, 41, 43, 44, 46, 47,
49, 50, 52, 53, 54, 56, 57, 58,
60, 61, 62, 64, 65, 66, 67, 69,
70, 71, 72, 73, 75, 76, 77, 78,
79, 80, 82, 83, 84, 85, 86, 87,
88, 89, 91, 92, 93, 94, 95, 96,
97, 98, 99, 100, 101, 102, 103, 104,
105, 106, 107, 108, 109, 110, 111, 112,
113, 114, 115, 116, 117, 118, 119, 120,
121, 122, 123, 124, 125, 126, 127, 128,
129, 130, 131, 132, 133, 134, 134, 135,
136, 137, 138, 139, 140, 141, 142, 143,
144, 144, 145, 146, 147, 148, 149, 150,
151, 152, 152, 153, 154, 155, 156, 157,
158, 158, 159, 160, 161, 162, 163, 164,
164, 165, 166, 167, 168, 169, 169, 170,
171, 172, 173, 174, 174, 175, 176, 177,
178, 179, 179, 180, 181, 182, 183, 183,
184, 185, 186, 187, 187, 188, 189, 190,
191, 191, 192, 193, 194, 195, 195, 196,
197, 198, 199, 199, 200, 201, 202, 202,
203, 204, 205, 205, 206, 207, 208, 209,
209, 210, 211, 212, 212, 213, 214, 215,
215, 216, 217, 218, 218, 219, 220, 221,
221, 222, 223, 224, 224, 225, 226, 227,
227, 228, 229, 230, 230, 231, 232, 232,
233, 234, 235, 235, 236, 237, 238, 238,
239, 240, 240, 241, 242, 243, 243, 244,
245, 245, 246, 247, 248, 248, 249, 250,
250, 251, 252, 252, 253, 254, 255, 255 },
{ 0, 9, 14, 18, 21, 24, 27, 29,
32, 34, 37, 39, 41, 43, 45, 47,
48, 50, 52, 54, 55, 57, 59, 60,
62, 63, 65, 66, 68, 69, 71, 72,
73, 75, 76, 77, 79, 80, 81, 83,
84, 85, 86, 88, 89, 90, 91, 92,
94, 95, 96, 97, 98, 99, 100, 102,
103, 104, 105, 106, 107, 108, 109, 110,
111, 112, 113, 114, 115, 116, 117, 118,
119, 120, 121, 122, 123, 124, 125, 126,
127, 128, 129, 130, 131, 132, 133, 134,
135, 136, 137, 137, 138, 139, 140, 141,
142, 143, 144, 145, 145, 146, 147, 148,
149, 150, 151, 151, 152, 153, 154, 155,
156, 156, 157, 158, 159, 160, 161, 161,
162, 163, 164, 165, 165, 166, 167, 168,
169, 169, 170, 171, 172, 173, 173, 174,
175, 176, 176, 177, 178, 179, 179, 180,
181, 182, 182, 183, 184, 185, 185, 186,
187, 188, 188, 189, 190, 191, 191, 192,
193, 194, 194, 195, 196, 196, 197, 198,
199, 199, 200, 201, 201, 202, 203, 203,
204, 205, 206, 206, 207, 208, 208, 209,
210, 210, 211, 212, 212, 213, 214, 214,
215, 216, 216, 217, 218, 218, 219, 220,
220, 221, 222, 222, 223, 224, 224, 225,
226, 226, 227, 228, 228, 229, 230, 230,
231, 231, 232, 233, 233, 234, 235, 235,
236, 237, 237, 238, 238, 239, 240, 240,
241, 242, 242, 243, 243, 244, 245, 245,
246, 247, 247, 248, 248, 249, 250, 250,
251, 251, 252, 253, 253, 254, 254, 255 },
{ 0, 16, 23, 28, 32, 36, 39, 42,
45, 48, 50, 53, 55, 58, 60, 62,
64, 66, 68, 70, 71, 73, 75, 77,
78, 80, 81, 83, 84, 86, 87, 89,
90, 92, 93, 94, 96, 97, 98, 100,
101, 102, 103, 105, 106, 107, 108, 109,
111, 112, 113, 114, 115, 116, 117, 118,
119, 121, 122, 123, 124, 125, 126, 127,
128, 129, 130, 131, 132, 133, 134, 135,
135, 136, 137, 138, 139, 140, 141, 142,
143, 144, 145, 145, 146, 147, 148, 149,
150, 151, 151, 152, 153, 154, 155, 156,
156, 157, 158, 159, 160, 160, 161, 162,
163, 164, 164, 165, 166, 167, 167, 168,
169, 170, 170, 171, 172, 173, 173, 174,
175, 176, 176, 177, 178, 179, 179, 180,
181, 181, 182, 183, 183, 184, 185, 186,
186, 187, 188, 188, 189, 190, 190, 191,
192, 192, 193, 194, 194, 195, 196, 196,
197, 198, 198, 199, 199, 200, 201, 201,
202, 203, 203, 204, 204, 205, 206, 206,
207, 208, 208, 209, 209, 210, 211, 211,
212, 212, 213, 214, 214, 215, 215, 216,
217, 217, 218, 218, 219, 220, 220, 221,
221, 222, 222, 223, 224, 224, 225, 225,
226, 226, 227, 228, 228, 229, 229, 230,
230, 231, 231, 232, 233, 233, 234, 234,
235, 235, 236, 236, 237, 237, 238, 238,
239, 240, 240, 241, 241, 242, 242, 243,
243, 244, 244, 245, 245, 246, 246, 247,
247, 248, 248, 249, 249, 250, 250, 251,
251, 252, 252, 253, 253, 254, 254, 255 }
};
#pragma mark -
#pragma mark GfxPalette32
GfxPalette32::GfxPalette32(ResourceManager *resMan)
: _resMan(resMan),
// Palette versioning
_version(1),
_needsUpdate(false),
#ifdef USE_RGB_COLOR
_hardwarePalette(),
#endif
_currentPalette(),
_sourcePalette(),
_nextPalette(),
// Palette varying
_varyStartPalette(nullptr),
_varyTargetPalette(nullptr),
_varyFromColor(0),
_varyToColor(255),
_varyLastTick(0),
_varyTime(0),
_varyDirection(0),
_varyPercent(0),
_varyTargetPercent(0),
_varyNumTimesPaused(0),
// Palette cycling
_cycleMap(),
// Gamma correction
_gammaLevel(-1),
_gammaChanged(false) {
for (int i = 0, len = ARRAYSIZE(_fadeTable); i < len; ++i) {
_fadeTable[i] = 100;
}
loadPalette(999);
}
bool GfxPalette32::loadPalette(const GuiResourceId resourceId) {
Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
if (!palResource) {
return false;
}
const HunkPalette palette(*palResource);
submit(palette);
return true;
}
int16 GfxPalette32::matchColor(const uint8 r, const uint8 g, const uint8 b) {
int16 bestIndex = 0;
int bestDifference = 0xFFFFF;
int difference;
for (int i = 0, channelDifference; i < g_sci->_gfxRemap32->getStartColor(); ++i) {
difference = _currentPalette.colors[i].r - r;
difference *= difference;
if (bestDifference <= difference) {
continue;
}
channelDifference = _currentPalette.colors[i].g - g;
difference += channelDifference * channelDifference;
if (bestDifference <= difference) {
continue;
}
channelDifference = _currentPalette.colors[i].b - b;
difference += channelDifference * channelDifference;
if (bestDifference <= difference) {
continue;
}
bestDifference = difference;
bestIndex = i;
}
return bestIndex;
}
void GfxPalette32::submit(const Palette &palette) {
// If `_needsUpdate` is already set, there is no need to test whether
// this palette submission causes a change to `_sourcePalette` since it is
// going to be updated already anyway
if (_needsUpdate) {
mergePalette(_sourcePalette, palette);
} else {
const Palette oldSourcePalette(_sourcePalette);
mergePalette(_sourcePalette, palette);
if (_sourcePalette != oldSourcePalette) {
++_version;
_needsUpdate = true;
}
}
}
void GfxPalette32::submit(const HunkPalette &hunkPalette) {
if (hunkPalette.getVersion() == _version) {
return;
}
submit(hunkPalette.toPalette());
hunkPalette.setVersion(_version);
}
bool GfxPalette32::updateForFrame() {
applyAll();
_needsUpdate = false;
return g_sci->_gfxRemap32->remapAllTables(_nextPalette != _currentPalette);
}
void GfxPalette32::updateFFrame() {
for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) {
_nextPalette.colors[i] = _sourcePalette.colors[i];
}
_needsUpdate = false;
g_sci->_gfxRemap32->remapAllTables(_nextPalette != _currentPalette);
}
void GfxPalette32::updateHardware() {
if (_currentPalette == _nextPalette && !_gammaChanged) {
return;
}
#ifdef USE_RGB_COLOR
uint8 *bpal = _hardwarePalette;
#else
uint8 bpal[256 * 3];
#endif
// HACK: There are resources in a couple of Windows-only games that seem to
// include bogus palette entries above 236. SSCI does a lot of extra work
// when running in Windows to shift palettes and rewrite view & pic pixel
// data on-the-fly to account for the way Windows palettes work, which
// seems to end up masking the fact that there is some bad palette data.
// Since only one demo and one game seem to have this problem, we instead
// "fix" the problem here by ignoring attempts to send high palette entries
// to the backend. This makes those high pixels render black, which seems to
// match what would happen in the original interpreter, and saves us from
// having to clutter up the engine with a bunch of palette shifting garbage.
int maxIndex = ARRAYSIZE(_currentPalette.colors) - 2;
if (g_sci->getGameId() == GID_HOYLE5 ||
(g_sci->getGameId() == GID_GK2 && g_sci->isDemo())) {
maxIndex = 235;
}
for (int i = 0; i <= maxIndex; ++i) {
_currentPalette.colors[i] = _nextPalette.colors[i];
// All color entries MUST be copied, not just "used" entries, otherwise
// uninitialised memory from bpal makes its way into the system palette.
// This would not normally be a problem, except that games sometimes use
// unused palette entries. e.g. Phant1 title screen references palette
// entries outside its own palette, so will render garbage colors where
// the game expects them to be black
if (_gammaLevel == -1) {
bpal[i * 3 ] = _currentPalette.colors[i].r;
bpal[i * 3 + 1] = _currentPalette.colors[i].g;
bpal[i * 3 + 2] = _currentPalette.colors[i].b;
} else {
bpal[i * 3 ] = gammaTables[_gammaLevel][_currentPalette.colors[i].r];
bpal[i * 3 + 1] = gammaTables[_gammaLevel][_currentPalette.colors[i].g];
bpal[i * 3 + 2] = gammaTables[_gammaLevel][_currentPalette.colors[i].b];
}
}
#ifndef USE_RGB_COLOR
// When creating a raw palette on the stack, any skipped area of the palette
// needs to be blacked out or else it will contain garbage memory
memset(bpal + (maxIndex + 1) * 3, 0, (255 - maxIndex - 1) * 3);
#endif
#ifdef ENABLE_SCI32_MAC
if (g_sci->getPlatform() == Common::kPlatformMacintosh) {
bpal[255 * 3 ] = 0;
bpal[255 * 3 + 1] = 0;
bpal[255 * 3 + 2] = 0;
} else {
#else
{
#endif
// The last color must always be white
bpal[255 * 3 ] = 255;
bpal[255 * 3 + 1] = 255;
bpal[255 * 3 + 2] = 255;
}
// If the system is in a high color mode, which can happen during video
// playback, attempting to send the palette to OSystem is illegal and will
// result in a crash
if (g_system->getScreenFormat().bytesPerPixel == 1) {
g_system->getPaletteManager()->setPalette(bpal, 0, 256);
}
_gammaChanged = false;
}
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);
return rawPalette.toPalette();
}
void GfxPalette32::mergePalette(Palette &to, const Palette &from) {
// All colors MUST be copied, even index 255, despite the fact that games
// cannot actually change index 255 (it is forced to white when generating
// the hardware palette in updateHardware). While this causes some
// additional unnecessary source palette invalidations, not doing it breaks
// some badly programmed rooms, like room 6400 in Phant1 (see Trac#9788).
// (Note, however, that that specific glitch is fully fixed by ignoring a
// bad palette in the CelObjView constructor)
for (int i = 0; i < ARRAYSIZE(to.colors); ++i) {
if (from.colors[i].used) {
to.colors[i] = from.colors[i];
}
}
}
void GfxPalette32::applyAll() {
applyVary();
applyCycles();
applyFade();
}
#pragma mark -
#pragma mark Varying
void GfxPalette32::setVary(const Palette &target, const int16 percent, const int32 ticks, const int16 fromColor, const int16 toColor) {
setTarget(target);
setVaryTime(percent, ticks);
if (fromColor > -1) {
_varyFromColor = fromColor;
}
if (toColor > -1) {
assert(toColor < 256);
_varyToColor = toColor;
}
}
void GfxPalette32::setVaryPercent(const int16 percent, const int32 ticks) {
if (_varyTargetPalette) {
setVaryTime(percent, ticks);
}
// SSCI had two additional parameters for this function to change the
// `_varyFromColor`, but they were always hardcoded to be ignored
}
void GfxPalette32::setVaryTime(const int32 time) {
if (_varyTargetPalette) {
setVaryTime(_varyTargetPercent, time);
}
}
void GfxPalette32::setVaryTime(const int16 percent, const int32 ticks) {
_varyLastTick = g_sci->getTickCount();
if (!ticks || _varyPercent == percent) {
_varyDirection = 0;
_varyTargetPercent = _varyPercent = percent;
} else {
_varyTime = ticks / (percent - _varyPercent);
_varyTargetPercent = percent;
if (_varyTime > 0) {
_varyDirection = 1;
} else if (_varyTime < 0) {
_varyDirection = -1;
_varyTime = -_varyTime;
} else {
_varyDirection = 0;
_varyTargetPercent = _varyPercent = percent;
}
}
}
void GfxPalette32::varyOff() {
_varyNumTimesPaused = 0;
_varyPercent = _varyTargetPercent = 0;
_varyFromColor = 0;
_varyToColor = 255;
_varyDirection = 0;
_varyTargetPalette.reset();
_varyStartPalette.reset();
}
void GfxPalette32::varyPause() {
_varyDirection = 0;
++_varyNumTimesPaused;
}
void GfxPalette32::varyOn() {
if (_varyNumTimesPaused > 0) {
--_varyNumTimesPaused;
}
if (_varyTargetPalette && _varyNumTimesPaused == 0) {
if (_varyPercent != _varyTargetPercent && _varyTime != 0) {
_varyDirection = (_varyTargetPercent - _varyPercent > 0) ? 1 : -1;
} else {
_varyPercent = _varyTargetPercent;
}
}
}
void GfxPalette32::setTarget(const Palette &palette) {
_varyTargetPalette.reset(new Palette(palette));
}
void GfxPalette32::setStart(const Palette &palette) {
_varyStartPalette.reset(new Palette(palette));
}
void GfxPalette32::mergeStart(const Palette &palette) {
if (_varyStartPalette) {
mergePalette(*_varyStartPalette, palette);
} else {
_varyStartPalette.reset(new Palette(palette));
}
}
void GfxPalette32::mergeTarget(const Palette &palette) {
if (_varyTargetPalette) {
mergePalette(*_varyTargetPalette, palette);
} else {
_varyTargetPalette.reset(new Palette(palette));
}
}
void GfxPalette32::applyVary() {
const uint32 now = g_sci->getTickCount();
while ((int32)(now - _varyLastTick) > _varyTime && _varyDirection != 0) {
_varyLastTick += _varyTime;
if (_varyPercent == _varyTargetPercent) {
_varyDirection = 0;
}
_varyPercent += _varyDirection;
}
if (_varyPercent == 0 || !_varyTargetPalette) {
for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) {
if (_varyStartPalette && i >= _varyFromColor && i <= _varyToColor) {
_nextPalette.colors[i] = _varyStartPalette->colors[i];
} else {
_nextPalette.colors[i] = _sourcePalette.colors[i];
}
}
} else {
for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) {
if (i >= _varyFromColor && i <= _varyToColor) {
Color targetColor = _varyTargetPalette->colors[i];
Color sourceColor;
if (_varyStartPalette) {
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];
}
}
}
}
void GfxPalette32::kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int32 ticks, const int16 fromColor, const int16 toColor) {
Palette palette;
if (getSciVersion() == SCI_VERSION_3 && paletteId == 0xFFFF) {
palette = _currentPalette;
assert(fromColor >= 0 && fromColor < 256);
assert(toColor >= 0 && toColor < 256);
// While palette varying is normally inclusive of `toColor`, the
// palette inversion code in SSCI excludes `toColor`, and RAMA room
// 6201 requires this or else parts of the game's UI get inverted
for (int i = fromColor; i < toColor; ++i) {
palette.colors[i].r = ~palette.colors[i].r;
palette.colors[i].g = ~palette.colors[i].g;
palette.colors[i].b = ~palette.colors[i].b;
}
} else {
palette = getPaletteFromResource(paletteId);
}
setVary(palette, percent, ticks, fromColor, toColor);
}
void GfxPalette32::kernelPalVaryMergeTarget(const GuiResourceId paletteId) {
const Palette palette = getPaletteFromResource(paletteId);
mergeTarget(palette);
}
void GfxPalette32::kernelPalVarySetTarget(const GuiResourceId paletteId) {
const Palette palette = getPaletteFromResource(paletteId);
setTarget(palette);
}
void GfxPalette32::kernelPalVarySetStart(const GuiResourceId paletteId) {
const Palette palette = getPaletteFromResource(paletteId);
setStart(palette);
}
void GfxPalette32::kernelPalVaryMergeStart(const GuiResourceId paletteId) {
const Palette palette = getPaletteFromResource(paletteId);
mergeStart(palette);
}
void GfxPalette32::kernelPalVaryPause(const bool pause) {
if (pause) {
varyPause();
} else {
varyOn();
}
}
#pragma mark -
#pragma mark Cycling
void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay) {
assert(fromColor < toColor);
PalCycler *cycler = getCycler(fromColor);
if (cycler != nullptr) {
clearCycleMap(fromColor, cycler->numColorsToCycle);
} else {
for (int i = 0; i < kNumCyclers; ++i) {
if (!_cyclers[i]) {
cycler = new PalCycler;
_cyclers[i].reset(cycler);
break;
}
}
}
// If there are no free cycler slots, SSCI 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 (int i = 0; i < kNumCyclers; ++i) {
PalCyclerOwner &candidate = _cyclers[i];
const uint32 updateDelta = now - candidate->lastUpdateTick;
if (updateDelta < minUpdateDelta) {
minUpdateDelta = updateDelta;
cycler = candidate.get();
}
}
clearCycleMap(cycler->fromColor, cycler->numColorsToCycle);
}
uint16 numColorsToCycle = toColor - fromColor;
if (g_sci->_features->hasMidPaletteCode()) {
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;
setCycleMap(fromColor, numColorsToCycle);
}
void GfxPalette32::doCycle(const uint8 fromColor, const int16 speed) {
PalCycler *const cycler = getCycler(fromColor);
if (cycler != nullptr) {
cycler->lastUpdateTick = g_sci->getTickCount();
updateCycler(*cycler, speed);
}
}
void GfxPalette32::cycleOn(const uint8 fromColor) {
PalCycler *const cycler = getCycler(fromColor);
if (cycler != nullptr && cycler->numTimesPaused > 0) {
--cycler->numTimesPaused;
}
}
void GfxPalette32::cyclePause(const uint8 fromColor) {
PalCycler *const cycler = getCycler(fromColor);
if (cycler != nullptr) {
++cycler->numTimesPaused;
}
}
void GfxPalette32::cycleAllOn() {
for (int i = 0; i < kNumCyclers; ++i) {
PalCyclerOwner &cycler = _cyclers[i];
if (cycler && cycler->numTimesPaused > 0) {
--cycler->numTimesPaused;
}
}
}
void GfxPalette32::cycleAllPause() {
// SSCI did not check for null pointers in the palette cyclers pointer array
for (int i = 0; i < kNumCyclers; ++i) {
PalCyclerOwner &cycler = _cyclers[i];
if (cycler) {
// 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; i < kNumCyclers; ++i) {
PalCyclerOwner &cycler = _cyclers[i];
if (cycler) {
++cycler->numTimesPaused;
}
}
}
void GfxPalette32::cycleOff(const uint8 fromColor) {
for (int i = 0; i < kNumCyclers; ++i) {
PalCyclerOwner &cycler = _cyclers[i];
if (cycler && cycler->fromColor == fromColor) {
clearCycleMap(fromColor, cycler->numColorsToCycle);
_cyclers[i].reset();
break;
}
}
}
void GfxPalette32::cycleAllOff() {
for (int i = 0; i < kNumCyclers; ++i) {
PalCyclerOwner &cycler = _cyclers[i];
if (cycler) {
clearCycleMap(cycler->fromColor, cycler->numColorsToCycle);
_cyclers[i].reset();
}
}
}
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) {
PalCyclerOwner &cycler = _cyclers[cyclerIndex];
if (cycler && cycler->fromColor == fromColor) {
return cycler.get();
}
}
return nullptr;
}
void GfxPalette32::applyAllCycles() {
Color paletteCopy[256];
memcpy(paletteCopy, _nextPalette.colors, sizeof(paletteCopy));
for (int i = 0; i < kNumCyclers; ++i) {
PalCyclerOwner &cycler = _cyclers[i];
if (cycler) {
cycler->currentCycle = (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];
}
}
}
}
void GfxPalette32::applyCycles() {
Color paletteCopy[256];
memcpy(paletteCopy, _nextPalette.colors, sizeof(paletteCopy));
const uint32 now = g_sci->getTickCount();
for (int i = 0; i < kNumCyclers; ++i) {
PalCyclerOwner &cycler = _cyclers[i];
if (!cycler) {
continue;
}
if (cycler->delay != 0 && cycler->numTimesPaused == 0) {
while ((cycler->delay + cycler->lastUpdateTick) < now) {
updateCycler(*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];
}
}
}
#pragma mark -
#pragma mark Fading
void GfxPalette32::setFade(const uint16 percent, const uint8 fromColor, uint16 toColor) {
if (fromColor > toColor) {
return;
}
// 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 <= toColor; i++) {
_fadeTable[i] = percent;
}
}
void GfxPalette32::fadeOff() {
setFade(100, 0, 255);
}
void GfxPalette32::applyFade() {
for (int i = 0; i < ARRAYSIZE(_fadeTable); ++i) {
if (_fadeTable[i] == 100) {
continue;
}
Color &color = _nextPalette.colors[i];
color.r = MIN(255, (uint16)color.r * _fadeTable[i] / 100);
color.g = MIN(255, (uint16)color.g * _fadeTable[i] / 100);
color.b = MIN(255, (uint16)color.b * _fadeTable[i] / 100);
}
}
} // End of namespace Sci