aboutsummaryrefslogtreecommitdiff
path: root/engines/cine/pal.cpp
blob: 97d4b319dddafa1ac93adc949b5744e2549efb28 (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
/* 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.
 *
 * $URL$
 * $Id$
 *
 */

#include "cine/cine.h"
#include "cine/various.h"
#include "cine/pal.h"

namespace Cine {

static const Graphics::PixelFormat kLowPalFormat  = {2, 5, 5, 5, 8, 8, 4,  0, 0};
static const Graphics::PixelFormat kHighPalFormat = {3, 0, 0, 0, 8, 0, 8, 16, 0};
static const Graphics::PixelFormat kSystemPalFormat = {4, 0, 0, 0, 8, 0, 8, 16, 0};

Common::Array<PalEntry> palArray;
static byte paletteBuffer1[16];
static byte paletteBuffer2[16];

void loadPal(const char *fileName) {
	char buffer[20];

	removeExtention(buffer, fileName);

	strcat(buffer, ".PAL");
	palArray.clear();

	Common::File palFileHandle;
	if (!palFileHandle.open(buffer))
		error("loadPal(): Cannot open file %s", fileName);

	uint16 palEntriesCount = palFileHandle.readUint16LE();
	palFileHandle.readUint16LE(); // entry size

	palArray.resize(palEntriesCount);
	for (uint i = 0; i < palArray.size(); ++i) {
		palFileHandle.read(palArray[i].name, 10);
		palFileHandle.read(palArray[i].pal1, 16);
		palFileHandle.read(palArray[i].pal2, 16);
	}
	palFileHandle.close();
}

int16 findPaletteFromName(const char *fileName) {
	char buffer[10];
	uint16 position = 0;
	uint16 i;

	strcpy(buffer, fileName);

	while (position < strlen(fileName)) {
		if (buffer[position] > 'a' && buffer[position] < 'z') {
			buffer[position] += 'A' - 'a';
		}

		position++;
	}

	for (i = 0; i < palArray.size(); i++) {
		if (!strcmp(buffer, palArray[i].name)) {
			return i;
		}
	}

	return -1;

}

void loadRelatedPalette(const char *fileName) {
	char localName[16];
	byte i;
	int16 paletteIndex;

	removeExtention(localName, fileName);

	paletteIndex = findPaletteFromName(localName);

	if (paletteIndex == -1) {
		for (i = 0; i < 16; i++) {	// generate default palette
			paletteBuffer1[i] = paletteBuffer2[i] = (i << 4) + i;
		}
	} else {
		assert(paletteIndex < (int32)palArray.size());
		memcpy(paletteBuffer1, palArray[paletteIndex].pal1, 16);
		memcpy(paletteBuffer2, palArray[paletteIndex].pal2, 16);
	}
}

void palRotate(uint16 *pal, byte a, byte b, byte c) {
	assert(pal);

	if (c == 1) {
		uint16 currentColor = pal[b];

		for (int i = b; i > a; i--) {
			pal[i] = pal[i - 1];
		}

		pal[a] = currentColor;
	}
}

void palRotate(byte *pal, byte a, byte b, byte c) {
	assert(pal);

	if (c == 1) {
		byte currentR = pal[3 * b + 0];
		byte currentG = pal[3 * b + 1];
		byte currentB = pal[3 * b + 2];

		for (int i = b; i > a; i--) {
			pal[3 * i + 0] = pal[3 * (i - 1) + 0];
			pal[3 * i + 1] = pal[3 * (i - 1) + 1];
			pal[3 * i + 2] = pal[3 * (i - 1) + 2];
		}

		pal[3 * a + 0] = currentR;
		pal[3 * a + 1] = currentG;
		pal[3 * a + 2] = currentB;
	}
}

uint16 transformColor(uint16 baseColor, int r, int g, int b) {
	int8 oriR = CLIP( (baseColor & 0x007)       + b, 0, 7);
	int8 oriG = CLIP(((baseColor & 0x070) >> 4) + g, 0, 7);
	int8 oriB = CLIP(((baseColor & 0x700) >> 8) + r, 0, 7);

	return oriR | (oriG << 4) | (oriB << 8);
}

void transformPaletteRange(uint16 *dstPal, uint16 *srcPal, int startColor, int stopColor, int r, int g, int b) {
	assert(srcPal && dstPal);

	for (int i = startColor; i <= stopColor; i++)
		dstPal[i] = transformColor(srcPal[i], r, g, b);
}

void transformPaletteRange(byte *dstPal, byte *srcPal, int startColor, int stopColor, int r, int g, int b) {
	assert(srcPal && dstPal);

	for (int i = startColor; i <= stopColor; i++) {
		dstPal[3 * i + 0] = CLIP(srcPal[3 * i + 0] + r * 36, 0, 252);
		dstPal[3 * i + 1] = CLIP(srcPal[3 * i + 1] + g * 36, 0, 252);
		dstPal[3 * i + 2] = CLIP(srcPal[3 * i + 2] + b * 36, 0, 252);
	}
}

// a.k.a. palRotate
Palette &Palette::rotateRight(byte firstIndex, byte lastIndex) {
	const Color lastColor = _colors[lastIndex];

	for (int i = lastIndex; i > firstIndex; i--)
		_colors[i] = _colors[i - 1];

	_colors[firstIndex] = lastColor;
	return *this;
}

uint Palette::colorCount() const {
	return _colors.size();
}

Graphics::PixelFormat Palette::colorFormat() const {
	return _format;
}

void Palette::setColorFormat(const Graphics::PixelFormat format) {
	_format = format;

	_rBits = (8 - format.rLoss);
	_gBits = (8 - format.gLoss);
	_bBits = (8 - format.bLoss);

	_rMax = (1 << _rBits) - 1;
	_gMax = (1 << _gBits) - 1;
	_bMax = (1 << _bBits) - 1;
}

// a.k.a. transformPaletteRange
Palette &Palette::saturatedAddColor(byte firstIndex, byte lastIndex, signed r, signed g, signed b) {
	assert(firstIndex < colorCount() && lastIndex < colorCount());

	for (uint i = firstIndex; i <= lastIndex; i++)
		saturatedAddColor(i, r, g, b);

	return *this;
}

// a.k.a. transformColor
// Parameter color components (i.e. r, g and b) are in range [-7, 7]
// e.g. r = 7 sets the resulting color's red component to maximum
// e.g. r = -7 sets the resulting color's red component to minimum (i.e. zero)
void Palette::saturatedAddColor(byte index, signed r, signed g, signed b) {
	_colors[index].r = CLIP<int>(_colors[index].r + r, 0, _rMax);
	_colors[index].g = CLIP<int>(_colors[index].g + g, 0, _gMax);
	_colors[index].b = CLIP<int>(_colors[index].b + b, 0, _bMax);
}

Palette &Palette::loadCineLowPal(const byte *colors, const uint numColors) {
	return load(colors, kLowPalFormat, numColors);
}

Palette &Palette::loadCineHighPal(const byte *colors, const uint numColors) {
	return load(colors, kHighPalFormat, numColors);
}

Palette &Palette::load(const byte *colors, const Graphics::PixelFormat format, const uint numColors) {
	assert(format.aLoss == 8); // No alpha	
	assert(format.rShift / 8 == (format.rShift + MAX<int>(0, 8 - format.rLoss - 1)) / 8); // R must be inside one byte
	assert(format.gShift / 8 == (format.gShift + MAX<int>(0, 8 - format.gLoss - 1)) / 8); // G must be inside one byte
	assert(format.bShift / 8 == (format.bShift + MAX<int>(0, 8 - format.bLoss - 1)) / 8); // B must be inside one byte

	setColorFormat(format);

	_colors.clear();
	_colors.resize(numColors);
	
	for (uint i = 0; i < numColors; i++) {
		// _rMax, _gMax, _bMax are also used as masks here
		_colors[i].r = (colors[i * format.bytesPerPixel + (format.rShift / 8)] >> (format.rShift % 8)) & _rMax;
		_colors[i].g = (colors[i * format.bytesPerPixel + (format.gShift / 8)] >> (format.gShift % 8)) & _gMax;
		_colors[i].b = (colors[i * format.bytesPerPixel + (format.bShift / 8)] >> (format.bShift % 8)) & _bMax;
	}

	return *this;
}

byte *Palette::save(byte *colors, const uint numBytes, const Graphics::PixelFormat format) const {
	assert(format.bytesPerPixel * colorCount() <= numBytes); // Make sure there's enough output space

	// Clear the part of the output palette we're going to be writing to with all black
	memset(colors, 0, format.bytesPerPixel * colorCount());

	// Save the palette to the output in the specified format
	for (uint i = 0; i < colorCount(); i++) {
		// _rMax, _gMax, _bMax are also used as masks here
		colors[i * format.bytesPerPixel + (format.rShift / 8)] |= ((_colors[i].r & _rMax) << (format.rShift % 8));
		colors[i * format.bytesPerPixel + (format.gShift / 8)] |= ((_colors[i].g & _gMax) << (format.gShift % 8));
		colors[i * format.bytesPerPixel + (format.bShift / 8)] |= ((_colors[i].b & _bMax) << (format.bShift % 8));
	}

	// Return the pointer to the output palette
	return colors;
}

byte *Palette::saveCineLowPal(byte *colors, const uint numBytes) const {
	return save(colors, numBytes, kLowPalFormat);
}

byte *Palette::saveCineHighPal(byte *colors, const uint numBytes) const {
	return save(colors, numBytes, kHighPalFormat);
}

byte *Palette::saveOrigFormat(byte *colors, const uint numBytes) const {
	return save(colors, numBytes, colorFormat());
}

byte *Palette::saveSystemFormat(byte *colors, const uint numBytes) const {
	return save(colors, numBytes, kSystemPalFormat);
}

} // End of namespace Cine