3 SDL_rotozoom.c: rotozoomer, zoomer and shrinker for 32bit or 8bit surfaces
5 Copyright (C) 2001-2012 Andreas Schiffler
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.
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:
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.
20 2. Altered source versions must be plainly marked as such, and must not be
21 misrepresented as being the original software.
23 3. This notice may not be removed or altered from any source
26 Andreas Schiffler -- aschiffler at ferzkopp dot net
37 #include "SDL_rotozoom.h"
39 /* ---- Internally used structures */
42 \brief A 32 bit RGBA pixel.
44 typedef struct tColorRGBA {
52 \brief A 8bit Y/palette pixel.
54 typedef struct tColorY {
59 \brief Returns maximum of two numbers a and b.
61 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
64 \brief Number of guard rows added to destination surfaces.
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.
73 #define GUARD_ROWS (2)
76 \brief Lower limit of absolute zoom factor or rotation degrees.
78 #define VALUE_LIMIT 0.001
81 \brief Returns colorkey info for a surface
83 static Uint32 _colorkey(SDL_Surface *src)
86 #if (SDL_MINOR_VERSION == 3)
87 SDL_GetColorKey(src, &key);
91 key = src->format->colorkey;
99 \brief Internal 32 bit integer-factor averaging Shrinker.
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.
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.
111 \return 0 for success or -1 for error.
113 static int _shrinkSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int factorx, int factory)
115 int x, y, dx, dy, sgap, dgap, ra, ga, ba, aa;
117 tColorRGBA *sp, *osp, *oosp;
121 * Averaging integer shrink
124 /* Precalculate division factor */
125 n_average = factorx*factory;
130 sp = (tColorRGBA *) src->pixels;
131 sgap = src->pitch - src->w * 4;
133 dp = (tColorRGBA *) dst->pixels;
134 dgap = dst->pitch - dst->w * 4;
136 for (y = 0; y < dst->h; y++) {
139 for (x = 0; x < dst->w; x++) {
141 /* Trace out source box and accumulate */
144 for (dy=0; dy < factory; dy++) {
145 for (dx=0; dx < factorx; dx++) {
154 sp = (tColorRGBA *)((Uint8*)sp + (src->pitch - 4*factorx)); // next y
159 sp = (tColorRGBA *)((Uint8*)oosp + 4*factorx);
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;
168 * Advance destination pointer
175 sp = (tColorRGBA *)((Uint8*)osp + src->pitch*factory);
178 * Advance destination pointers
180 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
188 \brief Internal 8 bit integer-factor averaging shrinker.
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.
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.
200 \return 0 for success or -1 for error.
202 static int _shrinkSurfaceY(SDL_Surface * src, SDL_Surface * dst, int factorx, int factory)
204 int x, y, dx, dy, sgap, dgap, a;
206 Uint8 *sp, *osp, *oosp;
210 * Averaging integer shrink
213 /* Precalculate division factor */
214 n_average = factorx*factory;
219 sp = (Uint8 *) src->pixels;
220 sgap = src->pitch - src->w;
222 dp = (Uint8 *) dst->pixels;
223 dgap = dst->pitch - dst->w;
225 for (y = 0; y < dst->h; y++) {
228 for (x = 0; x < dst->w; x++) {
230 /* Trace out source box and accumulate */
233 for (dy=0; dy < factory; dy++) {
234 for (dx=0; dx < factorx; dx++) {
239 /* end src dx loop */
241 sp = (Uint8 *)((Uint8*)sp + (src->pitch - factorx));
243 /* end src dy loop */
246 sp = (Uint8 *)((Uint8*)oosp + factorx);
248 /* Store result in destination */
252 * Advance destination pointer
259 sp = (Uint8 *)((Uint8*)osp + src->pitch*factory);
262 * Advance destination pointers
264 dp = (Uint8 *)((Uint8 *)dp + dgap);
272 \brief Internal 32 bit Zoomer with optional anti-aliasing by bilinear interpolation.
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.
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.
284 \return 0 for success or -1 for error.
286 static int _zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int flipx, int flipy, int smooth)
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;
294 * Allocate memory for row/column increments
296 if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) {
299 if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) {
305 * Precalculate row increments
307 spixelw = (src->w - 1);
308 spixelh = (src->h - 1);
310 sx = (int) (65536.0 * (float) spixelw / (float) (dst->w - 1));
311 sy = (int) (65536.0 * (float) spixelh / (float) (dst->h - 1));
313 sx = (int) (65536.0 * (float) (src->w) / (float) (dst->w));
314 sy = (int) (65536.0 * (float) (src->h) / (float) (dst->h));
317 /* Maximum scaled source size */
318 ssx = (src->w << 16) - 1;
319 ssy = (src->h << 16) - 1;
321 /* Precalculate horizontal row increments */
324 for (x = 0; x <= dst->w; x++) {
329 /* Guard from overflows */
335 /* Precalculate vertical row increments */
338 for (y = 0; y <= dst->h; y++) {
343 /* Guard from overflows */
349 sp = (tColorRGBA *) src->pixels;
350 dp = (tColorRGBA *) dst->pixels;
351 dgap = dst->pitch - dst->w * 4;
352 spixelgap = src->pitch/4;
354 if (flipx) sp += spixelw;
355 if (flipy) sp += (spixelgap * spixelh);
358 * Switch between interpolating and non-interpolating code
366 for (y = 0; y < dst->h; y++) {
369 for (x = 0; x < dst->w; x++) {
371 * Setup color source pointers
373 ex = (*csax & 0xffff);
374 ey = (*csay & 0xffff);
377 sstepx = cx < spixelw;
378 sstepy = cy < spixelh;
401 * Draw and interpolate colors
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;
416 * Advance source pointer x
420 sstep = (*csax >> 16) - (*salast >> 16);
428 * Advance destination pointer x
433 * Advance source pointer y
437 sstep = (*csay >> 16) - (*salast >> 16);
446 * Advance destination pointer y
448 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
452 * Non-Interpolating Zoom
455 for (y = 0; y < dst->h; y++) {
458 for (x = 0; x < dst->w; x++) {
465 * Advance source pointer x
469 sstep = (*csax >> 16) - (*salast >> 16);
470 if (flipx) sstep = -sstep;
474 * Advance destination pointer x
479 * Advance source pointer y
483 sstep = (*csay >> 16) - (*salast >> 16);
485 if (flipy) sstep = -sstep;
489 * Advance destination pointer y
491 dp = (tColorRGBA *) ((Uint8 *) dp + dgap);
506 \brief Internal 8 bit Zoomer without smoothing.
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.
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.
517 \return 0 for success or -1 for error.
519 static int _zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst, int flipx, int flipy)
522 Uint32 *sax, *say, *csax, *csay;
524 Uint8 *sp, *dp, *csp;
528 * Allocate memory for row increments
530 if ((sax = (Uint32 *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) {
533 if ((say = (Uint32 *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) {
541 sp = csp = (Uint8 *) src->pixels;
542 dp = (Uint8 *) dst->pixels;
543 dgap = dst->pitch - dst->w;
545 if (flipx) csp += (src->w-1);
546 if (flipy) csp = ( (Uint8*)csp + src->pitch*(src->h-1) );
549 * Precalculate row increments
553 for (x = 0; x < dst->w; x++) {
556 while (csx >= dst->w) {
560 (*csax) = (*csax) * (flipx ? -1 : 1);
565 for (y = 0; y < dst->h; y++) {
568 while (csy >= dst->h) {
572 (*csay) = (*csay) * (flipy ? -1 : 1);
580 for (y = 0; y < dst->h; y++) {
583 for (x = 0; x < dst->w; x++) {
589 * Advance source pointers
594 * Advance destination pointer
599 * Advance source pointer (for row)
601 csp += ((*csay) * src->pitch);
605 * Advance destination pointers
620 \brief Internal 32 bit rotozoomer with optional anti-aliasing.
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.
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.
638 static void _transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy, int smooth)
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;
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);
654 pc = (tColorRGBA*) dst->pixels;
655 gap = dst->pitch - dst->w * 4;
658 * Switch between interpolating and non-interpolating code
661 for (y = 0; y < dst->h; y++) {
663 sdx = (ax + (isin * dy)) + xd;
664 sdy = (ay - (icos * dy)) + yd;
665 for (x = 0; x < dst->w; x++) {
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);
677 sp += (src->pitch/4);
682 cswap = c00; c00=c01; c01=cswap;
683 cswap = c10; c10=c11; c11=cswap;
686 cswap = c00; c00=c10; c10=cswap;
687 cswap = c01; c01=c11; c11=cswap;
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;
711 pc = (tColorRGBA *) ((Uint8 *) pc + gap);
714 for (y = 0; y < dst->h; 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);
732 pc = (tColorRGBA *) ((Uint8 *) pc + gap);
739 \brief Rotates and zooms 8 bit palette/Y 'src' surface to 'dst' surface without smoothing.
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.
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.
755 static void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy)
757 int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh;
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);
770 pc = (tColorY*) dst->pixels;
771 gap = dst->pitch - dst->w;
773 * Clear surface to colorkey
775 memset(pc, (int)(_colorkey(src) & 0xff), dst->pitch * dst->h);
777 * Iterate through destination surface
779 for (y = 0; y < dst->h; 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);
802 \brief Rotates a 32 bit surface in increments of 90 degrees.
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)
809 \param src Source surface to rotate.
810 \param numClockwiseTurns Number of clockwise 90 degree turns to apply to the source.
812 \returns The new, rotated surface; or NULL for surfaces with incorrect input format.
814 SDL_Surface* rotateSurface90Degrees(SDL_Surface* src, int numClockwiseTurns)
816 int row, col, newWidth, newHeight;
817 int bpp, src_ipr, dst_ipr;
822 /* Has to be a valid surface pointer and only 32-bit surfaces (for now) */
823 if (!src || src->format->BitsPerPixel != 32) { return NULL; }
825 /* normalize numClockwiseTurns */
826 while(numClockwiseTurns < 0) { numClockwiseTurns += 4; }
827 numClockwiseTurns = (numClockwiseTurns % 4);
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,
841 if (SDL_MUSTLOCK(dst)) {
842 SDL_LockSurface(dst);
844 if (SDL_MUSTLOCK(dst)) {
845 SDL_LockSurface(dst);
848 /* Calculate int-per-row */
849 bpp = src->format->BitsPerPixel / 8;
850 src_ipr = src->pitch / bpp;
851 dst_ipr = dst->pitch / bpp;
853 switch(numClockwiseTurns) {
854 case 0: /* Make a copy of the surface */
856 /* Unfortunately SDL_BlitSurface cannot be used to make a copy of the surface
857 since it does not preserve alpha. */
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));
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);
877 /* rotate clockwise */
878 case 1: /* rotated 90 degrees clockwise */
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) {
894 case 2: /* rotated 180 degrees clockwise */
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) {
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) {
924 if (SDL_MUSTLOCK(src)) {
925 SDL_UnlockSurface(src);
927 if (SDL_MUSTLOCK(dst)) {
928 SDL_UnlockSurface(dst);
936 \brief Internal target surface sizing function for rotozooms with trig result return.
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.
949 static void _rotozoomSurfaceSizeTrig(int width, int height, double angle, double zoomx, double zoomy,
950 int *dstwidth, int *dstheight,
951 double *canglezoom, double *sanglezoom)
953 double x, y, cx, cy, sx, sy;
955 int dstwidthhalf, dstheighthalf;
958 * Determine destination width and height by rotating a centered source box
960 radangle = angle * (M_PI / 180.0);
961 *sanglezoom = sin(radangle);
962 *canglezoom = cos(radangle);
963 *sanglezoom *= zoomx;
964 *canglezoom *= zoomx;
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;
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;
981 \brief Returns the size of the resulting target surface for a rotozoomSurfaceXY() call.
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.
991 void rotozoomSurfaceSizeXY(int width, int height, double angle, double zoomx, double zoomy, int *dstwidth, int *dstheight)
993 double dummy_sanglezoom, dummy_canglezoom;
995 _rotozoomSurfaceSizeTrig(width, height, angle, zoomx, zoomy, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom);
999 \brief Returns the size of the resulting target surface for a rotozoomSurface() call.
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.
1008 void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight)
1010 double dummy_sanglezoom, dummy_canglezoom;
1012 _rotozoomSurfaceSizeTrig(width, height, angle, zoom, zoom, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom);
1016 \brief Rotates and zooms a surface and optional anti-aliasing.
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.
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.
1028 \return The new rotozoomed surface.
1030 SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth)
1032 return rotozoomSurfaceXY(src, angle, zoom, zoom, smooth);
1036 \brief Rotates and zooms a surface with different horizontal and vertival scaling factors and optional anti-aliasing.
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.
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.
1049 \return The new rotozoomed surface.
1051 SDL_Surface *rotozoomSurfaceXY(SDL_Surface * src, double angle, double zoomx, double zoomy, int smooth)
1053 SDL_Surface *rz_src;
1054 SDL_Surface *rz_dst;
1056 double sanglezoom, canglezoom, sanglezoominv, canglezoominv;
1057 int dstwidthhalf, dstwidth, dstheighthalf, dstheight;
1059 int i, src_converted;
1062 Uint32 colorkey = 0;
1063 int colorKeyAvailable = 0;
1071 if (src->flags & SDL_SRCCOLORKEY)
1073 colorkey = _colorkey(src);
1074 SDL_GetRGB(colorkey, src->format, &r, &g, &b);
1075 colorKeyAvailable = 1;
1078 * Determine if source surface is 32bit or 8bit
1080 is32bit = (src->format->BitsPerPixel == 32);
1081 if ((is32bit) || (src->format->BitsPerPixel == 8)) {
1083 * Use source surface 'as is'
1089 * New source surface is 32bit with a defined RGBA ordering
1092 SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1093 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1094 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
1096 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff
1099 if(colorKeyAvailable)
1100 SDL_SetColorKey(src, 0, 0);
1102 SDL_BlitSurface(src, NULL, rz_src, NULL);
1104 if(colorKeyAvailable)
1105 SDL_SetColorKey(src, SDL_SRCCOLORKEY, colorkey);
1111 * Sanity check zoom factor
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);
1122 * Check if we have a rotozoom or just a zoom
1124 if (fabs(angle) > VALUE_LIMIT) {
1127 * Angle!=0: full rotozoom
1130 * -----------------------
1133 /* Determine target size */
1134 _rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, zoomx, zoomy, &dstwidth, &dstheight, &canglezoom, &sanglezoom);
1137 * Calculate target factors from sin/cos and zoom
1139 sanglezoominv = sanglezoom;
1140 canglezoominv = canglezoom;
1141 sanglezoominv *= zoominv;
1142 canglezoominv *= zoominv;
1144 /* Calculate half size */
1145 dstwidthhalf = dstwidth / 2;
1146 dstheighthalf = dstheight / 2;
1149 * Alloc space to completely contain the rotated surface
1154 * Target surface is 32bit with source RGBA/ABGR ordering
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);
1162 * Target surface is 8bit
1164 rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1171 /* Adjust for guard rows */
1172 rz_dst->h = dstheight;
1174 if (colorKeyAvailable == 1){
1175 colorkey = SDL_MapRGB(rz_dst->format, r, g, b);
1177 SDL_FillRect(rz_dst, NULL, colorkey );
1181 * Lock source surface
1183 if (SDL_MUSTLOCK(rz_src)) {
1184 SDL_LockSurface(rz_src);
1188 * Check which kind of surface we have
1192 * Call the 32bit transformation routine to do the rotation (using alpha)
1194 _transformSurfaceRGBA(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
1195 (int) (sanglezoominv), (int) (canglezoominv),
1199 * Turn on source-alpha support
1201 SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
1202 SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1205 * Copy palette and colorkey info
1207 for (i = 0; i < rz_src->format->palette->ncolors; i++) {
1208 rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1210 rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1212 * Call the 8bit transformation routine to do the rotation
1214 transformSurfaceY(rz_src, rz_dst, dstwidthhalf, dstheighthalf,
1215 (int) (sanglezoominv), (int) (canglezoominv),
1217 SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1220 * Unlock source surface
1222 if (SDL_MUSTLOCK(rz_src)) {
1223 SDL_UnlockSurface(rz_src);
1229 * Angle=0: Just a zoom
1232 * --------------------
1236 * Calculate target size
1238 zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight);
1241 * Alloc space to completely contain the zoomed surface
1246 * Target surface is 32bit with source RGBA/ABGR ordering
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);
1254 * Target surface is 8bit
1256 rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1263 /* Adjust for guard rows */
1264 rz_dst->h = dstheight;
1266 if (colorKeyAvailable == 1){
1267 colorkey = SDL_MapRGB(rz_dst->format, r, g, b);
1269 SDL_FillRect(rz_dst, NULL, colorkey );
1273 * Lock source surface
1275 if (SDL_MUSTLOCK(rz_src)) {
1276 SDL_LockSurface(rz_src);
1280 * Check which kind of surface we have
1284 * Call the 32bit transformation routine to do the zooming (using alpha)
1286 _zoomSurfaceRGBA(rz_src, rz_dst, flipx, flipy, smooth);
1289 * Turn on source-alpha support
1291 SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
1292 SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1295 * Copy palette and colorkey info
1297 for (i = 0; i < rz_src->format->palette->ncolors; i++) {
1298 rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1300 rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1303 * Call the 8bit transformation routine to do the zooming
1305 _zoomSurfaceY(rz_src, rz_dst, flipx, flipy);
1306 SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1310 * Unlock source surface
1312 if (SDL_MUSTLOCK(rz_src)) {
1313 SDL_UnlockSurface(rz_src);
1318 * Cleanup temp surface
1320 if (src_converted) {
1321 SDL_FreeSurface(rz_src);
1325 * Return destination surface
1331 \brief Calculates the size of the target surface for a zoomSurface() call.
1333 The minimum size of the target surface is 1. The input factors can be positive or negative.
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.
1342 void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight)
1345 * Make zoom factors positive
1348 flipx = (zoomx<0.0);
1349 if (flipx) zoomx = -zoomx;
1350 flipy = (zoomy<0.0);
1351 if (flipy) zoomy = -zoomy;
1354 * Sanity check zoom factors
1356 if (zoomx < VALUE_LIMIT) {
1357 zoomx = VALUE_LIMIT;
1359 if (zoomy < VALUE_LIMIT) {
1360 zoomy = VALUE_LIMIT;
1364 * Calculate target size
1366 *dstwidth = (int) floor(((double) width * zoomx) + 0.5);
1367 *dstheight = (int) floor(((double) height * zoomy) + 0.5);
1368 if (*dstwidth < 1) {
1371 if (*dstheight < 1) {
1377 \brief Zoom a surface by independent horizontal and vertical factors with optional smoothing.
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.
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.
1390 \return The new, zoomed surface.
1392 SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth)
1394 SDL_Surface *rz_src;
1395 SDL_Surface *rz_dst;
1396 int dstwidth, dstheight;
1398 int i, src_converted;
1408 * Determine if source surface is 32bit or 8bit
1410 is32bit = (src->format->BitsPerPixel == 32);
1411 if ((is32bit) || (src->format->BitsPerPixel == 8)) {
1413 * Use source surface 'as is'
1419 * New source surface is 32bit with a defined RGBA ordering
1422 SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1423 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1424 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
1426 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff
1429 if (rz_src == NULL) {
1432 SDL_BlitSurface(src, NULL, rz_src, NULL);
1437 flipx = (zoomx<0.0);
1438 if (flipx) zoomx = -zoomx;
1439 flipy = (zoomy<0.0);
1440 if (flipy) zoomy = -zoomy;
1442 /* Get size if target */
1443 zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight);
1446 * Alloc space to completely contain the zoomed surface
1451 * Target surface is 32bit with source RGBA/ABGR ordering
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);
1459 * Target surface is 8bit
1461 rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1465 if (rz_dst == NULL) {
1467 * Cleanup temp surface
1469 if (src_converted) {
1470 SDL_FreeSurface(rz_src);
1475 /* Adjust for guard rows */
1476 rz_dst->h = dstheight;
1479 * Lock source surface
1481 if (SDL_MUSTLOCK(rz_src)) {
1482 SDL_LockSurface(rz_src);
1486 * Check which kind of surface we have
1490 * Call the 32bit transformation routine to do the zooming (using alpha)
1492 _zoomSurfaceRGBA(rz_src, rz_dst, flipx, flipy, smooth);
1494 * Turn on source-alpha support
1496 SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
1499 * Copy palette and colorkey info
1501 for (i = 0; i < rz_src->format->palette->ncolors; i++) {
1502 rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1504 rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1506 * Call the 8bit transformation routine to do the zooming
1508 _zoomSurfaceY(rz_src, rz_dst, flipx, flipy);
1509 SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1512 * Unlock source surface
1514 if (SDL_MUSTLOCK(rz_src)) {
1515 SDL_UnlockSurface(rz_src);
1519 * Cleanup temp surface
1521 if (src_converted) {
1522 SDL_FreeSurface(rz_src);
1526 * Return destination surface
1532 \brief Shrink a surface by an integer ratio using averaging.
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.
1541 \param src The surface to shrink.
1542 \param factorx The horizontal shrinking ratio.
1543 \param factory The vertical shrinking ratio.
1545 \return The new, shrunken surface.
1548 SDL_Surface *shrinkSurface(SDL_Surface *src, int factorx, int factory)
1551 SDL_Surface *rz_src;
1552 SDL_Surface *rz_dst = NULL;
1553 int dstwidth, dstheight;
1555 int i, src_converted;
1566 * Determine if source surface is 32bit or 8bit
1568 is32bit = (src->format->BitsPerPixel == 32);
1569 if ((is32bit) || (src->format->BitsPerPixel == 8)) {
1571 * Use source surface 'as is'
1577 * New source surface is 32bit with a defined RGBA ordering
1579 rz_src = SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
1580 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
1581 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
1583 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff
1588 goto exitShrinkSurface;
1591 SDL_BlitSurface(src, NULL, rz_src, NULL);
1599 if (SDL_MUSTLOCK(rz_src)) {
1600 if (SDL_LockSurface(rz_src) < 0) {
1602 goto exitShrinkSurface;
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--; }
1613 * Alloc space to completely contain the shrunken surface
1614 * (with added guard rows)
1618 * Target surface is 32bit with source RGBA/ABGR ordering
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);
1626 * Target surface is 8bit
1628 rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
1632 if (rz_dst == NULL) {
1634 goto exitShrinkSurface;
1637 /* Adjust for guard rows */
1638 rz_dst->h = dstheight;
1641 * Check which kind of surface we have
1645 * Call the 32bit transformation routine to do the shrinking (using alpha)
1647 result = _shrinkSurfaceRGBA(rz_src, rz_dst, factorx, factory);
1648 if ((result!=0) || (rz_dst==NULL)) {
1650 goto exitShrinkSurface;
1654 * Turn on source-alpha support
1656 result = SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
1659 goto exitShrinkSurface;
1663 * Copy palette and colorkey info
1665 for (i = 0; i < rz_src->format->palette->ncolors; i++) {
1666 rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
1668 rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
1670 * Call the 8bit transformation routine to do the shrinking
1672 result = _shrinkSurfaceY(rz_src, rz_dst, factorx, factory);
1675 goto exitShrinkSurface;
1679 * Set colorkey on target
1681 result = SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, _colorkey(rz_src));
1684 goto exitShrinkSurface;
1691 * Unlock source surface
1693 if (SDL_MUSTLOCK(rz_src)) {
1694 SDL_UnlockSurface(rz_src);
1698 * Cleanup temp surface
1700 if (src_converted==1) {
1701 SDL_FreeSurface(rz_src);
1705 /* Check error state; maybe need to cleanup destination */
1708 SDL_FreeSurface(rz_dst);
1714 * Return destination surface