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