src/render/software/SDL_rotate.c
changeset 6320 6077a1310907
child 6323 d3fa10b2d16e
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/render/software/SDL_rotate.c	Fri Jun 01 19:51:08 2012 -0300
     1.3 @@ -0,0 +1,500 @@
     1.4 +/*
     1.5 +
     1.6 +SDL_rotate.c: rotates 32bit or 8bit surfaces
     1.7 +
     1.8 +Shamelessly stolen from SDL_gfx by Andreas Schiffler. Original copyright follows:
     1.9 +
    1.10 +Copyright (C) 2001-2011  Andreas Schiffler
    1.11 +
    1.12 +This software is provided 'as-is', without any express or implied
    1.13 +warranty. In no event will the authors be held liable for any damages
    1.14 +arising from the use of this software.
    1.15 +
    1.16 +Permission is granted to anyone to use this software for any purpose,
    1.17 +including commercial applications, and to alter it and redistribute it
    1.18 +freely, subject to the following restrictions:
    1.19 +
    1.20 +   1. The origin of this software must not be misrepresented; you must not
    1.21 +   claim that you wrote the original software. If you use this software
    1.22 +   in a product, an acknowledgment in the product documentation would be
    1.23 +   appreciated but is not required.
    1.24 +
    1.25 +   2. Altered source versions must be plainly marked as such, and must not be
    1.26 +   misrepresented as being the original software.
    1.27 +
    1.28 +   3. This notice may not be removed or altered from any source
    1.29 +   distribution.
    1.30 +
    1.31 +Andreas Schiffler -- aschiffler at ferzkopp dot net
    1.32 +
    1.33 +*/
    1.34 +
    1.35 +#ifdef WIN32
    1.36 +#include <windows.h>
    1.37 +#endif
    1.38 +
    1.39 +#include <stdlib.h>
    1.40 +#include <string.h>
    1.41 +
    1.42 +#include "SDL.h"
    1.43 +#include "SDL_rotate.h"
    1.44 +
    1.45 +/* ---- Internally used structures */
    1.46 +
    1.47 +/*!
    1.48 +\brief A 32 bit RGBA pixel.
    1.49 +*/
    1.50 +typedef struct tColorRGBA {
    1.51 +    Uint8 r;
    1.52 +    Uint8 g;
    1.53 +    Uint8 b;
    1.54 +    Uint8 a;
    1.55 +} tColorRGBA;
    1.56 +
    1.57 +/*!
    1.58 +\brief A 8bit Y/palette pixel.
    1.59 +*/
    1.60 +typedef struct tColorY {
    1.61 +    Uint8 y;
    1.62 +} tColorY;
    1.63 +
    1.64 +/*!
    1.65 +\brief Returns maximum of two numbers a and b.
    1.66 +*/
    1.67 +#define MAX(a,b)    (((a) > (b)) ? (a) : (b))
    1.68 +
    1.69 +/*!
    1.70 +\brief Number of guard rows added to destination surfaces.
    1.71 +
    1.72 +This is a simple but effective workaround for observed issues.
    1.73 +These rows allocate extra memory and are then hidden from the surface.
    1.74 +Rows are added to the end of destination surfaces when they are allocated.
    1.75 +This catches any potential overflows which seem to happen with
    1.76 +just the right src image dimensions and scale/rotation and can lead
    1.77 +to a situation where the program can segfault.
    1.78 +*/
    1.79 +#define GUARD_ROWS (2)
    1.80 +
    1.81 +/*!
    1.82 +\brief Lower limit of absolute zoom factor or rotation degrees.
    1.83 +*/
    1.84 +#define VALUE_LIMIT 0.001
    1.85 +
    1.86 +/*!
    1.87 +\brief Returns colorkey info for a surface
    1.88 +*/
    1.89 +Uint32 _colorkey(SDL_Surface *src)
    1.90 +{
    1.91 +    Uint32 key = 0;
    1.92 +    SDL_GetColorKey(src, &key);
    1.93 +    return key;
    1.94 +}
    1.95 +
    1.96 +
    1.97 +/*!
    1.98 +\brief Internal target surface sizing function for rotations with trig result return.
    1.99 +
   1.100 +\param width The source surface width.
   1.101 +\param height The source surface height.
   1.102 +\param angle The angle to rotate in degrees.
   1.103 +\param dstwidth The calculated width of the destination surface.
   1.104 +\param dstheight The calculated height of the destination surface.
   1.105 +\param cangle The sine of the angle
   1.106 +\param sangle The cosine of the angle
   1.107 +
   1.108 +*/
   1.109 +void _rotozoomSurfaceSizeTrig(int width, int height, double angle,
   1.110 +                              int *dstwidth, int *dstheight,
   1.111 +                              double *cangle, double *sangle)
   1.112 +{
   1.113 +    double x, y, cx, cy, sx, sy;
   1.114 +    double radangle;
   1.115 +    int dstwidthhalf, dstheighthalf;
   1.116 +
   1.117 +    /*
   1.118 +    * Determine destination width and height by rotating a centered source box
   1.119 +    */
   1.120 +    radangle = angle * (M_PI / 180.0);
   1.121 +    *sangle = SDL_sin(radangle);
   1.122 +    *cangle = SDL_cos(radangle);
   1.123 +    x = (double)(width / 2);
   1.124 +    y = (double)(height / 2);
   1.125 +    cx = *cangle * x;
   1.126 +    cy = *cangle * y;
   1.127 +    sx = *sangle * x;
   1.128 +    sy = *sangle * y;
   1.129 +
   1.130 +    dstwidthhalf = MAX((int)
   1.131 +        SDL_ceil(MAX(MAX(MAX(SDL_fabs(cx + sy), SDL_fabs(cx - sy)), SDL_fabs(-cx + sy)), SDL_fabs(-cx - sy))), 1);
   1.132 +    dstheighthalf = MAX((int)
   1.133 +        SDL_ceil(MAX(MAX(MAX(SDL_fabs(sx + cy), SDL_fabs(sx - cy)), SDL_fabs(-sx + cy)), SDL_fabs(-sx - cy))), 1);
   1.134 +    *dstwidth = 2 * dstwidthhalf;
   1.135 +    *dstheight = 2 * dstheighthalf;
   1.136 +}
   1.137 +
   1.138 +
   1.139 +/*!
   1.140 +\brief Internal 32 bit rotozoomer with optional anti-aliasing.
   1.141 +
   1.142 +Rotates and zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
   1.143 +parameters by scanning the destination surface and applying optionally anti-aliasing
   1.144 +by bilinear interpolation.
   1.145 +Assumes src and dst surfaces are of 32 bit depth.
   1.146 +Assumes dst surface was allocated with the correct dimensions.
   1.147 +
   1.148 +\param src Source surface.
   1.149 +\param dst Destination surface.
   1.150 +\param cx Horizontal center coordinate.
   1.151 +\param cy Vertical center coordinate.
   1.152 +\param isin Integer version of sine of angle.
   1.153 +\param icos Integer version of cosine of angle.
   1.154 +\param flipx Flag indicating horizontal mirroring should be applied.
   1.155 +\param flipy Flag indicating vertical mirroring should be applied.
   1.156 +\param smooth Flag indicating anti-aliasing should be used.
   1.157 +*/
   1.158 +void _transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy, int smooth)
   1.159 +{
   1.160 +    int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh;
   1.161 +    tColorRGBA c00, c01, c10, c11, cswap;
   1.162 +    tColorRGBA *pc, *sp;
   1.163 +    int gap;
   1.164 +
   1.165 +    /*
   1.166 +    * Variable setup
   1.167 +    */
   1.168 +    xd = ((src->w - dst->w) << 15);
   1.169 +    yd = ((src->h - dst->h) << 15);
   1.170 +    ax = (cx << 16) - (icos * cx);
   1.171 +    ay = (cy << 16) - (isin * cx);
   1.172 +    sw = src->w - 1;
   1.173 +    sh = src->h - 1;
   1.174 +    pc = (tColorRGBA*) dst->pixels;
   1.175 +    gap = dst->pitch - dst->w * 4;
   1.176 +
   1.177 +    /*
   1.178 +    * Switch between interpolating and non-interpolating code
   1.179 +    */
   1.180 +    if (smooth) {
   1.181 +        for (y = 0; y < dst->h; y++) {
   1.182 +            dy = cy - y;
   1.183 +            sdx = (ax + (isin * dy)) + xd;
   1.184 +            sdy = (ay - (icos * dy)) + yd;
   1.185 +            for (x = 0; x < dst->w; x++) {
   1.186 +                dx = (sdx >> 16);
   1.187 +                dy = (sdy >> 16);
   1.188 +                if (flipx) dx = sw - dx;
   1.189 +                if (flipy) dy = sh - dy;
   1.190 +                if ((dx > -1) && (dy > -1) && (dx < (src->w-1)) && (dy < (src->h-1))) {
   1.191 +                    sp = (tColorRGBA *)src->pixels;;
   1.192 +                    sp += ((src->pitch/4) * dy);
   1.193 +                    sp += dx;
   1.194 +                    c00 = *sp;
   1.195 +                    sp += 1;
   1.196 +                    c01 = *sp;
   1.197 +                    sp += (src->pitch/4);
   1.198 +                    c11 = *sp;
   1.199 +                    sp -= 1;
   1.200 +                    c10 = *sp;
   1.201 +                    if (flipx) {
   1.202 +                        cswap = c00; c00=c01; c01=cswap;
   1.203 +                        cswap = c10; c10=c11; c11=cswap;
   1.204 +                    }
   1.205 +                    if (flipy) {
   1.206 +                        cswap = c00; c00=c10; c10=cswap;
   1.207 +                        cswap = c01; c01=c11; c11=cswap;
   1.208 +                    }
   1.209 +                    /*
   1.210 +                    * Interpolate colors
   1.211 +                    */
   1.212 +                    ex = (sdx & 0xffff);
   1.213 +                    ey = (sdy & 0xffff);
   1.214 +                    t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
   1.215 +                    t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
   1.216 +                    pc->r = (((t2 - t1) * ey) >> 16) + t1;
   1.217 +                    t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
   1.218 +                    t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
   1.219 +                    pc->g = (((t2 - t1) * ey) >> 16) + t1;
   1.220 +                    t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
   1.221 +                    t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
   1.222 +                    pc->b = (((t2 - t1) * ey) >> 16) + t1;
   1.223 +                    t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
   1.224 +                    t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
   1.225 +                    pc->a = (((t2 - t1) * ey) >> 16) + t1;
   1.226 +                }
   1.227 +                sdx += icos;
   1.228 +                sdy += isin;
   1.229 +                pc++;
   1.230 +            }
   1.231 +            pc = (tColorRGBA *) ((Uint8 *) pc + gap);
   1.232 +        }
   1.233 +    } else {
   1.234 +        for (y = 0; y < dst->h; y++) {
   1.235 +            dy = cy - y;
   1.236 +            sdx = (ax + (isin * dy)) + xd;
   1.237 +            sdy = (ay - (icos * dy)) + yd;
   1.238 +            for (x = 0; x < dst->w; x++) {
   1.239 +                dx = (short) (sdx >> 16);
   1.240 +                dy = (short) (sdy >> 16);
   1.241 +                if (flipx) dx = (src->w-1)-dx;
   1.242 +                if (flipy) dy = (src->h-1)-dy;
   1.243 +                if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
   1.244 +                    sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
   1.245 +                    sp += dx;
   1.246 +                    *pc = *sp;
   1.247 +                }
   1.248 +                sdx += icos;
   1.249 +                sdy += isin;
   1.250 +                pc++;
   1.251 +            }
   1.252 +            pc = (tColorRGBA *) ((Uint8 *) pc + gap);
   1.253 +        }
   1.254 +    }
   1.255 +}
   1.256 +
   1.257 +/*!
   1.258 +
   1.259 +\brief Rotates and zooms 8 bit palette/Y 'src' surface to 'dst' surface without smoothing.
   1.260 +
   1.261 +Rotates and zooms 8 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
   1.262 +parameters by scanning the destination surface.
   1.263 +Assumes src and dst surfaces are of 8 bit depth.
   1.264 +Assumes dst surface was allocated with the correct dimensions.
   1.265 +
   1.266 +\param src Source surface.
   1.267 +\param dst Destination surface.
   1.268 +\param cx Horizontal center coordinate.
   1.269 +\param cy Vertical center coordinate.
   1.270 +\param isin Integer version of sine of angle.
   1.271 +\param icos Integer version of cosine of angle.
   1.272 +\param flipx Flag indicating horizontal mirroring should be applied.
   1.273 +\param flipy Flag indicating vertical mirroring should be applied.
   1.274 +*/
   1.275 +void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy)
   1.276 +{
   1.277 +    int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh;
   1.278 +    tColorY *pc, *sp;
   1.279 +    int gap;
   1.280 +
   1.281 +    /*
   1.282 +    * Variable setup
   1.283 +    */
   1.284 +    xd = ((src->w - dst->w) << 15);
   1.285 +    yd = ((src->h - dst->h) << 15);
   1.286 +    ax = (cx << 16) - (icos * cx);
   1.287 +    ay = (cy << 16) - (isin * cx);
   1.288 +    sw = src->w - 1;
   1.289 +    sh = src->h - 1;
   1.290 +    pc = (tColorY*) dst->pixels;
   1.291 +    gap = dst->pitch - dst->w;
   1.292 +    /*
   1.293 +    * Clear surface to colorkey
   1.294 +    */
   1.295 +    SDL_memset(pc, (int)(_colorkey(src) & 0xff), dst->pitch * dst->h);
   1.296 +    /*
   1.297 +    * Iterate through destination surface
   1.298 +    */
   1.299 +    for (y = 0; y < dst->h; y++) {
   1.300 +        dy = cy - y;
   1.301 +        sdx = (ax + (isin * dy)) + xd;
   1.302 +        sdy = (ay - (icos * dy)) + yd;
   1.303 +        for (x = 0; x < dst->w; x++) {
   1.304 +            dx = (short) (sdx >> 16);
   1.305 +            dy = (short) (sdy >> 16);
   1.306 +            if (flipx) dx = (src->w-1)-dx;
   1.307 +            if (flipy) dy = (src->h-1)-dy;
   1.308 +            if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
   1.309 +                sp = (tColorY *) (src->pixels);
   1.310 +                sp += (src->pitch * dy + dx);
   1.311 +                *pc = *sp;
   1.312 +            }
   1.313 +            sdx += icos;
   1.314 +            sdy += isin;
   1.315 +            pc++;
   1.316 +        }
   1.317 +        pc += gap;
   1.318 +    }
   1.319 +}
   1.320 +
   1.321 +
   1.322 +
   1.323 +
   1.324 +/*!
   1.325 +\brief Rotates and zooms a surface with different horizontal and vertival scaling factors and optional anti-aliasing.
   1.326 +
   1.327 +Rotates a 32bit or 8bit 'src' surface to newly created 'dst' surface.
   1.328 +'angle' is the rotation in degrees, 'centerx' and 'centery' the rotation center. If 'smooth' is set
   1.329 +then the destination 32bit surface is anti-aliased. If the surface is not 8bit
   1.330 +or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
   1.331 +
   1.332 +\param src The surface to rotozoom.
   1.333 +\param angle The angle to rotate in degrees.
   1.334 +\param centerx The horizontal coordinate of the center of rotation
   1.335 +\param zoomy The vertical coordinate of the center of rotation
   1.336 +\param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
   1.337 +\param flipx Set to 1 to flip the image horizontally
   1.338 +\param flipy Set to 1 to flip the image vertically
   1.339 +\param dstwidth The destination surface width
   1.340 +\param dstheight The destination surface height
   1.341 +\param cangle The angle cosine
   1.342 +\param sangle The angle sine
   1.343 +\return The new rotated surface.
   1.344 +
   1.345 +*/
   1.346 +
   1.347 +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)
   1.348 +{
   1.349 +    SDL_Surface *rz_src;
   1.350 +    SDL_Surface *rz_dst;
   1.351 +    int is32bit;
   1.352 +    int i, src_converted;
   1.353 +    Uint8 r,g,b;
   1.354 +    Uint32 colorkey = 0;
   1.355 +    int colorKeyAvailable = 0;
   1.356 +    double sangleinv, cangleinv;
   1.357 +
   1.358 +    /*
   1.359 +    * Sanity check
   1.360 +    */
   1.361 +    if (src == NULL)
   1.362 +        return (NULL);
   1.363 +
   1.364 +    if (src->flags & SDL_TRUE/*SDL_SRCCOLORKEY*/)
   1.365 +    {
   1.366 +        colorkey = _colorkey(src);
   1.367 +        SDL_GetRGB(colorkey, src->format, &r, &g, &b);
   1.368 +        colorKeyAvailable = 1;
   1.369 +    }
   1.370 +    /*
   1.371 +    * Determine if source surface is 32bit or 8bit
   1.372 +    */
   1.373 +    is32bit = (src->format->BitsPerPixel == 32);
   1.374 +    if ((is32bit) || (src->format->BitsPerPixel == 8)) {
   1.375 +        /*
   1.376 +        * Use source surface 'as is'
   1.377 +        */
   1.378 +        rz_src = src;
   1.379 +        src_converted = 0;
   1.380 +    } else {
   1.381 +        /*
   1.382 +        * New source surface is 32bit with a defined RGBA ordering
   1.383 +        */
   1.384 +        rz_src =
   1.385 +            SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
   1.386 +#if SDL_BYTEORDER == SDL_LIL_ENDIAN
   1.387 +            0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
   1.388 +#else
   1.389 +            0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
   1.390 +#endif
   1.391 +            );
   1.392 +        if(colorKeyAvailable)
   1.393 +            SDL_SetColorKey(src, 0, 0);
   1.394 +
   1.395 +        SDL_BlitSurface(src, NULL, rz_src, NULL);
   1.396 +
   1.397 +        if(colorKeyAvailable)
   1.398 +            SDL_SetColorKey(src, SDL_TRUE /*SDL_SRCCOLORKEY*/, colorkey);
   1.399 +        src_converted = 1;
   1.400 +        is32bit = 1;
   1.401 +    }
   1.402 +
   1.403 +
   1.404 +    /* Determine target size */
   1.405 +    //_rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, &dstwidth, &dstheight, &cangle, &sangle);
   1.406 +
   1.407 +    /*
   1.408 +    * Calculate target factors from sin/cos and zoom
   1.409 +    */
   1.410 +    sangleinv = sangle*65536.0;
   1.411 +    cangleinv = cangle*65536.0;
   1.412 +
   1.413 +    /*
   1.414 +    * Alloc space to completely contain the rotated surface
   1.415 +    */
   1.416 +    rz_dst = NULL;
   1.417 +    if (is32bit) {
   1.418 +        /*
   1.419 +        * Target surface is 32bit with source RGBA/ABGR ordering
   1.420 +        */
   1.421 +        rz_dst =
   1.422 +            SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 32,
   1.423 +            rz_src->format->Rmask, rz_src->format->Gmask,
   1.424 +            rz_src->format->Bmask, rz_src->format->Amask);
   1.425 +    } else {
   1.426 +        /*
   1.427 +        * Target surface is 8bit
   1.428 +        */
   1.429 +        rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
   1.430 +    }
   1.431 +
   1.432 +    /* Check target */
   1.433 +    if (rz_dst == NULL)
   1.434 +        return NULL;
   1.435 +
   1.436 +    /* Adjust for guard rows */
   1.437 +    rz_dst->h = dstheight;
   1.438 +
   1.439 +    if (colorKeyAvailable == 1){
   1.440 +        colorkey = SDL_MapRGB(rz_dst->format, r, g, b);
   1.441 +
   1.442 +        SDL_FillRect(rz_dst, NULL, colorkey );
   1.443 +    }
   1.444 +
   1.445 +    /*
   1.446 +    * Lock source surface
   1.447 +    */
   1.448 +    if (SDL_MUSTLOCK(rz_src)) {
   1.449 +        SDL_LockSurface(rz_src);
   1.450 +    }
   1.451 +
   1.452 +    /*
   1.453 +    * Check which kind of surface we have
   1.454 +    */
   1.455 +    if (is32bit) {
   1.456 +        /*
   1.457 +        * Call the 32bit transformation routine to do the rotation (using alpha)
   1.458 +        */
   1.459 +        _transformSurfaceRGBA(rz_src, rz_dst, centerx, centery,
   1.460 +            (int) (sangleinv), (int) (cangleinv),
   1.461 +            flipx, flipy,
   1.462 +            smooth);
   1.463 +        /*
   1.464 +        * Turn on source-alpha support
   1.465 +        */
   1.466 +        //SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
   1.467 +        SDL_SetColorKey(rz_dst, /*SDL_SRCCOLORKEY*/ SDL_TRUE | SDL_RLEACCEL, _colorkey(rz_src));
   1.468 +    } else {
   1.469 +        /*
   1.470 +        * Copy palette and colorkey info
   1.471 +        */
   1.472 +        for (i = 0; i < rz_src->format->palette->ncolors; i++) {
   1.473 +            rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
   1.474 +        }
   1.475 +        rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
   1.476 +        /*
   1.477 +        * Call the 8bit transformation routine to do the rotation
   1.478 +        */
   1.479 +        transformSurfaceY(rz_src, rz_dst, centerx, centery,
   1.480 +            (int) (sangleinv), (int) (cangleinv),
   1.481 +            flipx, flipy);
   1.482 +        SDL_SetColorKey(rz_dst, /*SDL_SRCCOLORKEY*/ SDL_TRUE | SDL_RLEACCEL, _colorkey(rz_src));
   1.483 +    }
   1.484 +    /*
   1.485 +    * Unlock source surface
   1.486 +    */
   1.487 +    if (SDL_MUSTLOCK(rz_src)) {
   1.488 +        SDL_UnlockSurface(rz_src);
   1.489 +    }
   1.490 +
   1.491 +    /*
   1.492 +    * Cleanup temp surface
   1.493 +    */
   1.494 +    if (src_converted) {
   1.495 +        SDL_FreeSurface(rz_src);
   1.496 +    }
   1.497 +
   1.498 +    /*
   1.499 +    * Return destination surface
   1.500 +    */
   1.501 +    return (rz_dst);
   1.502 +}
   1.503 +