src/render/software/SDL_rotate.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 01 Jun 2012 20:31:50 -0400
changeset 6323 d3fa10b2d16e
parent 6320 6077a1310907
child 6389 43a190ad60a7
permissions -rw-r--r--
Fixed SDL_config.h build include path
     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, sw, sh;
   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     sw = src->w - 1;
   287     sh = src->h - 1;
   288     pc = (tColorY*) dst->pixels;
   289     gap = dst->pitch - dst->w;
   290     /*
   291     * Clear surface to colorkey
   292     */
   293     SDL_memset(pc, (int)(_colorkey(src) & 0xff), dst->pitch * dst->h);
   294     /*
   295     * Iterate through destination surface
   296     */
   297     for (y = 0; y < dst->h; y++) {
   298         dy = cy - y;
   299         sdx = (ax + (isin * dy)) + xd;
   300         sdy = (ay - (icos * dy)) + yd;
   301         for (x = 0; x < dst->w; x++) {
   302             dx = (short) (sdx >> 16);
   303             dy = (short) (sdy >> 16);
   304             if (flipx) dx = (src->w-1)-dx;
   305             if (flipy) dy = (src->h-1)-dy;
   306             if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
   307                 sp = (tColorY *) (src->pixels);
   308                 sp += (src->pitch * dy + dx);
   309                 *pc = *sp;
   310             }
   311             sdx += icos;
   312             sdy += isin;
   313             pc++;
   314         }
   315         pc += gap;
   316     }
   317 }
   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 *_rotateSurface(SDL_Surface * src, double angle, int centerx, int centery, int smooth, int flipx, int flipy, int dstwidth, int dstheight, double cangle, double sangle)
   346 {
   347     SDL_Surface *rz_src;
   348     SDL_Surface *rz_dst;
   349     int is32bit;
   350     int i, src_converted;
   351     Uint8 r,g,b;
   352     Uint32 colorkey = 0;
   353     int colorKeyAvailable = 0;
   354     double sangleinv, cangleinv;
   355 
   356     /*
   357     * Sanity check
   358     */
   359     if (src == NULL)
   360         return (NULL);
   361 
   362     if (src->flags & SDL_TRUE/*SDL_SRCCOLORKEY*/)
   363     {
   364         colorkey = _colorkey(src);
   365         SDL_GetRGB(colorkey, src->format, &r, &g, &b);
   366         colorKeyAvailable = 1;
   367     }
   368     /*
   369     * Determine if source surface is 32bit or 8bit
   370     */
   371     is32bit = (src->format->BitsPerPixel == 32);
   372     if ((is32bit) || (src->format->BitsPerPixel == 8)) {
   373         /*
   374         * Use source surface 'as is'
   375         */
   376         rz_src = src;
   377         src_converted = 0;
   378     } else {
   379         /*
   380         * New source surface is 32bit with a defined RGBA ordering
   381         */
   382         rz_src =
   383             SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
   384 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
   385             0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
   386 #else
   387             0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
   388 #endif
   389             );
   390         if(colorKeyAvailable)
   391             SDL_SetColorKey(src, 0, 0);
   392 
   393         SDL_BlitSurface(src, NULL, rz_src, NULL);
   394 
   395         if(colorKeyAvailable)
   396             SDL_SetColorKey(src, SDL_TRUE /*SDL_SRCCOLORKEY*/, colorkey);
   397         src_converted = 1;
   398         is32bit = 1;
   399     }
   400 
   401 
   402     /* Determine target size */
   403     //_rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, &dstwidth, &dstheight, &cangle, &sangle);
   404 
   405     /*
   406     * Calculate target factors from sin/cos and zoom
   407     */
   408     sangleinv = sangle*65536.0;
   409     cangleinv = cangle*65536.0;
   410 
   411     /*
   412     * Alloc space to completely contain the rotated surface
   413     */
   414     rz_dst = NULL;
   415     if (is32bit) {
   416         /*
   417         * Target surface is 32bit with source RGBA/ABGR ordering
   418         */
   419         rz_dst =
   420             SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 32,
   421             rz_src->format->Rmask, rz_src->format->Gmask,
   422             rz_src->format->Bmask, rz_src->format->Amask);
   423     } else {
   424         /*
   425         * Target surface is 8bit
   426         */
   427         rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
   428     }
   429 
   430     /* Check target */
   431     if (rz_dst == NULL)
   432         return NULL;
   433 
   434     /* Adjust for guard rows */
   435     rz_dst->h = dstheight;
   436 
   437     if (colorKeyAvailable == 1){
   438         colorkey = SDL_MapRGB(rz_dst->format, r, g, b);
   439 
   440         SDL_FillRect(rz_dst, NULL, colorkey );
   441     }
   442 
   443     /*
   444     * Lock source surface
   445     */
   446     if (SDL_MUSTLOCK(rz_src)) {
   447         SDL_LockSurface(rz_src);
   448     }
   449 
   450     /*
   451     * Check which kind of surface we have
   452     */
   453     if (is32bit) {
   454         /*
   455         * Call the 32bit transformation routine to do the rotation (using alpha)
   456         */
   457         _transformSurfaceRGBA(rz_src, rz_dst, centerx, centery,
   458             (int) (sangleinv), (int) (cangleinv),
   459             flipx, flipy,
   460             smooth);
   461         /*
   462         * Turn on source-alpha support
   463         */
   464         //SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
   465         SDL_SetColorKey(rz_dst, /*SDL_SRCCOLORKEY*/ SDL_TRUE | SDL_RLEACCEL, _colorkey(rz_src));
   466     } else {
   467         /*
   468         * Copy palette and colorkey info
   469         */
   470         for (i = 0; i < rz_src->format->palette->ncolors; i++) {
   471             rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
   472         }
   473         rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
   474         /*
   475         * Call the 8bit transformation routine to do the rotation
   476         */
   477         transformSurfaceY(rz_src, rz_dst, centerx, centery,
   478             (int) (sangleinv), (int) (cangleinv),
   479             flipx, flipy);
   480         SDL_SetColorKey(rz_dst, /*SDL_SRCCOLORKEY*/ SDL_TRUE | SDL_RLEACCEL, _colorkey(rz_src));
   481     }
   482     /*
   483     * Unlock source surface
   484     */
   485     if (SDL_MUSTLOCK(rz_src)) {
   486         SDL_UnlockSurface(rz_src);
   487     }
   488 
   489     /*
   490     * Cleanup temp surface
   491     */
   492     if (src_converted) {
   493         SDL_FreeSurface(rz_src);
   494     }
   495 
   496     /*
   497     * Return destination surface
   498     */
   499     return (rz_dst);
   500 }
   501