// Copyright (C) 1999 - 2003 Anders Lindström
// Copyright (C) 2009 - 2021 Marc Vester (XaserLE)
// Copyright (C) 2009 - 2021 Settlers Freaks <sf-team at siedler25.org>
//
// SPDX-License-Identifier: LGPL-2.1-or-later

/*
 *	SDL Graphics Extension
 *	Rotation routines
 *
 *	Started 000625
 */

#define _USE_MATH_DEFINES
#define HAVE_M_PI 1
#include "sge_rotation.h"
#include "sge_blib.h"
#include "sge_surface.h"
#include <array>
#include <cmath>

extern Uint8 _sge_lock;

SDL_Rect sge_transform_tmap(SDL_Surface* src, SDL_Surface* dst, float angle, float xscale, float yscale, Uint16 qx,
                            Uint16 qy);

//==================================================================================
// Helper function to sge_transform()
// Returns the bounding box
//==================================================================================
static void _calcRect(SDL_Surface* src, SDL_Surface* dst, float theta, float xscale, float yscale, Uint16 px, Uint16 py,
                      Uint16 qx, Uint16 qy, Sint16* xmin, Sint16* ymin, Sint16* xmax, Sint16* ymax)
{
    Sint16 x, y, rx, ry;

    // Clip to src surface
    Sint16 sxmin = sge_clip_xmin(src);
    Sint16 sxmax = sge_clip_xmax(src);
    Sint16 symin = sge_clip_ymin(src);
    Sint16 symax = sge_clip_ymax(src);
    std::array<Sint16, 4> sx = {sxmin, sxmax, sxmin, sxmax};
    std::array<Sint16, 4> sy = {symin, symax, symax, symin};

    // We don't really need fixed-point here
    // but why not?
    auto const istx = Sint32((std::sin(theta) * xscale) * 8192.0); /* Inverse transform */
    auto const ictx = Sint32((std::cos(theta) * xscale) * 8192.2);
    auto const isty = Sint32((std::sin(theta) * yscale) * 8192.0);
    auto const icty = Sint32((std::cos(theta) * yscale) * 8192.2);

    // Calculate the four corner points
    for(int i = 0; i < 4; i++)
    {
        rx = sx[i] - px;
        ry = sy[i] - py;

        x = Sint16(((ictx * rx - isty * ry) >> 13) + qx);
        y = Sint16(((icty * ry + istx * rx) >> 13) + qy);

        if(i == 0)
        {
            *xmax = *xmin = x;
            *ymax = *ymin = y;
        } else
        {
            if(x > *xmax)
                *xmax = x;
            else if(x < *xmin)
                *xmin = x;

            if(y > *ymax)
                *ymax = y;
            else if(y < *ymin)
                *ymin = y;
        }
    }

    // Better safe than sorry...
    *xmin -= 1;
    *ymin -= 1;
    *xmax += 1;
    *ymax += 1;

    // Clip to dst surface
    if(!dst)
        return;
    if(*xmin < sge_clip_xmin(dst))
        *xmin = sge_clip_xmin(dst);
    if(*xmax > sge_clip_xmax(dst))
        *xmax = sge_clip_xmax(dst);
    if(*ymin < sge_clip_ymin(dst))
        *ymin = sge_clip_ymin(dst);
    if(*ymax > sge_clip_ymax(dst))
        *ymax = sge_clip_ymax(dst);
}

/*==================================================================================
** Rotate by angle about pivot (px,py) scale by scale and place at
** position (qx,qy).
**
** Transformation matrix application (rotated coords "R"):
**
**   / rx \   /  cos(theta)  sin(theta) \  / dx \
**   |    | = |                         |  |    |
**   \ ry /   \ -sin(theta)  cos(theta) /  \ dy /
**
** =>  rx = cos(theta) dx + sin(theta) dy
**     ry = cos(theta) dy - sin(theta) dx
** but represented as a fixed-point float using integer math
**
** Developed with the help from Terry Hancock (hancock@earthlink.net)
**
**==================================================================================*/
// First we need some macros to handle different bpp
// I'm sorry about this...
#define TRANSFORM(UintXX_T, DIV)                                                 \
    using UintXX = UintXX_T;                                                     \
    Sint32 const src_pitch = src->pitch / (DIV);                                 \
    Sint32 const dst_pitch = dst->pitch / (DIV);                                 \
    UintXX const* src_row = (UintXX*)src->pixels;                                \
    UintXX* dst_row;                                                             \
                                                                                 \
    for(y = ymin; y < ymax; y++)                                                 \
    {                                                                            \
        dy = y - qy;                                                             \
                                                                                 \
        sx = Sint32(ctdx + stx * dy + mx); /* Compute source anchor points */    \
        sy = Sint32(cty * dy - stdx + my);                                       \
                                                                                 \
        /* Calculate pointer to dst surface */                                   \
        dst_row = (UintXX*)dst->pixels + y * dst_pitch;                          \
                                                                                 \
        for(x = xmin; x < xmax; x++)                                             \
        {                                                                        \
            rx = Sint16(sx >> 13); /* Convert from fixed-point */                \
            ry = Sint16(sy >> 13);                                               \
                                                                                 \
            /* Make sure the source pixel is actually in the source image. */    \
            if((rx >= sxmin) && (rx <= sxmax) && (ry >= symin) && (ry <= symax)) \
                *(dst_row + x) = *(src_row + ry * src_pitch + rx);               \
                                                                                 \
            sx += ctx; /* Incremental transformations */                         \
            sy -= sty;                                                           \
        }                                                                        \
    }

#define TRANSFORM_GENERIC                                                            \
    Uint8 R, G, B, A;                                                                \
                                                                                     \
    for(y = ymin; y < ymax; y++)                                                     \
    {                                                                                \
        dy = y - qy;                                                                 \
                                                                                     \
        sx = Sint32(ctdx + stx * dy + mx); /* Compute source anchor points */        \
        sy = Sint32(cty * dy - stdx + my);                                           \
                                                                                     \
        for(x = xmin; x < xmax; x++)                                                 \
        {                                                                            \
            rx = Sint16(sx >> 13); /* Convert from fixed-point */                    \
            ry = Sint16(sy >> 13);                                                   \
                                                                                     \
            /* Make sure the source pixel is actually in the source image. */        \
            if((rx >= sxmin) && (rx <= sxmax) && (ry >= symin) && (ry <= symax))     \
            {                                                                        \
                SDL_GetRGBA(sge_GetPixel(src, rx, ry), src->format, &R, &G, &B, &A); \
                _PutPixelX(dst, x, y, SDL_MapRGBA(dst->format, R, G, B, A));         \
            }                                                                        \
            sx += ctx; /* Incremental transformations */                             \
            sy -= sty;                                                               \
        }                                                                            \
    }

// Interpolated transform
#define TRANSFORM_AA(UintXX_T, DIV)                                                                                   \
    using UintXX = UintXX_T;                                                                                          \
    Sint32 const src_pitch = src->pitch / (DIV);                                                                      \
    Sint32 const dst_pitch = dst->pitch / (DIV);                                                                      \
    UintXX const* src_row = (UintXX*)src->pixels;                                                                     \
    UintXX* dst_row;                                                                                                  \
    UintXX c1, c2, c3, c4;                                                                                            \
    Uint32 R, G, B, A = 0;                                                                                            \
    UintXX Rmask = src->format->Rmask, Gmask = src->format->Gmask, Bmask = src->format->Bmask,                        \
           Amask = src->format->Amask;                                                                                \
    Uint32 wx, wy;                                                                                                    \
    Uint32 p1, p2, p3, p4;                                                                                            \
                                                                                                                      \
    /*                                                                                             */                 \
    /*  Interpolation:                                                                             */                 \
    /*  We calculate the distances from our point to the four nearest pixels, d1..d4.              */                 \
    /*  d(a,b) = sqrt(a²+b²) ~= 0.707(a+b)  (Pythagoras (Taylor) expanded around (0.5;0.5))        */               \
    /*                                                                                             */                 \
    /*    1  wx 2                                                                                  */                 \
    /*     *-|-*  (+ = our point at (x,y))                                                         */                 \
    /*     | | |  (* = the four nearest pixels)                                                    */                 \
    /*  wy --+ |  wx = float(x) - int(x)                                                           */                 \
    /*     |   |  wy = float(y) - int(y)                                                           */                 \
    /*     *---*                                                                                   */                 \
    /*    3     4                                                                                  */                 \
    /*  d1 = d(wx,wy)  d2 = d(1-wx,wy)  d3 = d(wx,1-wy)  d4 = d(1-wx,1-wy)                         */                 \
    /*  We now want to weight each pixels importance - it's vicinity to our point:                 */                 \
    /*  w1=d4  w2=d3  w3=d2  w4=d1  (Yes it works... just think a bit about it)                    */                 \
    /*                                                                                             */                 \
    /*  If the pixels have the colors c1..c4 then our point should have the color                  */                 \
    /*  c = (w1*c1 + w2*c2 + w3*c3 + w4*c4)/(w1+w2+w3+w4)   (the weighted average)                 */                 \
    /*  but  w1+w2+w3+w4 = 4*0.707  so we might as well write it as                                */                 \
    /*  c = p1*c1 + p2*c2 + p3*c3 + p4*c4  where  p1..p4 = (w1..w4)/(4*0.707)                      */                 \
    /*                                                                                             */                 \
    /*  But p1..p4 are fixed point so we can just divide the fixed point constant!                 */                 \
    /*  8192/(4*0.71) = 2897  and we can skip 0.71 too (the division will cancel it everywhere)    */                 \
    /*  8192/4 = 2048                                                                              */                 \
    /*                                                                                             */                 \
    /*  020102: I changed the fixed-point representation for the variables in the weighted average */                 \
    /*          to 24.7 to avoid problems with 32bit colors. Everything else is still 18.13. This  */                 \
    /*          does however not solve the problem with 32bit RGBA colors...                       */                 \
    /*                                                                                             */                 \
                                                                                                                      \
    Sint32 const one = 2048 >> 6;     /* 1 in Fixed-point */                                                          \
    Sint32 const two = 2 * 2048 >> 6; /* 2 in Fixed-point */                                                          \
                                                                                                                      \
    for(y = ymin; y < ymax; y++)                                                                                      \
    {                                                                                                                 \
        dy = y - qy;                                                                                                  \
                                                                                                                      \
        sx = Sint32(ctdx + stx * dy + mx); /* Compute source anchor points */                                         \
        sy = Sint32(cty * dy - stdx + my);                                                                            \
                                                                                                                      \
        /* Calculate pointer to dst surface */                                                                        \
        dst_row = (UintXX*)dst->pixels + y * dst_pitch;                                                               \
                                                                                                                      \
        for(x = xmin; x < xmax; x++)                                                                                  \
        {                                                                                                             \
            rx = Sint16(sx >> 13); /* Convert from fixed-point */                                                     \
            ry = Sint16(sy >> 13);                                                                                    \
                                                                                                                      \
            /* Make sure the source pixel is actually in the source image. */                                         \
            if((rx >= sxmin) && (rx + 1 <= sxmax) && (ry >= symin) && (ry + 1 <= symax))                              \
            {                                                                                                         \
                wx = (sx & 0x00001FFF) >> 8; /* (float(x) - int(x)) / 4 */                                            \
                wy = (sy & 0x00001FFF) >> 8;                                                                          \
                                                                                                                      \
                p4 = wx + wy;                                                                                         \
                p3 = one - wx + wy;                                                                                   \
                p2 = wx + one - wy;                                                                                   \
                p1 = two - wx - wy;                                                                                   \
                                                                                                                      \
                c1 = *(src_row + ry * src_pitch + rx);                                                                \
                c2 = *(src_row + ry * src_pitch + rx + 1);                                                            \
                c3 = *(src_row + (ry + 1) * src_pitch + rx);                                                          \
                c4 = *(src_row + (ry + 1) * src_pitch + rx + 1);                                                      \
                                                                                                                      \
                /* Calculate the average */                                                                           \
                R = ((p1 * (c1 & Rmask) + p2 * (c2 & Rmask) + p3 * (c3 & Rmask) + p4 * (c4 & Rmask)) >> 7) & Rmask;   \
                G = ((p1 * (c1 & Gmask) + p2 * (c2 & Gmask) + p3 * (c3 & Gmask) + p4 * (c4 & Gmask)) >> 7) & Gmask;   \
                B = ((p1 * (c1 & Bmask) + p2 * (c2 & Bmask) + p3 * (c3 & Bmask) + p4 * (c4 & Bmask)) >> 7) & Bmask;   \
                if(Amask)                                                                                             \
                    A =                                                                                               \
                      ((p1 * (c1 & Amask) + p2 * (c2 & Amask) + p3 * (c3 & Amask) + p4 * (c4 & Amask)) >> 7) & Amask; \
                                                                                                                      \
                *(dst_row + x) = R | G | B | A;                                                                       \
            }                                                                                                         \
            sx += ctx; /* Incremental transformations */                                                              \
            sy -= sty;                                                                                                \
        }                                                                                                             \
    }

#define TRANSFORM_GENERIC_AA                                                                          \
    Uint8 R, G, B, A, R1, G1, B1, A1 = 0, R2, G2, B2, A2 = 0, R3, G3, B3, A3 = 0, R4, G4, B4, A4 = 0; \
    Sint32 wx, wy, p1, p2, p3, p4;                                                                    \
                                                                                                      \
    Sint32 const one = 2048;     /* 1 in Fixed-point */                                               \
    Sint32 const two = 2 * 2048; /* 2 in Fixed-point */                                               \
                                                                                                      \
    for(y = ymin; y < ymax; y++)                                                                      \
    {                                                                                                 \
        dy = y - qy;                                                                                  \
                                                                                                      \
        sx = Sint32(ctdx + stx * dy + mx); /* Compute source anchor points */                         \
        sy = Sint32(cty * dy - stdx + my);                                                            \
                                                                                                      \
        for(x = xmin; x < xmax; x++)                                                                  \
        {                                                                                             \
            rx = Sint16(sx >> 13); /* Convert from fixed-point */                                     \
            ry = Sint16(sy >> 13);                                                                    \
                                                                                                      \
            /* Make sure the source pixel is actually in the source image. */                         \
            if((rx >= sxmin) && (rx + 1 <= sxmax) && (ry >= symin) && (ry + 1 <= symax))              \
            {                                                                                         \
                wx = (sx & 0x00001FFF) >> 2; /* (float(x) - int(x)) / 4 */                            \
                wy = (sy & 0x00001FFF) >> 2;                                                          \
                                                                                                      \
                p4 = wx + wy;                                                                         \
                p3 = one - wx + wy;                                                                   \
                p2 = wx + one - wy;                                                                   \
                p1 = two - wx - wy;                                                                   \
                                                                                                      \
                SDL_GetRGBA(sge_GetPixel(src, rx, ry), src->format, &R1, &G1, &B1, &A1);              \
                SDL_GetRGBA(sge_GetPixel(src, rx + 1, ry), src->format, &R2, &G2, &B2, &A2);          \
                SDL_GetRGBA(sge_GetPixel(src, rx, ry + 1), src->format, &R3, &G3, &B3, &A3);          \
                SDL_GetRGBA(sge_GetPixel(src, rx + 1, ry + 1), src->format, &R4, &G4, &B4, &A4);      \
                                                                                                      \
                /* Calculate the average */                                                           \
                R = (p1 * R1 + p2 * R2 + p3 * R3 + p4 * R4) >> 13;                                    \
                G = (p1 * G1 + p2 * G2 + p3 * G3 + p4 * G4) >> 13;                                    \
                B = (p1 * B1 + p2 * B2 + p3 * B3 + p4 * B4) >> 13;                                    \
                A = (p1 * A1 + p2 * A2 + p3 * A3 + p4 * A4) >> 13;                                    \
                                                                                                      \
                _PutPixelX(dst, x, y, SDL_MapRGBA(dst->format, R, G, B, A));                          \
            }                                                                                         \
            sx += ctx; /* Incremental transformations */                                              \
            sy -= sty;                                                                                \
        }                                                                                             \
    }

// We get better performance if AA and normal rendering is seperated into two functions (better optimization).
// sge_transform() is used as a wrapper.

static SDL_Rect sge_transformNorm(SDL_Surface* src, SDL_Surface* dst, float angle, float xscale, float yscale,
                                  Uint16 px, Uint16 py, Uint16 qx, Uint16 qy, Uint8 flags)
{
    Sint32 dy, sx, sy;
    Sint16 x, y, rx, ry;
    SDL_Rect r;
    r.x = r.y = r.w = r.h = 0;

    auto theta = float(angle * M_PI / 180.0); /* Convert to radians.  */

    // Here we use 18.13 fixed point integer math
    // Sint32 should have 31 usable bits and one for sign
    // 2^13 = 8192

    // Check scales
    auto maxint = Sint32(pow(2, sizeof(Sint32) * 8 - 1 - 13)); // 2^(31-13)

    if(xscale == 0 || yscale == 0)
        return r;

    if(8192.0 / xscale > maxint)
        xscale = float(8192.0 / maxint);
    else if(8192.0 / xscale < -maxint)
        xscale = float(-8192.0 / maxint);

    if(8192.0 / yscale > maxint)
        yscale = float(8192.0 / maxint);
    else if(8192.0 / yscale < -maxint)
        yscale = float(-8192.0 / maxint);

    // Fixed-point equivalents
    auto const stx = Sint32((sin(theta) / xscale) * 8192.0);
    auto const ctx = Sint32((cos(theta) / xscale) * 8192.0);
    auto const sty = Sint32((sin(theta) / yscale) * 8192.0);
    auto const cty = Sint32((cos(theta) / yscale) * 8192.0);
    auto const mx = Sint32(px * 8192.0);
    auto const my = Sint32(py * 8192.0);

    // Compute a bounding rectangle
    Sint16 xmin = 0, xmax = dst->w, ymin = 0, ymax = dst->h;
    _calcRect(src, dst, theta, xscale, yscale, px, py, qx, qy, &xmin, &ymin, &xmax, &ymax);

    // Clip to src surface
    Sint16 sxmin = sge_clip_xmin(src);
    Sint16 sxmax = sge_clip_xmax(src);
    Sint16 symin = sge_clip_ymin(src);
    Sint16 symax = sge_clip_ymax(src);

    // Some terms in the transform are constant
    Sint32 const dx = xmin - qx;
    Sint32 const ctdx = ctx * dx;
    Sint32 const stdx = sty * dx;

    // Lock surfaces... hopfully less than two needs locking!
    if(_sge_lock && SDL_MUSTLOCK(src))
        if(SDL_LockSurface(src) < 0)
            return r;
    if(_sge_lock && SDL_MUSTLOCK(dst))
    {
        if(SDL_LockSurface(dst) < 0)
        {
            if(_sge_lock && SDL_MUSTLOCK(src))
                SDL_UnlockSurface(src);
            return r;
        }
    }

    // Use the correct bpp
    if(src->format->BytesPerPixel == dst->format->BytesPerPixel && src->format->BytesPerPixel != 3
       && !(flags & SGE_TSAFE))
    {
        switch(src->format->BytesPerPixel)
        {
            case 1:
            { /* Assuming 8-bpp */
                TRANSFORM(Uint8, 1)
            }
            break;
            case 2:
            { /* Probably 15-bpp or 16-bpp */
                TRANSFORM(Uint16, 2)
            }
            break;
            case 4:
            { /* Probably 32-bpp */
                TRANSFORM(Uint32, 4)
            }
            break;
        }
    } else
    {
        TRANSFORM_GENERIC
    }

    // Unlock surfaces
    if(_sge_lock && SDL_MUSTLOCK(src))
        SDL_UnlockSurface(src);
    if(_sge_lock && SDL_MUSTLOCK(dst))
        SDL_UnlockSurface(dst);

    // Return the bounding rectangle
    r.x = xmin;
    r.y = ymin;
    r.w = xmax - xmin;
    r.h = ymax - ymin;
    return r;
}

static SDL_Rect sge_transformAA(SDL_Surface* src, SDL_Surface* dst, float angle, float xscale, float yscale, Uint16 px,
                                Uint16 py, Uint16 qx, Uint16 qy, Uint8 flags)
{
    Sint32 dy, sx, sy;
    Sint16 x, y, rx, ry;
    SDL_Rect r;
    r.x = r.y = r.w = r.h = 0;

    auto theta = float(angle * M_PI / 180.0); /* Convert to radians.  */

    // Here we use 18.13 fixed point integer math
    // Sint32 should have 31 usable bits and one for sign
    // 2^13 = 8192

    // Check scales
    auto maxint = Sint32(pow(2, sizeof(Sint32) * 8 - 1 - 13)); // 2^(31-13)

    if(xscale == 0 || yscale == 0)
        return r;

    if(8192.0 / xscale > maxint)
        xscale = float(8192.0 / maxint);
    else if(8192.0 / xscale < -maxint)
        xscale = float(-8192.0 / maxint);

    if(8192.0 / yscale > maxint)
        yscale = float(8192.0 / maxint);
    else if(8192.0 / yscale < -maxint)
        yscale = float(-8192.0 / maxint);

    // Fixed-point equivalents
    auto const stx = Sint32((sin(theta) / xscale) * 8192.0);
    auto const ctx = Sint32((cos(theta) / xscale) * 8192.0);
    auto const sty = Sint32((sin(theta) / yscale) * 8192.0);
    auto const cty = Sint32((cos(theta) / yscale) * 8192.0);
    auto const mx = Sint32(px * 8192.0);
    auto const my = Sint32(py * 8192.0);

    // Compute a bounding rectangle
    Sint16 xmin = 0, xmax = dst->w, ymin = 0, ymax = dst->h;
    _calcRect(src, dst, theta, xscale, yscale, px, py, qx, qy, &xmin, &ymin, &xmax, &ymax);

    // Clip to src surface
    Sint16 sxmin = sge_clip_xmin(src);
    Sint16 sxmax = sge_clip_xmax(src);
    Sint16 symin = sge_clip_ymin(src);
    Sint16 symax = sge_clip_ymax(src);

    // Some terms in the transform are constant
    Sint32 const dx = xmin - qx;
    Sint32 const ctdx = ctx * dx;
    Sint32 const stdx = sty * dx;

    // Lock surfaces... hopfully less than two needs locking!
    if(_sge_lock && SDL_MUSTLOCK(src))
        if(SDL_LockSurface(src) < 0)
            return r;
    if(_sge_lock && SDL_MUSTLOCK(dst))
    {
        if(SDL_LockSurface(dst) < 0)
        {
            if(_sge_lock && SDL_MUSTLOCK(src))
                SDL_UnlockSurface(src);
            return r;
        }
    }

    // Use the correct bpp
    if(src->format->BytesPerPixel == dst->format->BytesPerPixel && src->format->BytesPerPixel != 3
       && !(flags & SGE_TSAFE))
    {
        switch(src->format->BytesPerPixel)
        {
            case 1:
            { /* Assuming 8-bpp */
                // TRANSFORM_AA(Uint8, 1)
                TRANSFORM_GENERIC_AA
            }
            break;
            case 2:
            { /* Probably 15-bpp or 16-bpp */
                TRANSFORM_AA(Uint16, 2)
            }
            break;
            case 4:
            { /* Probably 32-bpp */
                TRANSFORM_AA(Uint32, 4)
            }
            break;
        }
    } else
    {
        TRANSFORM_GENERIC_AA
    }

    // Unlock surfaces
    if(_sge_lock && SDL_MUSTLOCK(src))
        SDL_UnlockSurface(src);
    if(_sge_lock && SDL_MUSTLOCK(dst))
        SDL_UnlockSurface(dst);

    // Return the bounding rectangle
    r.x = xmin;
    r.y = ymin;
    r.w = xmax - xmin;
    r.h = ymax - ymin;
    return r;
}

SDL_Rect sge_transform(SDL_Surface* src, SDL_Surface* dst, float angle, float xscale, float yscale, Uint16 px,
                       Uint16 py, Uint16 qx, Uint16 qy, Uint8 flags)
{
    if(flags & SGE_TTMAP)
        return sge_transform_tmap(src, dst, angle, xscale, yscale, qx, qy);
    else
    {
        if(flags & SGE_TAA)
            return sge_transformAA(src, dst, angle, xscale, yscale, px, py, qx, qy, flags);
        else
            return sge_transformNorm(src, dst, angle, xscale, yscale, px, py, qx, qy, flags);
    }
}

//==================================================================================
// Same as sge_transform() but returns an surface with the result
//==================================================================================
SDL_Surface* sge_transform_surface(SDL_Surface* src, Uint32 bcol, float angle, float xscale, float yscale, Uint8 flags)
{
    auto theta = float(angle * M_PI / 180.0); /* Convert to radians.  */

    // Compute a bounding rectangle
    Sint16 xmin = 0, xmax = 0, ymin = 0, ymax = 0;
    _calcRect(src, nullptr, theta, xscale, yscale, 0, 0, 0, 0, &xmin, &ymin, &xmax, &ymax);

    Sint16 w = xmax - xmin + 1;
    Sint16 h = ymax - ymin + 1;

    Sint16 qx = -xmin;
    Sint16 qy = -ymin;

    SDL_Surface* dest = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, src->format->BitsPerPixel, src->format->Rmask,
                                             src->format->Gmask, src->format->Bmask, src->format->Amask);
    if(!dest)
        return nullptr;

    sge_ClearSurface(dest, bcol); // Set background color

    sge_transform(src, dest, angle, xscale, yscale, 0, 0, qx, qy, flags);

    return dest;
}

//==================================================================================
// Rotate using texture mapping
//==================================================================================
SDL_Rect sge_transform_tmap(SDL_Surface* src, SDL_Surface* dst, float angle, float xscale, float yscale, Uint16 qx,
                            Uint16 qy)
{
    double rad;
    double a = (sge_clip_xmax(src) - sge_clip_xmin(src)) / 2.0;
    double b = (sge_clip_ymax(src) - sge_clip_ymin(src)) / 2.0;

    double cosv, sinv;

    // Get an exact value if possible
    if(angle == 0.0 || angle == 360.0)
    {
        cosv = 1;
        sinv = 0;
    } else if(angle == 90.0)
    {
        cosv = 0;
        sinv = 1;
    } else if(angle == 180.0)
    {
        cosv = -1;
        sinv = 0;
    } else if(angle == 270.0)
    {
        cosv = 0;
        sinv = -1;
    } else
    {                                 // Oh well
        rad = angle * (M_PI / 180.0); // Deg => rad
        cosv = cos(rad);
        sinv = sin(rad);
    }

    // Precalculate as much as possible
    double acosv = a * cosv * xscale, bcosv = b * cosv * yscale;
    double asinv = a * sinv * xscale, bsinv = b * sinv * yscale;

    /* Do the maths */
    std::array<Sint16, 4> xt, yt;

    xt[0] = Sint16((-acosv + bsinv) + qx);
    yt[0] = Sint16((-asinv - bcosv) + qy);

    xt[1] = Sint16((acosv + bsinv) + qx);
    yt[1] = Sint16((asinv - bcosv) + qy);

    xt[2] = Sint16((-acosv - bsinv) + qx);
    yt[2] = Sint16((-asinv + bcosv) + qy);

    xt[3] = Sint16((acosv - bsinv) + qx);
    yt[3] = Sint16((asinv + bcosv) + qy);

    // Use a texture mapped rectangle
    sge_TexturedRect(dst, xt[0], yt[0], xt[1], yt[1], xt[2], yt[2], xt[3], yt[3], src, sge_clip_xmin(src),
                     sge_clip_ymin(src), sge_clip_xmax(src), sge_clip_ymin(src), sge_clip_xmin(src), sge_clip_ymax(src),
                     sge_clip_xmax(src), sge_clip_ymax(src));

    // Or maybe two trigons...
    // sge_TexturedTrigon(dest,xt[0],yt[0],xt[1],yt[1],xt[2],yt[2],src, sge_clip_xmin(src),sge_clip_ymin(src),
    // sge_clip_xmax(src),sge_clip_ymin(src), sge_clip_xmin(src),sge_clip_ymax(src));
    // sge_TexturedTrigon(dest,xt[3],yt[3],xt[1],yt[1],xt[2],yt[2],src, sge_clip_xmax(src),sge_clip_ymax(src),
    // sge_clip_xmax(src),sge_clip_ymin(src), sge_clip_xmin(src),sge_clip_ymax(src));

    // For debug
    // sge_Trigon(dest,xt[0],yt[0],xt[1],yt[1],xt[2],yt[2],SDL_MapRGB(dest->format,255,0,0));
    // sge_Trigon(dest,xt[3],yt[3],xt[1],yt[1],xt[2],yt[2],SDL_MapRGB(dest->format,0,255,0));

    Sint16 xmax = xt[0], xmin = xt[0];
    xmax = (xmax > xt[1]) ? xmax : xt[1];
    xmin = (xmin < xt[1]) ? xmin : xt[1];
    xmax = (xmax > xt[2]) ? xmax : xt[2];
    xmin = (xmin < xt[2]) ? xmin : xt[2];
    xmax = (xmax > xt[3]) ? xmax : xt[3];
    xmin = (xmin < xt[3]) ? xmin : xt[3];

    Sint16 ymax = yt[0], ymin = yt[0];
    ymax = (ymax > yt[1]) ? ymax : yt[1];
    ymin = (ymin < yt[1]) ? ymin : yt[1];
    ymax = (ymax > yt[2]) ? ymax : yt[2];
    ymin = (ymin < yt[2]) ? ymin : yt[2];
    ymax = (ymax > yt[3]) ? ymax : yt[3];
    ymin = (ymin < yt[3]) ? ymin : yt[3];

    SDL_Rect r;
    r.x = xmin;
    r.y = ymin;
    r.w = xmax - xmin + 1;
    r.h = ymax - ymin + 1;
    return r;
}
