1 /* GemRB - Infinity Engine Emulator
2  * Copyright (C) 2006 The GemRB Project
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *
19  */
20 
21 #include "Palette.h"
22 
23 #include "Interface.h"
24 
25 namespace GemRB {
26 
27 #define MINCOL 2
28 #define MUL    2
29 
Palette(const Color & color,const Color & back)30 Palette::Palette(const Color &color, const Color &back)
31 : Palette()
32 {
33 	col[0] = Color(0, 0xff, 0, 0);
34 	for (int i = 1; i < 256; i++) {
35 		float p = i / 255.0f;
36 		col[i].r = std::min<int>(back.r * (1 - p) + color.r * p, 255);
37 		col[i].g = std::min<int>(back.g * (1 - p) + color.g * p, 255);
38 		col[i].b = std::min<int>(back.b * (1 - p) + color.b * p, 255);
39 		// FIXME: alpha value changed to opaque to fix these palettes on SDL 2
40 		// I'm not sure if the previous implementation had a purpose, but historically the alpha is ignored in IE
41 		col[i].a = 0xff;
42 	}
43 }
44 
UpdateAlpha()45 void Palette::UpdateAlpha()
46 {
47 	// skip index 0 which is always the color key
48 	for (int i = 1; i < 256; ++i) {
49 		if (col[i].a != 0xff) {
50 			alpha = true;
51 			return;
52 		}
53 	}
54 
55 	alpha = false;
56 }
57 
CopyColorRangePrivate(const Color * srcBeg,const Color * srcEnd,Color * dst)58 void Palette::CopyColorRangePrivate(const Color* srcBeg, const Color* srcEnd, Color* dst)
59 {
60 	// no update to alpha or version, hence being private
61 	std::copy(srcBeg, srcEnd, dst);
62 }
63 
CopyColorRange(const Color * srcBeg,const Color * srcEnd,uint8_t dst)64 void Palette::CopyColorRange(const Color* srcBeg, const Color* srcEnd, uint8_t dst)
65 {
66 	CopyColorRangePrivate(srcBeg, srcEnd, &col[dst]);
67 	UpdateAlpha();
68 	version++;
69 }
70 
CreateShadedAlphaChannel()71 void Palette::CreateShadedAlphaChannel()
72 {
73 	for (int i = 1; i < 256; ++i) {
74 		Color& c = col[i];
75 		unsigned int m = (c.r + c.g + c.b) / 3;
76 		if (m > MINCOL) {
77 			int tmp = m * MUL;
78 			c.a = std::min(tmp, 0xff);
79 		} else {
80 			c.a = 0;
81 		}
82 	}
83 	alpha = true;
84 	version++;
85 }
86 
Brighten()87 void Palette::Brighten()
88 {
89 	for (int i = 0; i<256;i++) {
90 		col[i].r = (col[i].r+256)/2;
91 		col[i].g = (col[i].g+256)/2;
92 		col[i].b = (col[i].b+256)/2;
93 	}
94 	version++;
95 }
96 
Copy() const97 PaletteHolder Palette::Copy() const
98 {
99 	return new Palette(std::begin(col), std::end(col));
100 }
101 
SetupPaperdollColours(const ieDword * Colors,unsigned int type)102 void Palette::SetupPaperdollColours(const ieDword* Colors, unsigned int type)
103 {
104 	unsigned int s = Clamp<ieDword>(8*type, 0, 8*sizeof(ieDword)-1);
105 	constexpr uint8_t numCols = 12;
106 
107 	enum PALETTES : uint8_t
108 	{
109 		METAL = 0, MINOR, MAJOR, SKIN, LEATHER, ARMOR, HAIR,
110 		END
111 	};
112 
113 	for (uint8_t idx = METAL; idx < END; ++idx) {
114 		const auto& pal16 = core->GetPalette16(Colors[idx]>>s);
115 		Color* dest = &col[0x04 + (idx * 12)];
116 		CopyColorRangePrivate(&pal16[0], &pal16[numCols], dest);
117 	}
118 
119 	//minor
120 	memcpy( &col[0x58], &col[0x11], 8 * sizeof( Color ) );
121 	//major
122 	memcpy( &col[0x60], &col[0x1d], 8 * sizeof( Color ) );
123 	//minor
124 	memcpy( &col[0x68], &col[0x11], 8 * sizeof( Color ) );
125 	//metal
126 	memcpy( &col[0x70], &col[0x05], 8 * sizeof( Color ) );
127 	//leather
128 	memcpy( &col[0x78], &col[0x35], 8 * sizeof( Color ) );
129 	//leather
130 	memcpy( &col[0x80], &col[0x35], 8 * sizeof( Color ) );
131 	//minor
132 	memcpy( &col[0x88], &col[0x11], 8 * sizeof( Color ) );
133 
134 	int i;
135 	for (i = 0x90; i < 0xA8; i += 0x08)
136 		//leather
137 		memcpy( &col[i], &col[0x35], 8 * sizeof( Color ) );
138 
139 	//skin
140 	memcpy( &col[0xB0], &col[0x29], 8 * sizeof( Color ) );
141 
142 	for (i = 0xB8; i < 0xFF; i += 0x08)
143 		//leather
144 		memcpy( &col[i], &col[0x35], 8 * sizeof( Color ) );
145 
146 	col[1] = Color(0, 0, 0, 128); // shadows are always half trans black
147 
148 	version++;
149 }
150 
151 
applyMod(const Color & src,Color & dest,const RGBModifier & mod)152 static inline void applyMod(const Color& src, Color& dest,
153 							const RGBModifier& mod) {
154 	if (mod.speed == -1) {
155 		if (mod.type == RGBModifier::TINT) {
156 			dest.r = ((unsigned int)src.r * mod.rgb.r)>>8;
157 			dest.g = ((unsigned int)src.g * mod.rgb.g)>>8;
158 			dest.b = ((unsigned int)src.b * mod.rgb.b)>>8;
159 		} else if (mod.type == RGBModifier::BRIGHTEN) {
160 			unsigned int r = (unsigned int)src.r * mod.rgb.r;
161 			if (r & (~0x7FF)) r = 0x7FF;
162 			dest.r = r >> 3;
163 
164 			unsigned int g = (unsigned int)src.g * mod.rgb.g;
165 			if (g & (~0x7FF)) g = 0x7FF;
166 			dest.g = g >> 3;
167 
168 			unsigned int b = (unsigned int)src.b * mod.rgb.b;
169 			if (b & (~0x7FF)) b = 0x7FF;
170 			dest.b = b >> 3;
171 		} else if (mod.type == RGBModifier::ADD) {
172 			unsigned int r = (unsigned int)src.r + mod.rgb.r;
173 			if (r & (~0xFF)) r = 0xFF;
174 			dest.r = r;
175 
176 			unsigned int g = (unsigned int)src.g + mod.rgb.g;
177 			if (g & (~0xFF)) g = 0xFF;
178 			dest.g = g;
179 
180 			unsigned int b = (unsigned int)src.b + mod.rgb.b;
181 			if (b & (~0xFF)) b = 0xFF;
182 			dest.b = b;
183 		} else {
184 			dest = src;
185 		}
186 	} else if (mod.speed > 0) {
187 
188 		// TODO: a sinewave will probably look better
189 		int phase = (mod.phase % (2*mod.speed));
190 		if (phase > mod.speed) {
191 			phase = 512 - (256*phase)/mod.speed;
192 		} else {
193 			phase = (256*phase)/mod.speed;
194 		}
195 
196 		if (mod.type == RGBModifier::TINT) {
197 			dest.r = ((unsigned int)src.r * (256*256 + phase*mod.rgb.r - 256*phase))>>16;
198 			dest.g = ((unsigned int)src.g * (256*256 + phase*mod.rgb.g - 256*phase))>>16;
199 			dest.b = ((unsigned int)src.b * (256*256 + phase*mod.rgb.b - 256*phase))>>16;
200 		} else if (mod.type == RGBModifier::BRIGHTEN) {
201 			unsigned int r = src.r + (256*256 + phase*mod.rgb.r - 256*phase);
202 			if (r & (~0x7FFFF)) r = 0x7FFFF;
203 			dest.r = r >> 11;
204 
205 			unsigned int g = src.g * (256*256 + phase*mod.rgb.g - 256*phase);
206 			if (g & (~0x7FFFF)) g = 0x7FFFF;
207 			dest.g = g >> 11;
208 
209 			unsigned int b = src.b * (256*256 + phase*mod.rgb.b - 256*phase);
210 			if (b & (~0x7FFFF)) b = 0x7FFFF;
211 			dest.b = b >> 11;
212 		} else if (mod.type == RGBModifier::ADD) {
213 			unsigned int r = src.r + ((phase*mod.rgb.r)>>8);
214 			if (r & (~0xFF)) r = 0xFF;
215 			dest.r = r;
216 
217 			unsigned int g = src.g + ((phase*mod.rgb.g)>>8);
218 			if (g & (~0xFF)) g = 0xFF;
219 			dest.g = g;
220 
221 			unsigned int b = src.b + ((phase*mod.rgb.b)>>8);
222 			if (b & (~0xFF)) b = 0xFF;
223 			dest.b = b;
224 		} else {
225 			dest = src;
226 		}
227 	} else {
228 		dest = src;
229 	}
230 }
231 
SetupRGBModification(const PaletteHolder src,const RGBModifier * mods,unsigned int type)232 void Palette::SetupRGBModification(const PaletteHolder src, const RGBModifier* mods,
233 	unsigned int type)
234 {
235 	const RGBModifier* tmods = mods+(8*type);
236 	int i;
237 
238 	for (i = 0; i < 4; ++i)
239 		col[i] = src->col[i];
240 
241 	for (i = 0; i < 12; ++i)
242 		applyMod(src->col[0x04+i],col[0x04+i],tmods[0]);
243 	for (i = 0; i < 12; ++i)
244 		applyMod(src->col[0x10+i],col[0x10+i],tmods[1]);
245 	for (i = 0; i < 12; ++i)
246 		applyMod(src->col[0x1c+i],col[0x1c+i],tmods[2]);
247 	for (i = 0; i < 12; ++i)
248 		applyMod(src->col[0x28+i],col[0x28+i],tmods[3]);
249 	for (i = 0; i < 12; ++i)
250 		applyMod(src->col[0x34+i],col[0x34+i],tmods[4]);
251 	for (i = 0; i < 12; ++i)
252 		applyMod(src->col[0x40+i],col[0x40+i],tmods[5]);
253 	for (i = 0; i < 12; ++i)
254 		applyMod(src->col[0x4c+i],col[0x4c+i],tmods[6]);
255 	for (i = 0; i < 8; ++i)
256 		applyMod(src->col[0x58+i],col[0x58+i],tmods[1]);
257 	for (i = 0; i < 8; ++i)
258 		applyMod(src->col[0x60+i],col[0x60+i],tmods[2]);
259 	for (i = 0; i < 8; ++i)
260 		applyMod(src->col[0x68+i],col[0x68+i],tmods[1]);
261 	for (i = 0; i < 8; ++i)
262 		applyMod(src->col[0x70+i],col[0x70+i],tmods[0]);
263 	for (i = 0; i < 8; ++i)
264 		applyMod(src->col[0x78+i],col[0x78+i],tmods[4]);
265 	for (i = 0; i < 8; ++i)
266 		applyMod(src->col[0x80+i],col[0x80+i],tmods[4]);
267 	for (i = 0; i < 8; ++i)
268 		applyMod(src->col[0x88+i],col[0x88+i],tmods[1]);
269 	for (i = 0; i < 24; ++i)
270 		applyMod(src->col[0x90+i],col[0x90+i],tmods[4]);
271 
272 	for (i = 0; i < 8; ++i)
273 		col[0xA8+i] = src->col[0xA8+i];
274 
275 	for (i = 0; i < 8; ++i)
276 		applyMod(src->col[0xB0+i],col[0xB0+i],tmods[3]);
277 	for (i = 0; i < 72; ++i)
278 		applyMod(src->col[0xB8+i],col[0xB8+i],tmods[4]);
279 
280 	version++;
281 }
282 
SetupGlobalRGBModification(const PaletteHolder src,const RGBModifier & mod)283 void Palette::SetupGlobalRGBModification(const PaletteHolder src,
284 	const RGBModifier& mod)
285 {
286 	int i;
287 	// don't modify the transparency and shadow colour
288 	for (i = 0; i < 2; ++i)
289 		col[i] = src->col[i];
290 
291 	for (i = 2; i < 256; ++i)
292 		applyMod(src->col[i],col[i],mod);
293 
294 	version++;
295 }
296 
operator ==(const Palette & other) const297 bool Palette::operator==(const Palette& other) const {
298 	return memcmp(col, other.col, sizeof(col)) == 0;
299 }
300 
operator !=(const Palette & other) const301 bool Palette::operator!=(const Palette& other) const {
302 	return !(*this == other);
303 }
304 
305 }
306