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  * The bottom part of this is file is adapted from SDL_rotozoom.c. The
23  * relevant copyright notice for those specific functions can be found at the
24  * top of that section.
25  *
26  */
27 
28 
29 
30 #include "common/algorithm.h"
31 #include "common/endian.h"
32 #include "common/util.h"
33 #include "common/rect.h"
34 #include "common/math.h"
35 #include "common/textconsole.h"
36 #include "graphics/primitives.h"
37 #include "graphics/transparent_surface.h"
38 #include "graphics/transform_tools.h"
39 
40 namespace Graphics {
41 
42 static const int kBModShift = 0;//img->format.bShift;
43 static const int kGModShift = 8;//img->format.gShift;
44 static const int kRModShift = 16;//img->format.rShift;
45 static const int kAModShift = 24;//img->format.aShift;
46 
47 #ifdef SCUMM_LITTLE_ENDIAN
48 static const int kAIndex = 0;
49 static const int kBIndex = 1;
50 static const int kGIndex = 2;
51 static const int kRIndex = 3;
52 
53 #else
54 static const int kAIndex = 3;
55 static const int kBIndex = 2;
56 static const int kGIndex = 1;
57 static const int kRIndex = 0;
58 #endif
59 
60 void doBlitOpaqueFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep);
61 void doBlitBinaryFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep);
62 void doBlitAlphaBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
63 void doBlitAdditiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
64 void doBlitSubtractiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
65 void doBlitMultiplyBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color);
66 
TransparentSurface()67 TransparentSurface::TransparentSurface() : Surface(), _alphaMode(ALPHA_FULL) {}
68 
TransparentSurface(const Surface & surf,bool copyData)69 TransparentSurface::TransparentSurface(const Surface &surf, bool copyData) : Surface(), _alphaMode(ALPHA_FULL) {
70 	if (copyData) {
71 		copyFrom(surf);
72 	} else {
73 		w = surf.w;
74 		h = surf.h;
75 		pitch = surf.pitch;
76 		format = surf.format;
77 		// We need to cast the const qualifier away here because 'pixels'
78 		// always needs to be writable. 'surf' however is a constant Surface,
79 		// thus getPixels will always return const pixel data.
80 		pixels = const_cast<void *>(surf.getPixels());
81 	}
82 }
83 
84 /**
85  * Optimized version of doBlit to be used w/opaque blitting (no alpha).
86  */
doBlitOpaqueFast(byte * ino,byte * outo,uint32 width,uint32 height,uint32 pitch,int32 inStep,int32 inoStep)87 void doBlitOpaqueFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) {
88 
89 	byte *in;
90 	byte *out;
91 
92 	for (uint32 i = 0; i < height; i++) {
93 		out = outo;
94 		in = ino;
95 		memcpy(out, in, width * 4);
96 		for (uint32 j = 0; j < width; j++) {
97 			out[kAIndex] = 0xFF;
98 			out += 4;
99 		}
100 		outo += pitch;
101 		ino += inoStep;
102 	}
103 }
104 
105 /**
106  * Optimized version of doBlit to be used w/binary blitting (blit or no-blit, no blending).
107  */
doBlitBinaryFast(byte * ino,byte * outo,uint32 width,uint32 height,uint32 pitch,int32 inStep,int32 inoStep)108 void doBlitBinaryFast(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep) {
109 
110 	byte *in;
111 	byte *out;
112 
113 	for (uint32 i = 0; i < height; i++) {
114 		out = outo;
115 		in = ino;
116 		for (uint32 j = 0; j < width; j++) {
117 			uint32 pix = *(uint32 *)in;
118 			int a = in[kAIndex];
119 
120 			if (a != 0) {   // Full opacity (Any value not exactly 0 is Opaque here)
121 				*(uint32 *)out = pix;
122 				out[kAIndex] = 0xFF;
123 			}
124 			out += 4;
125 			in += inStep;
126 		}
127 		outo += pitch;
128 		ino += inoStep;
129 	}
130 }
131 
132 /**
133  * Optimized version of doBlit to be used with alpha blended blitting
134  * @param ino a pointer to the input surface
135  * @param outo a pointer to the output surface
136  * @param width width of the input surface
137  * @param height height of the input surface
138  * @param pitch pitch of the output surface - that is, width in bytes of every row, usually bpp * width of the TARGET surface (the area we are blitting to might be smaller, do the math)
139  * @inStep size in bytes to skip to address each pixel, usually bpp of the source surface
140  * @inoStep width in bytes of every row on the *input* surface / kind of like pitch
141  * @color colormod in 0xAARRGGBB format - 0xFFFFFFFF for no colormod
142  */
doBlitAlphaBlend(byte * ino,byte * outo,uint32 width,uint32 height,uint32 pitch,int32 inStep,int32 inoStep,uint32 color)143 void doBlitAlphaBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
144 	byte *in;
145 	byte *out;
146 
147 	if (color == 0xffffffff) {
148 
149 		for (uint32 i = 0; i < height; i++) {
150 			out = outo;
151 			in = ino;
152 			for (uint32 j = 0; j < width; j++) {
153 
154 				if (in[kAIndex] != 0) {
155 					out[kAIndex] = 255;
156 					out[kRIndex] = ((in[kRIndex] * in[kAIndex]) + out[kRIndex] * (255 - in[kAIndex])) >> 8;
157 					out[kGIndex] = ((in[kGIndex] * in[kAIndex]) + out[kGIndex] * (255 - in[kAIndex])) >> 8;
158 					out[kBIndex] = ((in[kBIndex] * in[kAIndex]) + out[kBIndex] * (255 - in[kAIndex])) >> 8;
159 				}
160 
161 				in += inStep;
162 				out += 4;
163 			}
164 			outo += pitch;
165 			ino += inoStep;
166 		}
167 	} else {
168 
169 		byte ca = (color >> kAModShift) & 0xFF;
170 		byte cr = (color >> kRModShift) & 0xFF;
171 		byte cg = (color >> kGModShift) & 0xFF;
172 		byte cb = (color >> kBModShift) & 0xFF;
173 
174 		for (uint32 i = 0; i < height; i++) {
175 			out = outo;
176 			in = ino;
177 			for (uint32 j = 0; j < width; j++) {
178 
179 				uint32 ina = in[kAIndex] * ca >> 8;
180 
181 				if (ina != 0) {
182 					out[kAIndex] = 255;
183 					out[kBIndex] = (out[kBIndex] * (255 - ina) >> 8);
184 					out[kGIndex] = (out[kGIndex] * (255 - ina) >> 8);
185 					out[kRIndex] = (out[kRIndex] * (255 - ina) >> 8);
186 
187 					out[kBIndex] = out[kBIndex] + (in[kBIndex] * ina * cb >> 16);
188 					out[kGIndex] = out[kGIndex] + (in[kGIndex] * ina * cg >> 16);
189 					out[kRIndex] = out[kRIndex] + (in[kRIndex] * ina * cr >> 16);
190 				}
191 
192 				in += inStep;
193 				out += 4;
194 			}
195 			outo += pitch;
196 			ino += inoStep;
197 		}
198 	}
199 }
200 
201 /**
202  * Optimized version of doBlit to be used with additive blended blitting
203  */
doBlitAdditiveBlend(byte * ino,byte * outo,uint32 width,uint32 height,uint32 pitch,int32 inStep,int32 inoStep,uint32 color)204 void doBlitAdditiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
205 	byte *in;
206 	byte *out;
207 
208 	if (color == 0xffffffff) {
209 
210 		for (uint32 i = 0; i < height; i++) {
211 			out = outo;
212 			in = ino;
213 			for (uint32 j = 0; j < width; j++) {
214 
215 				if (in[kAIndex] != 0) {
216 					out[kRIndex] = MIN((in[kRIndex] * in[kAIndex] >> 8) + out[kRIndex], 255);
217 					out[kGIndex] = MIN((in[kGIndex] * in[kAIndex] >> 8) + out[kGIndex], 255);
218 					out[kBIndex] = MIN((in[kBIndex] * in[kAIndex] >> 8) + out[kBIndex], 255);
219 				}
220 
221 				in += inStep;
222 				out += 4;
223 			}
224 			outo += pitch;
225 			ino += inoStep;
226 		}
227 	} else {
228 
229 		byte ca = (color >> kAModShift) & 0xFF;
230 		byte cr = (color >> kRModShift) & 0xFF;
231 		byte cg = (color >> kGModShift) & 0xFF;
232 		byte cb = (color >> kBModShift) & 0xFF;
233 
234 		for (uint32 i = 0; i < height; i++) {
235 			out = outo;
236 			in = ino;
237 			for (uint32 j = 0; j < width; j++) {
238 
239 				uint32 ina = in[kAIndex] * ca >> 8;
240 
241 				if (cb != 255) {
242 					out[kBIndex] = MIN<uint>(out[kBIndex] + ((in[kBIndex] * cb * ina) >> 16), 255u);
243 				} else {
244 					out[kBIndex] = MIN<uint>(out[kBIndex] + (in[kBIndex] * ina >> 8), 255u);
245 				}
246 
247 				if (cg != 255) {
248 					out[kGIndex] = MIN<uint>(out[kGIndex] + ((in[kGIndex] * cg * ina) >> 16), 255u);
249 				} else {
250 					out[kGIndex] = MIN<uint>(out[kGIndex] + (in[kGIndex] * ina >> 8), 255u);
251 				}
252 
253 				if (cr != 255) {
254 					out[kRIndex] = MIN<uint>(out[kRIndex] + ((in[kRIndex] * cr * ina) >> 16), 255u);
255 				} else {
256 					out[kRIndex] = MIN<uint>(out[kRIndex] + (in[kRIndex] * ina >> 8), 255u);
257 				}
258 
259 				in += inStep;
260 				out += 4;
261 			}
262 			outo += pitch;
263 			ino += inoStep;
264 		}
265 	}
266 }
267 
268 /**
269  * Optimized version of doBlit to be used with subtractive blended blitting
270  */
doBlitSubtractiveBlend(byte * ino,byte * outo,uint32 width,uint32 height,uint32 pitch,int32 inStep,int32 inoStep,uint32 color)271 void doBlitSubtractiveBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
272 	byte *in;
273 	byte *out;
274 
275 	if (color == 0xffffffff) {
276 
277 		for (uint32 i = 0; i < height; i++) {
278 			out = outo;
279 			in = ino;
280 			for (uint32 j = 0; j < width; j++) {
281 
282 				if (in[kAIndex] != 0) {
283 					out[kRIndex] = MAX(out[kRIndex] - ((in[kRIndex] * out[kRIndex]) * in[kAIndex] >> 16), 0);
284 					out[kGIndex] = MAX(out[kGIndex] - ((in[kGIndex] * out[kGIndex]) * in[kAIndex] >> 16), 0);
285 					out[kBIndex] = MAX(out[kBIndex] - ((in[kBIndex] * out[kBIndex]) * in[kAIndex] >> 16), 0);
286 				}
287 
288 				in += inStep;
289 				out += 4;
290 			}
291 			outo += pitch;
292 			ino += inoStep;
293 		}
294 	} else {
295 
296 		byte cr = (color >> kRModShift) & 0xFF;
297 		byte cg = (color >> kGModShift) & 0xFF;
298 		byte cb = (color >> kBModShift) & 0xFF;
299 
300 		for (uint32 i = 0; i < height; i++) {
301 			out = outo;
302 			in = ino;
303 			for (uint32 j = 0; j < width; j++) {
304 
305 				out[kAIndex] = 255;
306 				if (cb != 255) {
307 					out[kBIndex] = MAX(out[kBIndex] - ((in[kBIndex] * cb  * (out[kBIndex]) * in[kAIndex]) >> 24), 0);
308 				} else {
309 					out[kBIndex] = MAX(out[kBIndex] - (in[kBIndex] * (out[kBIndex]) * in[kAIndex] >> 16), 0);
310 				}
311 
312 				if (cg != 255) {
313 					out[kGIndex] = MAX(out[kGIndex] - ((in[kGIndex] * cg  * (out[kGIndex]) * in[kAIndex]) >> 24), 0);
314 				} else {
315 					out[kGIndex] = MAX(out[kGIndex] - (in[kGIndex] * (out[kGIndex]) * in[kAIndex] >> 16), 0);
316 				}
317 
318 				if (cr != 255) {
319 					out[kRIndex] = MAX(out[kRIndex] - ((in[kRIndex] * cr * (out[kRIndex]) * in[kAIndex]) >> 24), 0);
320 				} else {
321 					out[kRIndex] = MAX(out[kRIndex] - (in[kRIndex] * (out[kRIndex]) * in[kAIndex] >> 16), 0);
322 				}
323 
324 				in += inStep;
325 				out += 4;
326 			}
327 			outo += pitch;
328 			ino += inoStep;
329 		}
330 	}
331 }
332 
333 /**
334  * Optimized version of doBlit to be used with multiply blended blitting
335  */
doBlitMultiplyBlend(byte * ino,byte * outo,uint32 width,uint32 height,uint32 pitch,int32 inStep,int32 inoStep,uint32 color)336 void doBlitMultiplyBlend(byte *ino, byte *outo, uint32 width, uint32 height, uint32 pitch, int32 inStep, int32 inoStep, uint32 color) {
337 	byte *in;
338 	byte *out;
339 
340 	if (color == 0xffffffff) {
341 		for (uint32 i = 0; i < height; i++) {
342 			out = outo;
343 			in = ino;
344 			for (uint32 j = 0; j < width; j++) {
345 
346 				if (in[kAIndex] != 0) {
347 					out[kRIndex] = MIN((in[kRIndex] * in[kAIndex] >> 8) * out[kRIndex] >> 8, 255);
348 					out[kGIndex] = MIN((in[kGIndex] * in[kAIndex] >> 8) * out[kGIndex] >> 8, 255);
349 					out[kBIndex] = MIN((in[kBIndex] * in[kAIndex] >> 8) * out[kBIndex] >> 8, 255);
350 				}
351 
352 				in += inStep;
353 				out += 4;
354 			}
355 			outo += pitch;
356 			ino += inoStep;
357 		}
358 	} else {
359 		byte ca = (color >> kAModShift) & 0xFF;
360 		byte cr = (color >> kRModShift) & 0xFF;
361 		byte cg = (color >> kGModShift) & 0xFF;
362 		byte cb = (color >> kBModShift) & 0xFF;
363 
364 		for (uint32 i = 0; i < height; i++) {
365 			out = outo;
366 			in = ino;
367 			for (uint32 j = 0; j < width; j++) {
368 
369 				uint32 ina = in[kAIndex] * ca >> 8;
370 
371 				if (cb != 255) {
372 					out[kBIndex] = MIN<uint>(out[kBIndex] * ((in[kBIndex] * cb * ina) >> 16) >> 8, 255u);
373 				} else {
374 					out[kBIndex] = MIN<uint>(out[kBIndex] * (in[kBIndex] * ina >> 8) >> 8, 255u);
375 				}
376 
377 				if (cg != 255) {
378 					out[kGIndex] = MIN<uint>(out[kGIndex] * ((in[kGIndex] * cg * ina) >> 16) >> 8, 255u);
379 				} else {
380 					out[kGIndex] = MIN<uint>(out[kGIndex] * (in[kGIndex] * ina >> 8) >> 8, 255u);
381 				}
382 
383 				if (cr != 255) {
384 					out[kRIndex] = MIN<uint>(out[kRIndex] * ((in[kRIndex] * cr * ina) >> 16) >> 8, 255u);
385 				} else {
386 					out[kRIndex] = MIN<uint>(out[kRIndex] * (in[kRIndex] * ina >> 8) >> 8, 255u);
387 				}
388 
389 				in += inStep;
390 				out += 4;
391 			}
392 			outo += pitch;
393 			ino += inoStep;
394 		}
395 	}
396 
397 }
398 
blit(Graphics::Surface & target,int posX,int posY,int flipping,Common::Rect * pPartRect,uint color,int width,int height,TSpriteBlendMode blendMode)399 Common::Rect TransparentSurface::blit(Graphics::Surface &target, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, TSpriteBlendMode blendMode) {
400 
401 	Common::Rect retSize;
402 	retSize.top = 0;
403 	retSize.left = 0;
404 	retSize.setWidth(0);
405 	retSize.setHeight(0);
406 	// Check if we need to draw anything at all
407 	int ca = (color >> kAModShift) & 0xff;
408 
409 	if (ca == 0) {
410 		return retSize;
411 	}
412 
413 	// Create an encapsulating surface for the data
414 	TransparentSurface srcImage(*this, false);
415 	// TODO: Is the data really in the screen format?
416 	if (format.bytesPerPixel != 4) {
417 		warning("TransparentSurface can only blit 32bpp images, but got %d", format.bytesPerPixel * 8);
418 		return retSize;
419 	}
420 
421 	if (pPartRect) {
422 
423 		int xOffset = pPartRect->left;
424 		int yOffset = pPartRect->top;
425 
426 		if (flipping & FLIP_V) {
427 			yOffset = srcImage.h - pPartRect->bottom;
428 		}
429 
430 		if (flipping & FLIP_H) {
431 			xOffset = srcImage.w - pPartRect->right;
432 		}
433 
434 		srcImage.pixels = getBasePtr(xOffset, yOffset);
435 		srcImage.w = pPartRect->width();
436 		srcImage.h = pPartRect->height();
437 
438 		debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping,
439 			  pPartRect->left,  pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height);
440 	} else {
441 
442 		debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0,
443 			  srcImage.w, srcImage.h, color, width, height);
444 	}
445 
446 	if (width == -1) {
447 		width = srcImage.w;
448 	}
449 	if (height == -1) {
450 		height = srcImage.h;
451 	}
452 
453 #ifdef SCALING_TESTING
454 	// Hardcode scaling to 66% to test scaling
455 	width = width * 2 / 3;
456 	height = height * 2 / 3;
457 #endif
458 
459 	Graphics::Surface *img = nullptr;
460 	Graphics::Surface *imgScaled = nullptr;
461 	byte *savedPixels = nullptr;
462 	if ((width != srcImage.w) || (height != srcImage.h)) {
463 		// Scale the image
464 		img = imgScaled = srcImage.scale(width, height);
465 		savedPixels = (byte *)img->getPixels();
466 	} else {
467 		img = &srcImage;
468 	}
469 
470 	// Handle off-screen clipping
471 	if (posY < 0) {
472 		img->h = MAX(0, (int)img->h - -posY);
473 		if (!(flipping & FLIP_V))
474 			img->setPixels((byte *)img->getBasePtr(0, -posY));
475 		posY = 0;
476 	}
477 
478 	if (posX < 0) {
479 		img->w = MAX(0, (int)img->w - -posX);
480 		if (!(flipping & FLIP_H))
481 			img->setPixels((byte *)img->getBasePtr(-posX, 0));
482 		posX = 0;
483 	}
484 
485 	if (img->w > target.w - posX) {
486 		if (flipping & FLIP_H)
487 			img->setPixels((byte *)img->getBasePtr(img->w - target.w + posX, 0));
488 		img->w = CLIP((int)img->w, 0, (int)MAX((int)target.w - posX, 0));
489 	}
490 
491 	if (img->h > target.h - posY) {
492 		if (flipping & FLIP_V)
493 			img->setPixels((byte *)img->getBasePtr(0, img->h - target.h + posY));
494 		img->h = CLIP((int)img->h, 0, (int)MAX((int)target.h - posY, 0));
495 	}
496 
497 	// Flip surface
498 	if ((img->w > 0) && (img->h > 0)) {
499 		int xp = 0, yp = 0;
500 
501 		int inStep = 4;
502 		int inoStep = img->pitch;
503 		if (flipping & FLIP_H) {
504 			inStep = -inStep;
505 			xp = img->w - 1;
506 		}
507 
508 		if (flipping & FLIP_V) {
509 			inoStep = -inoStep;
510 			yp = img->h - 1;
511 		}
512 
513 		byte *ino = (byte *)img->getBasePtr(xp, yp);
514 		byte *outo = (byte *)target.getBasePtr(posX, posY);
515 
516 		if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_OPAQUE) {
517 			doBlitOpaqueFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
518 		} else if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_BINARY) {
519 			doBlitBinaryFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
520 		} else {
521 			if (blendMode == BLEND_ADDITIVE) {
522 				doBlitAdditiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
523 			} else if (blendMode == BLEND_SUBTRACTIVE) {
524 				doBlitSubtractiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
525 			} else if (blendMode == BLEND_MULTIPLY) {
526 				doBlitMultiplyBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
527 			} else {
528 				assert(blendMode == BLEND_NORMAL);
529 				doBlitAlphaBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
530 			}
531 		}
532 
533 	}
534 
535 	retSize.setWidth(img->w);
536 	retSize.setHeight(img->h);
537 
538 	if (imgScaled) {
539 		imgScaled->setPixels(savedPixels);
540 		imgScaled->free();
541 		delete imgScaled;
542 	}
543 
544 	return retSize;
545 }
546 
blitClip(Graphics::Surface & target,Common::Rect clippingArea,int posX,int posY,int flipping,Common::Rect * pPartRect,uint color,int width,int height,TSpriteBlendMode blendMode)547 Common::Rect TransparentSurface::blitClip(Graphics::Surface &target, Common::Rect clippingArea, int posX, int posY, int flipping, Common::Rect *pPartRect, uint color, int width, int height, TSpriteBlendMode blendMode) {
548 	Common::Rect retSize;
549 	retSize.top = 0;
550 	retSize.left = 0;
551 	retSize.setWidth(0);
552 	retSize.setHeight(0);
553 	// Check if we need to draw anything at all
554 	int ca = (color >> kAModShift) & 0xff;
555 
556 	if (ca == 0) {
557 		return retSize;
558 	}
559 
560 	// Create an encapsulating surface for the data
561 	TransparentSurface srcImage(*this, false);
562 	// TODO: Is the data really in the screen format?
563 	if (format.bytesPerPixel != 4) {
564 		warning("TransparentSurface can only blit 32bpp images, but got %d", format.bytesPerPixel * 8);
565 		return retSize;
566 	}
567 
568 	if (pPartRect) {
569 
570 		int xOffset = pPartRect->left;
571 		int yOffset = pPartRect->top;
572 
573 		if (flipping & FLIP_V) {
574 			yOffset = srcImage.h - pPartRect->bottom;
575 		}
576 
577 		if (flipping & FLIP_H) {
578 			xOffset = srcImage.w - pPartRect->right;
579 		}
580 
581 		srcImage.pixels = getBasePtr(xOffset, yOffset);
582 		srcImage.w = pPartRect->width();
583 		srcImage.h = pPartRect->height();
584 
585 		debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping,
586 			pPartRect->left, pPartRect->top, pPartRect->width(), pPartRect->height(), color, width, height);
587 	} else {
588 
589 		debug(6, "Blit(%d, %d, %d, [%d, %d, %d, %d], %08x, %d, %d)", posX, posY, flipping, 0, 0,
590 			srcImage.w, srcImage.h, color, width, height);
591 	}
592 
593 	if (width == -1) {
594 		width = srcImage.w;
595 	}
596 	if (height == -1) {
597 		height = srcImage.h;
598 	}
599 
600 #ifdef SCALING_TESTING
601 	// Hardcode scaling to 66% to test scaling
602 	width = width * 2 / 3;
603 	height = height * 2 / 3;
604 #endif
605 
606 	Graphics::Surface *img = nullptr;
607 	Graphics::Surface *imgScaled = nullptr;
608 	byte *savedPixels = nullptr;
609 	if ((width != srcImage.w) || (height != srcImage.h)) {
610 		// Scale the image
611 		img = imgScaled = srcImage.scale(width, height);
612 		savedPixels = (byte *)img->getPixels();
613 	} else {
614 		img = &srcImage;
615 	}
616 
617 	// Handle off-screen clipping
618 	if (posY < clippingArea.top) {
619 		img->h = MAX(0, (int)img->h - (clippingArea.top - posY));
620 		if (!(flipping & FLIP_V))
621 			img->setPixels((byte *)img->getBasePtr(0, clippingArea.top - posY));
622 		posY = clippingArea.top;
623 	}
624 
625 	if (posX < clippingArea.left) {
626 		img->w = MAX(0, (int)img->w - (clippingArea.left - posX));
627 		if (!(flipping & FLIP_H))
628 			img->setPixels((byte *)img->getBasePtr(clippingArea.left - posX, 0));
629 		posX = clippingArea.left;
630 	}
631 
632 	if (img->w > clippingArea.right - posX) {
633 		if (flipping & FLIP_H)
634 			img->setPixels((byte *)img->getBasePtr(img->w - clippingArea.right + posX, 0));
635 		img->w = CLIP((int)img->w, 0, (int)MAX((int)clippingArea.right - posX, 0));
636 	}
637 
638 	if (img->h > clippingArea.bottom - posY) {
639 		if (flipping & FLIP_V)
640 			img->setPixels((byte *)img->getBasePtr(0, img->h - clippingArea.bottom + posY));
641 		img->h = CLIP((int)img->h, 0, (int)MAX((int)clippingArea.bottom - posY, 0));
642 	}
643 
644 	// Flip surface
645 	if ((img->w > 0) && (img->h > 0)) {
646 		int xp = 0, yp = 0;
647 
648 		int inStep = 4;
649 		int inoStep = img->pitch;
650 		if (flipping & FLIP_H) {
651 			inStep = -inStep;
652 			xp = img->w - 1;
653 		}
654 
655 		if (flipping & FLIP_V) {
656 			inoStep = -inoStep;
657 			yp = img->h - 1;
658 		}
659 
660 		byte *ino = (byte *)img->getBasePtr(xp, yp);
661 		byte *outo = (byte *)target.getBasePtr(posX, posY);
662 
663 		if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_OPAQUE) {
664 			doBlitOpaqueFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
665 		} else if (color == 0xFFFFFFFF && blendMode == BLEND_NORMAL && _alphaMode == ALPHA_BINARY) {
666 			doBlitBinaryFast(ino, outo, img->w, img->h, target.pitch, inStep, inoStep);
667 		} else {
668 			if (blendMode == BLEND_ADDITIVE) {
669 				doBlitAdditiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
670 			} else if (blendMode == BLEND_SUBTRACTIVE) {
671 				doBlitSubtractiveBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
672 			} else if (blendMode == BLEND_MULTIPLY) {
673 				doBlitMultiplyBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
674 			} else {
675 				assert(blendMode == BLEND_NORMAL);
676 				doBlitAlphaBlend(ino, outo, img->w, img->h, target.pitch, inStep, inoStep, color);
677 			}
678 		}
679 
680 	}
681 
682 	retSize.setWidth(img->w);
683 	retSize.setHeight(img->h);
684 
685 	if (imgScaled) {
686 		imgScaled->setPixels(savedPixels);
687 		imgScaled->free();
688 		delete imgScaled;
689 	}
690 
691 	return retSize;
692 }
693 
694 /**
695  * Writes a color key to the alpha channel of the surface
696  * @param rKey  the red component of the color key
697  * @param gKey  the green component of the color key
698  * @param bKey  the blue component of the color key
699  * @param overwriteAlpha if true, all other alpha will be set fully opaque
700  */
applyColorKey(uint8 rKey,uint8 gKey,uint8 bKey,bool overwriteAlpha)701 void TransparentSurface::applyColorKey(uint8 rKey, uint8 gKey, uint8 bKey, bool overwriteAlpha) {
702 	assert(format.bytesPerPixel == 4);
703 	for (int i = 0; i < h; i++) {
704 		for (int j = 0; j < w; j++) {
705 			uint32 pix = ((uint32 *)pixels)[i * w + j];
706 			uint8 r, g, b, a;
707 			format.colorToARGB(pix, a, r, g, b);
708 			if (r == rKey && g == gKey && b == bKey) {
709 				a = 0;
710 				((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b);
711 			} else if (overwriteAlpha) {
712 				a = 255;
713 				((uint32 *)pixels)[i * w + j] = format.ARGBToColor(a, r, g, b);
714 			}
715 		}
716 	}
717 }
718 
getAlphaMode() const719 AlphaType TransparentSurface::getAlphaMode() const {
720 	return _alphaMode;
721 }
722 
setAlphaMode(AlphaType mode)723 void TransparentSurface::setAlphaMode(AlphaType mode) {
724 	_alphaMode = mode;
725 }
726 
727 
728 
729 
730 
731 
732 /*
733 
734 The below two functions are adapted from SDL_rotozoom.c,
735 taken from SDL_gfx-2.0.18.
736 
737 Its copyright notice:
738 
739 =============================================================================
740 SDL_rotozoom.c: rotozoomer, zoomer and shrinker for 32bit or 8bit surfaces
741 
742 Copyright (C) 2001-2012  Andreas Schiffler
743 
744 This software is provided 'as-is', without any express or implied
745 warranty. In no event will the authors be held liable for any damages
746 arising from the use of this software.
747 
748 Permission is granted to anyone to use this software for any purpose,
749 including commercial applications, and to alter it and redistribute it
750 freely, subject to the following restrictions:
751 
752 1. The origin of this software must not be misrepresented; you must not
753 claim that you wrote the original software. If you use this software
754 in a product, an acknowledgment in the product documentation would be
755 appreciated but is not required.
756 
757 2. Altered source versions must be plainly marked as such, and must not be
758 misrepresented as being the original software.
759 
760 3. This notice may not be removed or altered from any source
761 distribution.
762 
763 Andreas Schiffler -- aschiffler at ferzkopp dot net
764 =============================================================================
765 
766 
767 The functions have been adapted for different structures and coordinate
768 systems.
769 
770 */
771 
772 
773 
774 
775 struct tColorRGBA { byte r; byte g; byte b; byte a; };
776 
777 template <TFilteringMode filteringMode>
rotoscaleT(const TransformStruct & transform) const778 TransparentSurface *TransparentSurface::rotoscaleT(const TransformStruct &transform) const {
779 
780 	assert(transform._angle != 0); // This would not be ideal; rotoscale() should never be called in conditional branches where angle = 0 anyway.
781 
782 	Common::Point newHotspot;
783 	Common::Rect srcRect(0, 0, (int16)w, (int16)h);
784 	Common::Rect rect = TransformTools::newRect(Common::Rect(srcRect), transform, &newHotspot);
785 	Common::Rect dstRect(0, 0, (int16)(rect.right - rect.left), (int16)(rect.bottom - rect.top));
786 
787 	TransparentSurface *target = new TransparentSurface();
788 	assert(format.bytesPerPixel == 4);
789 
790 	int srcW = w;
791 	int srcH = h;
792 	int dstW = dstRect.width();
793 	int dstH = dstRect.height();
794 
795 	target->create((uint16)dstW, (uint16)dstH, this->format);
796 
797 	if (transform._zoom.x == 0 || transform._zoom.y == 0) {
798 		return target;
799 	}
800 
801 	uint32 invAngle = 360 - (transform._angle % 360);
802 	float invAngleRad = Common::deg2rad<uint32,float>(invAngle);
803 	float invCos = cos(invAngleRad);
804 	float invSin = sin(invAngleRad);
805 
806 	int icosx = (int)(invCos * (65536.0f * kDefaultZoomX / transform._zoom.x));
807 	int isinx = (int)(invSin * (65536.0f * kDefaultZoomX / transform._zoom.x));
808 	int icosy = (int)(invCos * (65536.0f * kDefaultZoomY / transform._zoom.y));
809 	int isiny = (int)(invSin * (65536.0f * kDefaultZoomY / transform._zoom.y));
810 
811 
812 	bool flipx = false, flipy = false; // TODO: See mirroring comment in RenderTicket ctor
813 
814 	int xd = (srcRect.left + transform._hotspot.x) << 16;
815 	int yd = (srcRect.top + transform._hotspot.y) << 16;
816 	int cx = newHotspot.x;
817 	int cy = newHotspot.y;
818 
819 	int ax = -icosx * cx;
820 	int ay = -isiny * cx;
821 	int sw = srcW - 1;
822 	int sh = srcH - 1;
823 
824 	tColorRGBA *pc = (tColorRGBA*)target->getBasePtr(0, 0);
825 
826 	for (int y = 0; y < dstH; y++) {
827 		int t = cy - y;
828 		int sdx = ax + (isinx * t) + xd;
829 		int sdy = ay - (icosy * t) + yd;
830 		for (int x = 0; x < dstW; x++) {
831 			int dx = (sdx >> 16);
832 			int dy = (sdy >> 16);
833 			if (flipx) {
834 				dx = sw - dx;
835 			}
836 			if (flipy) {
837 				dy = sh - dy;
838 			}
839 
840 			if (filteringMode == FILTER_BILINEAR) {
841 				if ((dx > -1) && (dy > -1) && (dx < sw) && (dy < sh)) {
842 					const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy);
843 					tColorRGBA c00, c01, c10, c11, cswap;
844 					c00 = *sp;
845 					sp += 1;
846 					c01 = *sp;
847 					sp += (this->pitch / 4);
848 					c11 = *sp;
849 					sp -= 1;
850 					c10 = *sp;
851 					if (flipx) {
852 						cswap = c00; c00=c01; c01=cswap;
853 						cswap = c10; c10=c11; c11=cswap;
854 					}
855 					if (flipy) {
856 						cswap = c00; c00=c10; c10=cswap;
857 						cswap = c01; c01=c11; c11=cswap;
858 					}
859 					/*
860 					* Interpolate colors
861 					*/
862 					int ex = (sdx & 0xffff);
863 					int ey = (sdy & 0xffff);
864 					int t1, t2;
865 					t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
866 					t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
867 					pc->r = (((t2 - t1) * ey) >> 16) + t1;
868 					t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
869 					t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
870 					pc->g = (((t2 - t1) * ey) >> 16) + t1;
871 					t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
872 					t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
873 					pc->b = (((t2 - t1) * ey) >> 16) + t1;
874 					t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
875 					t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
876 					pc->a = (((t2 - t1) * ey) >> 16) + t1;
877 				}
878 			} else {
879 				if ((dx >= 0) && (dy >= 0) && (dx < srcW) && (dy < srcH)) {
880 					const tColorRGBA *sp = (const tColorRGBA *)getBasePtr(dx, dy);
881 					*pc = *sp;
882 				}
883 			}
884 			sdx += icosx;
885 			sdy += isiny;
886 			pc++;
887 		}
888 	}
889 	return target;
890 }
891 
892 template <TFilteringMode filteringMode>
scaleT(uint16 newWidth,uint16 newHeight) const893 TransparentSurface *TransparentSurface::scaleT(uint16 newWidth, uint16 newHeight) const {
894 
895 	TransparentSurface *target = new TransparentSurface();
896 
897 	int srcW = w;
898 	int srcH = h;
899 	int dstW = newWidth;
900 	int dstH = newHeight;
901 
902 	target->create((uint16)dstW, (uint16)dstH, format);
903 
904 	if (filteringMode == FILTER_BILINEAR) {
905 		assert(format.bytesPerPixel == 4);
906 
907 		bool flipx = false, flipy = false; // TODO: See mirroring comment in RenderTicket ctor
908 
909 
910 		int *sax = new int[dstW + 1];
911 		int *say = new int[dstH + 1];
912 		assert(sax && say);
913 
914 		/*
915 		* Precalculate row increments
916 		*/
917 		int spixelw = (srcW - 1);
918 		int spixelh = (srcH - 1);
919 		int sx = (int)(65536.0f * (float) spixelw / (float) (dstW - 1));
920 		int sy = (int)(65536.0f * (float) spixelh / (float) (dstH - 1));
921 
922 		/* Maximum scaled source size */
923 		int ssx = (srcW << 16) - 1;
924 		int ssy = (srcH << 16) - 1;
925 
926 		/* Precalculate horizontal row increments */
927 		int csx = 0;
928 		int *csax = sax;
929 		for (int x = 0; x <= dstW; x++) {
930 			*csax = csx;
931 			csax++;
932 			csx += sx;
933 
934 			/* Guard from overflows */
935 			if (csx > ssx) {
936 				csx = ssx;
937 			}
938 		}
939 
940 		/* Precalculate vertical row increments */
941 		int csy = 0;
942 		int *csay = say;
943 		for (int y = 0; y <= dstH; y++) {
944 			*csay = csy;
945 			csay++;
946 			csy += sy;
947 
948 			/* Guard from overflows */
949 			if (csy > ssy) {
950 				csy = ssy;
951 			}
952 		}
953 
954 		const tColorRGBA *sp = (const tColorRGBA *) getBasePtr(0, 0);
955 		tColorRGBA *dp = (tColorRGBA *) target->getBasePtr(0, 0);
956 		int spixelgap = srcW;
957 
958 		if (flipx) {
959 			sp += spixelw;
960 		}
961 		if (flipy) {
962 			sp += spixelgap * spixelh;
963 		}
964 
965 		csay = say;
966 		for (int y = 0; y < dstH; y++) {
967 			const tColorRGBA *csp = sp;
968 			csax = sax;
969 			for (int x = 0; x < dstW; x++) {
970 				/*
971 				* Setup color source pointers
972 				*/
973 				int ex = (*csax & 0xffff);
974 				int ey = (*csay & 0xffff);
975 				int cx = (*csax >> 16);
976 				int cy = (*csay >> 16);
977 
978 				const tColorRGBA *c00, *c01, *c10, *c11;
979 				c00 = sp;
980 				c01 = sp;
981 				c10 = sp;
982 				if (cy < spixelh) {
983 					if (flipy) {
984 						c10 -= spixelgap;
985 					} else {
986 						c10 += spixelgap;
987 					}
988 				}
989 				c11 = c10;
990 				if (cx < spixelw) {
991 					if (flipx) {
992 						c01--;
993 						c11--;
994 					} else {
995 						c01++;
996 						c11++;
997 					}
998 				}
999 
1000 				/*
1001 				* Draw and interpolate colors
1002 				*/
1003 				int t1, t2;
1004 				t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
1005 				t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
1006 				dp->r = (((t2 - t1) * ey) >> 16) + t1;
1007 				t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
1008 				t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
1009 				dp->g = (((t2 - t1) * ey) >> 16) + t1;
1010 				t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
1011 				t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
1012 				dp->b = (((t2 - t1) * ey) >> 16) + t1;
1013 				t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
1014 				t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
1015 				dp->a = (((t2 - t1) * ey) >> 16) + t1;
1016 
1017 				/*
1018 				* Advance source pointer x
1019 				*/
1020 				int *salastx = csax;
1021 				csax++;
1022 				int sstepx = (*csax >> 16) - (*salastx >> 16);
1023 				if (flipx) {
1024 					sp -= sstepx;
1025 				} else {
1026 					sp += sstepx;
1027 				}
1028 
1029 				/*
1030 				* Advance destination pointer x
1031 				*/
1032 				dp++;
1033 			}
1034 			/*
1035 			* Advance source pointer y
1036 			*/
1037 			int *salasty = csay;
1038 			csay++;
1039 			int sstepy = (*csay >> 16) - (*salasty >> 16);
1040 			sstepy *= spixelgap;
1041 			if (flipy) {
1042 				sp = csp - sstepy;
1043 			} else {
1044 				sp = csp + sstepy;
1045 			}
1046 		}
1047 
1048 		delete[] sax;
1049 		delete[] say;
1050 
1051 	} else {
1052 		int *scaleCacheX = new int[dstW];
1053 		for (int x = 0; x < dstW; x++) {
1054 			scaleCacheX[x] = (x * srcW) / dstW;
1055 		}
1056 
1057 		switch (format.bytesPerPixel) {
1058 		case 1:
1059 			scaleNN<uint8>(scaleCacheX, target);
1060 			break;
1061 		case 2:
1062 			scaleNN<uint16>(scaleCacheX, target);
1063 			break;
1064 		case 4:
1065 			scaleNN<uint32>(scaleCacheX, target);
1066 			break;
1067 		default:
1068 			error("Can only scale 8bpp, 16bpp, and 32bpp");
1069 		}
1070 
1071 		delete[] scaleCacheX;
1072 	}
1073 
1074 	return target;
1075 }
1076 
convertTo(const PixelFormat & dstFormat,const byte * palette) const1077 TransparentSurface *TransparentSurface::convertTo(const PixelFormat &dstFormat, const byte *palette) const {
1078 	assert(pixels);
1079 
1080 	TransparentSurface *surface = new TransparentSurface();
1081 
1082 	// If the target format is the same, just copy
1083 	if (format == dstFormat) {
1084 		surface->copyFrom(*this);
1085 		return surface;
1086 	}
1087 
1088 	if (format.bytesPerPixel == 0 || format.bytesPerPixel > 4)
1089 		error("Surface::convertTo(): Can only convert from 1Bpp, 2Bpp, 3Bpp, and 4Bpp");
1090 
1091 	if (dstFormat.bytesPerPixel != 2 && dstFormat.bytesPerPixel != 4)
1092 		error("Surface::convertTo(): Can only convert to 2Bpp and 4Bpp");
1093 
1094 	surface->create(w, h, dstFormat);
1095 
1096 	if (format.bytesPerPixel == 1) {
1097 		// Converting from paletted to high color
1098 		assert(palette);
1099 
1100 		for (int y = 0; y < h; y++) {
1101 			const byte *srcRow = (const byte *)getBasePtr(0, y);
1102 			byte *dstRow = (byte *)surface->getBasePtr(0, y);
1103 
1104 			for (int x = 0; x < w; x++) {
1105 				byte index = *srcRow++;
1106 				byte r = palette[index * 3];
1107 				byte g = palette[index * 3 + 1];
1108 				byte b = palette[index * 3 + 2];
1109 
1110 				uint32 color = dstFormat.RGBToColor(r, g, b);
1111 
1112 				if (dstFormat.bytesPerPixel == 2)
1113 					*((uint16 *)dstRow) = color;
1114 				else
1115 					*((uint32 *)dstRow) = color;
1116 
1117 				dstRow += dstFormat.bytesPerPixel;
1118 			}
1119 		}
1120 	} else {
1121 		// Converting from high color to high color
1122 		for (int y = 0; y < h; y++) {
1123 			const byte *srcRow = (const byte *)getBasePtr(0, y);
1124 			byte *dstRow = (byte *)surface->getBasePtr(0, y);
1125 
1126 			for (int x = 0; x < w; x++) {
1127 				uint32 srcColor;
1128 				if (format.bytesPerPixel == 2)
1129 					srcColor = READ_UINT16(srcRow);
1130 				else if (format.bytesPerPixel == 3)
1131 					srcColor = READ_UINT24(srcRow);
1132 				else
1133 					srcColor = READ_UINT32(srcRow);
1134 
1135 				srcRow += format.bytesPerPixel;
1136 
1137 				// Convert that color to the new format
1138 				byte r, g, b, a;
1139 				format.colorToARGB(srcColor, a, r, g, b);
1140 				uint32 color = dstFormat.ARGBToColor(a, r, g, b);
1141 
1142 				if (dstFormat.bytesPerPixel == 2)
1143 					*((uint16 *)dstRow) = color;
1144 				else
1145 					*((uint32 *)dstRow) = color;
1146 
1147 				dstRow += dstFormat.bytesPerPixel;
1148 			}
1149 		}
1150 	}
1151 
1152 	return surface;
1153 }
1154 
1155 template <typename Size>
scaleNN(int * scaleCacheX,TransparentSurface * target) const1156 void TransparentSurface::scaleNN(int *scaleCacheX, TransparentSurface *target) const {
1157 	for (int y = 0; y < target->h; y++) {
1158 		Size *destP = (Size *)target->getBasePtr(0, y);
1159 		const Size *srcP = (const Size *)getBasePtr(0, (y * h) / target->h);
1160 		for (int x = 0; x < target->w; x++) {
1161 			*destP++ = srcP[scaleCacheX[x]];
1162 		}
1163 	}
1164 }
1165 
1166 template TransparentSurface *TransparentSurface::rotoscaleT<FILTER_NEAREST>(const TransformStruct &transform) const;
1167 template TransparentSurface *TransparentSurface::rotoscaleT<FILTER_BILINEAR>(const TransformStruct &transform) const;
1168 template TransparentSurface *TransparentSurface::scaleT<FILTER_NEAREST>(uint16 newWidth, uint16 newHeight) const;
1169 template TransparentSurface *TransparentSurface::scaleT<FILTER_BILINEAR>(uint16 newWidth, uint16 newHeight) const;
1170 
1171 template void TransparentSurface::scaleNN<uint8>(int *scaleCacheX, TransparentSurface *target) const;
1172 template void TransparentSurface::scaleNN<uint16>(int *scaleCacheX, TransparentSurface *target) const;
1173 template void TransparentSurface::scaleNN<uint32>(int *scaleCacheX, TransparentSurface *target) const;
1174 
rotoscale(const TransformStruct & transform) const1175 TransparentSurface *TransparentSurface::rotoscale(const TransformStruct &transform) const {
1176 	return rotoscaleT<FILTER_BILINEAR>(transform);
1177 }
1178 
scale(uint16 newWidth,uint16 newHeight) const1179 TransparentSurface *TransparentSurface::scale(uint16 newWidth, uint16 newHeight) const {
1180 	return scaleT<FILTER_NEAREST>(newWidth, newHeight);
1181 }
1182 
1183 } // End of namespace Graphics
1184