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