1 /*
2 
3 SDL_rotozoom.c: rotozoomer, zoomer and shrinker for 32bit or 8bit surfaces
4 
5 Copyright (C) 2001-2012  Andreas Schiffler
6 
7 This software is provided 'as-is', without any express or implied
8 warranty. In no event will the authors be held liable for any damages
9 arising from the use of this software.
10 
11 Permission is granted to anyone to use this software for any purpose,
12 including commercial applications, and to alter it and redistribute it
13 freely, subject to the following restrictions:
14 
15 1. The origin of this software must not be misrepresented; you must not
16 claim that you wrote the original software. If you use this software
17 in a product, an acknowledgment in the product documentation would be
18 appreciated but is not required.
19 
20 2. Altered source versions must be plainly marked as such, and must not be
21 misrepresented as being the original software.
22 
23 3. This notice may not be removed or altered from any source
24 distribution.
25 
26 Andreas Schiffler -- aschiffler at ferzkopp dot net
27 
28 */
29 
30 #ifdef WIN32
31 #include <windows.h>
32 #endif
33 
34 #include <stdlib.h>
35 #include <string.h>
36 
37 #include "SDL_rotozoom.h"
38 
39 /* ---- Internally used structures */
40 
41 /*!
42 \brief A 32 bit RGBA pixel.
43 */
44 typedef struct tColorRGBA {
45 	Uint8 r;
46 	Uint8 g;
47 	Uint8 b;
48 	Uint8 a;
49 } tColorRGBA;
50 
51 /*!
52 \brief A 8bit Y/palette pixel.
53 */
54 typedef struct tColorY {
55 	Uint8 y;
56 } tColorY;
57 
58 /*!
59 \brief Returns maximum of two numbers a and b.
60 */
61 #define MAX(a,b)    (((a) > (b)) ? (a) : (b))
62 
63 /*!
64 \brief Number of guard rows added to destination surfaces.
65 
66 This is a simple but effective workaround for observed issues.
67 These rows allocate extra memory and are then hidden from the surface.
68 Rows are added to the end of destination surfaces when they are allocated.
69 This catches any potential overflows which seem to happen with
70 just the right src image dimensions and scale/rotation and can lead
71 to a situation where the program can segfault.
72 */
73 #define GUARD_ROWS (2)
74 
75 /*!
76 \brief Lower limit of absolute zoom factor or rotation degrees.
77 */
78 #define VALUE_LIMIT	0.001
79 
80 /*!
81 \brief Returns colorkey info for a surface
82 */
_colorkey(SDL_Surface * src)83 Uint32 _colorkey(SDL_Surface *src)
84 {
85 	Uint32 key = 0;
86 #if (SDL_MINOR_VERSION == 3)
87 	SDL_GetColorKey(src, &key);
88 #else
89 	if (src)
90 	{
91 		key = src->format->colorkey;
92 	}
93 #endif
94 	return key;
95 }
96 
97 
98 /*!
99 \brief Internal 32 bit integer-factor averaging Shrinker.
100 
101 Shrinks 32 bit RGBA/ABGR 'src' surface to 'dst' surface.
102 Averages color and alpha values values of src pixels to calculate dst pixels.
103 Assumes src and dst surfaces are of 32 bit depth.
104 Assumes dst surface was allocated with the correct dimensions.
105 
106 \param src The surface to shrink (input).
107 \param dst The shrunken surface (output).
108 \param factorx The horizontal shrinking ratio.
109 \param factory The vertical shrinking ratio.
110 
111 \return 0 for success or -1 for error.
112 */
_shrinkSurfaceRGBA(SDL_Surface * src,SDL_Surface * dst,int factorx,int factory)113 int _shrinkSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int factorx, int factory)
114 {
115 	int x, y, dx, dy, sgap, dgap, ra, ga, ba, aa;
116 	int n_average;
117 	tColorRGBA *sp, *osp, *oosp;
118 	tColorRGBA *dp;
119 
120 	/*
121 	* Averaging integer shrink
122 	*/
123 
124 	/* Precalculate division factor */
125 	n_average = factorx*factory;
126 
127 	/*
128 	* Scan destination
129 	*/
130 	sp = (tColorRGBA *) src->pixels;
131 	sgap = src->pitch - src->w * 4;
132 
133 	dp = (tColorRGBA *) dst->pixels;
134 	dgap = dst->pitch - dst->w * 4;
135 
136 	for (y = 0; y < dst->h; y++) {
137 
138 		osp=sp;
139 		for (x = 0; x < dst->w; x++) {
140 
141 			/* Trace out source box and accumulate */
142 			oosp=sp;
143 			ra=ga=ba=aa=0;
144 			for (dy=0; dy < factory; dy++) {
145 				for (dx=0; dx < factorx; dx++) {
146 					ra += sp->r;
147 					ga += sp->g;
148 					ba += sp->b;
149 					aa += sp->a;
150 
151 					sp++;
152 				}
153 				/* src dx loop */
154 				sp = (tColorRGBA *)((Uint8*)sp + (src->pitch - 4*factorx)); // next y
155 			}
156 			/* src dy loop */
157 
158 			/* next box-x */
159 			sp = (tColorRGBA *)((Uint8*)oosp + 4*factorx);
160 
161 			/* Store result in destination */
162 			dp->r = ra/n_average;
163 			dp->g = ga/n_average;
164 			dp->b = ba/n_average;
165 			dp->a = aa/n_average;
166 
167 			/*
168 			* Advance destination pointer
169 			*/
170 			dp++;
171 		}
172 		/* dst x loop */
173 
174 		/* next box-y */
175 		sp = (tColorRGBA *)((Uint8*)osp + src->pitch*factory);
176 
177 		/*
178 		* Advance destination pointers
179 		*/
180 		dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
181 	}
182 	/* dst y loop */
183 
184 	return (0);
185 }
186 
187 /*!
188 \brief Internal 8 bit integer-factor averaging shrinker.
189 
190 Shrinks 8bit Y 'src' surface to 'dst' surface.
191 Averages color (brightness) values values of src pixels to calculate dst pixels.
192 Assumes src and dst surfaces are of 8 bit depth.
193 Assumes dst surface was allocated with the correct dimensions.
194 
195 \param src The surface to shrink (input).
196 \param dst The shrunken surface (output).
197 \param factorx The horizontal shrinking ratio.
198 \param factory The vertical shrinking ratio.
199 
200 \return 0 for success or -1 for error.
201 */
_shrinkSurfaceY(SDL_Surface * src,SDL_Surface * dst,int factorx,int factory)202 int _shrinkSurfaceY(SDL_Surface * src, SDL_Surface * dst, int factorx, int factory)
203 {
204 	int x, y, dx, dy, sgap, dgap, a;
205 	int n_average;
206 	Uint8 *sp, *osp, *oosp;
207 	Uint8 *dp;
208 
209 	/*
210 	* Averaging integer shrink
211 	*/
212 
213 	/* Precalculate division factor */
214 	n_average = factorx*factory;
215 
216 	/*
217 	* Scan destination
218 	*/
219 	sp = (Uint8 *) src->pixels;
220 	sgap = src->pitch - src->w;
221 
222 	dp = (Uint8 *) dst->pixels;
223 	dgap = dst->pitch - dst->w;
224 
225 	for (y = 0; y < dst->h; y++) {
226 
227 		osp=sp;
228 		for (x = 0; x < dst->w; x++) {
229 
230 			/* Trace out source box and accumulate */
231 			oosp=sp;
232 			a=0;
233 			for (dy=0; dy < factory; dy++) {
234 				for (dx=0; dx < factorx; dx++) {
235 					a += (*sp);
236 					/* next x */
237 					sp++;
238 				}
239 				/* end src dx loop */
240 				/* next y */
241 				sp = (Uint8 *)((Uint8*)sp + (src->pitch - factorx));
242 			}
243 			/* end src dy loop */
244 
245 			/* next box-x */
246 			sp = (Uint8 *)((Uint8*)oosp + factorx);
247 
248 			/* Store result in destination */
249 			*dp = a/n_average;
250 
251 			/*
252 			* Advance destination pointer
253 			*/
254 			dp++;
255 		}
256 		/* end dst x loop */
257 
258 		/* next box-y */
259 		sp = (Uint8 *)((Uint8*)osp + src->pitch*factory);
260 
261 		/*
262 		* Advance destination pointers
263 		*/
264 		dp = (Uint8 *)((Uint8 *)dp + dgap);
265 	}
266 	/* end dst y loop */
267 
268 	return (0);
269 }
270 
271 /*!
272 \brief Internal 32 bit Zoomer with optional anti-aliasing by bilinear interpolation.
273 
274 Zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface.
275 Assumes src and dst surfaces are of 32 bit depth.
276 Assumes dst surface was allocated with the correct dimensions.
277 
278 \param src The surface to zoom (input).
279 \param dst The zoomed surface (output).
280 \param flipx Flag indicating if the image should be horizontally flipped.
281 \param flipy Flag indicating if the image should be vertically flipped.
282 \param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
283 
284 \return 0 for success or -1 for error.
285 */
_zoomSurfaceRGBA(SDL_Surface * src,SDL_Surface * dst,int flipx,int flipy,int smooth)286 int _zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int flipx, int flipy, int smooth)
287 {
288 	int x, y, sx, sy, ssx, ssy, *sax, *say, *csax, *csay, *salast, csx, csy, ex, ey, cx, cy, sstep, sstepx, sstepy;
289 	tColorRGBA *c00, *c01, *c10, *c11;
290 	tColorRGBA *sp, *csp, *dp;
291 	int spixelgap, spixelw, spixelh, dgap, t1, t2;
292 
293 	/*
294 	* Allocate memory for row/column increments
295 	*/
296 	if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) {
297 		return (-1);
298 	}
299 	if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) {
300 		free(sax);
301 		return (-1);
302 	}
303 
304 	/*
305 	* Precalculate row increments
306 	*/
307 	spixelw = (src->w - 1);
308 	spixelh = (src->h - 1);
309 	if (smooth) {
310 		sx = (int) (65536.0 * (float) spixelw / (float) (dst->w - 1));
311 		sy = (int) (65536.0 * (float) spixelh / (float) (dst->h - 1));
312 	} else {
313 		sx = (int) (65536.0 * (float) (src->w) / (float) (dst->w));
314 		sy = (int) (65536.0 * (float) (src->h) / (float) (dst->h));
315 	}
316 
317 	/* Maximum scaled source size */
318 	ssx = (src->w << 16) - 1;
319 	ssy = (src->h << 16) - 1;
320 
321 	/* Precalculate horizontal row increments */
322 	csx = 0;
323 	csax = sax;
324 	for (x = 0; x <= dst->w; x++) {
325 		*csax = csx;
326 		csax++;
327 		csx += sx;
328 
329 		/* Guard from overflows */
330 		if (csx > ssx) {
331 			csx = ssx;
332 		}
333 	}
334 
335 	/* Precalculate vertical row increments */
336 	csy = 0;
337 	csay = say;
338 	for (y = 0; y <= dst->h; y++) {
339 		*csay = csy;
340 		csay++;
341 		csy += sy;
342 
343 		/* Guard from overflows */
344 		if (csy > ssy) {
345 			csy = ssy;
346 		}
347 	}
348 
349 	sp = (tColorRGBA *) src->pixels;
350 	dp = (tColorRGBA *) dst->pixels;
351 	dgap = dst->pitch - dst->w * 4;
352 	spixelgap = src->pitch/4;
353 
354 	if (flipx) sp += spixelw;
355 	if (flipy) sp += (spixelgap * spixelh);
356 
357 	/*
358 	* Switch between interpolating and non-interpolating code
359 	*/
360 	if (smooth) {
361 
362 		/*
363 		* Interpolating Zoom
364 		*/
365 		csay = say;
366 		for (y = 0; y < dst->h; y++) {
367 			csp = sp;
368 			csax = sax;
369 			for (x = 0; x < dst->w; x++) {
370 				/*
371 				* Setup color source pointers
372 				*/
373 				ex = (*csax & 0xffff);
374 				ey = (*csay & 0xffff);
375 				cx = (*csax >> 16);
376 				cy = (*csay >> 16);
377 				sstepx = cx < spixelw;
378 				sstepy = cy < spixelh;
379 				c00 = sp;
380 				c01 = sp;
381 				c10 = sp;
382 				if (sstepy) {
383 					if (flipy) {
384 						c10 -= spixelgap;
385 					} else {
386 						c10 += spixelgap;
387 					}
388 				}
389 				c11 = c10;
390 				if (sstepx) {
391 					if (flipx) {
392 						c01--;
393 						c11--;
394 					} else {
395 						c01++;
396 						c11++;
397 					}
398 				}
399 
400 				/*
401 				* Draw and interpolate colors
402 				*/
403 				t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
404 				t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
405 				dp->r = (((t2 - t1) * ey) >> 16) + t1;
406 				t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
407 				t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
408 				dp->g = (((t2 - t1) * ey) >> 16) + t1;
409 				t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
410 				t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
411 				dp->b = (((t2 - t1) * ey) >> 16) + t1;
412 				t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
413 				t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
414 				dp->a = (((t2 - t1) * ey) >> 16) + t1;
415 				/*
416 				* Advance source pointer x
417 				*/
418 				salast = csax;
419 				csax++;
420 				sstep = (*csax >> 16) - (*salast >> 16);
421 				if (flipx) {
422 					sp -= sstep;
423 				} else {
424 					sp += sstep;
425 				}
426 
427 				/*
428 				* Advance destination pointer x
429 				*/
430 				dp++;
431 			}
432 			/*
433 			* Advance source pointer y
434 			*/
435 			salast = csay;
436 			csay++;
437 			sstep = (*csay >> 16) - (*salast >> 16);
438 			sstep *= spixelgap;
439 			if (flipy) {
440 				sp = csp - sstep;
441 			} else {
442 				sp = csp + sstep;
443 			}
444 
445 			/*
446 			* Advance destination pointer y
447 			*/
448 			dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
449 		}
450 	} else {
451 		/*
452 		* Non-Interpolating Zoom
453 		*/
454 		csay = say;
455 		for (y = 0; y < dst->h; y++) {
456 			csp = sp;
457 			csax = sax;
458 			for (x = 0; x < dst->w; x++) {
459 				/*
460 				* Draw
461 				*/
462 				*dp = *sp;
463 
464 				/*
465 				* Advance source pointer x
466 				*/
467 				salast = csax;
468 				csax++;
469 				sstep = (*csax >> 16) - (*salast >> 16);
470 				if (flipx) sstep = -sstep;
471 				sp += sstep;
472 
473 				/*
474 				* Advance destination pointer x
475 				*/
476 				dp++;
477 			}
478 			/*
479 			* Advance source pointer y
480 			*/
481 			salast = csay;
482 			csay++;
483 			sstep = (*csay >> 16) - (*salast >> 16);
484 			sstep *= spixelgap;
485 			if (flipy) sstep = -sstep;
486 			sp = csp + sstep;
487 
488 			/*
489 			* Advance destination pointer y
490 			*/
491 			dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
492 		}
493 	}
494 
495 	/*
496 	* Remove temp arrays
497 	*/
498 	free(sax);
499 	free(say);
500 
501 	return (0);
502 }
503 
504 /*!
505 
506 \brief Internal 8 bit Zoomer without smoothing.
507 
508 Zooms 8bit palette/Y 'src' surface to 'dst' surface.
509 Assumes src and dst surfaces are of 8 bit depth.
510 Assumes dst surface was allocated with the correct dimensions.
511 
512 \param src The surface to zoom (input).
513 \param dst The zoomed surface (output).
514 \param flipx Flag indicating if the image should be horizontally flipped.
515 \param flipy Flag indicating if the image should be vertically flipped.
516 
517 \return 0 for success or -1 for error.
518 */
_zoomSurfaceY(SDL_Surface * src,SDL_Surface * dst,int flipx,int flipy)519 int _zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst, int flipx, int flipy)
520 {
521 	int x, y;
522 	Uint32 *sax, *say, *csax, *csay;
523 	int csx, csy;
524 	Uint8 *sp, *dp, *csp;
525 	int dgap;
526 
527 	/*
528 	* Allocate memory for row increments
529 	*/
530 	if ((sax = (Uint32 *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) {
531 		return (-1);
532 	}
533 	if ((say = (Uint32 *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) {
534 		free(sax);
535 		return (-1);
536 	}
537 
538 	/*
539 	* Pointer setup
540 	*/
541 	sp = csp = (Uint8 *) src->pixels;
542 	dp = (Uint8 *) dst->pixels;
543 	dgap = dst->pitch - dst->w;
544 
545 	if (flipx) csp += (src->w-1);
546 	if (flipy) csp  = ( (Uint8*)csp + src->pitch*(src->h-1) );
547 
548 	/*
549 	* Precalculate row increments
550 	*/
551 	csx = 0;
552 	csax = sax;
553 	for (x = 0; x < dst->w; x++) {
554 		csx += src->w;
555 		*csax = 0;
556 		while (csx >= dst->w) {
557 			csx -= dst->w;
558 			(*csax)++;
559 		}
560 		(*csax) = (*csax) * (flipx ? -1 : 1);
561 		csax++;
562 	}
563 	csy = 0;
564 	csay = say;
565 	for (y = 0; y < dst->h; y++) {
566 		csy += src->h;
567 		*csay = 0;
568 		while (csy >= dst->h) {
569 			csy -= dst->h;
570 			(*csay)++;
571 		}
572 		(*csay) = (*csay) * (flipy ? -1 : 1);
573 		csay++;
574 	}
575 
576 	/*
577 	* Draw
578 	*/
579 	csay = say;
580 	for (y = 0; y < dst->h; y++) {
581 		csax = sax;
582 		sp = csp;
583 		for (x = 0; x < dst->w; x++) {
584 			/*
585 			* Draw
586 			*/
587 			*dp = *sp;
588 			/*
589 			* Advance source pointers
590 			*/
591 			sp += (*csax);
592 			csax++;
593 			/*
594 			* Advance destination pointer
595 			*/
596 			dp++;
597 		}
598 		/*
599 		* Advance source pointer (for row)
600 		*/
601 		csp += ((*csay) * src->pitch);
602 		csay++;
603 
604 		/*
605 		* Advance destination pointers
606 		*/
607 		dp += dgap;
608 	}
609 
610 	/*
611 	* Remove temp arrays
612 	*/
613 	free(sax);
614 	free(say);
615 
616 	return (0);
617 }
618 
619 /*!
620 \brief Internal 32 bit rotozoomer with optional anti-aliasing.
621 
622 Rotates and zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
623 parameters by scanning the destination surface and applying optionally anti-aliasing
624 by bilinear interpolation.
625 Assumes src and dst surfaces are of 32 bit depth.
626 Assumes dst surface was allocated with the correct dimensions.
627 
628 \param src Source surface.
629 \param dst Destination surface.
630 \param cx Horizontal center coordinate.
631 \param cy Vertical center coordinate.
632 \param isin Integer version of sine of angle.
633 \param icos Integer version of cosine of angle.
634 \param flipx Flag indicating horizontal mirroring should be applied.
635 \param flipy Flag indicating vertical mirroring should be applied.
636 \param smooth Flag indicating anti-aliasing should be used.
637 */
_transformSurfaceRGBA(SDL_Surface * src,SDL_Surface * dst,int cx,int cy,int isin,int icos,int flipx,int flipy,int smooth)638 void _transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy, int smooth)
639 {
640 	int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh;
641 	tColorRGBA c00, c01, c10, c11, cswap;
642 	tColorRGBA *pc, *sp;
643 	int gap;
644 
645 	/*
646 	* Variable setup
647 	*/
648 	xd = ((src->w - dst->w) << 15);
649 	yd = ((src->h - dst->h) << 15);
650 	ax = (cx << 16) - (icos * cx);
651 	ay = (cy << 16) - (isin * cx);
652 	sw = src->w - 1;
653 	sh = src->h - 1;
654 	pc = (tColorRGBA*) dst->pixels;
655 	gap = dst->pitch - dst->w * 4;
656 
657 	/*
658 	* Switch between interpolating and non-interpolating code
659 	*/
660 	if (smooth) {
661 		for (y = 0; y < dst->h; y++) {
662 			dy = cy - y;
663 			sdx = (ax + (isin * dy)) + xd;
664 			sdy = (ay - (icos * dy)) + yd;
665 			for (x = 0; x < dst->w; x++) {
666 				dx = (sdx >> 16);
667 				dy = (sdy >> 16);
668 				if (flipx) dx = sw - dx;
669 				if (flipy) dy = sh - dy;
670 				if ((dx > -1) && (dy > -1) && (dx < (src->w-1)) && (dy < (src->h-1))) {
671 					sp = (tColorRGBA *)src->pixels;;
672 					sp += ((src->pitch/4) * dy);
673 					sp += dx;
674 					c00 = *sp;
675 					sp += 1;
676 					c01 = *sp;
677 					sp += (src->pitch/4);
678 					c11 = *sp;
679 					sp -= 1;
680 					c10 = *sp;
681 					if (flipx) {
682 						cswap = c00; c00=c01; c01=cswap;
683 						cswap = c10; c10=c11; c11=cswap;
684 					}
685 					if (flipy) {
686 						cswap = c00; c00=c10; c10=cswap;
687 						cswap = c01; c01=c11; c11=cswap;
688 					}
689 					/*
690 					* Interpolate colors
691 					*/
692 					ex = (sdx & 0xffff);
693 					ey = (sdy & 0xffff);
694 					t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
695 					t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
696 					pc->r = (((t2 - t1) * ey) >> 16) + t1;
697 					t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
698 					t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
699 					pc->g = (((t2 - t1) * ey) >> 16) + t1;
700 					t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
701 					t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
702 					pc->b = (((t2 - t1) * ey) >> 16) + t1;
703 					t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
704 					t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
705 					pc->a = (((t2 - t1) * ey) >> 16) + t1;
706 				}
707 				sdx += icos;
708 				sdy += isin;
709 				pc++;
710 			}
711 			pc = (tColorRGBA *) ((Uint8 *) pc + gap);
712 		}
713 	} else {
714 		for (y = 0; y < dst->h; y++) {
715 			dy = cy - y;
716 			sdx = (ax + (isin * dy)) + xd;
717 			sdy = (ay - (icos * dy)) + yd;
718 			for (x = 0; x < dst->w; x++) {
719 				dx = (short) (sdx >> 16);
720 				dy = (short) (sdy >> 16);
721 				if (flipx) dx = (src->w-1)-dx;
722 				if (flipy) dy = (src->h-1)-dy;
723 				if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
724 					sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
725 					sp += dx;
726 					*pc = *sp;
727 				}
728 				sdx += icos;
729 				sdy += isin;
730 				pc++;
731 			}
732 			pc = (tColorRGBA *) ((Uint8 *) pc + gap);
733 		}
734 	}
735 }
736 
737 /*!
738 
739 \brief Rotates and zooms 8 bit palette/Y 'src' surface to 'dst' surface without smoothing.
740 
741 Rotates and zooms 8 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
742 parameters by scanning the destination surface.
743 Assumes src and dst surfaces are of 8 bit depth.
744 Assumes dst surface was allocated with the correct dimensions.
745 
746 \param src Source surface.
747 \param dst Destination surface.
748 \param cx Horizontal center coordinate.
749 \param cy Vertical center coordinate.
750 \param isin Integer version of sine of angle.
751 \param icos Integer version of cosine of angle.
752 \param flipx Flag indicating horizontal mirroring should be applied.
753 \param flipy Flag indicating vertical mirroring should be applied.
754 */
transformSurfaceY(SDL_Surface * src,SDL_Surface * dst,int cx,int cy,int isin,int icos,int flipx,int flipy)755 void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy)
756 {
757 	int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh;
758 	tColorY *pc, *sp;
759 	int gap;
760 
761 	/*
762 	* Variable setup
763 	*/
764 	xd = ((src->w - dst->w) << 15);
765 	yd = ((src->h - dst->h) << 15);
766 	ax = (cx << 16) - (icos * cx);
767 	ay = (cy << 16) - (isin * cx);
768 	sw = src->w - 1;
769 	sh = src->h - 1;
770 	pc = (tColorY*) dst->pixels;
771 	gap = dst->pitch - dst->w;
772 	/*
773 	* Clear surface to colorkey
774 	*/
775 	memset(pc, (int)(_colorkey(src) & 0xff), dst->pitch * dst->h);
776 	/*
777 	* Iterate through destination surface
778 	*/
779 	for (y = 0; y < dst->h; y++) {
780 		dy = cy - y;
781 		sdx = (ax + (isin * dy)) + xd;
782 		sdy = (ay - (icos * dy)) + yd;
783 		for (x = 0; x < dst->w; x++) {
784 			dx = (short) (sdx >> 16);
785 			dy = (short) (sdy >> 16);
786 			if (flipx) dx = (src->w-1)-dx;
787 			if (flipy) dy = (src->h-1)-dy;
788 			if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
789 				sp = (tColorY *) (src->pixels);
790 				sp += (src->pitch * dy + dx);
791 				*pc = *sp;
792 			}
793 			sdx += icos;
794 			sdy += isin;
795 			pc++;
796 		}
797 		pc += gap;
798 	}
799 }
800 
801 /*!
802 \brief Rotates a 32 bit surface in increments of 90 degrees.
803 
804 Specialized 90 degree rotator which rotates a 'src' surface in 90 degree
805 increments clockwise returning a new surface. Faster than rotozoomer since
806 not scanning or interpolation takes place. Input surface must be 32 bit.
807 (code contributed by J. Schiller, improved by C. Allport and A. Schiffler)
808 
809 \param src Source surface to rotate.
810 \param numClockwiseTurns Number of clockwise 90 degree turns to apply to the source.
811 
812 \returns The new, rotated surface; or NULL for surfaces with incorrect input format.
813 */
rotateSurface90Degrees(SDL_Surface * src,int numClockwiseTurns)814 SDL_Surface* rotateSurface90Degrees(SDL_Surface* src, int numClockwiseTurns)
815 {
816 	int row, col, newWidth, newHeight;
817 	int bpp, src_ipr, dst_ipr;
818 	SDL_Surface* dst;
819 	Uint32* srcBuf;
820 	Uint32* dstBuf;
821 
822 	/* Has to be a valid surface pointer and only 32-bit surfaces (for now) */
823 	if (!src || src->format->BitsPerPixel != 32) { return NULL; }
824 
825 	/* normalize numClockwiseTurns */
826 	while(numClockwiseTurns < 0) { numClockwiseTurns += 4; }
827 	numClockwiseTurns = (numClockwiseTurns % 4);
828 
829 	/* if it's even, our new width will be the same as the source surface */
830 	newWidth = (numClockwiseTurns % 2) ? (src->h) : (src->w);
831 	newHeight = (numClockwiseTurns % 2) ? (src->w) : (src->h);
832 	dst = SDL_CreateRGBSurface( src->flags, newWidth, newHeight, src->format->BitsPerPixel,
833 		src->format->Rmask,
834 		src->format->Gmask,
835 		src->format->Bmask,
836 		src->format->Amask);
837 	if(!dst) {
838 		return NULL;
839 	}
840 
841 	if (SDL_MUSTLOCK(dst)) {
842 		SDL_LockSurface(dst);
843 	}
844 	if (SDL_MUSTLOCK(dst)) {
845 		SDL_LockSurface(dst);
846 	}
847 
848 	/* Calculate int-per-row */
849 	bpp = src->format->BitsPerPixel / 8;
850 	src_ipr = src->pitch / bpp;
851 	dst_ipr = dst->pitch / bpp;
852 
853 	switch(numClockwiseTurns) {
854 	case 0: /* Make a copy of the surface */
855 		{
856 			/* Unfortunately SDL_BlitSurface cannot be used to make a copy of the surface
857 			since it does not preserve alpha. */
858 
859 			if (src->pitch == dst->pitch) {
860 				/* If the pitch is the same for both surfaces, the memory can be copied all at once. */
861 				memcpy(dst->pixels, src->pixels, (src->h * src->pitch));
862 			}
863 			else
864 			{
865 				/* If the pitch differs, copy each row separately */
866 				srcBuf = (Uint32*)(src->pixels);
867 				dstBuf = (Uint32*)(dst->pixels);
868 				for (row = 0; row < src->h; row++) {
869 					memcpy(dstBuf, srcBuf, dst->w * bpp);
870 					srcBuf += src_ipr;
871 					dstBuf += dst_ipr;
872 				} /* end for(col) */
873 			} /* end for(row) */
874 		}
875 		break;
876 
877 		/* rotate clockwise */
878 	case 1: /* rotated 90 degrees clockwise */
879 		{
880 			for (row = 0; row < src->h; ++row) {
881 				srcBuf = (Uint32*)(src->pixels) + (row * src_ipr);
882 				dstBuf = (Uint32*)(dst->pixels) + (dst->w - row - 1);
883 				for (col = 0; col < src->w; ++col) {
884 					*dstBuf = *srcBuf;
885 					++srcBuf;
886 					dstBuf += dst_ipr;
887 				}
888 				/* end for(col) */
889 			}
890 			/* end for(row) */
891 		}
892 		break;
893 
894 	case 2: /* rotated 180 degrees clockwise */
895 		{
896 			for (row = 0; row < src->h; ++row) {
897 				srcBuf = (Uint32*)(src->pixels) + (row * src_ipr);
898 				dstBuf = (Uint32*)(dst->pixels) + ((dst->h - row - 1) * dst_ipr) + (dst->w - 1);
899 				for (col = 0; col < src->w; ++col) {
900 					*dstBuf = *srcBuf;
901 					++srcBuf;
902 					--dstBuf;
903 				}
904 			}
905 		}
906 		break;
907 
908 	case 3:
909 		{
910 			for (row = 0; row < src->h; ++row) {
911 				srcBuf = (Uint32*)(src->pixels) + (row * src_ipr);
912 				dstBuf = (Uint32*)(dst->pixels) + row + ((dst->h - 1) * dst_ipr);
913 				for (col = 0; col < src->w; ++col) {
914 					*dstBuf = *srcBuf;
915 					++srcBuf;
916 					dstBuf -= dst_ipr;
917 				}
918 			}
919 		}
920 		break;
921 	}
922 	/* end switch */
923 
924 	if (SDL_MUSTLOCK(src)) {
925 		SDL_UnlockSurface(src);
926 	}
927 	if (SDL_MUSTLOCK(dst)) {
928 		SDL_UnlockSurface(dst);
929 	}
930 
931 	return dst;
932 }
933 
934 
935 /*!
936 \brief Internal target surface sizing function for rotozooms with trig result return.
937 
938 \param width The source surface width.
939 \param height The source surface height.
940 \param angle The angle to rotate in degrees.
941 \param zoomx The horizontal scaling factor.
942 \param zoomy The vertical scaling factor.
943 \param dstwidth The calculated width of the destination surface.
944 \param dstheight The calculated height of the destination surface.
945 \param canglezoom The sine of the angle adjusted by the zoom factor.
946 \param sanglezoom The cosine of the angle adjusted by the zoom factor.
947 
948 */
_rotozoomSurfaceSizeTrig(int width,int height,double angle,double zoomx,double zoomy,int * dstwidth,int * dstheight,double * canglezoom,double * sanglezoom)949 void _rotozoomSurfaceSizeTrig(int width, int height, double angle, double zoomx, double zoomy,
950 	int *dstwidth, int *dstheight,
951 	double *canglezoom, double *sanglezoom)
952 {
953 	double x, y, cx, cy, sx, sy;
954 	double radangle;
955 	int dstwidthhalf, dstheighthalf;
956 
957 	/*
958 	* Determine destination width and height by rotating a centered source box
959 	*/
960 	radangle = angle * (M_PI / 180.0);
961 	*sanglezoom = sin(radangle);
962 	*canglezoom = cos(radangle);
963 	*sanglezoom *= zoomx;
964 	*canglezoom *= zoomy;
965 	x = (double)(width / 2);
966 	y = (double)(height / 2);
967 	cx = *canglezoom * x;
968 	cy = *canglezoom * y;
969 	sx = *sanglezoom * x;
970 	sy = *sanglezoom * y;
971 
972 	dstwidthhalf = MAX((int)
973 		ceil(MAX(MAX(MAX(fabs(cx + sy), fabs(cx - sy)), fabs(-cx + sy)), fabs(-cx - sy))), 1);
974 	dstheighthalf = MAX((int)
975 		ceil(MAX(MAX(MAX(fabs(sx + cy), fabs(sx - cy)), fabs(-sx + cy)), fabs(-sx - cy))), 1);
976 	*dstwidth = 2 * dstwidthhalf;
977 	*dstheight = 2 * dstheighthalf;
978 }
979 
980 /*!
981 \brief Returns the size of the resulting target surface for a rotozoomSurfaceXY() call.
982 
983 \param width The source surface width.
984 \param height The source surface height.
985 \param angle The angle to rotate in degrees.
986 \param zoomx The horizontal scaling factor.
987 \param zoomy The vertical scaling factor.
988 \param dstwidth The calculated width of the rotozoomed destination surface.
989 \param dstheight The calculated height of the rotozoomed destination surface.
990 */
rotozoomSurfaceSizeXY(int width,int height,double angle,double zoomx,double zoomy,int * dstwidth,int * dstheight)991 void rotozoomSurfaceSizeXY(int width, int height, double angle, double zoomx, double zoomy, int *dstwidth, int *dstheight)
992 {
993 	double dummy_sanglezoom, dummy_canglezoom;
994 
995 	_rotozoomSurfaceSizeTrig(width, height, angle, zoomx, zoomy, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom);
996 }
997 
998 /*!
999 \brief Returns the size of the resulting target surface for a rotozoomSurface() call.
1000 
1001 \param width The source surface width.
1002 \param height The source surface height.
1003 \param angle The angle to rotate in degrees.
1004 \param zoom The scaling factor.
1005 \param dstwidth The calculated width of the rotozoomed destination surface.
1006 \param dstheight The calculated height of the rotozoomed destination surface.
1007 */
rotozoomSurfaceSize(int width,int height,double angle,double zoom,int * dstwidth,int * dstheight)1008 void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight)
1009 {
1010 	double dummy_sanglezoom, dummy_canglezoom;
1011 
1012 	_rotozoomSurfaceSizeTrig(width, height, angle, zoom, zoom, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom);
1013 }
1014 
1015 /*!
1016 \brief Rotates and zooms a surface and optional anti-aliasing.
1017 
1018 Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1019 'angle' is the rotation in degrees and 'zoom' a scaling factor. If 'smooth' is set
1020 then the destination 32bit surface is anti-aliased. If the surface is not 8bit
1021 or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
1022 
1023 \param src The surface to rotozoom.
1024 \param angle The angle to rotate in degrees.
1025 \param zoom The scaling factor.
1026 \param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
1027 
1028 \return The new rotozoomed surface.
1029 */
rotozoomSurface(SDL_Surface * src,double angle,double zoom,int smooth)1030 SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth)
1031 {
1032 	return rotozoomSurfaceXY(src, angle, zoom, zoom, smooth);
1033 }
1034 
1035 /*!
1036 \brief Rotates and zooms a surface with different horizontal and vertival scaling factors and optional anti-aliasing.
1037 
1038 Rotates and zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1039 'angle' is the rotation in degrees, 'zoomx and 'zoomy' scaling factors. If 'smooth' is set
1040 then the destination 32bit surface is anti-aliased. If the surface is not 8bit
1041 or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
1042 
1043 \param src The surface to rotozoom.
1044 \param angle The angle to rotate in degrees.
1045 \param zoomx The horizontal scaling factor.
1046 \param zoomy The vertical scaling factor.
1047 \param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
1048 
1049 \return The new rotozoomed surface.
1050 */
rotozoomSurfaceXY(SDL_Surface * src,double angle,double zoomx,double zoomy,int smooth)1051 SDL_Surface *rotozoomSurfaceXY(SDL_Surface * src, double angle, double zoomx, double zoomy, int smooth)
1052 {
1053 	SDL_Surface *rz_src;
1054 	SDL_Surface *rz_dst;
1055 	double zoominv;
1056 	double sanglezoom, canglezoom, sanglezoominv, canglezoominv;
1057 	int dstwidthhalf, dstwidth, dstheighthalf, dstheight;
1058 	int is32bit;
1059 	int i, src_converted;
1060 	int flipx,flipy;
1061 	Uint8 r,g,b;
1062 	Uint32 colorkey = 0;
1063 	int colorKeyAvailable = 0;
1064 
1065 	/*
1066 	* Sanity check
1067 	*/
1068 	if (src == NULL)
1069 		return (NULL);
1070 
1071 	if (src->flags & SDL_SRCCOLORKEY)
1072 	{
1073 		colorkey = _colorkey(src);
1074 		SDL_GetRGB(colorkey, src->format, &r, &g, &b);
1075 		colorKeyAvailable = 1;
1076 	}
1077 	/*
1078 	* Determine if source surface is 32bit or 8bit
1079 	*/
1080 	is32bit = (src->format->BitsPerPixel == 32);
1081 	if ((is32bit) || (src->format->BitsPerPixel == 8)) {
1082 		/*
1083 		* Use source surface 'as is'
1084 		*/
1085 		rz_src = src;
1086 		src_converted = 0;
1087 	} else {
1088 		/*
1089 		* New source surface is 32bit with a defined RGBA ordering
1090 		*/
1091 		rz_src =
1092 			SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1093 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1094 			0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
1095 #else
1096 			0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
1097 #endif
1098 			);
1099 		if(colorKeyAvailable)
1100 			SDL_SetColorKey(src, 0, 0);
1101 
1102 		SDL_BlitSurface(src, NULL, rz_src, NULL);
1103 
1104 		if(colorKeyAvailable)
1105 			SDL_SetColorKey(src, SDL_SRCCOLORKEY, colorkey);
1106 		src_converted = 1;
1107 		is32bit = 1;
1108 	}
1109 
1110 	/*
1111 	* Sanity check zoom factor
1112 	*/
1113 	flipx = (zoomx<0.0);
1114 	if (flipx) zoomx=-zoomx;
1115 	flipy = (zoomy<0.0);
1116 	if (flipy) zoomy=-zoomy;
1117 	if (zoomx < VALUE_LIMIT) zoomx = VALUE_LIMIT;
1118 	if (zoomy < VALUE_LIMIT) zoomy = VALUE_LIMIT;
1119 	zoominv = 65536.0 / (zoomx * zoomx);
1120 
1121 	/*
1122 	* Check if we have a rotozoom or just a zoom
1123 	*/
1124 	if (fabs(angle) > VALUE_LIMIT) {
1125 
1126 		/*
1127 		* Angle!=0: full rotozoom
1128 		*/
1129 		/*
1130 		* -----------------------
1131 		*/
1132 
1133 		/* Determine target size */
1134 		_rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, zoomx, zoomy, &dstwidth, &dstheight, &canglezoom, &sanglezoom);
1135 
1136 		/*
1137 		* Calculate target factors from sin/cos and zoom
1138 		*/
1139 		sanglezoominv = sanglezoom;
1140 		canglezoominv = canglezoom;
1141 		sanglezoominv *= zoominv;
1142 		canglezoominv *= zoominv;
1143 
1144 		/* Calculate half size */
1145 		dstwidthhalf = dstwidth / 2;
1146 		dstheighthalf = dstheight / 2;
1147 
1148 		/*
1149 		* Alloc space to completely contain the rotated surface
1150 		*/
1151 		rz_dst = NULL;
1152 		if (is32bit) {
1153 			/*
1154 			* Target surface is 32bit with source RGBA/ABGR ordering
1155 			*/
1156 			rz_dst =
1157 				SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 32,
1158 				rz_src->format->Rmask, rz_src->format->Gmask,
1159 				rz_src->format->Bmask, rz_src->format->Amask);
1160 		} else {
1161 			/*
1162 			* Target surface is 8bit
1163 			*/
1164 			rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1165 		}
1166 
1167 		/* Check target */
1168 		if (rz_dst == NULL)
1169 			return NULL;
1170 
1171 		/* Adjust for guard rows */
1172 		rz_dst->h = dstheight;
1173 
1174 		if (colorKeyAvailable == 1){
1175 			colorkey = SDL_MapRGB(rz_dst->format, r, g, b);
1176 
1177 			SDL_FillRect(rz_dst, NULL, colorkey );
1178 		}
1179 
1180 		/*
1181 		* Lock source surface
1182 		*/
1183 		if (SDL_MUSTLOCK(rz_src)) {
1184 			SDL_LockSurface(rz_src);
1185 		}
1186 
1187 		/*
1188 		* Check which kind of surface we have
1189 		*/
1190 		if (is32bit) {
1191 			/*
1192 			* Call the 32bit transformation routine to do the rotation (using alpha)
1193 			*/
1194 			_transformSurfaceRGBA(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
1195 				(int) (sanglezoominv), (int) (canglezoominv),
1196 				flipx, flipy,
1197 				smooth);
1198 			/*
1199 			* Turn on source-alpha support
1200 			*/
1201 			SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
1202 			SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1203 		} else {
1204 			/*
1205 			* Copy palette and colorkey info
1206 			*/
1207 			for (i = 0; i < rz_src->format->palette->ncolors; i++) {
1208 				rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1209 			}
1210 			rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1211 			/*
1212 			* Call the 8bit transformation routine to do the rotation
1213 			*/
1214 			transformSurfaceY(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
1215 				(int) (sanglezoominv), (int) (canglezoominv),
1216 				flipx, flipy);
1217 			SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1218 		}
1219 		/*
1220 		* Unlock source surface
1221 		*/
1222 		if (SDL_MUSTLOCK(rz_src)) {
1223 			SDL_UnlockSurface(rz_src);
1224 		}
1225 
1226 	} else {
1227 
1228 		/*
1229 		* Angle=0: Just a zoom
1230 		*/
1231 		/*
1232 		* --------------------
1233 		*/
1234 
1235 		/*
1236 		* Calculate target size
1237 		*/
1238 		zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight);
1239 
1240 		/*
1241 		* Alloc space to completely contain the zoomed surface
1242 		*/
1243 		rz_dst = NULL;
1244 		if (is32bit) {
1245 			/*
1246 			* Target surface is 32bit with source RGBA/ABGR ordering
1247 			*/
1248 			rz_dst =
1249 				SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 32,
1250 				rz_src->format->Rmask, rz_src->format->Gmask,
1251 				rz_src->format->Bmask, rz_src->format->Amask);
1252 		} else {
1253 			/*
1254 			* Target surface is 8bit
1255 			*/
1256 			rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1257 		}
1258 
1259 		/* Check target */
1260 		if (rz_dst == NULL)
1261 			return NULL;
1262 
1263 		/* Adjust for guard rows */
1264 		rz_dst->h = dstheight;
1265 
1266 		if (colorKeyAvailable == 1){
1267 			colorkey = SDL_MapRGB(rz_dst->format, r, g, b);
1268 
1269 			SDL_FillRect(rz_dst, NULL, colorkey );
1270 		}
1271 
1272 		/*
1273 		* Lock source surface
1274 		*/
1275 		if (SDL_MUSTLOCK(rz_src)) {
1276 			SDL_LockSurface(rz_src);
1277 		}
1278 
1279 		/*
1280 		* Check which kind of surface we have
1281 		*/
1282 		if (is32bit) {
1283 			/*
1284 			* Call the 32bit transformation routine to do the zooming (using alpha)
1285 			*/
1286 			_zoomSurfaceRGBA(rz_src, rz_dst, flipx, flipy, smooth);
1287 
1288 			/*
1289 			* Turn on source-alpha support
1290 			*/
1291 			SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
1292 			SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1293 		} else {
1294 			/*
1295 			* Copy palette and colorkey info
1296 			*/
1297 			for (i = 0; i < rz_src->format->palette->ncolors; i++) {
1298 				rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1299 			}
1300 			rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1301 
1302 			/*
1303 			* Call the 8bit transformation routine to do the zooming
1304 			*/
1305 			_zoomSurfaceY(rz_src, rz_dst, flipx, flipy);
1306 			SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1307 		}
1308 
1309 		/*
1310 		* Unlock source surface
1311 		*/
1312 		if (SDL_MUSTLOCK(rz_src)) {
1313 			SDL_UnlockSurface(rz_src);
1314 		}
1315 	}
1316 
1317 	/*
1318 	* Cleanup temp surface
1319 	*/
1320 	if (src_converted) {
1321 		SDL_FreeSurface(rz_src);
1322 	}
1323 
1324 	/*
1325 	* Return destination surface
1326 	*/
1327 	return (rz_dst);
1328 }
1329 
1330 /*!
1331 \brief Calculates the size of the target surface for a zoomSurface() call.
1332 
1333 The minimum size of the target surface is 1. The input factors can be positive or negative.
1334 
1335 \param width The width of the source surface to zoom.
1336 \param height The height of the source surface to zoom.
1337 \param zoomx The horizontal zoom factor.
1338 \param zoomy The vertical zoom factor.
1339 \param dstwidth Pointer to an integer to store the calculated width of the zoomed target surface.
1340 \param dstheight Pointer to an integer to store the calculated height of the zoomed target surface.
1341 */
zoomSurfaceSize(int width,int height,double zoomx,double zoomy,int * dstwidth,int * dstheight)1342 void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight)
1343 {
1344 	/*
1345 	* Make zoom factors positive
1346 	*/
1347 	int flipx, flipy;
1348 	flipx = (zoomx<0.0);
1349 	if (flipx) zoomx = -zoomx;
1350 	flipy = (zoomy<0.0);
1351 	if (flipy) zoomy = -zoomy;
1352 
1353 	/*
1354 	* Sanity check zoom factors
1355 	*/
1356 	if (zoomx < VALUE_LIMIT) {
1357 		zoomx = VALUE_LIMIT;
1358 	}
1359 	if (zoomy < VALUE_LIMIT) {
1360 		zoomy = VALUE_LIMIT;
1361 	}
1362 
1363 	/*
1364 	* Calculate target size
1365 	*/
1366 	*dstwidth = (int) floor(((double) width * zoomx) + 0.5);
1367 	*dstheight = (int) floor(((double) height * zoomy) + 0.5);
1368 	if (*dstwidth < 1) {
1369 		*dstwidth = 1;
1370 	}
1371 	if (*dstheight < 1) {
1372 		*dstheight = 1;
1373 	}
1374 }
1375 
1376 /*!
1377 \brief Zoom a surface by independent horizontal and vertical factors with optional smoothing.
1378 
1379 Zooms a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1380 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is on
1381 then the destination 32bit surface is anti-aliased. If the surface is not 8bit
1382 or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
1383 If zoom factors are negative, the image is flipped on the axes.
1384 
1385 \param src The surface to zoom.
1386 \param zoomx The horizontal zoom factor.
1387 \param zoomy The vertical zoom factor.
1388 \param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
1389 
1390 \return The new, zoomed surface.
1391 */
zoomSurface(SDL_Surface * src,double zoomx,double zoomy,int smooth)1392 SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth)
1393 {
1394 	SDL_Surface *rz_src;
1395 	SDL_Surface *rz_dst;
1396 	int dstwidth, dstheight;
1397 	int is32bit;
1398 	int i, src_converted;
1399 	int flipx, flipy;
1400 
1401 	/*
1402 	* Sanity check
1403 	*/
1404 	if (src == NULL)
1405 		return (NULL);
1406 
1407 	/*
1408 	* Determine if source surface is 32bit or 8bit
1409 	*/
1410 	is32bit = (src->format->BitsPerPixel == 32);
1411 	if ((is32bit) || (src->format->BitsPerPixel == 8)) {
1412 		/*
1413 		* Use source surface 'as is'
1414 		*/
1415 		rz_src = src;
1416 		src_converted = 0;
1417 	} else {
1418 		/*
1419 		* New source surface is 32bit with a defined RGBA ordering
1420 		*/
1421 		rz_src =
1422 			SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1423 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1424 			0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
1425 #else
1426 			0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
1427 #endif
1428 			);
1429 		if (rz_src == NULL) {
1430 			return NULL;
1431 		}
1432 		SDL_BlitSurface(src, NULL, rz_src, NULL);
1433 		src_converted = 1;
1434 		is32bit = 1;
1435 	}
1436 
1437 	flipx = (zoomx<0.0);
1438 	if (flipx) zoomx = -zoomx;
1439 	flipy = (zoomy<0.0);
1440 	if (flipy) zoomy = -zoomy;
1441 
1442 	/* Get size if target */
1443 	zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight);
1444 
1445 	/*
1446 	* Alloc space to completely contain the zoomed surface
1447 	*/
1448 	rz_dst = NULL;
1449 	if (is32bit) {
1450 		/*
1451 		* Target surface is 32bit with source RGBA/ABGR ordering
1452 		*/
1453 		rz_dst =
1454 			SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 32,
1455 			rz_src->format->Rmask, rz_src->format->Gmask,
1456 			rz_src->format->Bmask, rz_src->format->Amask);
1457 	} else {
1458 		/*
1459 		* Target surface is 8bit
1460 		*/
1461 		rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1462 	}
1463 
1464 	/* Check target */
1465 	if (rz_dst == NULL) {
1466 		/*
1467 		* Cleanup temp surface
1468 		*/
1469 		if (src_converted) {
1470 			SDL_FreeSurface(rz_src);
1471 		}
1472 		return NULL;
1473 	}
1474 
1475 	/* Adjust for guard rows */
1476 	rz_dst->h = dstheight;
1477 
1478 	/*
1479 	* Lock source surface
1480 	*/
1481 	if (SDL_MUSTLOCK(rz_src)) {
1482 		SDL_LockSurface(rz_src);
1483 	}
1484 
1485 	/*
1486 	* Check which kind of surface we have
1487 	*/
1488 	if (is32bit) {
1489 		/*
1490 		* Call the 32bit transformation routine to do the zooming (using alpha)
1491 		*/
1492 		_zoomSurfaceRGBA(rz_src, rz_dst, flipx, flipy, smooth);
1493 		/*
1494 		* Turn on source-alpha support
1495 		*/
1496 		SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
1497 	} else {
1498 		/*
1499 		* Copy palette and colorkey info
1500 		*/
1501 		for (i = 0; i < rz_src->format->palette->ncolors; i++) {
1502 			rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1503 		}
1504 		rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1505 		/*
1506 		* Call the 8bit transformation routine to do the zooming
1507 		*/
1508 		_zoomSurfaceY(rz_src, rz_dst, flipx, flipy);
1509 		SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1510 	}
1511 	/*
1512 	* Unlock source surface
1513 	*/
1514 	if (SDL_MUSTLOCK(rz_src)) {
1515 		SDL_UnlockSurface(rz_src);
1516 	}
1517 
1518 	/*
1519 	* Cleanup temp surface
1520 	*/
1521 	if (src_converted) {
1522 		SDL_FreeSurface(rz_src);
1523 	}
1524 
1525 	/*
1526 	* Return destination surface
1527 	*/
1528 	return (rz_dst);
1529 }
1530 
1531 /*!
1532 \brief Shrink a surface by an integer ratio using averaging.
1533 
1534 Shrinks a 32bit or 8bit 'src' surface to a newly created 'dst' surface.
1535 'factorx' and 'factory' are the shrinking ratios (i.e. 2=1/2 the size,
1536 3=1/3 the size, etc.) The destination surface is antialiased by averaging
1537 the source box RGBA or Y information. If the surface is not 8bit
1538 or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
1539 The input surface is not modified. The output surface is newly allocated.
1540 
1541 \param src The surface to shrink.
1542 \param factorx The horizontal shrinking ratio.
1543 \param factory The vertical shrinking ratio.
1544 
1545 \return The new, shrunken surface.
1546 */
1547 /*@null@*/
shrinkSurface(SDL_Surface * src,int factorx,int factory)1548 SDL_Surface *shrinkSurface(SDL_Surface *src, int factorx, int factory)
1549 {
1550 	int result;
1551 	SDL_Surface *rz_src;
1552 	SDL_Surface *rz_dst = NULL;
1553 	int dstwidth, dstheight;
1554 	int is32bit;
1555 	int i, src_converted;
1556 	int haveError = 0;
1557 
1558 	/*
1559 	* Sanity check
1560 	*/
1561 	if (src == NULL) {
1562 		return (NULL);
1563 	}
1564 
1565 	/*
1566 	* Determine if source surface is 32bit or 8bit
1567 	*/
1568 	is32bit = (src->format->BitsPerPixel == 32);
1569 	if ((is32bit) || (src->format->BitsPerPixel == 8)) {
1570 		/*
1571 		* Use source surface 'as is'
1572 		*/
1573 		rz_src = src;
1574 		src_converted = 0;
1575 	} else {
1576 		/*
1577 		* New source surface is 32bit with a defined RGBA ordering
1578 		*/
1579 		rz_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1580 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1581 			0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
1582 #else
1583 			0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
1584 #endif
1585 			);
1586 		if (rz_src==NULL) {
1587 			haveError = 1;
1588 			goto exitShrinkSurface;
1589 		}
1590 
1591 		SDL_BlitSurface(src, NULL, rz_src, NULL);
1592 		src_converted = 1;
1593 		is32bit = 1;
1594 	}
1595 
1596 	/*
1597 	* Lock the surface
1598 	*/
1599 	if (SDL_MUSTLOCK(rz_src)) {
1600 		if (SDL_LockSurface(rz_src) < 0) {
1601 			haveError = 1;
1602 			goto exitShrinkSurface;
1603 		}
1604 	}
1605 
1606 	/* Get size for target */
1607 	dstwidth=rz_src->w/factorx;
1608 	while (dstwidth*factorx>rz_src->w) { dstwidth--; }
1609 	dstheight=rz_src->h/factory;
1610 	while (dstheight*factory>rz_src->h) { dstheight--; }
1611 
1612 	/*
1613 	* Alloc space to completely contain the shrunken surface
1614 	* (with added guard rows)
1615 	*/
1616 	if (is32bit==1) {
1617 		/*
1618 		* Target surface is 32bit with source RGBA/ABGR ordering
1619 		*/
1620 		rz_dst =
1621 			SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 32,
1622 			rz_src->format->Rmask, rz_src->format->Gmask,
1623 			rz_src->format->Bmask, rz_src->format->Amask);
1624 	} else {
1625 		/*
1626 		* Target surface is 8bit
1627 		*/
1628 		rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1629 	}
1630 
1631 	/* Check target */
1632 	if (rz_dst == NULL) {
1633 		haveError = 1;
1634 		goto exitShrinkSurface;
1635 	}
1636 
1637 	/* Adjust for guard rows */
1638 	rz_dst->h = dstheight;
1639 
1640 	/*
1641 	* Check which kind of surface we have
1642 	*/
1643 	if (is32bit==1) {
1644 		/*
1645 		* Call the 32bit transformation routine to do the shrinking (using alpha)
1646 		*/
1647 		result = _shrinkSurfaceRGBA(rz_src, rz_dst, factorx, factory);
1648 		if ((result!=0) || (rz_dst==NULL)) {
1649 			haveError = 1;
1650 			goto exitShrinkSurface;
1651 		}
1652 
1653 		/*
1654 		* Turn on source-alpha support
1655 		*/
1656 		result = SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
1657 		if (result!=0) {
1658 			haveError = 1;
1659 			goto exitShrinkSurface;
1660 		}
1661 	} else {
1662 		/*
1663 		* Copy palette and colorkey info
1664 		*/
1665 		for (i = 0; i < rz_src->format->palette->ncolors; i++) {
1666 			rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1667 		}
1668 		rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1669 		/*
1670 		* Call the 8bit transformation routine to do the shrinking
1671 		*/
1672 		result = _shrinkSurfaceY(rz_src, rz_dst, factorx, factory);
1673 		if (result!=0) {
1674 			haveError = 1;
1675 			goto exitShrinkSurface;
1676 		}
1677 
1678 		/*
1679 		* Set colorkey on target
1680 		*/
1681 		result = SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1682 		if (result!=0) {
1683 			haveError = 1;
1684 			goto exitShrinkSurface;
1685 		}
1686 	}
1687 
1688 exitShrinkSurface:
1689 	if (rz_src!=NULL) {
1690 		/*
1691 		* Unlock source surface
1692 		*/
1693 		if (SDL_MUSTLOCK(rz_src)) {
1694 			SDL_UnlockSurface(rz_src);
1695 		}
1696 
1697 		/*
1698 		* Cleanup temp surface
1699 		*/
1700 		if (src_converted==1) {
1701 			SDL_FreeSurface(rz_src);
1702 		}
1703 	}
1704 
1705 	/* Check error state; maybe need to cleanup destination */
1706 	if (haveError==1) {
1707 		if (rz_dst!=NULL) {
1708 			SDL_FreeSurface(rz_dst);
1709 		}
1710 		rz_dst=NULL;
1711 	}
1712 
1713 	/*
1714 	* Return destination surface
1715 	*/
1716 	return (rz_dst);
1717 }
1718