1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/file.h"
24 #include "common/system.h"
25 #include "graphics/palette.h"
26
27 #include "sci/sci.h"
28 #include "sci/event.h"
29 #include "sci/resource/resource.h"
30 #include "sci/util.h"
31 #include "sci/engine/features.h"
32 #include "sci/graphics/palette32.h"
33 #include "sci/graphics/remap32.h"
34 #include "sci/graphics/screen.h"
35
36 namespace Sci {
37
38 #pragma mark HunkPalette
39
HunkPalette(const SciSpan<const byte> & rawPalette)40 HunkPalette::HunkPalette(const SciSpan<const byte> &rawPalette) :
41 _version(0),
42 // The header size in palettes is garbage. In at least KQ7 2.00b and Phant1,
43 // the 999.pal sets this value to 0. In most other palettes it is set to 14,
44 // but the *actual* size of the header structure used in SSCI is 13, which
45 // is reflected by `kHunkPaletteHeaderSize`.
46 _numPalettes(rawPalette.getUint8At(kNumPaletteEntriesOffset)),
47 _data() {
48 assert(_numPalettes == 0 || _numPalettes == 1);
49 if (_numPalettes) {
50 _data = rawPalette;
51 _version = getEntryHeader().version;
52 }
53 }
54
write(SciSpan<byte> & out,const Palette & palette)55 void HunkPalette::write(SciSpan<byte> &out, const Palette &palette) {
56 const uint8 numPalettes = 1;
57 const uint16 paletteOffset = kHunkPaletteHeaderSize + 2 * numPalettes;
58
59 out[kNumPaletteEntriesOffset] = numPalettes;
60 out[kHunkPaletteHeaderSize + 2] = paletteOffset;
61
62 SciSpan<byte> entry = out.subspan(paletteOffset);
63 entry[kEntryStartColorOffset] = 0;
64 entry.setUint16SEAt(kEntryNumColorsOffset, ARRAYSIZE(palette.colors));
65 entry[kEntryUsedOffset] = 1;
66 entry[kEntrySharedUsedOffset] = 0;
67 entry.setUint32SEAt(kEntryVersionOffset, 1);
68
69 SciSpan<byte> paletteData = entry.subspan(kEntryHeaderSize);
70 for (uint i = 0; i < ARRAYSIZE(palette.colors); ++i) {
71 *paletteData++ = palette.colors[i].used;
72 *paletteData++ = palette.colors[i].r;
73 *paletteData++ = palette.colors[i].g;
74 *paletteData++ = palette.colors[i].b;
75 }
76 }
77
setVersion(const uint32 version) const78 void HunkPalette::setVersion(const uint32 version) const {
79 if (_numPalettes != _data.getUint8At(kNumPaletteEntriesOffset)) {
80 error("Invalid HunkPalette");
81 }
82
83 if (_numPalettes) {
84 const EntryHeader header = getEntryHeader();
85 if (header.version != _version) {
86 error("Invalid HunkPalette");
87 }
88
89 byte *palette = const_cast<byte *>(getPalPointer().getUnsafeDataAt(kEntryVersionOffset, sizeof(uint32)));
90 WRITE_SCI11ENDIAN_UINT32(palette, version);
91 _version = version;
92 }
93 }
94
getEntryHeader() const95 const HunkPalette::EntryHeader HunkPalette::getEntryHeader() const {
96 const SciSpan<const byte> data(getPalPointer());
97
98 EntryHeader header;
99 header.startColor = data.getUint8At(kEntryStartColorOffset);
100 header.numColors = data.getUint16SEAt(kEntryNumColorsOffset);
101 header.used = data.getUint8At(kEntryUsedOffset);
102 header.sharedUsed = data.getUint8At(kEntrySharedUsedOffset);
103 header.version = data.getUint32SEAt(kEntryVersionOffset);
104
105 return header;
106 }
107
toPalette() const108 const Palette HunkPalette::toPalette() const {
109 Palette outPalette;
110
111 // Set outPalette structures to 0
112 for (int16 i = 0; i < ARRAYSIZE(outPalette.mapping); ++i) {
113 outPalette.mapping[i] = 0;
114 }
115 outPalette.timestamp = 0;
116 for (int16 i = 0; i < ARRAYSIZE(outPalette.colors); ++i) {
117 outPalette.colors[i].used = false;
118 outPalette.colors[i].r = 0;
119 outPalette.colors[i].g = 0;
120 outPalette.colors[i].b = 0;
121 }
122 for (int16 i = 0; i < ARRAYSIZE(outPalette.intensity); ++i) {
123 outPalette.intensity[i] = 0;
124 }
125
126 if (_numPalettes) {
127 const EntryHeader header = getEntryHeader();
128 const uint32 dataSize = header.numColors * (/* RGB */ 3 + (header.sharedUsed ? 0 : 1));
129 const byte *data = getPalPointer().getUnsafeDataAt(kEntryHeaderSize, dataSize);
130
131 const int16 end = header.startColor + header.numColors;
132 assert(end <= 256);
133
134 if (header.sharedUsed) {
135 for (int16 i = header.startColor; i < end; ++i) {
136 outPalette.colors[i].used = header.used;
137 outPalette.colors[i].r = *data++;
138 outPalette.colors[i].g = *data++;
139 outPalette.colors[i].b = *data++;
140 }
141 } else {
142 for (int16 i = header.startColor; i < end; ++i) {
143 outPalette.colors[i].used = *data++;
144 outPalette.colors[i].r = *data++;
145 outPalette.colors[i].g = *data++;
146 outPalette.colors[i].b = *data++;
147 }
148 }
149 }
150
151 return outPalette;
152 }
153
154 #pragma mark -
155 #pragma mark Gamma correction tables
156
157 static const uint8 gammaTables[GfxPalette32::numGammaTables][256] = {
158 { 0, 2, 3, 5, 6, 7, 9, 10,
159 11, 13, 14, 15, 16, 18, 19, 20,
160 21, 22, 23, 25, 26, 27, 28, 29,
161 30, 32, 33, 34, 35, 36, 37, 38,
162 39, 41, 42, 43, 44, 45, 46, 47,
163 48, 49, 50, 51, 52, 54, 55, 56,
164 57, 58, 59, 60, 61, 62, 63, 64,
165 65, 66, 67, 68, 69, 70, 71, 72,
166 74, 75, 76, 77, 78, 79, 80, 81,
167 82, 83, 84, 85, 86, 87, 88, 89,
168 90, 91, 92, 93, 94, 95, 96, 97,
169 98, 99, 100, 101, 102, 103, 104, 105,
170 106, 107, 108, 109, 110, 111, 112, 113,
171 114, 115, 116, 117, 118, 119, 120, 121,
172 122, 123, 124, 125, 126, 127, 128, 128,
173 129, 130, 131, 132, 133, 134, 135, 136,
174 137, 138, 139, 140, 141, 142, 143, 144,
175 145, 146, 147, 148, 149, 150, 151, 152,
176 153, 153, 154, 155, 156, 157, 158, 159,
177 160, 161, 162, 163, 164, 165, 166, 167,
178 168, 169, 170, 171, 171, 172, 173, 174,
179 175, 176, 177, 178, 179, 180, 181, 182,
180 183, 184, 185, 186, 186, 187, 188, 189,
181 190, 191, 192, 193, 194, 195, 196, 197,
182 198, 199, 199, 200, 201, 202, 203, 204,
183 205, 206, 207, 208, 209, 210, 211, 211,
184 212, 213, 214, 215, 216, 217, 218, 219,
185 220, 221, 222, 222, 223, 224, 225, 226,
186 227, 228, 229, 230, 231, 232, 232, 233,
187 234, 235, 236, 237, 238, 239, 240, 241,
188 242, 242, 243, 244, 245, 246, 247, 248,
189 249, 250, 251, 251, 252, 253, 254, 255 },
190
191 { 0, 3, 5, 6, 8, 10, 11, 13,
192 14, 16, 17, 19, 20, 22, 23, 24,
193 26, 27, 28, 30, 31, 32, 33, 35,
194 36, 37, 38, 40, 41, 42, 43, 44,
195 46, 47, 48, 49, 50, 51, 53, 54,
196 55, 56, 57, 58, 59, 60, 62, 63,
197 64, 65, 66, 67, 68, 69, 70, 71,
198 73, 74, 75, 76, 77, 78, 79, 80,
199 81, 82, 83, 84, 85, 86, 87, 88,
200 89, 90, 91, 92, 93, 94, 95, 96,
201 97, 99, 100, 101, 102, 103, 104, 105,
202 106, 107, 108, 108, 109, 110, 111, 112,
203 113, 114, 115, 116, 117, 118, 119, 120,
204 121, 122, 123, 124, 125, 126, 127, 128,
205 129, 130, 131, 132, 133, 134, 135, 136,
206 136, 137, 138, 139, 140, 141, 142, 143,
207 144, 145, 146, 147, 148, 149, 150, 151,
208 151, 152, 153, 154, 155, 156, 157, 158,
209 159, 160, 161, 162, 162, 163, 164, 165,
210 166, 167, 168, 169, 170, 171, 172, 172,
211 173, 174, 175, 176, 177, 178, 179, 180,
212 180, 181, 182, 183, 184, 185, 186, 187,
213 188, 188, 189, 190, 191, 192, 193, 194,
214 195, 196, 196, 197, 198, 199, 200, 201,
215 202, 202, 203, 204, 205, 206, 207, 208,
216 209, 209, 210, 211, 212, 213, 214, 215,
217 215, 216, 217, 218, 219, 220, 221, 221,
218 222, 223, 224, 225, 226, 227, 227, 228,
219 229, 230, 231, 232, 233, 233, 234, 235,
220 236, 237, 238, 238, 239, 240, 241, 242,
221 243, 243, 244, 245, 246, 247, 248, 249,
222 249, 250, 251, 252, 253, 254, 254, 255 },
223
224 { 0, 4, 6, 8, 10, 12, 14, 16,
225 18, 19, 21, 23, 24, 26, 27, 29,
226 30, 32, 33, 35, 36, 37, 39, 40,
227 41, 43, 44, 45, 47, 48, 49, 50,
228 52, 53, 54, 55, 57, 58, 59, 60,
229 61, 62, 64, 65, 66, 67, 68, 69,
230 71, 72, 73, 74, 75, 76, 77, 78,
231 79, 81, 82, 83, 84, 85, 86, 87,
232 88, 89, 90, 91, 92, 93, 94, 95,
233 96, 97, 98, 99, 100, 102, 103, 104,
234 105, 106, 107, 108, 109, 110, 111, 112,
235 112, 113, 114, 115, 116, 117, 118, 119,
236 120, 121, 122, 123, 124, 125, 126, 127,
237 128, 129, 130, 131, 132, 133, 134, 135,
238 135, 136, 137, 138, 139, 140, 141, 142,
239 143, 144, 145, 146, 146, 147, 148, 149,
240 150, 151, 152, 153, 154, 155, 156, 156,
241 157, 158, 159, 160, 161, 162, 163, 163,
242 164, 165, 166, 167, 168, 169, 170, 170,
243 171, 172, 173, 174, 175, 176, 177, 177,
244 178, 179, 180, 181, 182, 183, 183, 184,
245 185, 186, 187, 188, 188, 189, 190, 191,
246 192, 193, 194, 194, 195, 196, 197, 198,
247 199, 199, 200, 201, 202, 203, 203, 204,
248 205, 206, 207, 208, 208, 209, 210, 211,
249 212, 212, 213, 214, 215, 216, 217, 217,
250 218, 219, 220, 221, 221, 222, 223, 224,
251 225, 225, 226, 227, 228, 229, 229, 230,
252 231, 232, 233, 233, 234, 235, 236, 237,
253 237, 238, 239, 240, 240, 241, 242, 243,
254 244, 244, 245, 246, 247, 247, 248, 249,
255 250, 251, 251, 252, 253, 254, 254, 255 },
256
257 { 0, 5, 9, 11, 14, 16, 19, 21,
258 23, 25, 26, 28, 30, 32, 33, 35,
259 37, 38, 40, 41, 43, 44, 46, 47,
260 49, 50, 52, 53, 54, 56, 57, 58,
261 60, 61, 62, 64, 65, 66, 67, 69,
262 70, 71, 72, 73, 75, 76, 77, 78,
263 79, 80, 82, 83, 84, 85, 86, 87,
264 88, 89, 91, 92, 93, 94, 95, 96,
265 97, 98, 99, 100, 101, 102, 103, 104,
266 105, 106, 107, 108, 109, 110, 111, 112,
267 113, 114, 115, 116, 117, 118, 119, 120,
268 121, 122, 123, 124, 125, 126, 127, 128,
269 129, 130, 131, 132, 133, 134, 134, 135,
270 136, 137, 138, 139, 140, 141, 142, 143,
271 144, 144, 145, 146, 147, 148, 149, 150,
272 151, 152, 152, 153, 154, 155, 156, 157,
273 158, 158, 159, 160, 161, 162, 163, 164,
274 164, 165, 166, 167, 168, 169, 169, 170,
275 171, 172, 173, 174, 174, 175, 176, 177,
276 178, 179, 179, 180, 181, 182, 183, 183,
277 184, 185, 186, 187, 187, 188, 189, 190,
278 191, 191, 192, 193, 194, 195, 195, 196,
279 197, 198, 199, 199, 200, 201, 202, 202,
280 203, 204, 205, 205, 206, 207, 208, 209,
281 209, 210, 211, 212, 212, 213, 214, 215,
282 215, 216, 217, 218, 218, 219, 220, 221,
283 221, 222, 223, 224, 224, 225, 226, 227,
284 227, 228, 229, 230, 230, 231, 232, 232,
285 233, 234, 235, 235, 236, 237, 238, 238,
286 239, 240, 240, 241, 242, 243, 243, 244,
287 245, 245, 246, 247, 248, 248, 249, 250,
288 250, 251, 252, 252, 253, 254, 255, 255 },
289
290 { 0, 9, 14, 18, 21, 24, 27, 29,
291 32, 34, 37, 39, 41, 43, 45, 47,
292 48, 50, 52, 54, 55, 57, 59, 60,
293 62, 63, 65, 66, 68, 69, 71, 72,
294 73, 75, 76, 77, 79, 80, 81, 83,
295 84, 85, 86, 88, 89, 90, 91, 92,
296 94, 95, 96, 97, 98, 99, 100, 102,
297 103, 104, 105, 106, 107, 108, 109, 110,
298 111, 112, 113, 114, 115, 116, 117, 118,
299 119, 120, 121, 122, 123, 124, 125, 126,
300 127, 128, 129, 130, 131, 132, 133, 134,
301 135, 136, 137, 137, 138, 139, 140, 141,
302 142, 143, 144, 145, 145, 146, 147, 148,
303 149, 150, 151, 151, 152, 153, 154, 155,
304 156, 156, 157, 158, 159, 160, 161, 161,
305 162, 163, 164, 165, 165, 166, 167, 168,
306 169, 169, 170, 171, 172, 173, 173, 174,
307 175, 176, 176, 177, 178, 179, 179, 180,
308 181, 182, 182, 183, 184, 185, 185, 186,
309 187, 188, 188, 189, 190, 191, 191, 192,
310 193, 194, 194, 195, 196, 196, 197, 198,
311 199, 199, 200, 201, 201, 202, 203, 203,
312 204, 205, 206, 206, 207, 208, 208, 209,
313 210, 210, 211, 212, 212, 213, 214, 214,
314 215, 216, 216, 217, 218, 218, 219, 220,
315 220, 221, 222, 222, 223, 224, 224, 225,
316 226, 226, 227, 228, 228, 229, 230, 230,
317 231, 231, 232, 233, 233, 234, 235, 235,
318 236, 237, 237, 238, 238, 239, 240, 240,
319 241, 242, 242, 243, 243, 244, 245, 245,
320 246, 247, 247, 248, 248, 249, 250, 250,
321 251, 251, 252, 253, 253, 254, 254, 255 },
322
323 { 0, 16, 23, 28, 32, 36, 39, 42,
324 45, 48, 50, 53, 55, 58, 60, 62,
325 64, 66, 68, 70, 71, 73, 75, 77,
326 78, 80, 81, 83, 84, 86, 87, 89,
327 90, 92, 93, 94, 96, 97, 98, 100,
328 101, 102, 103, 105, 106, 107, 108, 109,
329 111, 112, 113, 114, 115, 116, 117, 118,
330 119, 121, 122, 123, 124, 125, 126, 127,
331 128, 129, 130, 131, 132, 133, 134, 135,
332 135, 136, 137, 138, 139, 140, 141, 142,
333 143, 144, 145, 145, 146, 147, 148, 149,
334 150, 151, 151, 152, 153, 154, 155, 156,
335 156, 157, 158, 159, 160, 160, 161, 162,
336 163, 164, 164, 165, 166, 167, 167, 168,
337 169, 170, 170, 171, 172, 173, 173, 174,
338 175, 176, 176, 177, 178, 179, 179, 180,
339 181, 181, 182, 183, 183, 184, 185, 186,
340 186, 187, 188, 188, 189, 190, 190, 191,
341 192, 192, 193, 194, 194, 195, 196, 196,
342 197, 198, 198, 199, 199, 200, 201, 201,
343 202, 203, 203, 204, 204, 205, 206, 206,
344 207, 208, 208, 209, 209, 210, 211, 211,
345 212, 212, 213, 214, 214, 215, 215, 216,
346 217, 217, 218, 218, 219, 220, 220, 221,
347 221, 222, 222, 223, 224, 224, 225, 225,
348 226, 226, 227, 228, 228, 229, 229, 230,
349 230, 231, 231, 232, 233, 233, 234, 234,
350 235, 235, 236, 236, 237, 237, 238, 238,
351 239, 240, 240, 241, 241, 242, 242, 243,
352 243, 244, 244, 245, 245, 246, 246, 247,
353 247, 248, 248, 249, 249, 250, 250, 251,
354 251, 252, 252, 253, 253, 254, 254, 255 }
355 };
356
357 #pragma mark -
358 #pragma mark GfxPalette32
359
GfxPalette32(ResourceManager * resMan)360 GfxPalette32::GfxPalette32(ResourceManager *resMan)
361 : _resMan(resMan),
362
363 // Palette versioning
364 _version(1),
365 _needsUpdate(false),
366 #ifdef USE_RGB_COLOR
367 _hardwarePalette(),
368 #endif
369 _currentPalette(),
370 _sourcePalette(),
371 _nextPalette(),
372
373 // Palette varying
374 _varyStartPalette(nullptr),
375 _varyTargetPalette(nullptr),
376 _varyFromColor(0),
377 _varyToColor(255),
378 _varyLastTick(0),
379 _varyTime(0),
380 _varyDirection(0),
381 _varyPercent(0),
382 _varyTargetPercent(0),
383 _varyNumTimesPaused(0),
384
385 // Palette cycling
386 _cycleMap(),
387
388 // Gamma correction
389 _gammaLevel(g_sci->_features->useMacGammaLevel() ? 2 : -1),
390 _gammaChanged(false) {
391
392 for (int i = 0, len = ARRAYSIZE(_fadeTable); i < len; ++i) {
393 _fadeTable[i] = 100;
394 }
395
396 loadPalette(999);
397 }
398
loadPalette(const GuiResourceId resourceId)399 bool GfxPalette32::loadPalette(const GuiResourceId resourceId) {
400 Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
401
402 if (!palResource) {
403 return false;
404 }
405
406 const HunkPalette palette(*palResource);
407 submit(palette);
408 return true;
409 }
410
matchColor(const uint8 r,const uint8 g,const uint8 b)411 int16 GfxPalette32::matchColor(const uint8 r, const uint8 g, const uint8 b) {
412 int16 bestIndex = 0;
413 int bestDifference = 0xFFFFF;
414 int difference;
415
416 for (int i = 0, channelDifference; i < g_sci->_gfxRemap32->getStartColor(); ++i) {
417 difference = _currentPalette.colors[i].r - r;
418 difference *= difference;
419 if (bestDifference <= difference) {
420 continue;
421 }
422
423 channelDifference = _currentPalette.colors[i].g - g;
424 difference += channelDifference * channelDifference;
425 if (bestDifference <= difference) {
426 continue;
427 }
428
429 channelDifference = _currentPalette.colors[i].b - b;
430 difference += channelDifference * channelDifference;
431 if (bestDifference <= difference) {
432 continue;
433 }
434 bestDifference = difference;
435 bestIndex = i;
436 }
437
438 return bestIndex;
439 }
440
getPlatformBlack() const441 uint8 GfxPalette32::getPlatformBlack() const {
442 return (g_sci->getPlatform() == Common::kPlatformMacintosh) ? 255 : 0;
443 }
444
getPlatformWhite() const445 uint8 GfxPalette32::getPlatformWhite() const {
446 return (g_sci->getPlatform() == Common::kPlatformMacintosh) ? 0 : 255;
447 }
448
submit(const Palette & palette)449 void GfxPalette32::submit(const Palette &palette) {
450 // If `_needsUpdate` is already set, there is no need to test whether
451 // this palette submission causes a change to `_sourcePalette` since it is
452 // going to be updated already anyway
453 if (_needsUpdate) {
454 mergePalette(_sourcePalette, palette);
455 } else {
456 const Palette oldSourcePalette(_sourcePalette);
457 mergePalette(_sourcePalette, palette);
458
459 if (_sourcePalette != oldSourcePalette) {
460 ++_version;
461 _needsUpdate = true;
462 }
463 }
464 }
465
submit(const HunkPalette & hunkPalette)466 void GfxPalette32::submit(const HunkPalette &hunkPalette) {
467 if (hunkPalette.getVersion() == _version) {
468 return;
469 }
470
471 submit(hunkPalette.toPalette());
472 hunkPalette.setVersion(_version);
473 }
474
updateForFrame()475 bool GfxPalette32::updateForFrame() {
476 applyAll();
477 _needsUpdate = false;
478 return g_sci->_gfxRemap32->remapAllTables(_nextPalette != _currentPalette);
479 }
480
updateFFrame()481 void GfxPalette32::updateFFrame() {
482 for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) {
483 _nextPalette.colors[i] = _sourcePalette.colors[i];
484 }
485 _needsUpdate = false;
486 g_sci->_gfxRemap32->remapAllTables(_nextPalette != _currentPalette);
487 }
488
updateHardware()489 void GfxPalette32::updateHardware() {
490 if (_currentPalette == _nextPalette && !_gammaChanged) {
491 return;
492 }
493
494 #ifdef USE_RGB_COLOR
495 uint8 *bpal = _hardwarePalette;
496 #else
497 uint8 bpal[256 * 3];
498 #endif
499
500 // HACK: There are resources in a couple of Windows-only games that seem to
501 // include bogus palette entries above 236. SSCI does a lot of extra work
502 // when running in Windows to shift palettes and rewrite view & pic pixel
503 // data on-the-fly to account for the way Windows palettes work, which
504 // seems to end up masking the fact that there is some bad palette data.
505 // Since only one demo and one game seem to have this problem, we instead
506 // "fix" the problem here by ignoring attempts to send high palette entries
507 // to the backend. This makes those high pixels render black, which seems to
508 // match what would happen in the original interpreter, and saves us from
509 // having to clutter up the engine with a bunch of palette shifting garbage.
510 //
511 // This workaround also handles Mac games, as they use 236 for black to avoid
512 // conflicting with the operating system's palette which uses 0 for white.
513 int maxIndex = ARRAYSIZE(_currentPalette.colors) - 2;
514 if (g_sci->getGameId() == GID_HOYLE5 ||
515 (g_sci->getGameId() == GID_GK2 && g_sci->isDemo()) ||
516 g_sci->getPlatform() == Common::kPlatformMacintosh) {
517 maxIndex = 235;
518 }
519
520 for (int i = 0; i <= maxIndex; ++i) {
521 _currentPalette.colors[i] = _nextPalette.colors[i];
522
523 // All color entries MUST be copied, not just "used" entries, otherwise
524 // uninitialised memory from bpal makes its way into the system palette.
525 // This would not normally be a problem, except that games sometimes use
526 // unused palette entries. e.g. Phant1 title screen references palette
527 // entries outside its own palette, so will render garbage colors where
528 // the game expects them to be black
529 if (_gammaLevel == -1) {
530 bpal[i * 3 ] = _currentPalette.colors[i].r;
531 bpal[i * 3 + 1] = _currentPalette.colors[i].g;
532 bpal[i * 3 + 2] = _currentPalette.colors[i].b;
533 } else {
534 bpal[i * 3 ] = gammaTables[_gammaLevel][_currentPalette.colors[i].r];
535 bpal[i * 3 + 1] = gammaTables[_gammaLevel][_currentPalette.colors[i].g];
536 bpal[i * 3 + 2] = gammaTables[_gammaLevel][_currentPalette.colors[i].b];
537 }
538 }
539
540 #ifndef USE_RGB_COLOR
541 // When creating a raw palette on the stack, any skipped area of the palette
542 // needs to be blacked out or else it will contain garbage memory
543 memset(bpal + (maxIndex + 1) * 3, 0, (255 - maxIndex - 1) * 3);
544 #endif
545
546 // The last color must always be white
547 bpal[255 * 3 ] = 255;
548 bpal[255 * 3 + 1] = 255;
549 bpal[255 * 3 + 2] = 255;
550
551 // If the system is in a high color mode, which can happen during video
552 // playback, attempting to send the palette to OSystem is illegal and will
553 // result in a crash
554 if (g_system->getScreenFormat().bytesPerPixel == 1) {
555 g_system->getPaletteManager()->setPalette(bpal, 0, 256);
556 }
557
558 _gammaChanged = false;
559 }
560
getPaletteFromResource(const GuiResourceId resourceId) const561 Palette GfxPalette32::getPaletteFromResource(const GuiResourceId resourceId) const {
562 Resource *palResource = _resMan->findResource(ResourceId(kResourceTypePalette, resourceId), false);
563
564 if (!palResource) {
565 error("Could not load vary palette %d", resourceId);
566 }
567
568 const HunkPalette rawPalette(*palResource);
569 return rawPalette.toPalette();
570 }
571
mergePalette(Palette & to,const Palette & from)572 void GfxPalette32::mergePalette(Palette &to, const Palette &from) {
573 // All colors MUST be copied, even index 255, despite the fact that games
574 // cannot actually change index 255 (it is forced to white when generating
575 // the hardware palette in updateHardware). While this causes some
576 // additional unnecessary source palette invalidations, not doing it breaks
577 // some badly programmed rooms, like room 6400 in Phant1 (see Trac#9788).
578 // (Note, however, that that specific glitch is fully fixed by ignoring a
579 // bad palette in the CelObjView constructor)
580 for (int i = 0; i < ARRAYSIZE(to.colors); ++i) {
581 if (from.colors[i].used) {
582 to.colors[i] = from.colors[i];
583 }
584 }
585 }
586
applyAll()587 void GfxPalette32::applyAll() {
588 applyVary();
589 applyCycles();
590 applyFade();
591 }
592
593 #pragma mark -
594 #pragma mark Varying
595
setVary(const Palette & target,const int16 percent,const int32 ticks,const int16 fromColor,const int16 toColor)596 void GfxPalette32::setVary(const Palette &target, const int16 percent, const int32 ticks, const int16 fromColor, const int16 toColor) {
597 setTarget(target);
598 setVaryTime(percent, ticks);
599
600 if (fromColor > -1) {
601 _varyFromColor = fromColor;
602 }
603 if (toColor > -1) {
604 assert(toColor < 256);
605 _varyToColor = toColor;
606 }
607 }
608
setVaryPercent(const int16 percent,const int32 ticks)609 void GfxPalette32::setVaryPercent(const int16 percent, const int32 ticks) {
610 if (_varyTargetPalette) {
611 setVaryTime(percent, ticks);
612 }
613
614 // SSCI had two additional parameters for this function to change the
615 // `_varyFromColor`, but they were always hardcoded to be ignored
616 }
617
setVaryTime(const int32 time)618 void GfxPalette32::setVaryTime(const int32 time) {
619 if (_varyTargetPalette) {
620 setVaryTime(_varyTargetPercent, time);
621 }
622 }
623
setVaryTime(const int16 percent,const int32 ticks)624 void GfxPalette32::setVaryTime(const int16 percent, const int32 ticks) {
625 _varyLastTick = g_sci->getTickCount();
626 if (!ticks || _varyPercent == percent) {
627 _varyDirection = 0;
628 _varyTargetPercent = _varyPercent = percent;
629 } else {
630 _varyTime = ticks / (percent - _varyPercent);
631 _varyTargetPercent = percent;
632
633 if (_varyTime > 0) {
634 _varyDirection = 1;
635 } else if (_varyTime < 0) {
636 _varyDirection = -1;
637 _varyTime = -_varyTime;
638 } else {
639 _varyDirection = 0;
640 _varyTargetPercent = _varyPercent = percent;
641 }
642 }
643 }
644
varyOff()645 void GfxPalette32::varyOff() {
646 _varyNumTimesPaused = 0;
647 _varyPercent = _varyTargetPercent = 0;
648 _varyFromColor = 0;
649 _varyToColor = 255;
650 _varyDirection = 0;
651 _varyTargetPalette.reset();
652 _varyStartPalette.reset();
653 }
654
varyPause()655 void GfxPalette32::varyPause() {
656 _varyDirection = 0;
657 ++_varyNumTimesPaused;
658 }
659
varyOn()660 void GfxPalette32::varyOn() {
661 if (_varyNumTimesPaused > 0) {
662 --_varyNumTimesPaused;
663 }
664
665 if (_varyTargetPalette && _varyNumTimesPaused == 0) {
666 if (_varyPercent != _varyTargetPercent && _varyTime != 0) {
667 _varyDirection = (_varyTargetPercent - _varyPercent > 0) ? 1 : -1;
668 } else {
669 _varyPercent = _varyTargetPercent;
670 }
671 }
672 }
673
setTarget(const Palette & palette)674 void GfxPalette32::setTarget(const Palette &palette) {
675 _varyTargetPalette.reset(new Palette(palette));
676 }
677
setStart(const Palette & palette)678 void GfxPalette32::setStart(const Palette &palette) {
679 _varyStartPalette.reset(new Palette(palette));
680 }
681
mergeStart(const Palette & palette)682 void GfxPalette32::mergeStart(const Palette &palette) {
683 if (_varyStartPalette) {
684 mergePalette(*_varyStartPalette, palette);
685 } else {
686 _varyStartPalette.reset(new Palette(palette));
687 }
688 }
689
mergeTarget(const Palette & palette)690 void GfxPalette32::mergeTarget(const Palette &palette) {
691 if (_varyTargetPalette) {
692 mergePalette(*_varyTargetPalette, palette);
693 } else {
694 _varyTargetPalette.reset(new Palette(palette));
695 }
696 }
697
applyVary()698 void GfxPalette32::applyVary() {
699 const uint32 now = g_sci->getTickCount();
700 while ((int32)(now - _varyLastTick) > _varyTime && _varyDirection != 0) {
701 _varyLastTick += _varyTime;
702
703 if (_varyPercent == _varyTargetPercent) {
704 _varyDirection = 0;
705 }
706
707 _varyPercent += _varyDirection;
708 }
709
710 if (_varyPercent == 0 || !_varyTargetPalette) {
711 for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) {
712 if (_varyStartPalette && i >= _varyFromColor && i <= _varyToColor) {
713 _nextPalette.colors[i] = _varyStartPalette->colors[i];
714 } else {
715 _nextPalette.colors[i] = _sourcePalette.colors[i];
716 }
717 }
718 } else {
719 for (int i = 0; i < ARRAYSIZE(_nextPalette.colors); ++i) {
720 if (i >= _varyFromColor && i <= _varyToColor) {
721 Color targetColor = _varyTargetPalette->colors[i];
722 Color sourceColor;
723
724 if (_varyStartPalette) {
725 sourceColor = _varyStartPalette->colors[i];
726 } else {
727 sourceColor = _sourcePalette.colors[i];
728 }
729
730 Color computedColor;
731
732 int color;
733 color = targetColor.r - sourceColor.r;
734 computedColor.r = ((color * _varyPercent) / 100) + sourceColor.r;
735 color = targetColor.g - sourceColor.g;
736 computedColor.g = ((color * _varyPercent) / 100) + sourceColor.g;
737 color = targetColor.b - sourceColor.b;
738 computedColor.b = ((color * _varyPercent) / 100) + sourceColor.b;
739 computedColor.used = sourceColor.used;
740
741 _nextPalette.colors[i] = computedColor;
742 }
743 else {
744 _nextPalette.colors[i] = _sourcePalette.colors[i];
745 }
746 }
747 }
748 }
749
kernelPalVarySet(const GuiResourceId paletteId,const int16 percent,const int32 ticks,const int16 fromColor,const int16 toColor)750 void GfxPalette32::kernelPalVarySet(const GuiResourceId paletteId, const int16 percent, const int32 ticks, const int16 fromColor, const int16 toColor) {
751 Palette palette;
752
753 if (getSciVersion() == SCI_VERSION_3 && paletteId == 0xFFFF) {
754 palette = _currentPalette;
755 assert(fromColor >= 0 && fromColor < 256);
756 assert(toColor >= 0 && toColor < 256);
757 // While palette varying is normally inclusive of `toColor`, the
758 // palette inversion code in SSCI excludes `toColor`, and RAMA room
759 // 6201 requires this or else parts of the game's UI get inverted
760 for (int i = fromColor; i < toColor; ++i) {
761 palette.colors[i].r = ~palette.colors[i].r;
762 palette.colors[i].g = ~palette.colors[i].g;
763 palette.colors[i].b = ~palette.colors[i].b;
764 }
765 } else {
766 palette = getPaletteFromResource(paletteId);
767 }
768
769 setVary(palette, percent, ticks, fromColor, toColor);
770 }
771
kernelPalVaryMergeTarget(const GuiResourceId paletteId)772 void GfxPalette32::kernelPalVaryMergeTarget(const GuiResourceId paletteId) {
773 const Palette palette = getPaletteFromResource(paletteId);
774 mergeTarget(palette);
775 }
776
kernelPalVarySetTarget(const GuiResourceId paletteId)777 void GfxPalette32::kernelPalVarySetTarget(const GuiResourceId paletteId) {
778 const Palette palette = getPaletteFromResource(paletteId);
779 setTarget(palette);
780 }
781
kernelPalVarySetStart(const GuiResourceId paletteId)782 void GfxPalette32::kernelPalVarySetStart(const GuiResourceId paletteId) {
783 const Palette palette = getPaletteFromResource(paletteId);
784 setStart(palette);
785 }
786
kernelPalVaryMergeStart(const GuiResourceId paletteId)787 void GfxPalette32::kernelPalVaryMergeStart(const GuiResourceId paletteId) {
788 const Palette palette = getPaletteFromResource(paletteId);
789 mergeStart(palette);
790 }
791
kernelPalVaryPause(const bool pause)792 void GfxPalette32::kernelPalVaryPause(const bool pause) {
793 if (pause) {
794 varyPause();
795 } else {
796 varyOn();
797 }
798 }
799
800 #pragma mark -
801 #pragma mark Cycling
802
setCycle(const uint8 fromColor,const uint8 toColor,const int16 direction,const int16 delay)803 void GfxPalette32::setCycle(const uint8 fromColor, const uint8 toColor, const int16 direction, const int16 delay) {
804 assert(fromColor < toColor);
805
806 PalCycler *cycler = getCycler(fromColor);
807
808 if (cycler != nullptr) {
809 clearCycleMap(fromColor, cycler->numColorsToCycle);
810 } else {
811 for (int i = 0; i < kNumCyclers; ++i) {
812 if (!_cyclers[i]) {
813 cycler = new PalCycler;
814 _cyclers[i].reset(cycler);
815 break;
816 }
817 }
818 }
819
820 // If there are no free cycler slots, SSCI overrides the first oldest cycler
821 // that it finds, where "oldest" is determined by the difference between the
822 // tick and now
823 if (cycler == nullptr) {
824 const uint32 now = g_sci->getTickCount();
825 uint32 minUpdateDelta = 0xFFFFFFFF;
826
827 for (int i = 0; i < kNumCyclers; ++i) {
828 PalCyclerOwner &candidate = _cyclers[i];
829
830 const uint32 updateDelta = now - candidate->lastUpdateTick;
831 if (updateDelta < minUpdateDelta) {
832 minUpdateDelta = updateDelta;
833 cycler = candidate.get();
834 }
835 }
836
837 clearCycleMap(cycler->fromColor, cycler->numColorsToCycle);
838 }
839
840 uint16 numColorsToCycle = toColor - fromColor;
841 if (g_sci->_features->hasMidPaletteCode()) {
842 numColorsToCycle += 1;
843 }
844 cycler->fromColor = fromColor;
845 cycler->numColorsToCycle = numColorsToCycle;
846 cycler->currentCycle = fromColor;
847 cycler->direction = direction < 0 ? kPalCycleBackward : kPalCycleForward;
848 cycler->delay = delay;
849 cycler->lastUpdateTick = g_sci->getTickCount();
850 cycler->numTimesPaused = 0;
851
852 setCycleMap(fromColor, numColorsToCycle);
853 }
854
doCycle(const uint8 fromColor,const int16 speed)855 void GfxPalette32::doCycle(const uint8 fromColor, const int16 speed) {
856 PalCycler *const cycler = getCycler(fromColor);
857 if (cycler != nullptr) {
858 cycler->lastUpdateTick = g_sci->getTickCount();
859 updateCycler(*cycler, speed);
860 }
861 }
862
cycleOn(const uint8 fromColor)863 void GfxPalette32::cycleOn(const uint8 fromColor) {
864 PalCycler *const cycler = getCycler(fromColor);
865 if (cycler != nullptr && cycler->numTimesPaused > 0) {
866 --cycler->numTimesPaused;
867 }
868 }
869
cyclePause(const uint8 fromColor)870 void GfxPalette32::cyclePause(const uint8 fromColor) {
871 PalCycler *const cycler = getCycler(fromColor);
872 if (cycler != nullptr) {
873 ++cycler->numTimesPaused;
874 }
875 }
876
cycleAllOn()877 void GfxPalette32::cycleAllOn() {
878 for (int i = 0; i < kNumCyclers; ++i) {
879 PalCyclerOwner &cycler = _cyclers[i];
880 if (cycler && cycler->numTimesPaused > 0) {
881 --cycler->numTimesPaused;
882 }
883 }
884 }
885
cycleAllPause()886 void GfxPalette32::cycleAllPause() {
887 // SSCI did not check for null pointers in the palette cyclers pointer array
888 for (int i = 0; i < kNumCyclers; ++i) {
889 PalCyclerOwner &cycler = _cyclers[i];
890 if (cycler) {
891 // This seems odd, because currentCycle is 0..numColorsPerCycle,
892 // but fromColor is 0..255. When applyAllCycles runs, the values
893 // end up back in range
894 cycler->currentCycle = cycler->fromColor;
895 }
896 }
897
898 applyAllCycles();
899
900 for (int i = 0; i < kNumCyclers; ++i) {
901 PalCyclerOwner &cycler = _cyclers[i];
902 if (cycler) {
903 ++cycler->numTimesPaused;
904 }
905 }
906 }
907
cycleOff(const uint8 fromColor)908 void GfxPalette32::cycleOff(const uint8 fromColor) {
909 for (int i = 0; i < kNumCyclers; ++i) {
910 PalCyclerOwner &cycler = _cyclers[i];
911 if (cycler && cycler->fromColor == fromColor) {
912 clearCycleMap(fromColor, cycler->numColorsToCycle);
913 _cyclers[i].reset();
914 break;
915 }
916 }
917 }
918
cycleAllOff()919 void GfxPalette32::cycleAllOff() {
920 for (int i = 0; i < kNumCyclers; ++i) {
921 PalCyclerOwner &cycler = _cyclers[i];
922 if (cycler) {
923 clearCycleMap(cycler->fromColor, cycler->numColorsToCycle);
924 _cyclers[i].reset();
925 }
926 }
927 }
928
updateCycler(PalCycler & cycler,const int16 speed)929 void GfxPalette32::updateCycler(PalCycler &cycler, const int16 speed) {
930 int16 currentCycle = cycler.currentCycle;
931 const uint16 numColorsToCycle = cycler.numColorsToCycle;
932
933 if (cycler.direction == kPalCycleBackward) {
934 currentCycle = (currentCycle - (speed % numColorsToCycle)) + numColorsToCycle;
935 } else {
936 currentCycle = currentCycle + speed;
937 }
938
939 cycler.currentCycle = currentCycle % numColorsToCycle;
940 }
941
clearCycleMap(const uint16 fromColor,const uint16 numColorsToClear)942 void GfxPalette32::clearCycleMap(const uint16 fromColor, const uint16 numColorsToClear) {
943 bool *mapEntry = _cycleMap + fromColor;
944 const bool *const lastEntry = _cycleMap + numColorsToClear;
945 while (mapEntry < lastEntry) {
946 *mapEntry++ = false;
947 }
948 }
949
setCycleMap(const uint16 fromColor,const uint16 numColorsToSet)950 void GfxPalette32::setCycleMap(const uint16 fromColor, const uint16 numColorsToSet) {
951 bool *mapEntry = _cycleMap + fromColor;
952 const bool *const lastEntry = _cycleMap + numColorsToSet;
953 while (mapEntry < lastEntry) {
954 if (*mapEntry != false) {
955 error("Cycles intersect");
956 }
957 *mapEntry++ = true;
958 }
959 }
960
getCycler(const uint16 fromColor)961 PalCycler *GfxPalette32::getCycler(const uint16 fromColor) {
962 for (int cyclerIndex = 0; cyclerIndex < kNumCyclers; ++cyclerIndex) {
963 PalCyclerOwner &cycler = _cyclers[cyclerIndex];
964 if (cycler && cycler->fromColor == fromColor) {
965 return cycler.get();
966 }
967 }
968
969 return nullptr;
970 }
971
applyAllCycles()972 void GfxPalette32::applyAllCycles() {
973 Color paletteCopy[256];
974 memcpy(paletteCopy, _nextPalette.colors, sizeof(paletteCopy));
975
976 for (int i = 0; i < kNumCyclers; ++i) {
977 PalCyclerOwner &cycler = _cyclers[i];
978 if (cycler) {
979 cycler->currentCycle = (cycler->currentCycle + 1) % cycler->numColorsToCycle;
980 for (int j = 0; j < cycler->numColorsToCycle; j++) {
981 _nextPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
982 }
983 }
984 }
985 }
986
applyCycles()987 void GfxPalette32::applyCycles() {
988 Color paletteCopy[256];
989 memcpy(paletteCopy, _nextPalette.colors, sizeof(paletteCopy));
990
991 const uint32 now = g_sci->getTickCount();
992 for (int i = 0; i < kNumCyclers; ++i) {
993 PalCyclerOwner &cycler = _cyclers[i];
994 if (!cycler) {
995 continue;
996 }
997
998 if (cycler->delay != 0 && cycler->numTimesPaused == 0) {
999 while ((cycler->delay + cycler->lastUpdateTick) < now) {
1000 updateCycler(*cycler, 1);
1001 cycler->lastUpdateTick += cycler->delay;
1002 }
1003 }
1004
1005 for (int j = 0; j < cycler->numColorsToCycle; j++) {
1006 _nextPalette.colors[cycler->fromColor + j] = paletteCopy[cycler->fromColor + (cycler->currentCycle + j) % cycler->numColorsToCycle];
1007 }
1008 }
1009 }
1010
1011 #pragma mark -
1012 #pragma mark Fading
1013
setFade(const uint16 percent,const uint8 fromColor,uint16 toColor)1014 void GfxPalette32::setFade(const uint16 percent, const uint8 fromColor, uint16 toColor) {
1015 if (fromColor > toColor) {
1016 return;
1017 }
1018
1019 // Some game scripts (like SQ6 Sierra logo and main menu) incorrectly call
1020 // setFade with toColor set to 256
1021 if (toColor > 255) {
1022 toColor = 255;
1023 }
1024
1025 for (int i = fromColor; i <= toColor; i++) {
1026 _fadeTable[i] = percent;
1027 }
1028 }
1029
fadeOff()1030 void GfxPalette32::fadeOff() {
1031 setFade(100, 0, 255);
1032 }
1033
applyFade()1034 void GfxPalette32::applyFade() {
1035 for (int i = 0; i < ARRAYSIZE(_fadeTable); ++i) {
1036 if (_fadeTable[i] == 100) {
1037 continue;
1038 }
1039
1040 Color &color = _nextPalette.colors[i];
1041
1042 color.r = MIN(255, (uint16)color.r * _fadeTable[i] / 100);
1043 color.g = MIN(255, (uint16)color.g * _fadeTable[i] / 100);
1044 color.b = MIN(255, (uint16)color.b * _fadeTable[i] / 100);
1045 }
1046 }
1047
1048 } // End of namespace Sci
1049