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