1 /* GemRB - Infinity Engine Emulator
2  * Copyright (C) 2012 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 using namespace GemRB;
22 
23 template<bool PALALPHA>
24 struct SRTinter_NoTint {
operatorSRTinter_NoTint25 	void operator()(Uint8&, Uint8&, Uint8&, Uint8& a, unsigned int) const {
26 		if (!PALALPHA) a = 255;
27 	}
28 };
29 
30 template<bool PALALPHA, bool TINTALPHA>
31 struct SRTinter_Tint {
SRTinter_TintSRTinter_Tint32 	SRTinter_Tint(const Color& t) : tint(t) { }
33 
operatorSRTinter_Tint34 	void operator()(Uint8& r, Uint8& g, Uint8& b, Uint8& a, unsigned int) const {
35 		r = (tint.r * r) >> 8;
36 		g = (tint.g * g) >> 8;
37 		b = (tint.b * b) >> 8;
38 		if (TINTALPHA && PALALPHA) a = (tint.a * a) >> 8;
39 		//if (!TINTALPHA && PALALPHA) a = a;
40 		if (TINTALPHA && !PALALPHA) a = tint.a;
41 		if (!TINTALPHA && !PALALPHA) a = 255;
42 	}
43 	Color tint;
44 };
45 
46 // Always tint, and conditionally handle grey, red
47 template<bool PALALPHA>
48 struct SRTinter_Flags {
SRTinter_FlagsSRTinter_Flags49 	SRTinter_Flags(const Color& t) : tint(t) { }
50 
operatorSRTinter_Flags51 	void operator()(Uint8& r, Uint8& g, Uint8& b, Uint8& a, unsigned int flags) const {
52 		if (flags & BlitFlags::GREY) {
53 			r = (tint.r * r) >> 10;
54 			g = (tint.g * g) >> 10;
55 			b = (tint.b * b) >> 10;
56 			Uint8 avg = r + g + b;
57 			r = g = b = avg;
58 		} else if (flags & BlitFlags::SEPIA) {
59 			r = (tint.r * r) >> 10;
60 			g = (tint.g * g) >> 10;
61 			b = (tint.b * b) >> 10;
62 			Uint8 avg = r + g + b;
63 			r = avg + 21; // can't overflow, since a is at most 189
64 			g = avg;
65 			b = avg < 32 ? 0 : avg - 32;
66 		} else {
67 			r = (tint.r * r) >> 8;
68 			g = (tint.g * g) >> 8;
69 			b = (tint.b * b) >> 8;
70 		}
71 
72 		if (!PALALPHA)
73 			a = tint.a;
74 		else
75 			a = (tint.a * a) >> 8;
76 	}
77 
78 	Color tint;
79 };
80 
81 // Don't tint, but conditionally handle grey, sepia
82 template<bool PALALPHA>
83 struct SRTinter_FlagsNoTint {
SRTinter_FlagsNoTintSRTinter_FlagsNoTint84 	SRTinter_FlagsNoTint() { }
85 
operatorSRTinter_FlagsNoTint86 	void operator()(Uint8& r, Uint8& g, Uint8& b, Uint8& a, unsigned int flags) const {
87 		if (flags & BlitFlags::GREY) {
88 			r >>= 2;
89 			g >>= 2;
90 			b >>= 2;
91 			Uint8 avg = r + g + b;
92 			r = g = b = avg;
93 		} else if (flags & BlitFlags::SEPIA) {
94 			r >>= 2;
95 			g >>= 2;
96 			b >>= 2;
97 			Uint8 avg = r + g + b;
98 			r = avg + 21; // can't overflow, since a is at most 189
99 			g = avg;
100 			b = avg < 32 ? 0 : avg - 32;
101 		}
102 
103 		if (!PALALPHA) a = 255;
104 	}
105 };
106 
107 
108 struct SRBlender_NoAlpha { };
109 struct SRBlender_HalfAlpha { };
110 struct SRBlender_Alpha { };
111 
112 template<typename PTYPE, typename BLENDER>
113 struct SRBlender {
114 	SRBlender(SDL_PixelFormat* format);
115 
116 	void operator()(PTYPE& /*pix*/, Uint8 /*r*/, Uint8 /*g*/, Uint8 /*b*/, Uint8 /*a*/) const;
117 };
118 
119 template<typename BLENDER>
120 struct SRBlender<Uint16, BLENDER> {
121 	uint8_t RLOSS;
122 	uint8_t GLOSS;
123 	uint8_t BLOSS;
124 
125 	uint8_t RSHIFT;
126 	uint8_t GSHIFT;
127 	uint8_t BSHIFT;
128 
129 	Uint16 AMASK;
130 	Uint16 halfmask;
131 
132 	SRBlender(SDL_PixelFormat* fmt) {
133 		RLOSS = fmt->Rloss;
134 		GLOSS = fmt->Gloss;
135 		BLOSS = fmt->Bloss;
136 
137 		RSHIFT = fmt->Rshift;
138 		GSHIFT = fmt->Gshift;
139 		BSHIFT = fmt->Bshift;
140 
141 		AMASK = fmt->Amask;
142 
143 		halfmask = ((0xFFU >> (RLOSS + 1)) << RSHIFT) | ((0xFFU >> (GLOSS + 1)) << GSHIFT) | ((0xFFU >> (BLOSS + 1)) << BSHIFT);
144 	}
145 
146 	void operator()(Uint16& /*pix*/, Uint8 /*r*/, Uint8 /*g*/, Uint8 /*b*/, Uint8 /*a*/) const;
147 };
148 
149 template<typename BLENDER>
150 struct SRBlender<Uint32, BLENDER> {
151 	uint8_t RSHIFT;
152 	uint8_t GSHIFT;
153 	uint8_t BSHIFT;
154 
155 	Uint32 AMASK;
156 	Uint32 halfmask;
157 
158 	SRBlender(SDL_PixelFormat* fmt) {
159 		RSHIFT = fmt->Rshift;
160 		GSHIFT = fmt->Gshift;
161 		BSHIFT = fmt->Bshift;
162 
163 		AMASK = fmt->Amask;
164 
165 		halfmask = ((0xFFU >> 1) << RSHIFT) | ((0xFFU >> 1) << GSHIFT) | ((0xFFU >> 1) << BSHIFT);
166 	}
167 
168 	void operator()(Uint32& /*pix*/, Uint8 /*r*/, Uint8 /*g*/, Uint8 /*b*/, Uint8 /*a*/) const;
169 };
170 
171 template<> // 16 bpp, 565
172 void SRBlender<Uint16, SRBlender_NoAlpha>::operator()(Uint16& pix, Uint8 r, Uint8 g, Uint8 b, Uint8) const {
173 	pix = ((r >> RLOSS) << RSHIFT) |
174 		  ((g >> GLOSS) << GSHIFT) |
175 		  ((b >> BLOSS) << BSHIFT);
176 }
177 
178 template<> // 16 bpp, 565
179 void SRBlender<Uint16, SRBlender_HalfAlpha>::operator()(Uint16& pix, Uint8 r, Uint8 g, Uint8 b, Uint8) const {
180 	pix = ((pix >> 1) & halfmask) +
181 	((((r >> (RLOSS + 1)) << RSHIFT) | ((g >> (GLOSS + 1)) << GSHIFT) | ((b >> (BLOSS + 1)) << BSHIFT)) & halfmask);
182 }
183 
184 template<> // 16 bpp, 565
185 void SRBlender<Uint16, SRBlender_Alpha>::operator()(Uint16& pix, Uint8 r, Uint8 g, Uint8 b, Uint8 a) const {
186 	unsigned int dr = 1 + a*(r >> RLOSS) + (255-a)*((pix >> RSHIFT) & ((1 << (8-RLOSS))-1));
187 	unsigned int dg = 1 + a*(g >> GLOSS) + (255-a)*((pix >> GSHIFT) & ((1 << (8-GLOSS))-1));
188 	unsigned int db = 1 + a*(b >> BLOSS) + (255-a)*((pix >> BSHIFT) & ((1 << (8-BLOSS))-1));
189 
190 	r = (dr + (dr>>8)) >> 8;
191 	g = (dg + (dg>>8)) >> 8;
192 	b = (db + (db>>8)) >> 8;
193 	pix = (r << RSHIFT) |
194 		  (g << GSHIFT) |
195 		  (b << BSHIFT);
196 }
197 
198 template<> // 32 bpp, 888
199 void SRBlender<Uint32, SRBlender_NoAlpha>::operator()(Uint32& pix, Uint8 r, Uint8 g, Uint8 b, Uint8) const {
200 	pix = (r << RSHIFT) | (g << GSHIFT) | (b << BSHIFT);
201 }
202 
203 template<> // 32 bpp, 888
204 void SRBlender<Uint32, SRBlender_HalfAlpha>::operator()(Uint32& pix, Uint8 r, Uint8 g, Uint8 b, Uint8) const {
205 	pix = ((pix >> 1) & halfmask) +
206 	((((r << RSHIFT) | (g << GSHIFT) | (b << BSHIFT)) >> 1) & halfmask);
207 }
208 
209 template<> // 32 bpp, 888
210 void SRBlender<Uint32, SRBlender_Alpha>::operator()(Uint32& pix, Uint8 r, Uint8 g, Uint8 b, Uint8 a) const {
211 	unsigned int dr = 1 + a*r + (255-a)*((pix >> RSHIFT) & 0xFF);
212 	unsigned int dg = 1 + a*g + (255-a)*((pix >> GSHIFT) & 0xFF);
213 	unsigned int db = 1 + a*b + (255-a)*((pix >> BSHIFT) & 0xFF);
214 	r = (dr + (dr>>8)) >> 8;
215 	g = (dg + (dg>>8)) >> 8;
216 	b = (db + (db>>8)) >> 8;
217 	pix = (r << RSHIFT) | (g << GSHIFT) | (b << BSHIFT);
218 }
219 
220 // these always change together
221 #define ADVANCE_ITERATORS(count) dest.Advance(count); cover.Advance(count);
222 
223 template<typename PTYPE, typename Tinter, typename Blender>
224 void TintedBlend(SDLPixelIterator& dest, Uint8 alpha,
225 				 Color col, BlitFlags flags,
226 				 const Tinter& tint, const Blender& blend)
227 {
228 	PTYPE& pix = (PTYPE&)*dest;
229 
230 	tint(col.r, col.g, col.b, col.a, flags);
231 	col.a = col.a - alpha; // FIXME: seems like this should be handled by something else, we shouldn't need the 'alpha' param
232 	blend(pix, col.r, col.g, col.b, col.a);
233 
234 	// FIXME: we should probably address this in the blenders instead
235 	pix |= blend.AMASK; // color keyed surface is 100% opaque
236 }
237 
238 template<typename PTYPE, typename Tinter, typename Blender>
239 void MaskedTintedBlend(SDLPixelIterator& dest, Uint8 maskval,
240 					   const Color& col, BlitFlags flags,
241 					   const Tinter& tint, const Blender& blend)
242 {
243 	if (maskval < 0xff) {
244 		if ((flags & BlitFlags::STENCIL_DITHER) && maskval == 128) {
245 			const Point& pos = dest.Position();
246 			if (pos.y % 2 == 0) {
247 				maskval = (pos.x % 2) ? 0xC0 : 0x80;
248 			} else {
249 				maskval = (pos.x % 2) ? 0x80 : 0xC0;
250 			}
251 		}
252 
253 		TintedBlend<PTYPE>(dest, maskval, col, flags, tint, blend);
254 	}
255 }
256 
257 // use this when you need to copy the entire source sprite
258 template<typename PTYPE, typename Tinter, typename Blender>
259 static void BlitSpriteRLE_Total(const Uint8* rledata,
260 								const Color* pal, Uint8 transindex,
261 								SDLPixelIterator& dest, IAlphaIterator& cover,
262 								BlitFlags flags, const Tinter& tint, const Blender& blend)
263 {
264 	SDLPixelIterator end = SDLPixelIterator::end(dest);
265 	while (dest != end) {
266 		Uint8 p = *rledata++;
267 		if (p == transindex) {
268 			int count = (*rledata++) + 1;
269 			ADVANCE_ITERATORS(count);
270 			continue;
271 		}
272 
273 		MaskedTintedBlend<PTYPE>(dest, *cover, pal[p], flags, tint, blend);
274 		ADVANCE_ITERATORS(1);
275 	}
276 }
277 
278 // use this when you need a partial copy of the source sprite
279 template<typename PTYPE, typename Tinter, typename Blender>
280 static void BlitSpriteRLE_Partial(const Uint8* rledata, const int pitch, const Region& srect,
281 								  const Color* pal, Uint8 transindex,
282 								  SDLPixelIterator& dest, IAlphaIterator& cover,
283 								  BlitFlags flags, const Tinter& tint, const Blender& blend)
284 {
285 	int count = srect.y * pitch;
286 	while (count > 0) {
287 		Uint8 p = *rledata++;
288 		if (p == transindex) {
289 			count -= (*rledata++) + 1;
290 		} else {
291 			--count;
292 		}
293 	}
294 
295 	int transQueue = -count;
296 	const int endx = srect.x + srect.w;
297 	const int endy = srect.y + srect.h;
298 	for (int y = srect.y; y < endy; ++y) {
299 		// We assume 'dest' and 'cover' are setup appropriately to accept 'srect.size'
300 
301 		if (transQueue >= pitch) {
302 			transQueue -= pitch;
303 			ADVANCE_ITERATORS(srect.w);
304 			continue;
305 		}
306 
307 		for (int x = 0; x < pitch;) {
308 			assert(transQueue >= 0);
309 
310 			if (transQueue > 0) {
311 				int segment = 0;
312 				bool advance = false;
313 				if (x < srect.x) {
314 					segment = srect.x - x;
315 				} else if (x >= endx) {
316 					segment = pitch - x;
317 				} else {
318 					segment = endx - x;
319 					advance = true;
320 				}
321 				assert(segment > 0);
322 
323 				if (transQueue < segment) {
324 					if (advance) {
325 						ADVANCE_ITERATORS(transQueue);
326 					}
327 
328 					x += transQueue;
329 					transQueue = 0;
330 				} else {
331 					if (advance) {
332 						ADVANCE_ITERATORS(segment);
333 					}
334 
335 					transQueue -= segment;
336 					x += segment;
337 				}
338 			} else {
339 				Uint8 p = *rledata++;
340 				if (p == transindex) {
341 					transQueue = (*rledata++) + 1;
342 				} else {
343 					if (x >= srect.x && x < endx) {
344 						MaskedTintedBlend<PTYPE>(dest, *cover, pal[p], flags, tint, blend);
345 						ADVANCE_ITERATORS(1);
346 					}
347 					++x;
348 				}
349 			}
350 
351 			assert(x <= pitch);
352 		}
353 	}
354 }
355 
356 template<typename Blender, typename Tinter>
357 static void BlitSpriteRLE(Holder<Sprite2D> spr, const Region& srect,
358 						  SDL_Surface* dst, const Region& drect,
359 						  IAlphaIterator* cover,
360 						  BlitFlags flags, const Tinter& tint)
361 {
362 	assert(spr->BAM);
363 
364 	if (srect.Dimensions().IsEmpty())
365 		return;
366 
367 	if (drect.Dimensions().IsEmpty())
368 		return;
369 
370 	PaletteHolder palette = spr->GetPalette();
371 	const Uint8* rledata = (const Uint8*)spr->LockSprite();
372 	uint8_t ck = spr->GetColorKey();
373 
374 	bool partial = spr->Frame.Dimensions() != srect.Dimensions();
375 
376 	IPixelIterator::Direction xdir = (flags&BlitFlags::MIRRORX) ? IPixelIterator::Reverse : IPixelIterator::Forward;
377 	IPixelIterator::Direction ydir = (flags&BlitFlags::MIRRORY) ? IPixelIterator::Reverse : IPixelIterator::Forward;
378 
379 	SDLPixelIterator dstit = SDLPixelIterator(dst, xdir, ydir, RectFromRegion(drect));
380 
381 	static StaticAlphaIterator nomask(0);
382 	if (cover == nullptr) {
383 		cover = &nomask;
384 	}
385 
386 	switch (dstit.format->BytesPerPixel) {
387 		case 4:
388 		{
389 			SRBlender<Uint32, Blender> blend(dstit.format);
390 			if (partial) {
391 				BlitSpriteRLE_Partial<Uint32>(rledata, spr->Frame.w, srect, palette->col, ck, dstit, *cover, flags, tint, blend);
392 			} else {
393 				BlitSpriteRLE_Total<Uint32>(rledata, palette->col, ck, dstit, *cover, flags, tint, blend);
394 			}
395 			break;
396 		}
397 		case 2:
398 		{
399 			SRBlender<Uint16, Blender> blend(dstit.format);
400 			if (partial) {
401 				BlitSpriteRLE_Partial<Uint16>(rledata, spr->Frame.w, srect, palette->col, ck, dstit, *cover, flags, tint, blend);
402 			} else {
403 				BlitSpriteRLE_Total<Uint16>(rledata, palette->col, ck, dstit, *cover, flags, tint, blend);
404 			}
405 			break;
406 		}
407 		default:
408 			Log(ERROR, "SpriteRenderer", "Invalid Bpp");
409 			break;
410 	}
411 }
412 
413 #undef ADVANCE_ITERATORS
414