aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/graphics/remap32.h
blob: 9fc7ef637dd3d98758c189715195da1ededc1a24 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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
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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
/* 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.
 *
 */

#ifndef SCI_GRAPHICS_REMAP32_H
#define SCI_GRAPHICS_REMAP32_H

#include "common/algorithm.h"
#include "common/array.h"
#include "common/scummsys.h"
#include "sci/graphics/helpers.h"

namespace Sci {
class GfxPalette32;

enum RemapType {
	kRemapNone = 0,
	kRemapByRange = 1,
	kRemapByPercent = 2,
	kRemapToGray = 3,
	kRemapToPercentGray = 4
};

#pragma mark -
#pragma mark SingleRemap

/**
 * SingleRemap objects each manage one remapping operation.
 */
class SingleRemap {
public:
	SingleRemap() : _type(kRemapNone) {}

	/**
	 * The type of remap.
	 */
	RemapType _type;

	/**
	 * The first color that should be shifted by a range remap.
	 */
	uint8 _from;

	/**
	 * The last color that should be shifted a range remap.
	 */
	uint8 _to;

	/**
	 * The direction and amount that the colors should be shifted in a range
	 * remap.
	 */
	int16 _delta;

	/**
	 * The difference in brightness that should be applied by a brightness
	 * (percent) remap.
	 *
	 * This value may be be greater than 100, in which case the color will be
	 * oversaturated.
	 */
	int16 _percent;

	/**
	 * The amount of desaturation that should be applied by a saturation (gray)
	 * remap, where 0 is full saturation and 100 is full desaturation.
	 */
	uint8 _gray;

	/**
	 * The final array used by CelObj renderers to composite remapped pixels to
	 * the screen buffer.
	 *
	 * Here is how it works:
	 *
	 * The source bitmap being rendered will have pixels within the remap range
	 * (236-245 or 236-254), and the target buffer will have colors in the
	 * non-remapped range (0-235).
	 *
	 * To arrive at the correct color, first the source pixel is used to look up
	 * the correct SingleRemap for that pixel. Then, the final composited color
	 * is looked up in this array using the target's pixel color. In other
	 * words,
	 * `target = _remaps[remapEndColor - source].remapColors[target]`.
	 */
	uint8 _remapColors[236];

	/**
	 * Resets this SingleRemap's color information to default values.
	 */
	void reset();

	/**
	 * Recalculates and reapplies remap colors to the `_remapColors` array.
	 */
	bool update();

private:
	/**
	 * The previous brightness value. Used to determine whether or not
	 * `_idealColors` needs to be updated.
	 */
	int16 _lastPercent;

	/**
	 * The previous saturation value. Used to determine whether or not
	 * `_idealColors` needs to be updated.
	 */
	uint8 _lastGray;

	/**
	 * The colors from the current GfxPalette32 palette before this SingleRemap
	 * is applied.
	 */
	Color _originalColors[236];

	/**
	 * Map of colors that changed in `_originalColors` when this SingleRemap was
	 * updated. This map is transient and gets reset to `false` after the
	 * SingleRemap finishes updating.
	 */
	bool _originalColorsChanged[236];

	/**
	 * The ideal target RGB color values for each generated remap color.
	 */
	Color _idealColors[236];

	/**
	 * Map of colors that changed in `_idealColors` when this SingleRemap was
	 * updated. This map is transient and gets reset to `false` after the
	 * SingleRemap finishes applying.
	 */
	bool _idealColorsChanged[236];

	/**
	 * When applying a SingleRemap, finding an appropriate color in the palette
	 * is the responsibility of a distance function. Once a match is found, the
	 * distance of that match is stored here so that the next time the
	 * SingleRemap is applied, it can check the distance from the previous
	 * application and avoid triggering an expensive redraw of the entire screen
	 * if the new palette value only changed slightly.
	 */
	int _matchDistances[236];

	/**
	 * Computes the final target values for a range remap and applies them
	 * directly to the `_remaps` map.
	 *
	 * @note Was ByRange in SSCI.
	 */
	bool updateRange();

	/**
	 * Computes the intermediate target values for a brightness remap and
	 * applies them indirectly via the `apply` method.
	 *
	 * @note Was ByPercent in SSCI.
	 */
	bool updateBrightness();

	/**
	 * Computes the intermediate target values for a saturation remap and
	 * applies them indirectly via the `apply` method.
	 *
	 * @note Was ToGray in SSCI.
	 */
	bool updateSaturation();

	/**
	 * Computes the intermediate target values for a saturation + brightness
	 * bitmap and applies them indirectly via the `apply` method.
	 *
	 * @note Was ToPercentGray in SSCI.
	 */
	bool updateSaturationAndBrightness();

	/**
	 * Computes and applies the final values to the `_remaps` map.
	 *
	 * @note In SSCI, a boolean array of changed values was passed into this
	 * method, but this was done by creating arrays on the stack in the caller.
	 * Instead of doing this, we simply add another member property
	 * `_idealColorsChanged` and use that instead.
	 */
	bool apply();

	/**
	 * Calculates the square distance of two colors.
	 *
	 * @note In SSCI this method is Rgb24::Dist, but it is only used by
	 * SingleRemap.
	 */
	int colorDistance(const Color &a, const Color &b) const;

	/**
	 * Finds the closest index in the next palette matching the given RGB color.
	 * Returns -1 if no match can be found that is closer than
	 * `minimumDistance`.
	 *
	 * @note In SSCI, this method is SOLPalette::Match, but this particular
	 * signature is only used by SingleRemap.
	 */
	int16 matchColor(const Color &color, const int minimumDistance, int &outDistance, const bool *const blockedIndexes) const;
};

#pragma mark -
#pragma mark GfxRemap32

/**
 * This class provides color remapping support for SCI32 games.
 */
class GfxRemap32 : public Common::Serializable {
public:
	GfxRemap32();

	void saveLoadWithSerializer(Common::Serializer &s);

	inline uint8 getRemapCount() const { return _numActiveRemaps; }
	inline uint8 getStartColor() const { return _remapStartColor; }
	inline uint8 getEndColor() const { return _remapEndColor; }
	inline uint8 getBlockedRangeStart() const { return _blockedRangeStart; }
	inline int16 getBlockedRangeCount() const { return _blockedRangeCount; }

	/**
	 * Turns off remapping of the given color. If `color` is 0, all remaps are
	 * turned off.
	 */
	void remapOff(const uint8 color);

	/**
	 * Turns off all color remaps.
	 */
	void remapAllOff();

	/**
	 * Configures a SingleRemap for the remap color `color`. The SingleRemap
	 * will shift palette colors between `from` and `to` (inclusive) by `delta`
	 * palette entries when the remap is applied.
	 */
	void remapByRange(const uint8 color, const int16 from, const int16 to, const int16 delta);

	/**
	 * Configures a SingleRemap for the remap color `color` to modify the
	 * brightness of remapped colors by `percent`.
	 */
	void remapByPercent(const uint8 color, const int16 percent);

	/**
	 * Configures a SingleRemap for the remap color `color` to modify the
	 * saturation of remapped colors by `gray`.
	 */
	void remapToGray(const uint8 color, const int8 gray);

	/**
	 * Configures a SingleRemap for the remap color `color` to modify the
	 * brightness of remapped colors by `percent`, and saturation of remapped
	 * colors by `gray`.
	 */
	void remapToPercentGray(const uint8 color, const int16 gray, const int16 percent);

	/**
	 * Prevents GfxRemap32 from using the given range of palette entries as
	 * potential remap targets.
	 *
	 * @NOTE Was DontMapToRange in SSCI.
	 */
	void blockRange(const uint8 from, const int16 count);

	/**
	 * Determines whether or not the given color has an active remapper. If it
	 * does not, it is treated as a skip color and the pixel is not drawn.
	 *
	 * @note SSCI uses a boolean array to decide whether a pixel is remapped,
	 * but it is possible to get the same information from `_remaps`, as this
	 * function does. Presumably, the separate array was created for performance
	 * reasons, since this is called a lot in the most critical section of the
	 * renderer.
	 */
	inline bool remapEnabled(uint8 color) const {
		const uint8 index = _remapEndColor - color;
		// At least KQ7 DOS uses remap colors that are outside the valid remap
		// range; in these cases, just treat those pixels as skip pixels (which
		// is how they would be treated in SSCI)
		if (index >= _remaps.size()) {
			return false;
		}
		return (_remaps[index]._type != kRemapNone);
	}

	/**
	 * Calculates the correct color for a target by looking up the target color
	 * in the SingleRemap that controls the given sourceColor. If there is no
	 * remap for the given color, it will be treated as a skip color.
	 */
	inline uint8 remapColor(const uint8 sourceColor, const uint8 targetColor) const {
		const uint8 index = _remapEndColor - sourceColor;
		assert(index < _remaps.size());
		const SingleRemap &singleRemap = _remaps[index];
		assert(singleRemap._type != kRemapNone);
		// SSCI never really properly handled attempts to draw to a target with
		// pixels above the remap color maximum. In RAMA, the cursor views have
		// a remap color outlining the cursor, and so get drawn into a target
		// surface filled with a skip color of 255. In SSCI, this causes the
		// remapped color to be read from some statically allocated, never
		// written memory and so always ends up being 0 (black).
		if (targetColor >= ARRAYSIZE(singleRemap._remapColors)) {
			return 0;
		}
		return singleRemap._remapColors[targetColor];
	}

	/**
	 * Updates all active remaps in response to a palette change or a remap
	 * settings change.
	 *
	 * `paletteChanged` is true if the next palette in GfxPalette32 has been
	 * previously modified by other palette operations.
	 */
	bool remapAllTables(const bool paletteUpdated);

private:
	typedef Common::Array<SingleRemap> SingleRemapsList;

	/**
	 * The first index of the remap area in the system palette.
	 */
	const uint8 _remapStartColor;

	/**
	 * The last index of the remap area in the system palette.
	 */
	uint8 _remapEndColor;

	/**
	 * The number of currently active remaps.
	 */
	uint8 _numActiveRemaps;

	/**
	 * The list of SingleRemaps.
	 */
	SingleRemapsList _remaps;

	/**
	 * If true, indicates that one or more SingleRemaps were reconfigured and
	 * all remaps need to be recalculated.
	 */
	bool _needsUpdate;

	/**
	 * The first color that is blocked from being used as a remap target color.
	 */
	uint8 _blockedRangeStart;

	/**
	 * The size of the range of blocked colors. If zero, all colors are
	 * potential targets for remapping.
	 */
	int16 _blockedRangeCount;
};
} // End of namespace Sci
#endif