1 /*
2 
3   SDL_rotozoom.c - rotozoomer for 32bit or 8bit surfaces
4 
5   LGPL (c) A. Schiffler
6 
7 */
8 
9 #ifdef WIN32
10 #include <windows.h>
11 #endif
12 
13 #include <stdlib.h>
14 #include <string.h>
15 
16 #include "SDL_rotozoom.h"
17 
18 #define MAX(a,b)    (((a) > (b)) ? (a) : (b))
19 
20 
21 /*
22 
23  32bit integer-factor averaging Shrinker
24 
25  Shrinks 32bit RGBA/ABGR 'src' surface to 'dst' surface.
26 
27 */
28 
shrinkSurfaceRGBA(SDL_Surface * src,SDL_Surface * dst,int factorx,int factory)29 int shrinkSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int factorx, int factory)
30 {
31     int x, y, dx, dy, sgap, dgap, ra, ga, ba, aa;
32     int n_average;
33     tColorRGBA *sp, *osp, *oosp;
34     tColorRGBA *dp;
35 
36     /*
37      * Averaging integer shrink
38      */
39 
40     /* Precalculate division factor */
41     n_average = factorx*factory;
42 
43     /*
44      * Scan destination
45      */
46     sp = (tColorRGBA *) src->pixels;
47     sgap = src->pitch - src->w * 4;
48 
49     dp = (tColorRGBA *) dst->pixels;
50     dgap = dst->pitch - dst->w * 4;
51 
52     for (y = 0; y < dst->h; y++) {
53 
54       osp=sp;
55       for (x = 0; x < dst->w; x++) {
56 
57         /* Trace out source box and accumulate */
58         oosp=sp;
59         ra=ga=ba=aa=0;
60         for (dy=0; dy < factory; dy++) {
61          for (dx=0; dx < factorx; dx++) {
62           ra += sp->r;
63           ga += sp->g;
64           ba += sp->b;
65           aa += sp->a;
66 
67           sp++;
68          } // src dx loop
69          sp = (tColorRGBA *)((Uint8*)sp + (src->pitch - 4*factorx)); // next y
70         } // src dy loop
71 
72         // next box-x
73         sp = (tColorRGBA *)((Uint8*)oosp + 4*factorx);
74 
75         /* Store result in destination */
76         dp->r = ra/n_average;
77         dp->g = ga/n_average;
78         dp->b = ba/n_average;
79         dp->a = aa/n_average;
80 
81         /*
82          * Advance destination pointer
83          */
84          dp++;
85         } // dst x loop
86 
87         // next box-y
88         sp = (tColorRGBA *)((Uint8*)osp + src->pitch*factory);
89 
90         /*
91          * Advance destination pointers
92          */
93         dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
94       } // dst y loop
95 
96     return (0);
97 }
98 
99 /*
100 
101  8bit integer-factor averaging Shrinker
102 
103  Shrinks 8bit Y 'src' surface to 'dst' surface.
104 
105 */
106 
shrinkSurfaceY(SDL_Surface * src,SDL_Surface * dst,int factorx,int factory)107 int shrinkSurfaceY(SDL_Surface * src, SDL_Surface * dst, int factorx, int factory)
108 {
109     int x, y, dx, dy, sgap, dgap, a;
110     int n_average;
111     Uint8 *sp, *osp, *oosp;
112     Uint8 *dp;
113 
114     /*
115      * Averaging integer shrink
116      */
117 
118     /* Precalculate division factor */
119     n_average = factorx*factory;
120 
121     /*
122      * Scan destination
123      */
124     sp = (Uint8 *) src->pixels;
125     sgap = src->pitch - src->w;
126 
127     dp = (Uint8 *) dst->pixels;
128     dgap = dst->pitch - dst->w;
129 
130     for (y = 0; y < dst->h; y++) {
131 
132       osp=sp;
133       for (x = 0; x < dst->w; x++) {
134 
135         /* Trace out source box and accumulate */
136         oosp=sp;
137         a=0;
138         for (dy=0; dy < factory; dy++) {
139          for (dx=0; dx < factorx; dx++) {
140           a += (*sp);
141           sp++; // next x
142          } // src dx loop
143          sp = (Uint8 *)((Uint8*)sp + (src->pitch - factorx)); // next y
144         } // src dy loop
145 
146         // next box-x
147         sp = (Uint8 *)((Uint8*)oosp + factorx);
148 
149         /* Store result in destination */
150         *dp = a/n_average;
151 
152         /*
153          * Advance destination pointer
154          */
155          dp++;
156         } // dst x loop
157 
158         // next box-y
159         sp = (Uint8 *)((Uint8*)osp + src->pitch*factory);
160 
161         /*
162          * Advance destination pointers
163          */
164         dp = (Uint8 *)((Uint8 *)dp + dgap);
165       } // dst y loop
166 
167     return (0);
168 }
169 
170 /*
171 
172  32bit Zoomer with optional anti-aliasing by bilinear interpolation.
173 
174  Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
175 
176 */
177 
zoomSurfaceRGBA(SDL_Surface * src,SDL_Surface * dst,int flipx,int flipy,int smooth)178 int zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int flipx, int flipy, int smooth)
179 {
180     int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep;
181     tColorRGBA *c00, *c01, *c10, *c11;
182     tColorRGBA *sp, *csp, *dp;
183     int dgap;
184 
185     /*
186      * Variable setup
187      */
188     if (smooth) {
189 	/*
190 	 * For interpolation: assume source dimension is one pixel
191 	 */
192 	/*
193 	 * smaller to avoid overflow on right and bottom edge.
194 	 */
195 	sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w);
196 	sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h);
197     } else {
198 	sx = (int) (65536.0 * (float) src->w / (float) dst->w);
199 	sy = (int) (65536.0 * (float) src->h / (float) dst->h);
200     }
201 
202     /*
203      * Allocate memory for row increments
204      */
205     if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) {
206 	return (-1);
207     }
208     if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) {
209 	free(sax);
210 	return (-1);
211     }
212 
213     /*
214      * Precalculate row increments
215      */
216     sp = csp = (tColorRGBA *) src->pixels;
217     dp = (tColorRGBA *) dst->pixels;
218 
219     if (flipx) csp += (src->w-1);
220     if (flipy) csp  = (tColorRGBA*)( (Uint8*)csp + src->pitch*(src->h-1) );
221 
222     csx = 0;
223     csax = sax;
224     for (x = 0; x <= dst->w; x++) {
225 	*csax = csx;
226 	csax++;
227 	csx &= 0xffff;
228 	csx += sx;
229     }
230     csy = 0;
231     csay = say;
232     for (y = 0; y <= dst->h; y++) {
233 	*csay = csy;
234 	csay++;
235 	csy &= 0xffff;
236 	csy += sy;
237     }
238 
239     dgap = dst->pitch - dst->w * 4;
240 
241     /*
242      * Switch between interpolating and non-interpolating code
243      */
244     if (smooth) {
245 
246 	/*
247 	 * Interpolating Zoom
248 	 */
249 
250 	/*
251 	 * Scan destination
252 	 */
253 	csay = say;
254 	for (y = 0; y < dst->h; y++) {
255 	    /*
256 	     * Setup color source pointers
257 	     */
258 	    c00 = csp;
259 	    c01 = csp;
260 	    c01++;
261 	    c10 = (tColorRGBA *) ((Uint8 *) csp + src->pitch);
262 	    c11 = c10;
263 	    c11++;
264 	    csax = sax;
265 	    for (x = 0; x < dst->w; x++) {
266 
267 		/*
268 		 * Interpolate colors
269 		 */
270 		ex = (*csax & 0xffff);
271 		ey = (*csay & 0xffff);
272 		t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff;
273 		t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff;
274 		dp->r = (((t2 - t1) * ey) >> 16) + t1;
275 		t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff;
276 		t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff;
277 		dp->g = (((t2 - t1) * ey) >> 16) + t1;
278 		t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff;
279 		t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff;
280 		dp->b = (((t2 - t1) * ey) >> 16) + t1;
281 		t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff;
282 		t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff;
283 		dp->a = (((t2 - t1) * ey) >> 16) + t1;
284 
285 		/*
286 		 * Advance source pointers
287 		 */
288 		csax++;
289 		sstep = (*csax >> 16);
290 		c00 += sstep;
291 		c01 += sstep;
292 		c10 += sstep;
293 		c11 += sstep;
294 		/*
295 		 * Advance destination pointer
296 		 */
297 		dp++;
298 	    }
299 	    /*
300 	     * Advance source pointer
301 	     */
302 	    csay++;
303 	    csp = (tColorRGBA *) ((Uint8 *) csp + (*csay >> 16) * src->pitch);
304 	    /*
305 	     * Advance destination pointers
306 	     */
307 	    dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
308 	}
309 
310     } else {
311 
312 	/*
313 	 * Non-Interpolating Zoom
314 	 */
315 
316 	csay = say;
317 	for (y = 0; y < dst->h; y++) {
318 	    sp = csp;
319 	    csax = sax;
320 	    for (x = 0; x < dst->w; x++) {
321 		/*
322 		 * Draw
323 		 */
324 		*dp = *sp;
325 		/*
326 		 * Advance source pointers
327 		 */
328 		csax++;
329 		sstep = (*csax >> 16);
330 		if (flipx) sstep = -sstep;
331 		sp += sstep;
332 		/*
333 		 * Advance destination pointer
334 		 */
335 		dp++;
336 	    }
337 	    /*
338 	     * Advance source pointer
339 	     */
340 	    csay++;
341 	    sstep = (*csay >> 16) * src->pitch;
342 	    if (flipy) sstep = -sstep;
343 	    csp = (tColorRGBA *) ((Uint8 *) csp + sstep);
344 
345 	    /*
346 	     * Advance destination pointers
347 	     */
348 	    dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
349 	}
350 
351     }
352 
353     /*
354      * Remove temp arrays
355      */
356     free(sax);
357     free(say);
358 
359     return (0);
360 }
361 
362 /*
363 
364  8bit Zoomer without smoothing.
365 
366  Zoomes 8bit palette/Y 'src' surface to 'dst' surface.
367 
368 */
369 
zoomSurfaceY(SDL_Surface * src,SDL_Surface * dst,int flipx,int flipy)370 int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst, int flipx, int flipy)
371 {
372     Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy;
373     Uint8 *sp, *dp, *csp;
374     int dgap;
375 
376     /*
377      * Variable setup
378      */
379     sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w);
380     sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h);
381 
382     /*
383      * Allocate memory for row increments
384      */
385     if ((sax = (Uint32 *) malloc(dst->w * sizeof(Uint32))) == NULL) {
386 	return (-1);
387     }
388     if ((say = (Uint32 *) malloc(dst->h * sizeof(Uint32))) == NULL) {
389 	if (sax != NULL) {
390 	    free(sax);
391 	}
392 	return (-1);
393     }
394 
395     /*
396      * Precalculate row increments
397      */
398     csx = 0;
399     csax = sax;
400     for (x = 0; x < dst->w; x++) {
401 	csx += sx;
402 	*csax = (csx >> 16);
403 	csx &= 0xffff;
404 	csax++;
405     }
406     csy = 0;
407     csay = say;
408     for (y = 0; y < dst->h; y++) {
409 	csy += sy;
410 	*csay = (csy >> 16);
411 	csy &= 0xffff;
412 	csay++;
413     }
414 
415     csx = 0;
416     csax = sax;
417     for (x = 0; x < dst->w; x++) {
418 	csx += (*csax);
419 	csax++;
420     }
421     csy = 0;
422     csay = say;
423     for (y = 0; y < dst->h; y++) {
424 	csy += (*csay);
425 	csay++;
426     }
427 
428     /*
429      * Pointer setup
430      */
431     sp = csp = (Uint8 *) src->pixels;
432     dp = (Uint8 *) dst->pixels;
433     dgap = dst->pitch - dst->w;
434 
435     /*
436      * Draw
437      */
438     csay = say;
439     for (y = 0; y < dst->h; y++) {
440 	csax = sax;
441 	sp = csp;
442 	for (x = 0; x < dst->w; x++) {
443 	    /*
444 	     * Draw
445 	     */
446 	    *dp = *sp;
447 	    /*
448 	     * Advance source pointers
449 	     */
450 	    sp += (*csax);
451 	    csax++;
452 	    /*
453 	     * Advance destination pointer
454 	     */
455 	    dp++;
456 	}
457 	/*
458 	 * Advance source pointer (for row)
459 	 */
460 	csp += ((*csay) * src->pitch);
461 	csay++;
462 	/*
463 	 * Advance destination pointers
464 	 */
465 	dp += dgap;
466     }
467 
468     /*
469      * Remove temp arrays
470      */
471     free(sax);
472     free(say);
473 
474     return (0);
475 }
476 
477 /*
478 
479  32bit Rotozoomer with optional anti-aliasing by bilinear interpolation.
480 
481  Rotates and zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface.
482 
483 */
484 
transformSurfaceRGBA(SDL_Surface * src,SDL_Surface * dst,int cx,int cy,int isin,int icos,int flipx,int flipy,int smooth)485 void transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy, int smooth)
486 {
487     int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh;
488     tColorRGBA c00, c01, c10, c11;
489     tColorRGBA *pc, *sp;
490     int gap;
491 
492     /*
493      * Variable setup
494      */
495     xd = ((src->w - dst->w) << 15);
496     yd = ((src->h - dst->h) << 15);
497     ax = (cx << 16) - (icos * cx);
498     ay = (cy << 16) - (isin * cx);
499     sw = src->w - 1;
500     sh = src->h - 1;
501     pc = dst->pixels;
502     gap = dst->pitch - dst->w * 4;
503 
504     /*
505      * Switch between interpolating and non-interpolating code
506      */
507     if (smooth) {
508 	for (y = 0; y < dst->h; y++) {
509 	    dy = cy - y;
510 	    sdx = (ax + (isin * dy)) + xd;
511 	    sdy = (ay - (icos * dy)) + yd;
512 	    for (x = 0; x < dst->w; x++) {
513 		dx = (sdx >> 16);
514 		dy = (sdy >> 16);
515 		if ((dx >= -1) && (dy >= -1) && (dx < src->w) && (dy < src->h)) {
516 		    if ((dx >= 0) && (dy >= 0) && (dx < sw) && (dy < sh)) {
517 			sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
518 			sp += dx;
519 			c00 = *sp;
520 			sp += 1;
521 			c01 = *sp;
522 			sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
523 			sp -= 1;
524 			c10 = *sp;
525 			sp += 1;
526 			c11 = *sp;
527 		    } else if ((dx == sw) && (dy == sh)) {
528 			sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
529 			sp += dx;
530 			c00 = *sp;
531 			c01 = *sp;
532 			c10 = *sp;
533 			c11 = *sp;
534 		    } else if ((dx == -1) && (dy == -1)) {
535 			sp = (tColorRGBA *) (src->pixels);
536 			c00 = *sp;
537 			c01 = *sp;
538 			c10 = *sp;
539 			c11 = *sp;
540 		    } else if ((dx == -1) && (dy == sh)) {
541 			sp = (tColorRGBA *) (src->pixels);
542 			sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
543 			c00 = *sp;
544 			c01 = *sp;
545 			c10 = *sp;
546 			c11 = *sp;
547 		    } else if ((dx == sw) && (dy == -1)) {
548 			sp = (tColorRGBA *) (src->pixels);
549 			sp += dx;
550 			c00 = *sp;
551 			c01 = *sp;
552 			c10 = *sp;
553 			c11 = *sp;
554 		    } else if (dx == -1) {
555 			sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
556 			c00 = *sp;
557 			c01 = *sp;
558 			c10 = *sp;
559 			sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
560 			c11 = *sp;
561 		    } else if (dy == -1) {
562 			sp = (tColorRGBA *) (src->pixels);
563 			sp += dx;
564 			c00 = *sp;
565 			c01 = *sp;
566 			c10 = *sp;
567 			sp += 1;
568 			c11 = *sp;
569 		    } else if (dx == sw) {
570 			sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
571 			sp += dx;
572 			c00 = *sp;
573 			c01 = *sp;
574 			sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch);
575 			c10 = *sp;
576 			c11 = *sp;
577 		    } else if (dy == sh) {
578 			sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
579 			sp += dx;
580 			c00 = *sp;
581 			sp += 1;
582 			c01 = *sp;
583 			c10 = *sp;
584 			c11 = *sp;
585 		    }
586 		    /*
587 		     * Interpolate colors
588 		     */
589 		    ex = (sdx & 0xffff);
590 		    ey = (sdy & 0xffff);
591 		    t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
592 		    t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
593 		    pc->r = (((t2 - t1) * ey) >> 16) + t1;
594 		    t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
595 		    t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
596 		    pc->g = (((t2 - t1) * ey) >> 16) + t1;
597 		    t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
598 		    t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
599 		    pc->b = (((t2 - t1) * ey) >> 16) + t1;
600 		    t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
601 		    t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
602 		    pc->a = (((t2 - t1) * ey) >> 16) + t1;
603 		}
604 		sdx += icos;
605 		sdy += isin;
606 		pc++;
607 	    }
608 	    pc = (tColorRGBA *) ((Uint8 *) pc + gap);
609 	}
610     } else {
611 	for (y = 0; y < dst->h; y++) {
612 	    dy = cy - y;
613 	    sdx = (ax + (isin * dy)) + xd;
614 	    sdy = (ay - (icos * dy)) + yd;
615 	    for (x = 0; x < dst->w; x++) {
616 		dx = (short) (sdx >> 16);
617 		dy = (short) (sdy >> 16);
618 		if (flipx) dx = (src->w-1)-dx;
619 		if (flipy) dy = (src->h-1)-dy;
620 		if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
621 		    sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
622 		    sp += dx;
623 		    *pc = *sp;
624 		}
625 		sdx += icos;
626 		sdy += isin;
627 		pc++;
628 	    }
629 	    pc = (tColorRGBA *) ((Uint8 *) pc + gap);
630 	}
631     }
632 }
633 
634 /*
635 
636  8bit Rotozoomer without smoothing
637 
638  Rotates and zoomes 8bit palette/Y 'src' surface to 'dst' surface.
639 
640 */
641 
transformSurfaceY(SDL_Surface * src,SDL_Surface * dst,int cx,int cy,int isin,int icos)642 void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos)
643 {
644     int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh;
645     tColorY *pc, *sp;
646     int gap;
647 
648     /*
649      * Variable setup
650      */
651     xd = ((src->w - dst->w) << 15);
652     yd = ((src->h - dst->h) << 15);
653     ax = (cx << 16) - (icos * cx);
654     ay = (cy << 16) - (isin * cx);
655     sw = src->w - 1;
656     sh = src->h - 1;
657     pc = dst->pixels;
658     gap = dst->pitch - dst->w;
659     /*
660      * Clear surface to colorkey
661      */
662     memset(pc, (unsigned char) (src->format->colorkey & 0xff), dst->pitch * dst->h);
663     /*
664      * Iterate through destination surface
665      */
666     for (y = 0; y < dst->h; y++) {
667 	dy = cy - y;
668 	sdx = (ax + (isin * dy)) + xd;
669 	sdy = (ay - (icos * dy)) + yd;
670 	for (x = 0; x < dst->w; x++) {
671 	    dx = (short) (sdx >> 16);
672 	    dy = (short) (sdy >> 16);
673 	    if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
674 		sp = (tColorY *) (src->pixels);
675 		sp += (src->pitch * dy + dx);
676 		*pc = *sp;
677 	    }
678 	    sdx += icos;
679 	    sdy += isin;
680 	    pc++;
681 	}
682 	pc += gap;
683     }
684 }
685 
686 
687 /*
688 
689  32bit specialized 90degree rotator
690 
691  Rotates and zooms 'src' surface to 'dst' surface in 90degree increments.
692 
693  (contributed by Jeff Schiller)
694 
695 */
rotateSurface90Degrees(SDL_Surface * pSurf,int numClockwiseTurns)696 SDL_Surface* rotateSurface90Degrees(SDL_Surface* pSurf, int numClockwiseTurns)
697 {
698  int row, col;
699 
700  // Has to be a valid surface pointer and only 32-bit surfaces (for now)
701  if (!pSurf || pSurf->format->BitsPerPixel != 32) { return NULL; }
702 
703  // normalize numClockwiseTurns
704  while(numClockwiseTurns < 0) { numClockwiseTurns += 4; }
705  numClockwiseTurns = (numClockwiseTurns % 4);
706 
707  // if it's even, our new width will be the same as the source surface
708  int newWidth = (numClockwiseTurns % 2) ? (pSurf->h) : (pSurf->w);
709  int newHeight = (numClockwiseTurns % 2) ? (pSurf->w) : (pSurf->h);
710  SDL_Surface* pSurfOut = SDL_CreateRGBSurface( pSurf->flags, newWidth, newHeight, pSurf->format->BitsPerPixel,
711                            pSurf->format->Rmask,
712                            pSurf->format->Gmask,
713                            pSurf->format->Bmask,
714                            pSurf->format->Amask);
715  if(!pSurfOut) {
716    return NULL;
717  }
718 
719  if(numClockwiseTurns != 0) {
720    SDL_LockSurface(pSurf);
721    SDL_LockSurface(pSurfOut);
722    switch(numClockwiseTurns) {
723      // rotate clockwise
724      case 1: // rotated 90 degrees clockwise
725      {
726        Uint32* srcBuf = NULL;
727        Uint32* dstBuf = NULL;
728 
729        for (row = 0; row < pSurf->h; ++row) {
730          srcBuf = (Uint32*)(pSurf->pixels) + (row*pSurf->pitch/4);
731          dstBuf = (Uint32*)(pSurfOut->pixels) + (pSurfOut->w - row - 1);
732          for (col = 0; col < pSurf->w; ++col) {
733            *dstBuf = *srcBuf;
734            ++srcBuf;
735            dstBuf += pSurfOut->pitch/4;
736          } // for(col)
737        } // for(row)
738      }
739      break;
740 
741      case 2: // rotated 180 degrees clockwise
742      {
743        Uint32* srcBuf = NULL;
744        Uint32* dstBuf = NULL;
745 
746        for(row = 0; row < pSurf->h; ++row) {
747          srcBuf = (Uint32*)(pSurf->pixels) + (row*pSurf->pitch/4);
748          dstBuf = (Uint32*)(pSurfOut->pixels) + ((pSurfOut->h - row - 1)*pSurfOut->pitch/4) + (pSurfOut->w - 1);
749          for(col = 0; col < pSurf->w; ++col) {
750            *dstBuf = *srcBuf;
751            ++srcBuf;
752            --dstBuf;
753          } // for(col)
754        } // for(row)
755      }
756      break;
757 
758      case 3:
759      {
760        Uint32* srcBuf = NULL;
761        Uint32* dstBuf = NULL;
762 
763        for(row = 0; row < pSurf->h; ++row) {
764          srcBuf = (Uint32*)(pSurf->pixels) + (row*pSurf->pitch/4);
765          dstBuf = (Uint32*)(pSurfOut->pixels) + row + ((pSurfOut->h - 1)*pSurfOut->pitch/4);
766          for(col = 0; col < pSurf->w; ++col) {
767            *dstBuf = *srcBuf;
768            ++srcBuf;
769            dstBuf -= pSurfOut->pitch/4;
770          } // for(col)
771        } // for(row)
772      }
773      break;
774    } // switch
775 
776    SDL_UnlockSurface(pSurf);
777    SDL_UnlockSurface(pSurfOut);
778  } // if numClockwiseTurns > 0
779  else {
780    // simply copy surface to output
781    if(SDL_BlitSurface(pSurf, NULL, pSurfOut, NULL)) {
782      return NULL;
783    }
784  }
785  return pSurfOut;
786 }
787 
788 /*
789 
790  rotozoomSurface()
791 
792  Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
793  'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1
794  then the destination 32bit surface is anti-aliased. If the surface is not 8bit
795  or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
796 
797 */
798 
799 #define VALUE_LIMIT	0.001
800 
801 
802 /* Local rotozoom-size function with trig result return */
803 
rotozoomSurfaceSizeTrig(int width,int height,double angle,double zoomx,double zoomy,int * dstwidth,int * dstheight,double * canglezoom,double * sanglezoom)804 void rotozoomSurfaceSizeTrig(int width, int height, double angle, double zoomx, double zoomy, int *dstwidth, int *dstheight,
805 			     double *canglezoom, double *sanglezoom)
806 {
807     double x, y, cx, cy, sx, sy;
808     double radangle;
809     int dstwidthhalf, dstheighthalf;
810 
811     /*
812      * Determine destination width and height by rotating a centered source box
813      */
814     radangle = angle * (M_PI / 180.0);
815     *sanglezoom = sin(radangle);
816     *canglezoom = cos(radangle);
817     *sanglezoom *= zoomx;
818     *canglezoom *= zoomx;
819     x = width / 2;
820     y = height / 2;
821     cx = *canglezoom * x;
822     cy = *canglezoom * y;
823     sx = *sanglezoom * x;
824     sy = *sanglezoom * y;
825 
826     dstwidthhalf = MAX((int)
827 		       ceil(MAX(MAX(MAX(fabs(cx + sy), fabs(cx - sy)), fabs(-cx + sy)), fabs(-cx - sy))), 1);
828     dstheighthalf = MAX((int)
829 			ceil(MAX(MAX(MAX(fabs(sx + cy), fabs(sx - cy)), fabs(-sx + cy)), fabs(-sx - cy))), 1);
830     *dstwidth = 2 * dstwidthhalf;
831     *dstheight = 2 * dstheighthalf;
832 }
833 
834 
835 /* Publically available rotozoom-size function */
836 
rotozoomSurfaceSizeXY(int width,int height,double angle,double zoomx,double zoomy,int * dstwidth,int * dstheight)837 void rotozoomSurfaceSizeXY(int width, int height, double angle, double zoomx, double zoomy, int *dstwidth, int *dstheight)
838 {
839     double dummy_sanglezoom, dummy_canglezoom;
840 
841     rotozoomSurfaceSizeTrig(width, height, angle, zoomx, zoomy, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom);
842 }
843 
844 /* Publically available rotozoom-size function */
845 
rotozoomSurfaceSize(int width,int height,double angle,double zoom,int * dstwidth,int * dstheight)846 void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight)
847 {
848     double dummy_sanglezoom, dummy_canglezoom;
849 
850     rotozoomSurfaceSizeTrig(width, height, angle, zoom, zoom, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom);
851 }
852 
853 /* Publically available rotozoom function */
854 
rotozoomSurface(SDL_Surface * src,double angle,double zoom,int smooth)855 SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth)
856 {
857   return rotozoomSurfaceXY(src, angle, zoom, zoom, smooth);
858 }
859 
860 /* Publically available rotozoom function */
861 
rotozoomSurfaceXY(SDL_Surface * src,double angle,double zoomx,double zoomy,int smooth)862 SDL_Surface *rotozoomSurfaceXY(SDL_Surface * src, double angle, double zoomx, double zoomy, int smooth)
863 {
864     SDL_Surface *rz_src;
865     SDL_Surface *rz_dst;
866     double zoominv;
867     double sanglezoom, canglezoom, sanglezoominv, canglezoominv;
868     int dstwidthhalf, dstwidth, dstheighthalf, dstheight;
869     int is32bit;
870     int i, src_converted;
871     int flipx,flipy;
872 
873     /*
874      * Sanity check
875      */
876     if (src == NULL)
877 	return (NULL);
878 
879     /*
880      * Determine if source surface is 32bit or 8bit
881      */
882     is32bit = (src->format->BitsPerPixel == 32);
883     if ((is32bit) || (src->format->BitsPerPixel == 8)) {
884 	/*
885 	 * Use source surface 'as is'
886 	 */
887 	rz_src = src;
888 	src_converted = 0;
889     } else {
890 	/*
891 	 * New source surface is 32bit with a defined RGBA ordering
892 	 */
893 	rz_src =
894 	    SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
895 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
896                                 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
897 #else
898                                 0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
899 #endif
900 	    );
901 	SDL_BlitSurface(src, NULL, rz_src, NULL);
902 	src_converted = 1;
903 	is32bit = 1;
904     }
905 
906     /*
907      * Sanity check zoom factor
908      */
909     flipx = (zoomx<0);
910     if (flipx) zoomx=-zoomx;
911     flipy = (zoomy<0);
912     if (flipy) zoomy=-zoomy;
913     if (zoomx < VALUE_LIMIT) zoomx = VALUE_LIMIT;
914     if (zoomy < VALUE_LIMIT) zoomy = VALUE_LIMIT;
915     zoominv = 65536.0 / (zoomx * zoomx);
916 
917     /*
918      * Check if we have a rotozoom or just a zoom
919      */
920     if (fabs(angle) > VALUE_LIMIT) {
921 
922 	/*
923 	 * Angle!=0: full rotozoom
924 	 */
925 	/*
926 	 * -----------------------
927 	 */
928 
929 	/* Determine target size */
930 	rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, zoomx, zoomy, &dstwidth, &dstheight, &canglezoom, &sanglezoom);
931 
932 	/*
933 	 * Calculate target factors from sin/cos and zoom
934 	 */
935 	sanglezoominv = sanglezoom;
936 	canglezoominv = canglezoom;
937 	sanglezoominv *= zoominv;
938 	canglezoominv *= zoominv;
939 
940 	/* Calculate half size */
941 	dstwidthhalf = dstwidth / 2;
942 	dstheighthalf = dstheight / 2;
943 
944 	/*
945 	 * Alloc space to completely contain the rotated surface
946 	 */
947 	rz_dst = NULL;
948 	if (is32bit) {
949 	    /*
950 	     * Target surface is 32bit with source RGBA/ABGR ordering
951 	     */
952 	    rz_dst =
953 		SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32,
954 				     rz_src->format->Rmask, rz_src->format->Gmask,
955 				     rz_src->format->Bmask, rz_src->format->Amask);
956 	} else {
957 	    /*
958 	     * Target surface is 8bit
959 	     */
960 	    rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0);
961 	}
962 
963 	/*
964 	 * Lock source surface
965 	 */
966 	SDL_LockSurface(rz_src);
967 	/*
968 	 * Check which kind of surface we have
969 	 */
970 	if (is32bit) {
971 	    /*
972 	     * Call the 32bit transformation routine to do the rotation (using alpha)
973 	     */
974 	    transformSurfaceRGBA(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
975 				 (int) (sanglezoominv), (int) (canglezoominv),
976 				 flipx, flipy,
977 				 smooth);
978 	    /*
979 	     * Turn on source-alpha support
980 	     */
981 	    SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
982 	} else {
983 	    /*
984 	     * Copy palette and colorkey info
985 	     */
986 	    for (i = 0; i < rz_src->format->palette->ncolors; i++) {
987 		rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
988 	    }
989 	    rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
990 	    /*
991 	     * Call the 8bit transformation routine to do the rotation
992 	     */
993 	    transformSurfaceY(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
994 			      (int) (sanglezoominv), (int) (canglezoominv));
995 	    SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey);
996 	}
997 	/*
998 	 * Unlock source surface
999 	 */
1000 	SDL_UnlockSurface(rz_src);
1001 
1002     } else {
1003 
1004 	/*
1005 	 * Angle=0: Just a zoom
1006 	 */
1007 	/*
1008 	 * --------------------
1009 	 */
1010 
1011 	/*
1012 	 * Calculate target size
1013 	 */
1014 	zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight);
1015 
1016 	/*
1017 	 * Alloc space to completely contain the zoomed surface
1018 	 */
1019 	rz_dst = NULL;
1020 	if (is32bit) {
1021 	    /*
1022 	     * Target surface is 32bit with source RGBA/ABGR ordering
1023 	     */
1024 	    rz_dst =
1025 		SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32,
1026 				     rz_src->format->Rmask, rz_src->format->Gmask,
1027 				     rz_src->format->Bmask, rz_src->format->Amask);
1028 	} else {
1029 	    /*
1030 	     * Target surface is 8bit
1031 	     */
1032 	    rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0);
1033 	}
1034 
1035 	/*
1036 	 * Lock source surface
1037 	 */
1038 	SDL_LockSurface(rz_src);
1039 	/*
1040 	 * Check which kind of surface we have
1041 	 */
1042 	if (is32bit) {
1043 	    /*
1044 	     * Call the 32bit transformation routine to do the zooming (using alpha)
1045 	     */
1046 	    zoomSurfaceRGBA(rz_src, rz_dst, flipx, flipy, smooth);
1047 	    /*
1048 	     * Turn on source-alpha support
1049 	     */
1050 	    SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
1051 	} else {
1052 	    /*
1053 	     * Copy palette and colorkey info
1054 	     */
1055 	    for (i = 0; i < rz_src->format->palette->ncolors; i++) {
1056 		rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1057 	    }
1058 	    rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1059 	    /*
1060 	     * Call the 8bit transformation routine to do the zooming
1061 	     */
1062 	    zoomSurfaceY(rz_src, rz_dst, flipx, flipy);
1063 	    SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey);
1064 	}
1065 	/*
1066 	 * Unlock source surface
1067 	 */
1068 	SDL_UnlockSurface(rz_src);
1069     }
1070 
1071     /*
1072      * Cleanup temp surface
1073      */
1074     if (src_converted) {
1075 	SDL_FreeSurface(rz_src);
1076     }
1077 
1078     /*
1079      * Return destination surface
1080      */
1081     return (rz_dst);
1082 }
1083 
1084 /*
1085 
1086  zoomSurface()
1087 
1088  Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface.
1089  'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1
1090  then the destination 32bit surface is anti-aliased. If the surface is not 8bit
1091  or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
1092 
1093 */
1094 
1095 #define VALUE_LIMIT	0.001
1096 
zoomSurfaceSize(int width,int height,double zoomx,double zoomy,int * dstwidth,int * dstheight)1097 void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight)
1098 {
1099     /*
1100      * Sanity check zoom factors
1101      */
1102     if (zoomx < VALUE_LIMIT) {
1103 	zoomx = VALUE_LIMIT;
1104     }
1105     if (zoomy < VALUE_LIMIT) {
1106 	zoomy = VALUE_LIMIT;
1107     }
1108 
1109     /*
1110      * Calculate target size
1111      */
1112     *dstwidth = (int) ((double) width * zoomx);
1113     *dstheight = (int) ((double) height * zoomy);
1114     if (*dstwidth < 1) {
1115 	*dstwidth = 1;
1116     }
1117     if (*dstheight < 1) {
1118 	*dstheight = 1;
1119     }
1120 }
1121 
zoomSurface(SDL_Surface * src,double zoomx,double zoomy,int smooth)1122 SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth)
1123 {
1124     SDL_Surface *rz_src;
1125     SDL_Surface *rz_dst;
1126     int dstwidth, dstheight;
1127     int is32bit;
1128     int i, src_converted;
1129     int flipx, flipy;
1130 
1131     /*
1132      * Sanity check
1133      */
1134     if (src == NULL)
1135 	return (NULL);
1136 
1137     /*
1138      * Determine if source surface is 32bit or 8bit
1139      */
1140     is32bit = (src->format->BitsPerPixel == 32);
1141     if ((is32bit) || (src->format->BitsPerPixel == 8)) {
1142 	/*
1143 	 * Use source surface 'as is'
1144 	 */
1145 	rz_src = src;
1146 	src_converted = 0;
1147     } else {
1148 	/*
1149 	 * New source surface is 32bit with a defined RGBA ordering
1150 	 */
1151 	rz_src =
1152 	    SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1153 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1154                                 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
1155 #else
1156                                 0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
1157 #endif
1158 	    );
1159 	SDL_BlitSurface(src, NULL, rz_src, NULL);
1160 	src_converted = 1;
1161 	is32bit = 1;
1162     }
1163 
1164     flipx = (zoomx<0);
1165     if (flipx) zoomx = -zoomx;
1166     flipy = (zoomy<0);
1167     if (flipy) zoomy = -zoomy;
1168 
1169     /* Get size if target */
1170     zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight);
1171 
1172     /*
1173      * Alloc space to completely contain the zoomed surface
1174      */
1175     rz_dst = NULL;
1176     if (is32bit) {
1177 	/*
1178 	 * Target surface is 32bit with source RGBA/ABGR ordering
1179 	 */
1180 	rz_dst =
1181 	    SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32,
1182 				 rz_src->format->Rmask, rz_src->format->Gmask,
1183 				 rz_src->format->Bmask, rz_src->format->Amask);
1184     } else {
1185 	/*
1186 	 * Target surface is 8bit
1187 	 */
1188 	rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0);
1189     }
1190 
1191     /*
1192      * Lock source surface
1193      */
1194     SDL_LockSurface(rz_src);
1195     /*
1196      * Check which kind of surface we have
1197      */
1198     if (is32bit) {
1199 	/*
1200 	 * Call the 32bit transformation routine to do the zooming (using alpha)
1201 	 */
1202 	zoomSurfaceRGBA(rz_src, rz_dst, flipx, flipy, smooth);
1203 	/*
1204 	 * Turn on source-alpha support
1205 	 */
1206 	SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
1207     } else {
1208 	/*
1209 	 * Copy palette and colorkey info
1210 	 */
1211 	for (i = 0; i < rz_src->format->palette->ncolors; i++) {
1212 	    rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1213 	}
1214 	rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1215 	/*
1216 	 * Call the 8bit transformation routine to do the zooming
1217 	 */
1218 	zoomSurfaceY(rz_src, rz_dst, flipx, flipy);
1219 	SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey);
1220     }
1221     /*
1222      * Unlock source surface
1223      */
1224     SDL_UnlockSurface(rz_src);
1225 
1226     /*
1227      * Cleanup temp surface
1228      */
1229     if (src_converted) {
1230 	SDL_FreeSurface(rz_src);
1231     }
1232 
1233     /*
1234      * Return destination surface
1235      */
1236     return (rz_dst);
1237 }
1238 
shrinkSurface(SDL_Surface * src,int factorx,int factory)1239 SDL_Surface *shrinkSurface(SDL_Surface * src, int factorx, int factory)
1240 {
1241     SDL_Surface *rz_src;
1242     SDL_Surface *rz_dst;
1243     int dstwidth, dstheight;
1244     int is32bit;
1245     int i, src_converted;
1246 
1247     /*
1248      * Sanity check
1249      */
1250     if (src == NULL)
1251 	return (NULL);
1252 
1253     /*
1254      * Determine if source surface is 32bit or 8bit
1255      */
1256     is32bit = (src->format->BitsPerPixel == 32);
1257     if ((is32bit) || (src->format->BitsPerPixel == 8)) {
1258 	/*
1259 	 * Use source surface 'as is'
1260 	 */
1261 	rz_src = src;
1262 	src_converted = 0;
1263     } else {
1264 	/*
1265 	 * New source surface is 32bit with a defined RGBA ordering
1266 	 */
1267 	rz_src =
1268 	    SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1269 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1270                                 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
1271 #else
1272                                 0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
1273 #endif
1274 	    );
1275 	SDL_BlitSurface(src, NULL, rz_src, NULL);
1276 	src_converted = 1;
1277 	is32bit = 1;
1278     }
1279 
1280     /* Get size for target */
1281     dstwidth=rz_src->w/factorx;
1282     while (dstwidth*factorx>rz_src->w) { dstwidth--; }
1283     dstheight=rz_src->h/factory;
1284     while (dstheight*factory>rz_src->h) { dstheight--; }
1285 
1286     /*
1287      * Alloc space to completely contain the shrunken surface
1288      */
1289     rz_dst = NULL;
1290     if (is32bit) {
1291 	/*
1292 	 * Target surface is 32bit with source RGBA/ABGR ordering
1293 	 */
1294 	rz_dst =
1295 	    SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32,
1296 				 rz_src->format->Rmask, rz_src->format->Gmask,
1297 				 rz_src->format->Bmask, rz_src->format->Amask);
1298     } else {
1299 	/*
1300 	 * Target surface is 8bit
1301 	 */
1302 	rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0);
1303     }
1304 
1305     /*
1306      * Lock source surface
1307      */
1308     SDL_LockSurface(rz_src);
1309     /*
1310      * Check which kind of surface we have
1311      */
1312     if (is32bit) {
1313 	/*
1314 	 * Call the 32bit transformation routine to do the shrinking (using alpha)
1315 	 */
1316 	shrinkSurfaceRGBA(rz_src, rz_dst, factorx, factory);
1317 	/*
1318 	 * Turn on source-alpha support
1319 	 */
1320 	SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
1321     } else {
1322 	/*
1323 	 * Copy palette and colorkey info
1324 	 */
1325 	for (i = 0; i < rz_src->format->palette->ncolors; i++) {
1326 	    rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1327 	}
1328 	rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1329 	/*
1330 	 * Call the 8bit transformation routine to do the shrinking
1331 	 */
1332 	shrinkSurfaceY(rz_src, rz_dst, factorx, factory);
1333 	SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey);
1334     }
1335     /*
1336      * Unlock source surface
1337      */
1338     SDL_UnlockSurface(rz_src);
1339 
1340     /*
1341      * Cleanup temp surface
1342      */
1343     if (src_converted) {
1344 	SDL_FreeSurface(rz_src);
1345     }
1346 
1347     /*
1348      * Return destination surface
1349      */
1350     return (rz_dst);
1351 }
1352