src/render/software/SDL_rotate.c
author Sam Lantinga
Tue, 15 Nov 2016 01:12:27 -0800
changeset 10612 6b2307dbec54
parent 10609 d702ecbd8ba7
child 10615 f85f1e6d857c
permissions -rw-r--r--
Fixed bug 3359 - Software renderer does incorrect blending with SDL_RenderCopyEx

Simon Hug

The software renderer produces incorrect results when blending textures at an angle with certain blend modes. It seems that there were some edge cases that weren't considered when the SW_RenderCopyEx function was last changed. Or another bug possibly covered up the problem. (More on that in another bug report.)

Most of the issues come from the fact that the rotating function sets a black colorkey. This is problematic because black is most likely appearing in the surface and the final blit will ignore these pixels. Unless a colorkey is already set (the software renderer currently never sets one), it's very hard to find a free color. Of course it could scan over the whole image until one is found, but that seems inefficient.

The following blend modes have issues when drawn at an angle.

NONE: The black pixels get ignored, making them essentially transparent. This breaks the 'dstRGBA = srcRGBA' definition of the NONE blend mode.

MOD: Again, the black pixels get ignored. This also breaks the 'dstRGB = dstRGB * srcRGB' definition of the MOD blend mode, where black pixels would make the destination black as well. A white colorkey will work though, with some preparations.

BLEND: There are some issues when blending a texture with a translucent RGBA target texture. I - uh - forgot what the problem here exactly is.

This patch fixes the issues mentioned above. It mainly changes the code so it tries to do things without the colorkey and removes the automatic format conversion part from the SDLgfx_rotateSurface function. Getting the format right is something the caller has to do now and the required code has been added to the SW_RenderCopyEx function.

There's a small change to the SW_CreateTexture function. RLE encoding a surface with an alpha mask can be a lossy process. Depending on how the user uses the RGBA channels, this may be undesired. The change that surfaces with an alpha mask don't get encoded makes the software renderer consistent with the other renderers.

The SW_RenderCopyEx function now does these steps: Lock the source surface if necessary. Create a clone of the source by using the pixel buffer directly. Check the format and set a flag if a conversion is necessary. Check if scaling or cropping is necessary and set the flag for that as well. Check if color and alpha modulation has to be done before the rotate. Check if the source is an opaque surface. If not, it creates a mask surface that is necessary for the NONE blend mode. If any of the flags were set, a new surface is created and the source will be converted, scaled, cropped, and modulated. The rest of the function stays somewhat the same. The mask also needs to be rotated of course and then there is the NONE blend mode...

It's surprisingly hard to get the pixel from a rotated surface to the destination buffer without affecting the pixel outside the rotated area. I found a way to do this with three blits which is pretty hard on the performance. Perhaps someone has an idea how to do this faster?

As mentioned above, the SDLgfx_rotateSurface now only takes 8-bit paletted or 32-bit with alpha mask surfaces. It additionally sets the new surfaces up for the MOD blend mode.

I shortly tested the 8-bit path of SDLgfx_rotateSurface and it seemed to work so far. This path is not used by the software renderer anyway.
gabomdq@6320
     1
/*
gabomdq@6320
     2
gabomdq@6320
     3
SDL_rotate.c: rotates 32bit or 8bit surfaces
gabomdq@6320
     4
gabomdq@6320
     5
Shamelessly stolen from SDL_gfx by Andreas Schiffler. Original copyright follows:
gabomdq@6320
     6
gabomdq@6320
     7
Copyright (C) 2001-2011  Andreas Schiffler
gabomdq@6320
     8
gabomdq@6320
     9
This software is provided 'as-is', without any express or implied
gabomdq@6320
    10
warranty. In no event will the authors be held liable for any damages
gabomdq@6320
    11
arising from the use of this software.
gabomdq@6320
    12
gabomdq@6320
    13
Permission is granted to anyone to use this software for any purpose,
gabomdq@6320
    14
including commercial applications, and to alter it and redistribute it
gabomdq@6320
    15
freely, subject to the following restrictions:
gabomdq@6320
    16
gabomdq@6320
    17
   1. The origin of this software must not be misrepresented; you must not
gabomdq@6320
    18
   claim that you wrote the original software. If you use this software
gabomdq@6320
    19
   in a product, an acknowledgment in the product documentation would be
gabomdq@6320
    20
   appreciated but is not required.
gabomdq@6320
    21
gabomdq@6320
    22
   2. Altered source versions must be plainly marked as such, and must not be
gabomdq@6320
    23
   misrepresented as being the original software.
gabomdq@6320
    24
gabomdq@6320
    25
   3. This notice may not be removed or altered from any source
gabomdq@6320
    26
   distribution.
gabomdq@6320
    27
gabomdq@6320
    28
Andreas Schiffler -- aschiffler at ferzkopp dot net
gabomdq@6320
    29
gabomdq@6320
    30
*/
icculus@8093
    31
#include "../../SDL_internal.h"
gabomdq@6320
    32
slouken@7828
    33
#if defined(__WIN32__)
slouken@7828
    34
#include "../../core/windows/SDL_windows.h"
gabomdq@6320
    35
#endif
gabomdq@6320
    36
gabomdq@6320
    37
#include <stdlib.h>
gabomdq@6320
    38
#include <string.h>
gabomdq@6320
    39
gabomdq@6320
    40
#include "SDL.h"
gabomdq@6320
    41
#include "SDL_rotate.h"
gabomdq@6320
    42
gabomdq@6320
    43
/* ---- Internally used structures */
gabomdq@6320
    44
gabomdq@7678
    45
/* !
gabomdq@6320
    46
\brief A 32 bit RGBA pixel.
gabomdq@6320
    47
*/
gabomdq@6320
    48
typedef struct tColorRGBA {
gabomdq@6320
    49
    Uint8 r;
gabomdq@6320
    50
    Uint8 g;
gabomdq@6320
    51
    Uint8 b;
gabomdq@6320
    52
    Uint8 a;
gabomdq@6320
    53
} tColorRGBA;
gabomdq@6320
    54
gabomdq@7678
    55
/* !
gabomdq@6320
    56
\brief A 8bit Y/palette pixel.
gabomdq@6320
    57
*/
gabomdq@6320
    58
typedef struct tColorY {
gabomdq@6320
    59
    Uint8 y;
gabomdq@6320
    60
} tColorY;
gabomdq@6320
    61
gabomdq@7678
    62
/* !
gabomdq@6320
    63
\brief Returns maximum of two numbers a and b.
gabomdq@6320
    64
*/
gabomdq@6320
    65
#define MAX(a,b)    (((a) > (b)) ? (a) : (b))
gabomdq@6320
    66
gabomdq@7678
    67
/* !
gabomdq@6320
    68
\brief Number of guard rows added to destination surfaces.
gabomdq@6320
    69
gabomdq@6320
    70
This is a simple but effective workaround for observed issues.
gabomdq@6320
    71
These rows allocate extra memory and are then hidden from the surface.
gabomdq@6320
    72
Rows are added to the end of destination surfaces when they are allocated.
gabomdq@6320
    73
This catches any potential overflows which seem to happen with
gabomdq@6320
    74
just the right src image dimensions and scale/rotation and can lead
gabomdq@6320
    75
to a situation where the program can segfault.
gabomdq@6320
    76
*/
gabomdq@6320
    77
#define GUARD_ROWS (2)
gabomdq@6320
    78
gabomdq@7678
    79
/* !
gabomdq@6320
    80
\brief Lower limit of absolute zoom factor or rotation degrees.
gabomdq@6320
    81
*/
slouken@10609
    82
/* #define VALUE_LIMIT 0.001 */
gabomdq@6320
    83
gabomdq@7678
    84
/* !
gabomdq@6320
    85
\brief Returns colorkey info for a surface
gabomdq@6320
    86
*/
slouken@7859
    87
static Uint32
slouken@7859
    88
_colorkey(SDL_Surface *src)
gabomdq@6320
    89
{
gabomdq@6320
    90
    Uint32 key = 0;
gabomdq@6320
    91
    SDL_GetColorKey(src, &key);
gabomdq@6320
    92
    return key;
gabomdq@6320
    93
}
gabomdq@6320
    94
gabomdq@6320
    95
gabomdq@7678
    96
/* !
gabomdq@6320
    97
\brief Internal target surface sizing function for rotations with trig result return.
gabomdq@6320
    98
gabomdq@6320
    99
\param width The source surface width.
gabomdq@6320
   100
\param height The source surface height.
gabomdq@6320
   101
\param angle The angle to rotate in degrees.
gabomdq@6320
   102
\param dstwidth The calculated width of the destination surface.
gabomdq@6320
   103
\param dstheight The calculated height of the destination surface.
gabomdq@6320
   104
\param cangle The sine of the angle
gabomdq@6320
   105
\param sangle The cosine of the angle
gabomdq@6320
   106
gabomdq@6320
   107
*/
slouken@7859
   108
void
slouken@7859
   109
SDLgfx_rotozoomSurfaceSizeTrig(int width, int height, double angle,
slouken@7859
   110
                               int *dstwidth, int *dstheight,
slouken@7859
   111
                               double *cangle, double *sangle)
gabomdq@6320
   112
{
slouken@10489
   113
    /* The trig code below gets the wrong size (due to FP inaccuracy?) when angle is a multiple of 90 degrees */
slouken@10489
   114
    int angle90 = (int)(angle/90);
slouken@10489
   115
    if(angle90 == angle/90) { /* if the angle is a multiple of 90 degrees */
slouken@10489
   116
        angle90 %= 4;
slouken@10489
   117
        if(angle90 < 0) angle90 += 4; /* 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg */
slouken@10489
   118
        if(angle90 & 1) {
slouken@10489
   119
            *dstwidth  = height;
slouken@10489
   120
            *dstheight = width;
slouken@10489
   121
            *cangle = 0;
slouken@10489
   122
            *sangle = angle90 == 1 ? -1 : 1; /* reversed because our rotations are clockwise */
slouken@10489
   123
        } else {
slouken@10489
   124
            *dstwidth  = width;
slouken@10489
   125
            *dstheight = height;
slouken@10489
   126
            *cangle = angle90 == 0 ? 1 : -1;
slouken@10489
   127
            *sangle = 0;
slouken@10489
   128
        }
slouken@10489
   129
    } else {
slouken@10489
   130
        double x, y, cx, cy, sx, sy;
slouken@10489
   131
        double radangle;
slouken@10489
   132
        int dstwidthhalf, dstheighthalf;
slouken@10489
   133
        /*
slouken@10489
   134
        * Determine destination width and height by rotating a centered source box
slouken@10489
   135
        */
slouken@10489
   136
        radangle = angle * (M_PI / -180.0); /* reverse the angle because our rotations are clockwise */
slouken@10489
   137
        *sangle = SDL_sin(radangle);
slouken@10489
   138
        *cangle = SDL_cos(radangle);
slouken@10489
   139
        x = (double)(width / 2);
slouken@10489
   140
        y = (double)(height / 2);
slouken@10489
   141
        cx = *cangle * x;
slouken@10489
   142
        cy = *cangle * y;
slouken@10489
   143
        sx = *sangle * x;
slouken@10489
   144
        sy = *sangle * y;
slouken@10612
   145
slouken@10489
   146
        dstwidthhalf = MAX((int)
slouken@10489
   147
            SDL_ceil(MAX(MAX(MAX(SDL_fabs(cx + sy), SDL_fabs(cx - sy)), SDL_fabs(-cx + sy)), SDL_fabs(-cx - sy))), 1);
slouken@10489
   148
        dstheighthalf = MAX((int)
slouken@10489
   149
            SDL_ceil(MAX(MAX(MAX(SDL_fabs(sx + cy), SDL_fabs(sx - cy)), SDL_fabs(-sx + cy)), SDL_fabs(-sx - cy))), 1);
slouken@10489
   150
        *dstwidth = 2 * dstwidthhalf;
slouken@10489
   151
        *dstheight = 2 * dstheighthalf;
slouken@10489
   152
    }
gabomdq@6320
   153
}
gabomdq@6320
   154
slouken@10489
   155
/* Computes source pointer X/Y increments for a rotation that's a multiple of 90 degrees. */
slouken@10489
   156
static void
slouken@10489
   157
computeSourceIncrements90(SDL_Surface * src, int bpp, int angle, int flipx, int flipy,
slouken@10489
   158
                          int *sincx, int *sincy, int *signx, int *signy)
slouken@10489
   159
{
slouken@10489
   160
    int pitch = flipy ? -src->pitch : src->pitch;
slouken@10489
   161
    if (flipx) {
slouken@10489
   162
        bpp = -bpp;
slouken@10489
   163
    }
slouken@10489
   164
    switch (angle) { /* 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg */
slouken@10489
   165
    case 0: *sincx = bpp; *sincy = pitch - src->w * *sincx; *signx = *signy = 1; break;
slouken@10489
   166
    case 1: *sincx = -pitch; *sincy = bpp - *sincx * src->h; *signx = 1; *signy = -1; break;
slouken@10489
   167
    case 2: *sincx = -bpp; *sincy = -src->w * *sincx - pitch; *signx = *signy = -1; break;
slouken@10489
   168
    case 3: default: *sincx = pitch; *sincy = -*sincx * src->h - bpp; *signx = -1; *signy = 1; break;
slouken@10489
   169
    }
slouken@10489
   170
    if (flipx) {
slouken@10489
   171
        *signx = -*signx;
slouken@10489
   172
    }
slouken@10489
   173
    if (flipy) {
slouken@10489
   174
        *signy = -*signy;
slouken@10489
   175
    }
slouken@10489
   176
}
slouken@10489
   177
slouken@10489
   178
/* Performs a relatively fast rotation/flip when the angle is a multiple of 90 degrees. */
slouken@10489
   179
#define TRANSFORM_SURFACE_90(pixelType) \
slouken@10489
   180
    int dy, dincy = dst->pitch - dst->w*sizeof(pixelType), sincx, sincy, signx, signy;                      \
slouken@10489
   181
    Uint8 *sp = (Uint8*)src->pixels, *dp = (Uint8*)dst->pixels, *de;                                        \
slouken@10489
   182
                                                                                                            \
slouken@10489
   183
    computeSourceIncrements90(src, sizeof(pixelType), angle, flipx, flipy, &sincx, &sincy, &signx, &signy); \
slouken@10489
   184
    if (signx < 0) sp += (src->w-1)*sizeof(pixelType);                                                      \
slouken@10489
   185
    if (signy < 0) sp += (src->h-1)*src->pitch;                                                             \
slouken@10489
   186
                                                                                                            \
slouken@10489
   187
    for (dy = 0; dy < dst->h; sp += sincy, dp += dincy, dy++) {                                             \
slouken@10489
   188
        if (sincx == sizeof(pixelType)) { /* if advancing src and dest equally, use memcpy */               \
slouken@10489
   189
            SDL_memcpy(dp, sp, dst->w*sizeof(pixelType));                                                   \
slouken@10489
   190
            sp += dst->w*sizeof(pixelType);                                                                 \
slouken@10489
   191
            dp += dst->w*sizeof(pixelType);                                                                 \
slouken@10489
   192
        } else {                                                                                            \
slouken@10489
   193
            for (de = dp + dst->w*sizeof(pixelType); dp != de; sp += sincx, dp += sizeof(pixelType)) {      \
slouken@10489
   194
                *(pixelType*)dp = *(pixelType*)sp;                                                          \
slouken@10489
   195
            }                                                                                               \
slouken@10489
   196
        }                                                                                                   \
slouken@10489
   197
    }
slouken@10489
   198
slouken@10489
   199
static void
slouken@10489
   200
transformSurfaceRGBA90(SDL_Surface * src, SDL_Surface * dst, int angle, int flipx, int flipy)
slouken@10489
   201
{
slouken@10489
   202
    TRANSFORM_SURFACE_90(tColorRGBA);
slouken@10489
   203
}
slouken@10489
   204
slouken@10489
   205
static void
slouken@10489
   206
transformSurfaceY90(SDL_Surface * src, SDL_Surface * dst, int angle, int flipx, int flipy)
slouken@10489
   207
{
slouken@10489
   208
    TRANSFORM_SURFACE_90(tColorY);
slouken@10489
   209
}
slouken@10489
   210
slouken@10489
   211
#undef TRANSFORM_SURFACE_90
gabomdq@6320
   212
gabomdq@7678
   213
/* !
gabomdq@6320
   214
\brief Internal 32 bit rotozoomer with optional anti-aliasing.
gabomdq@6320
   215
gabomdq@6320
   216
Rotates and zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
gabomdq@6320
   217
parameters by scanning the destination surface and applying optionally anti-aliasing
gabomdq@6320
   218
by bilinear interpolation.
gabomdq@6320
   219
Assumes src and dst surfaces are of 32 bit depth.
gabomdq@6320
   220
Assumes dst surface was allocated with the correct dimensions.
gabomdq@6320
   221
gabomdq@6320
   222
\param src Source surface.
gabomdq@6320
   223
\param dst Destination surface.
gabomdq@6320
   224
\param cx Horizontal center coordinate.
gabomdq@6320
   225
\param cy Vertical center coordinate.
gabomdq@6320
   226
\param isin Integer version of sine of angle.
gabomdq@6320
   227
\param icos Integer version of cosine of angle.
gabomdq@6320
   228
\param flipx Flag indicating horizontal mirroring should be applied.
gabomdq@6320
   229
\param flipy Flag indicating vertical mirroring should be applied.
gabomdq@6320
   230
\param smooth Flag indicating anti-aliasing should be used.
gabomdq@6320
   231
*/
slouken@7859
   232
static void
slouken@7859
   233
_transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy, int smooth)
gabomdq@6320
   234
{
gabomdq@6320
   235
    int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh;
gabomdq@6320
   236
    tColorRGBA c00, c01, c10, c11, cswap;
gabomdq@6320
   237
    tColorRGBA *pc, *sp;
gabomdq@6320
   238
    int gap;
gabomdq@6320
   239
gabomdq@6320
   240
    /*
gabomdq@6320
   241
    * Variable setup
gabomdq@6320
   242
    */
gabomdq@6320
   243
    xd = ((src->w - dst->w) << 15);
gabomdq@6320
   244
    yd = ((src->h - dst->h) << 15);
gabomdq@6320
   245
    ax = (cx << 16) - (icos * cx);
gabomdq@6320
   246
    ay = (cy << 16) - (isin * cx);
gabomdq@6320
   247
    sw = src->w - 1;
gabomdq@6320
   248
    sh = src->h - 1;
gabomdq@6320
   249
    pc = (tColorRGBA*) dst->pixels;
gabomdq@6320
   250
    gap = dst->pitch - dst->w * 4;
gabomdq@6320
   251
gabomdq@6320
   252
    /*
gabomdq@6320
   253
    * Switch between interpolating and non-interpolating code
gabomdq@6320
   254
    */
gabomdq@6320
   255
    if (smooth) {
gabomdq@6320
   256
        for (y = 0; y < dst->h; y++) {
gabomdq@6320
   257
            dy = cy - y;
gabomdq@6320
   258
            sdx = (ax + (isin * dy)) + xd;
gabomdq@6320
   259
            sdy = (ay - (icos * dy)) + yd;
gabomdq@6320
   260
            for (x = 0; x < dst->w; x++) {
gabomdq@6320
   261
                dx = (sdx >> 16);
gabomdq@6320
   262
                dy = (sdy >> 16);
gabomdq@6320
   263
                if (flipx) dx = sw - dx;
gabomdq@6320
   264
                if (flipy) dy = sh - dy;
slouken@9762
   265
                if ((unsigned)dx < (unsigned)sw && (unsigned)dy < (unsigned)sh) {
slouken@9762
   266
                    sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy) + dx;
gabomdq@6320
   267
                    c00 = *sp;
gabomdq@6320
   268
                    sp += 1;
gabomdq@6320
   269
                    c01 = *sp;
gabomdq@6320
   270
                    sp += (src->pitch/4);
gabomdq@6320
   271
                    c11 = *sp;
gabomdq@6320
   272
                    sp -= 1;
gabomdq@6320
   273
                    c10 = *sp;
gabomdq@6320
   274
                    if (flipx) {
gabomdq@6320
   275
                        cswap = c00; c00=c01; c01=cswap;
gabomdq@6320
   276
                        cswap = c10; c10=c11; c11=cswap;
gabomdq@6320
   277
                    }
gabomdq@6320
   278
                    if (flipy) {
gabomdq@6320
   279
                        cswap = c00; c00=c10; c10=cswap;
gabomdq@6320
   280
                        cswap = c01; c01=c11; c11=cswap;
gabomdq@6320
   281
                    }
gabomdq@6320
   282
                    /*
gabomdq@6320
   283
                    * Interpolate colors
gabomdq@6320
   284
                    */
gabomdq@6320
   285
                    ex = (sdx & 0xffff);
gabomdq@6320
   286
                    ey = (sdy & 0xffff);
gabomdq@6320
   287
                    t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
gabomdq@6320
   288
                    t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
gabomdq@6320
   289
                    pc->r = (((t2 - t1) * ey) >> 16) + t1;
gabomdq@6320
   290
                    t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
gabomdq@6320
   291
                    t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
gabomdq@6320
   292
                    pc->g = (((t2 - t1) * ey) >> 16) + t1;
gabomdq@6320
   293
                    t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
gabomdq@6320
   294
                    t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
gabomdq@6320
   295
                    pc->b = (((t2 - t1) * ey) >> 16) + t1;
gabomdq@6320
   296
                    t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
gabomdq@6320
   297
                    t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
gabomdq@6320
   298
                    pc->a = (((t2 - t1) * ey) >> 16) + t1;
gabomdq@6320
   299
                }
gabomdq@6320
   300
                sdx += icos;
gabomdq@6320
   301
                sdy += isin;
gabomdq@6320
   302
                pc++;
gabomdq@6320
   303
            }
gabomdq@6320
   304
            pc = (tColorRGBA *) ((Uint8 *) pc + gap);
gabomdq@6320
   305
        }
gabomdq@6320
   306
    } else {
gabomdq@6320
   307
        for (y = 0; y < dst->h; y++) {
gabomdq@6320
   308
            dy = cy - y;
gabomdq@6320
   309
            sdx = (ax + (isin * dy)) + xd;
gabomdq@6320
   310
            sdy = (ay - (icos * dy)) + yd;
gabomdq@6320
   311
            for (x = 0; x < dst->w; x++) {
slouken@9762
   312
                dx = (sdx >> 16);
slouken@9762
   313
                dy = (sdy >> 16);
slouken@9762
   314
                if ((unsigned)dx < (unsigned)src->w && (unsigned)dy < (unsigned)src->h) {
slouken@9762
   315
                    if(flipx) dx = sw - dx;
slouken@9762
   316
                    if(flipy) dy = sh - dy;
slouken@9762
   317
                    *pc = *((tColorRGBA *)((Uint8 *)src->pixels + src->pitch * dy) + dx);
gabomdq@6320
   318
                }
gabomdq@6320
   319
                sdx += icos;
gabomdq@6320
   320
                sdy += isin;
gabomdq@6320
   321
                pc++;
gabomdq@6320
   322
            }
gabomdq@6320
   323
            pc = (tColorRGBA *) ((Uint8 *) pc + gap);
gabomdq@6320
   324
        }
gabomdq@6320
   325
    }
gabomdq@6320
   326
}
gabomdq@6320
   327
gabomdq@7678
   328
/* !
gabomdq@6320
   329
gabomdq@6320
   330
\brief Rotates and zooms 8 bit palette/Y 'src' surface to 'dst' surface without smoothing.
gabomdq@6320
   331
gabomdq@6320
   332
Rotates and zooms 8 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
gabomdq@6320
   333
parameters by scanning the destination surface.
gabomdq@6320
   334
Assumes src and dst surfaces are of 8 bit depth.
gabomdq@6320
   335
Assumes dst surface was allocated with the correct dimensions.
gabomdq@6320
   336
gabomdq@6320
   337
\param src Source surface.
gabomdq@6320
   338
\param dst Destination surface.
gabomdq@6320
   339
\param cx Horizontal center coordinate.
gabomdq@6320
   340
\param cy Vertical center coordinate.
gabomdq@6320
   341
\param isin Integer version of sine of angle.
gabomdq@6320
   342
\param icos Integer version of cosine of angle.
gabomdq@6320
   343
\param flipx Flag indicating horizontal mirroring should be applied.
gabomdq@6320
   344
\param flipy Flag indicating vertical mirroring should be applied.
gabomdq@6320
   345
*/
slouken@7859
   346
static void
slouken@7859
   347
transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy)
gabomdq@6320
   348
{
icculus@6389
   349
    int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay;
slouken@9762
   350
    tColorY *pc;
gabomdq@6320
   351
    int gap;
gabomdq@6320
   352
gabomdq@6320
   353
    /*
gabomdq@6320
   354
    * Variable setup
gabomdq@6320
   355
    */
gabomdq@6320
   356
    xd = ((src->w - dst->w) << 15);
gabomdq@6320
   357
    yd = ((src->h - dst->h) << 15);
gabomdq@6320
   358
    ax = (cx << 16) - (icos * cx);
gabomdq@6320
   359
    ay = (cy << 16) - (isin * cx);
gabomdq@6320
   360
    pc = (tColorY*) dst->pixels;
gabomdq@6320
   361
    gap = dst->pitch - dst->w;
gabomdq@6320
   362
    /*
gabomdq@6320
   363
    * Clear surface to colorkey
gabomdq@6320
   364
    */
gabomdq@6320
   365
    SDL_memset(pc, (int)(_colorkey(src) & 0xff), dst->pitch * dst->h);
gabomdq@6320
   366
    /*
gabomdq@6320
   367
    * Iterate through destination surface
gabomdq@6320
   368
    */
gabomdq@6320
   369
    for (y = 0; y < dst->h; y++) {
gabomdq@6320
   370
        dy = cy - y;
gabomdq@6320
   371
        sdx = (ax + (isin * dy)) + xd;
gabomdq@6320
   372
        sdy = (ay - (icos * dy)) + yd;
gabomdq@6320
   373
        for (x = 0; x < dst->w; x++) {
slouken@9762
   374
            dx = (sdx >> 16);
slouken@9762
   375
            dy = (sdy >> 16);
slouken@9762
   376
            if ((unsigned)dx < (unsigned)src->w && (unsigned)dy < (unsigned)src->h) {
slouken@9762
   377
                if (flipx) dx = (src->w-1)-dx;
slouken@9762
   378
                if (flipy) dy = (src->h-1)-dy;
slouken@9762
   379
                *pc = *((tColorY *)src->pixels + src->pitch * dy + dx);
gabomdq@6320
   380
            }
gabomdq@6320
   381
            sdx += icos;
gabomdq@6320
   382
            sdy += isin;
gabomdq@6320
   383
            pc++;
gabomdq@6320
   384
        }
gabomdq@6320
   385
        pc += gap;
gabomdq@6320
   386
    }
gabomdq@6320
   387
}
gabomdq@6320
   388
gabomdq@6320
   389
gabomdq@7678
   390
/* !
gabomdq@6320
   391
\brief Rotates and zooms a surface with different horizontal and vertival scaling factors and optional anti-aliasing.
gabomdq@6320
   392
slouken@10612
   393
Rotates a 32-bit or 8-bit 'src' surface to newly created 'dst' surface.
gabomdq@6320
   394
'angle' is the rotation in degrees, 'centerx' and 'centery' the rotation center. If 'smooth' is set
slouken@10612
   395
then the destination 32-bit surface is anti-aliased. 8-bit surfaces must have a colorkey. 32-bit
slouken@10612
   396
surfaces must have a 8888 layout with red, green, blue and alpha masks (any ordering goes).
slouken@10612
   397
The blend mode of the 'src' surface has some effects on generation of the 'dst' surface: The NONE
slouken@10612
   398
mode will set the BLEND mode on the 'dst' surface. The MOD mode either generates a white 'dst'
slouken@10612
   399
surface and sets the colorkey or fills the it with the colorkey before copying the pixels.
slouken@10612
   400
When using the NONE and MOD modes, color and alpha modulation must be applied before using this function.
gabomdq@6320
   401
gabomdq@6320
   402
\param src The surface to rotozoom.
gabomdq@6320
   403
\param angle The angle to rotate in degrees.
gabomdq@6320
   404
\param centerx The horizontal coordinate of the center of rotation
gabomdq@6320
   405
\param zoomy The vertical coordinate of the center of rotation
gabomdq@6320
   406
\param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
gabomdq@6320
   407
\param flipx Set to 1 to flip the image horizontally
gabomdq@6320
   408
\param flipy Set to 1 to flip the image vertically
gabomdq@6320
   409
\param dstwidth The destination surface width
gabomdq@6320
   410
\param dstheight The destination surface height
gabomdq@6320
   411
\param cangle The angle cosine
gabomdq@6320
   412
\param sangle The angle sine
gabomdq@6320
   413
\return The new rotated surface.
gabomdq@6320
   414
gabomdq@6320
   415
*/
gabomdq@6320
   416
slouken@7859
   417
SDL_Surface *
slouken@7859
   418
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)
gabomdq@6320
   419
{
gabomdq@6320
   420
    SDL_Surface *rz_dst;
slouken@10612
   421
    int is8bit, angle90;
slouken@9763
   422
    int i;
slouken@10612
   423
    SDL_BlendMode blendmode;
gabomdq@6320
   424
    Uint32 colorkey = 0;
slouken@10612
   425
    int colorKeyAvailable = SDL_FALSE;
gabomdq@6320
   426
    double sangleinv, cangleinv;
gabomdq@6320
   427
slouken@10612
   428
    /* Sanity check */
gabomdq@6320
   429
    if (src == NULL)
slouken@10612
   430
        return NULL;
gabomdq@6320
   431
slouken@10612
   432
    if (SDL_GetColorKey(src, &colorkey) == 0) {
slouken@10612
   433
        colorKeyAvailable = SDL_TRUE;
gabomdq@6320
   434
    }
slouken@10606
   435
slouken@10612
   436
    /* This function requires a 32-bit surface or 8-bit surface with a colorkey */
slouken@10612
   437
    is8bit = src->format->BitsPerPixel == 8 && colorKeyAvailable;
slouken@10612
   438
    if (!(is8bit || (src->format->BitsPerPixel == 32 && src->format->Amask)))
slouken@10612
   439
        return NULL;
gabomdq@6320
   440
slouken@10612
   441
    /* Calculate target factors from sin/cos and zoom */
gabomdq@6320
   442
    sangleinv = sangle*65536.0;
gabomdq@6320
   443
    cangleinv = cangle*65536.0;
gabomdq@6320
   444
slouken@10612
   445
    /* Alloc space to completely contain the rotated surface */
slouken@10612
   446
    rz_dst = NULL;
slouken@10612
   447
    if (is8bit) {
slouken@10612
   448
        /* Target surface is 8 bit */
slouken@10612
   449
        rz_dst = SDL_CreateRGBSurface(0, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
slouken@10612
   450
        if (rz_dst != NULL) {
slouken@10612
   451
            for (i = 0; i < src->format->palette->ncolors; i++) {
slouken@10612
   452
                rz_dst->format->palette->colors[i] = src->format->palette->colors[i];
slouken@10612
   453
            }
slouken@10612
   454
            rz_dst->format->palette->ncolors = src->format->palette->ncolors;
slouken@10612
   455
        }
gabomdq@6320
   456
    } else {
slouken@10612
   457
        /* Target surface is 32 bit with source RGBA ordering */
slouken@10612
   458
        rz_dst = SDL_CreateRGBSurface(0, dstwidth, dstheight + GUARD_ROWS, 32,
slouken@10612
   459
                                      src->format->Rmask, src->format->Gmask,
slouken@10612
   460
                                      src->format->Bmask, src->format->Amask);
gabomdq@6320
   461
    }
gabomdq@6320
   462
gabomdq@6320
   463
    /* Check target */
gabomdq@6320
   464
    if (rz_dst == NULL)
gabomdq@6320
   465
        return NULL;
gabomdq@6320
   466
gabomdq@6320
   467
    /* Adjust for guard rows */
gabomdq@6320
   468
    rz_dst->h = dstheight;
gabomdq@6320
   469
slouken@10612
   470
    SDL_GetSurfaceBlendMode(src, &blendmode);
gabomdq@6320
   471
slouken@10612
   472
    if (colorKeyAvailable == SDL_TRUE) {
slouken@10612
   473
        /* If available, the colorkey will be used to discard the pixels that are outside of the rotated area. */
slouken@10612
   474
        SDL_SetColorKey(rz_dst, SDL_TRUE, colorkey);
slouken@10606
   475
        SDL_FillRect(rz_dst, NULL, colorkey);
slouken@10612
   476
    } else if (blendmode == SDL_BLENDMODE_NONE) {
slouken@10612
   477
        blendmode = SDL_BLENDMODE_BLEND;
slouken@10612
   478
    } else if (blendmode == SDL_BLENDMODE_MOD) {
slouken@10612
   479
        /* Without a colorkey, the target texture has to be white for the MOD blend mode so
slouken@10612
   480
         * that the pixels outside the rotated area don't affect the destination surface.
slouken@10612
   481
         */
slouken@10612
   482
        colorkey = SDL_MapRGBA(rz_dst->format, 255, 255, 255, 0);
slouken@10612
   483
        SDL_FillRect(rz_dst, NULL, colorkey);
slouken@10612
   484
        /* Setting a white colorkey for the destination surface makes the final blit discard
slouken@10612
   485
         * all pixels outside of the rotated area. This doesn't interfere with anything because
slouken@10612
   486
         * white pixels are already a no-op and the MOD blend mode does not interact with alpha.
slouken@10612
   487
         */
slouken@10612
   488
        SDL_SetColorKey(rz_dst, SDL_TRUE, colorkey);
gabomdq@6320
   489
    }
gabomdq@6320
   490
slouken@10612
   491
    SDL_SetSurfaceBlendMode(rz_dst, blendmode);
slouken@10612
   492
slouken@10612
   493
    /* Lock source surface */
slouken@10612
   494
    if (SDL_MUSTLOCK(src)) {
slouken@10612
   495
        SDL_LockSurface(src);
gabomdq@6320
   496
    }
gabomdq@6320
   497
slouken@10489
   498
    /* check if the rotation is a multiple of 90 degrees so we can take a fast path and also somewhat reduce
slouken@10489
   499
     * the off-by-one problem in _transformSurfaceRGBA that expresses itself when the rotation is near
slouken@10489
   500
     * multiples of 90 degrees.
slouken@10489
   501
     */
slouken@10489
   502
    angle90 = (int)(angle/90);
slouken@10489
   503
    if (angle90 == angle/90) {
slouken@10489
   504
        angle90 %= 4;
slouken@10489
   505
        if (angle90 < 0) angle90 += 4; /* 0:0 deg, 1:90 deg, 2:180 deg, 3:270 deg */
slouken@10489
   506
    } else {
slouken@10489
   507
        angle90 = -1;
slouken@10489
   508
    }
slouken@10489
   509
slouken@10612
   510
    if (is8bit) {
slouken@10612
   511
        /* Call the 8-bit transformation routine to do the rotation */
slouken@10612
   512
        if(angle90 >= 0) {
slouken@10612
   513
            transformSurfaceY90(src, rz_dst, angle90, flipx, flipy);
slouken@10489
   514
        } else {
slouken@10612
   515
            transformSurfaceY(src, rz_dst, centerx, centery, (int)sangleinv, (int)cangleinv,
slouken@10612
   516
                              flipx, flipy);
slouken@10489
   517
        }
gabomdq@6320
   518
    } else {
slouken@10612
   519
        /* Call the 32-bit transformation routine to do the rotation */
slouken@10612
   520
        if (angle90 >= 0) {
slouken@10612
   521
            transformSurfaceRGBA90(src, rz_dst, angle90, flipx, flipy);
slouken@10489
   522
        } else {
slouken@10612
   523
            _transformSurfaceRGBA(src, rz_dst, centerx, centery, (int)sangleinv, (int)cangleinv,
slouken@10612
   524
                                  flipx, flipy, smooth);
slouken@10489
   525
        }
slouken@10606
   526
    }
slouken@10606
   527
slouken@10612
   528
    /* Unlock source surface */
slouken@10612
   529
    if (SDL_MUSTLOCK(src)) {
slouken@10612
   530
        SDL_UnlockSurface(src);
slouken@9763
   531
    }
slouken@9763
   532
slouken@10612
   533
    /* Return rotated surface */
slouken@10612
   534
    return rz_dst;
gabomdq@6320
   535
}