src/render/software/SDL_rotate.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 20 Oct 2013 21:34:38 -0700
changeset 7859 af63b63e7aac
parent 7828 1451063c8ecd
child 8093 b43765095a6f
permissions -rw-r--r--
Prevent conflicts when linking both SDL2 and SDL2_gfx
     1 /*
     2 
     3 SDL_rotate.c: rotates 32bit or 8bit surfaces
     4 
     5 Shamelessly stolen from SDL_gfx by Andreas Schiffler. Original copyright follows:
     6 
     7 Copyright (C) 2001-2011  Andreas Schiffler
     8 
     9 This software is provided 'as-is', without any express or implied
    10 warranty. In no event will the authors be held liable for any damages
    11 arising from the use of this software.
    12 
    13 Permission is granted to anyone to use this software for any purpose,
    14 including commercial applications, and to alter it and redistribute it
    15 freely, subject to the following restrictions:
    16 
    17    1. The origin of this software must not be misrepresented; you must not
    18    claim that you wrote the original software. If you use this software
    19    in a product, an acknowledgment in the product documentation would be
    20    appreciated but is not required.
    21 
    22    2. Altered source versions must be plainly marked as such, and must not be
    23    misrepresented as being the original software.
    24 
    25    3. This notice may not be removed or altered from any source
    26    distribution.
    27 
    28 Andreas Schiffler -- aschiffler at ferzkopp dot net
    29 
    30 */
    31 #include "SDL_config.h"
    32 
    33 #if defined(__WIN32__)
    34 #include "../../core/windows/SDL_windows.h"
    35 #endif
    36 
    37 #include <stdlib.h>
    38 #include <string.h>
    39 
    40 #include "SDL.h"
    41 #include "SDL_rotate.h"
    42 
    43 /* ---- Internally used structures */
    44 
    45 /* !
    46 \brief A 32 bit RGBA pixel.
    47 */
    48 typedef struct tColorRGBA {
    49     Uint8 r;
    50     Uint8 g;
    51     Uint8 b;
    52     Uint8 a;
    53 } tColorRGBA;
    54 
    55 /* !
    56 \brief A 8bit Y/palette pixel.
    57 */
    58 typedef struct tColorY {
    59     Uint8 y;
    60 } tColorY;
    61 
    62 /* !
    63 \brief Returns maximum of two numbers a and b.
    64 */
    65 #define MAX(a,b)    (((a) > (b)) ? (a) : (b))
    66 
    67 /* !
    68 \brief Number of guard rows added to destination surfaces.
    69 
    70 This is a simple but effective workaround for observed issues.
    71 These rows allocate extra memory and are then hidden from the surface.
    72 Rows are added to the end of destination surfaces when they are allocated.
    73 This catches any potential overflows which seem to happen with
    74 just the right src image dimensions and scale/rotation and can lead
    75 to a situation where the program can segfault.
    76 */
    77 #define GUARD_ROWS (2)
    78 
    79 /* !
    80 \brief Lower limit of absolute zoom factor or rotation degrees.
    81 */
    82 #define VALUE_LIMIT 0.001
    83 
    84 /* !
    85 \brief Returns colorkey info for a surface
    86 */
    87 static Uint32
    88 _colorkey(SDL_Surface *src)
    89 {
    90     Uint32 key = 0;
    91     SDL_GetColorKey(src, &key);
    92     return key;
    93 }
    94 
    95 
    96 /* !
    97 \brief Internal target surface sizing function for rotations with trig result return.
    98 
    99 \param width The source surface width.
   100 \param height The source surface height.
   101 \param angle The angle to rotate in degrees.
   102 \param dstwidth The calculated width of the destination surface.
   103 \param dstheight The calculated height of the destination surface.
   104 \param cangle The sine of the angle
   105 \param sangle The cosine of the angle
   106 
   107 */
   108 void
   109 SDLgfx_rotozoomSurfaceSizeTrig(int width, int height, double angle,
   110                                int *dstwidth, int *dstheight,
   111                                double *cangle, double *sangle)
   112 {
   113     double x, y, cx, cy, sx, sy;
   114     double radangle;
   115     int dstwidthhalf, dstheighthalf;
   116 
   117     /*
   118     * Determine destination width and height by rotating a centered source box
   119     */
   120     radangle = angle * (M_PI / 180.0);
   121     *sangle = SDL_sin(radangle);
   122     *cangle = SDL_cos(radangle);
   123     x = (double)(width / 2);
   124     y = (double)(height / 2);
   125     cx = *cangle * x;
   126     cy = *cangle * y;
   127     sx = *sangle * x;
   128     sy = *sangle * y;
   129 
   130     dstwidthhalf = MAX((int)
   131         SDL_ceil(MAX(MAX(MAX(SDL_fabs(cx + sy), SDL_fabs(cx - sy)), SDL_fabs(-cx + sy)), SDL_fabs(-cx - sy))), 1);
   132     dstheighthalf = MAX((int)
   133         SDL_ceil(MAX(MAX(MAX(SDL_fabs(sx + cy), SDL_fabs(sx - cy)), SDL_fabs(-sx + cy)), SDL_fabs(-sx - cy))), 1);
   134     *dstwidth = 2 * dstwidthhalf;
   135     *dstheight = 2 * dstheighthalf;
   136 }
   137 
   138 
   139 /* !
   140 \brief Internal 32 bit rotozoomer with optional anti-aliasing.
   141 
   142 Rotates and zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
   143 parameters by scanning the destination surface and applying optionally anti-aliasing
   144 by bilinear interpolation.
   145 Assumes src and dst surfaces are of 32 bit depth.
   146 Assumes dst surface was allocated with the correct dimensions.
   147 
   148 \param src Source surface.
   149 \param dst Destination surface.
   150 \param cx Horizontal center coordinate.
   151 \param cy Vertical center coordinate.
   152 \param isin Integer version of sine of angle.
   153 \param icos Integer version of cosine of angle.
   154 \param flipx Flag indicating horizontal mirroring should be applied.
   155 \param flipy Flag indicating vertical mirroring should be applied.
   156 \param smooth Flag indicating anti-aliasing should be used.
   157 */
   158 static void
   159 _transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy, int smooth)
   160 {
   161     int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh;
   162     tColorRGBA c00, c01, c10, c11, cswap;
   163     tColorRGBA *pc, *sp;
   164     int gap;
   165 
   166     /*
   167     * Variable setup
   168     */
   169     xd = ((src->w - dst->w) << 15);
   170     yd = ((src->h - dst->h) << 15);
   171     ax = (cx << 16) - (icos * cx);
   172     ay = (cy << 16) - (isin * cx);
   173     sw = src->w - 1;
   174     sh = src->h - 1;
   175     pc = (tColorRGBA*) dst->pixels;
   176     gap = dst->pitch - dst->w * 4;
   177 
   178     /*
   179     * Switch between interpolating and non-interpolating code
   180     */
   181     if (smooth) {
   182         for (y = 0; y < dst->h; y++) {
   183             dy = cy - y;
   184             sdx = (ax + (isin * dy)) + xd;
   185             sdy = (ay - (icos * dy)) + yd;
   186             for (x = 0; x < dst->w; x++) {
   187                 dx = (sdx >> 16);
   188                 dy = (sdy >> 16);
   189                 if (flipx) dx = sw - dx;
   190                 if (flipy) dy = sh - dy;
   191                 if ((dx > -1) && (dy > -1) && (dx < (src->w-1)) && (dy < (src->h-1))) {
   192                     sp = (tColorRGBA *)src->pixels;;
   193                     sp += ((src->pitch/4) * dy);
   194                     sp += dx;
   195                     c00 = *sp;
   196                     sp += 1;
   197                     c01 = *sp;
   198                     sp += (src->pitch/4);
   199                     c11 = *sp;
   200                     sp -= 1;
   201                     c10 = *sp;
   202                     if (flipx) {
   203                         cswap = c00; c00=c01; c01=cswap;
   204                         cswap = c10; c10=c11; c11=cswap;
   205                     }
   206                     if (flipy) {
   207                         cswap = c00; c00=c10; c10=cswap;
   208                         cswap = c01; c01=c11; c11=cswap;
   209                     }
   210                     /*
   211                     * Interpolate colors
   212                     */
   213                     ex = (sdx & 0xffff);
   214                     ey = (sdy & 0xffff);
   215                     t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
   216                     t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
   217                     pc->r = (((t2 - t1) * ey) >> 16) + t1;
   218                     t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
   219                     t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
   220                     pc->g = (((t2 - t1) * ey) >> 16) + t1;
   221                     t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
   222                     t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
   223                     pc->b = (((t2 - t1) * ey) >> 16) + t1;
   224                     t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
   225                     t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
   226                     pc->a = (((t2 - t1) * ey) >> 16) + t1;
   227                 }
   228                 sdx += icos;
   229                 sdy += isin;
   230                 pc++;
   231             }
   232             pc = (tColorRGBA *) ((Uint8 *) pc + gap);
   233         }
   234     } else {
   235         for (y = 0; y < dst->h; y++) {
   236             dy = cy - y;
   237             sdx = (ax + (isin * dy)) + xd;
   238             sdy = (ay - (icos * dy)) + yd;
   239             for (x = 0; x < dst->w; x++) {
   240                 dx = (short) (sdx >> 16);
   241                 dy = (short) (sdy >> 16);
   242                 if (flipx) dx = (src->w-1)-dx;
   243                 if (flipy) dy = (src->h-1)-dy;
   244                 if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
   245                     sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
   246                     sp += dx;
   247                     *pc = *sp;
   248                 }
   249                 sdx += icos;
   250                 sdy += isin;
   251                 pc++;
   252             }
   253             pc = (tColorRGBA *) ((Uint8 *) pc + gap);
   254         }
   255     }
   256 }
   257 
   258 /* !
   259 
   260 \brief Rotates and zooms 8 bit palette/Y 'src' surface to 'dst' surface without smoothing.
   261 
   262 Rotates and zooms 8 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
   263 parameters by scanning the destination surface.
   264 Assumes src and dst surfaces are of 8 bit depth.
   265 Assumes dst surface was allocated with the correct dimensions.
   266 
   267 \param src Source surface.
   268 \param dst Destination surface.
   269 \param cx Horizontal center coordinate.
   270 \param cy Vertical center coordinate.
   271 \param isin Integer version of sine of angle.
   272 \param icos Integer version of cosine of angle.
   273 \param flipx Flag indicating horizontal mirroring should be applied.
   274 \param flipy Flag indicating vertical mirroring should be applied.
   275 */
   276 static void
   277 transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy)
   278 {
   279     int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay;
   280     tColorY *pc, *sp;
   281     int gap;
   282 
   283     /*
   284     * Variable setup
   285     */
   286     xd = ((src->w - dst->w) << 15);
   287     yd = ((src->h - dst->h) << 15);
   288     ax = (cx << 16) - (icos * cx);
   289     ay = (cy << 16) - (isin * cx);
   290     pc = (tColorY*) dst->pixels;
   291     gap = dst->pitch - dst->w;
   292     /*
   293     * Clear surface to colorkey
   294     */
   295     SDL_memset(pc, (int)(_colorkey(src) & 0xff), dst->pitch * dst->h);
   296     /*
   297     * Iterate through destination surface
   298     */
   299     for (y = 0; y < dst->h; y++) {
   300         dy = cy - y;
   301         sdx = (ax + (isin * dy)) + xd;
   302         sdy = (ay - (icos * dy)) + yd;
   303         for (x = 0; x < dst->w; x++) {
   304             dx = (short) (sdx >> 16);
   305             dy = (short) (sdy >> 16);
   306             if (flipx) dx = (src->w-1)-dx;
   307             if (flipy) dy = (src->h-1)-dy;
   308             if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
   309                 sp = (tColorY *) (src->pixels);
   310                 sp += (src->pitch * dy + dx);
   311                 *pc = *sp;
   312             }
   313             sdx += icos;
   314             sdy += isin;
   315             pc++;
   316         }
   317         pc += gap;
   318     }
   319 }
   320 
   321 
   322 /* !
   323 \brief Rotates and zooms a surface with different horizontal and vertival scaling factors and optional anti-aliasing.
   324 
   325 Rotates a 32bit or 8bit 'src' surface to newly created 'dst' surface.
   326 'angle' is the rotation in degrees, 'centerx' and 'centery' the rotation center. If 'smooth' is set
   327 then the destination 32bit surface is anti-aliased. If the surface is not 8bit
   328 or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
   329 
   330 \param src The surface to rotozoom.
   331 \param angle The angle to rotate in degrees.
   332 \param centerx The horizontal coordinate of the center of rotation
   333 \param zoomy The vertical coordinate of the center of rotation
   334 \param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
   335 \param flipx Set to 1 to flip the image horizontally
   336 \param flipy Set to 1 to flip the image vertically
   337 \param dstwidth The destination surface width
   338 \param dstheight The destination surface height
   339 \param cangle The angle cosine
   340 \param sangle The angle sine
   341 \return The new rotated surface.
   342 
   343 */
   344 
   345 SDL_Surface *
   346 SDLgfx_rotateSurface(SDL_Surface * src, double angle, int centerx, int centery, int smooth, int flipx, int flipy, int dstwidth, int dstheight, double cangle, double sangle)
   347 {
   348     SDL_Surface *rz_src;
   349     SDL_Surface *rz_dst;
   350     int is32bit;
   351     int i, src_converted;
   352     Uint8 r,g,b;
   353     Uint32 colorkey = 0;
   354     int colorKeyAvailable = 0;
   355     double sangleinv, cangleinv;
   356 
   357     /*
   358     * Sanity check
   359     */
   360     if (src == NULL)
   361         return (NULL);
   362 
   363     if (src->flags & SDL_TRUE/* SDL_SRCCOLORKEY */)
   364     {
   365         colorkey = _colorkey(src);
   366         SDL_GetRGB(colorkey, src->format, &r, &g, &b);
   367         colorKeyAvailable = 1;
   368     }
   369     /*
   370     * Determine if source surface is 32bit or 8bit
   371     */
   372     is32bit = (src->format->BitsPerPixel == 32);
   373     if ((is32bit) || (src->format->BitsPerPixel == 8)) {
   374         /*
   375         * Use source surface 'as is'
   376         */
   377         rz_src = src;
   378         src_converted = 0;
   379     } else {
   380         /*
   381         * New source surface is 32bit with a defined RGBA ordering
   382         */
   383         rz_src =
   384             SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
   385 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   386             0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
   387 #else
   388             0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
   389 #endif
   390             );
   391         if(colorKeyAvailable)
   392             SDL_SetColorKey(src, 0, 0);
   393 
   394         SDL_BlitSurface(src, NULL, rz_src, NULL);
   395 
   396         if(colorKeyAvailable)
   397             SDL_SetColorKey(src, SDL_TRUE /* SDL_SRCCOLORKEY */, colorkey);
   398         src_converted = 1;
   399         is32bit = 1;
   400     }
   401 
   402 
   403     /* Determine target size */
   404     /* _rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, &dstwidth, &dstheight, &cangle, &sangle); */
   405 
   406     /*
   407     * Calculate target factors from sin/cos and zoom
   408     */
   409     sangleinv = sangle*65536.0;
   410     cangleinv = cangle*65536.0;
   411 
   412     /*
   413     * Alloc space to completely contain the rotated surface
   414     */
   415     rz_dst = NULL;
   416     if (is32bit) {
   417         /*
   418         * Target surface is 32bit with source RGBA/ABGR ordering
   419         */
   420         rz_dst =
   421             SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 32,
   422             rz_src->format->Rmask, rz_src->format->Gmask,
   423             rz_src->format->Bmask, rz_src->format->Amask);
   424     } else {
   425         /*
   426         * Target surface is 8bit
   427         */
   428         rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
   429     }
   430 
   431     /* Check target */
   432     if (rz_dst == NULL)
   433         return NULL;
   434 
   435     /* Adjust for guard rows */
   436     rz_dst->h = dstheight;
   437 
   438     if (colorKeyAvailable == 1){
   439         colorkey = SDL_MapRGB(rz_dst->format, r, g, b);
   440 
   441         SDL_FillRect(rz_dst, NULL, colorkey );
   442     }
   443 
   444     /*
   445     * Lock source surface
   446     */
   447     if (SDL_MUSTLOCK(rz_src)) {
   448         SDL_LockSurface(rz_src);
   449     }
   450 
   451     /*
   452     * Check which kind of surface we have
   453     */
   454     if (is32bit) {
   455         /*
   456         * Call the 32bit transformation routine to do the rotation (using alpha)
   457         */
   458         _transformSurfaceRGBA(rz_src, rz_dst, centerx, centery,
   459             (int) (sangleinv), (int) (cangleinv),
   460             flipx, flipy,
   461             smooth);
   462         /*
   463         * Turn on source-alpha support
   464         */
   465         /* SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255); */
   466         SDL_SetColorKey(rz_dst, /* SDL_SRCCOLORKEY */ SDL_TRUE | SDL_RLEACCEL, _colorkey(rz_src));
   467     } else {
   468         /*
   469         * Copy palette and colorkey info
   470         */
   471         for (i = 0; i < rz_src->format->palette->ncolors; i++) {
   472             rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
   473         }
   474         rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
   475         /*
   476         * Call the 8bit transformation routine to do the rotation
   477         */
   478         transformSurfaceY(rz_src, rz_dst, centerx, centery,
   479             (int) (sangleinv), (int) (cangleinv),
   480             flipx, flipy);
   481         SDL_SetColorKey(rz_dst, /* SDL_SRCCOLORKEY */ SDL_TRUE | SDL_RLEACCEL, _colorkey(rz_src));
   482     }
   483     /*
   484     * Unlock source surface
   485     */
   486     if (SDL_MUSTLOCK(rz_src)) {
   487         SDL_UnlockSurface(rz_src);
   488     }
   489 
   490     /*
   491     * Cleanup temp surface
   492     */
   493     if (src_converted) {
   494         SDL_FreeSurface(rz_src);
   495     }
   496 
   497     /*
   498     * Return destination surface
   499     */
   500     return (rz_dst);
   501 }