RenderCopyEx,rotation and flipping for all hardware/software backends (#1308)
authorGabriel Jacobo <gabomdq@gmail.com>
Fri, 01 Jun 2012 19:51:08 -0300
changeset 6320 6077a1310907
parent 6319 698c98b83cbb
child 6321 71fa3d8976a3
RenderCopyEx,rotation and flipping for all hardware/software backends (#1308)
include/SDL_render.h
src/render/SDL_render.c
src/render/SDL_sysrender.h
src/render/direct3d/SDL_render_d3d.c
src/render/opengl/SDL_glfuncs.h
src/render/opengl/SDL_render_gl.c
src/render/opengles/SDL_render_gles.c
src/render/opengles2/SDL_render_gles2.c
src/render/opengles2/SDL_shaders_gles2.c
src/render/software/SDL_render_sw.c
src/render/software/SDL_rotate.c
src/render/software/SDL_rotate.h
test/Makefile.in
test/testrendercopyex.c
--- a/include/SDL_render.h	Fri Jun 01 19:42:15 2012 -0300
+++ b/include/SDL_render.h	Fri Jun 01 19:51:08 2012 -0300
@@ -105,6 +105,16 @@
 } SDL_TextureModulate;
 
 /**
+ *  \brief Flip constants for SDL_RenderCopyEx
+ */
+typedef enum
+{
+    SDL_FLIP_NONE = 0x00000000,     /**< Do not flip */
+    SDL_FLIP_HORIZONTAL = 0x00000001,    /**< flip horizontally */
+    SDL_FLIP_VERTICAL = 0x00000002     /**< flip vertically */
+} SDL_RendererFlip;
+
+/**
  *  \brief A structure representing rendering state
  */
 struct SDL_Renderer;
@@ -599,6 +609,27 @@
                                            const SDL_Rect * srcrect,
                                            const SDL_Rect * dstrect);
 
+/**
+ *  \brief Copy a portion of the source texture to the current rendering target, rotating it by angle around the given center 
+ *
+ *  \param texture The source texture.
+ *  \param srcrect   A pointer to the source rectangle, or NULL for the entire
+ *                   texture.
+ *  \param dstrect   A pointer to the destination rectangle, or NULL for the
+ *                   entire rendering target.
+ *  \param angle    An angle in degrees that indicates the rotation that will be applied to dstrect
+ *  \param center   A pointer to a point indicating the point around which dstrect will be rotated (if NULL, rotation will be done aroud dstrect.w/2, dstrect.h/2)
+ *  \param flip     A SFL_Flip value stating which flipping actions should be performed on the texture
+ * 
+ *  \return 0 on success, or -1 on error
+ */
+extern DECLSPEC int SDLCALL SDL_RenderCopyEx(SDL_Renderer * renderer,
+                                           SDL_Texture * texture,
+                                           const SDL_Rect * srcrect,
+                                           const SDL_Rect * dstrect,
+                                           const double angle,
+                                           const SDL_Point *center,
+                                           const SDL_RendererFlip flip);
 
 /**
  *  \brief Read pixels from the current rendering target.
--- a/src/render/SDL_render.c	Fri Jun 01 19:42:15 2012 -0300
+++ b/src/render/SDL_render.c	Fri Jun 01 19:51:08 2012 -0300
@@ -1231,6 +1231,62 @@
                                 &real_dstrect);
 }
 
+
+int
+SDL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
+               const SDL_Rect * srcrect, const SDL_Rect * dstrect,
+               const double angle, const SDL_Point *center, const SDL_RendererFlip flip)
+{
+    SDL_Window *window;
+    SDL_Rect real_srcrect, real_dstrect;
+    SDL_Point real_center;
+
+    CHECK_RENDERER_MAGIC(renderer, -1);
+    CHECK_TEXTURE_MAGIC(texture, -1);
+
+    if (renderer != texture->renderer) {
+        SDL_SetError("Texture was not created with this renderer");
+        return -1;
+    }
+    if (!renderer->RenderCopyEx) {
+        SDL_SetError("Renderer does not support RenderCopyEx");
+        return -1;
+    }
+    
+    window = renderer->window;
+
+    real_srcrect.x = 0;
+    real_srcrect.y = 0;
+    real_srcrect.w = texture->w;
+    real_srcrect.h = texture->h;
+    if (srcrect) {
+        if (!SDL_IntersectRect(srcrect, &real_srcrect, &real_srcrect)) {
+            return 0;
+        }
+    }
+
+    /* We don't intersect the dstrect with the viewport as RenderCopy does because of potential rotation clipping issues... TODO: should we? */
+    if (dstrect) real_dstrect = *dstrect;
+    else {
+        real_srcrect.x = 0;
+        real_srcrect.y = 0;
+        real_srcrect.w = renderer->viewport.w;
+        real_srcrect.h = renderer->viewport.h;
+    }
+
+    if (texture->native) {
+        texture = texture->native;
+    }
+
+    if(center) real_center = *center;
+    else {
+        real_center.x = real_dstrect.w/2;
+        real_center.y = real_dstrect.h/2;
+    }
+
+    return renderer->RenderCopyEx(renderer, texture, &real_srcrect, &real_dstrect, angle, &real_center, flip);
+}
+
 int
 SDL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                      Uint32 format, void * pixels, int pitch)
--- a/src/render/SDL_sysrender.h	Fri Jun 01 19:42:15 2012 -0300
+++ b/src/render/SDL_sysrender.h	Fri Jun 01 19:51:08 2012 -0300
@@ -88,6 +88,9 @@
                             int count);
     int (*RenderCopy) (SDL_Renderer * renderer, SDL_Texture * texture,
                        const SDL_Rect * srcrect, const SDL_Rect * dstrect);
+    int (*RenderCopyEx) (SDL_Renderer * renderer, SDL_Texture * texture,
+                       const SDL_Rect * srcquad, const SDL_Rect * dstrect,
+                       const double angle, const SDL_Point *center, const SDL_RendererFlip flip);
     int (*RenderReadPixels) (SDL_Renderer * renderer, const SDL_Rect * rect,
                              Uint32 format, void * pixels, int pitch);
     void (*RenderPresent) (SDL_Renderer * renderer);
--- a/src/render/direct3d/SDL_render_d3d.c	Fri Jun 01 19:42:15 2012 -0300
+++ b/src/render/direct3d/SDL_render_d3d.c	Fri Jun 01 19:51:08 2012 -0300
@@ -29,12 +29,97 @@
 #include "SDL_loadso.h"
 #include "SDL_syswm.h"
 #include "../SDL_sysrender.h"
+#include "stdio.h"
 
 #if SDL_VIDEO_RENDER_D3D
 #define D3D_DEBUG_INFO
 #include <d3d9.h>
 #endif
 
+
+typedef interface ID3DXMatrixStack *LPD3DXMATRIXSTACK;
+typedef struct _D3DMATRIX D3DXMATRIX, *LPD3DXMATRIX;
+typedef struct _D3DVECTOR D3DXVECTOR3, *LPD3DXVECTOR3;
+
+DEFINE_GUID(IID_ID3DXMatrixStack,
+0xc7885ba7, 0xf990, 0x4fe7, 0x92, 0x2d, 0x85, 0x15, 0xe4, 0x77, 0xdd, 0x85);
+
+#undef INTERFACE
+#define INTERFACE ID3DXMatrixStack
+
+DECLARE_INTERFACE_(ID3DXMatrixStack, IUnknown)
+{
+    STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID * ppvObj) PURE;
+    STDMETHOD_(ULONG,AddRef)(THIS) PURE;
+    STDMETHOD_(ULONG,Release)(THIS) PURE;
+    STDMETHOD(Pop)(THIS) PURE;
+    STDMETHOD(Push)(THIS) PURE;
+    STDMETHOD(LoadIdentity)(THIS) PURE;
+    STDMETHOD(LoadMatrix)(THIS_ CONST D3DXMATRIX* pM ) PURE;
+    STDMETHOD(MultMatrix)(THIS_ CONST D3DXMATRIX* pM ) PURE;
+    STDMETHOD(MultMatrixLocal)(THIS_ CONST D3DXMATRIX* pM ) PURE;
+    STDMETHOD(RotateAxis)(THIS_ CONST D3DXVECTOR3* pV, FLOAT Angle) PURE;
+    STDMETHOD(RotateAxisLocal)(THIS_ CONST D3DXVECTOR3* pV, FLOAT Angle) PURE;
+    STDMETHOD(RotateYawPitchRoll)(THIS_ FLOAT Yaw, FLOAT Pitch, FLOAT Roll) PURE;
+    STDMETHOD(RotateYawPitchRollLocal)(THIS_ FLOAT Yaw, FLOAT Pitch, FLOAT Roll) PURE;
+    STDMETHOD(Scale)(THIS_ FLOAT x, FLOAT y, FLOAT z) PURE;
+    STDMETHOD(ScaleLocal)(THIS_ FLOAT x, FLOAT y, FLOAT z) PURE;
+    STDMETHOD(Translate)(THIS_ FLOAT x, FLOAT y, FLOAT z ) PURE;
+    STDMETHOD(TranslateLocal)(THIS_ FLOAT x, FLOAT y, FLOAT z) PURE;
+    STDMETHOD_(D3DXMATRIX*, GetTop)(THIS) PURE;
+};
+
+#undef INTERFACE
+
+#if !defined(__cplusplus) || defined(CINTERFACE)
+#define ID3DXMatrixStack_QueryInterface(p,a,b)            (p)->lpVtbl->QueryInterface(p,a,b)
+#define ID3DXMatrixStack_AddRef(p)                        (p)->lpVtbl->AddRef(p)
+#define ID3DXMatrixStack_Release(p)                       (p)->lpVtbl->Release(p)
+#define ID3DXMatrixStack_Pop(p)                           (p)->lpVtbl->Pop(p)
+#define ID3DXMatrixStack_Push(p)                          (p)->lpVtbl->Push(p)
+#define ID3DXMatrixStack_LoadIdentity(p)                  (p)->lpVtbl->LoadIdentity(p)
+#define ID3DXMatrixStack_LoadMatrix(p,a)                  (p)->lpVtbl->LoadMatrix(p,a)
+#define ID3DXMatrixStack_MultMatrix(p,a)                  (p)->lpVtbl->MultMatrix(p,a)
+#define ID3DXMatrixStack_MultMatrixLocal(p,a)             (p)->lpVtbl->MultMatrixLocal(p,a)
+#define ID3DXMatrixStack_RotateAxis(p,a,b)                (p)->lpVtbl->RotateAxis(p,a,b)
+#define ID3DXMatrixStack_RotateAxisLocal(p,a,b)           (p)->lpVtbl->RotateAxisLocal(p,a,b)
+#define ID3DXMatrixStack_RotateYawPitchRoll(p,a,b,c)      (p)->lpVtbl->RotateYawPitchRoll(p,a,b,c)
+#define ID3DXMatrixStack_RotateYawPitchRollLocal(p,a,b,c) (p)->lpVtbl->RotateYawPitchRollLocal(p,a,b,c)
+#define ID3DXMatrixStack_Scale(p,a,b,c)                   (p)->lpVtbl->Scale(p,a,b,c)
+#define ID3DXMatrixStack_ScaleLocal(p,a,b,c)              (p)->lpVtbl->ScaleLocal(p,a,b,c)
+#define ID3DXMatrixStack_Translate(p,a,b,c)               (p)->lpVtbl->Translate(p,a,b,c)
+#define ID3DXMatrixStack_TranslateLocal(p,a,b,c)          (p)->lpVtbl->TranslateLocal(p,a,b,c)
+#define ID3DXMatrixStack_GetTop(p)                        (p)->lpVtbl->GetTop(p)
+#else
+#define ID3DXMatrixStack_QueryInterface(p,a,b)            (p)->QueryInterface(a,b)
+#define ID3DXMatrixStack_AddRef(p)                        (p)->AddRef()
+#define ID3DXMatrixStack_Release(p)                       (p)->Release()
+#define ID3DXMatrixStack_Pop(p)    (p)->Pop()
+#define ID3DXMatrixStack_Push(p)    (p)->Push()
+#define ID3DXMatrixStack_LoadIdentity(p)    (p)->LoadIdentity()
+#define ID3DXMatrixStack_LoadMatrix(p,a)    (p)->LoadMatrix(a)
+#define ID3DXMatrixStack_MultMatrix(p,a)    (p)->MultMatrix(a)
+#define ID3DXMatrixStack_MultMatrixLocal(p,a)    (p)->MultMatrixLocal(a)
+#define ID3DXMatrixStack_RotateAxis(p,a,b)    (p)->RotateAxis(a,b)
+#define ID3DXMatrixStack_RotateAxisLocal(p,a,b)    (p)->RotateAxisLocal(a,b)
+#define ID3DXMatrixStack_RotateYawPitchRoll(p,a,b,c)    (p)->RotateYawPitchRollLocal(a,b,c)
+#define ID3DXMatrixStack_Scale(p,a,b,c)    (p)->Scale(a,b,c)
+#define ID3DXMatrixStack_ScaleLocal(p,a,b,c)    (p)->ScaleLocal(a,b,c)
+#define ID3DXMatrixStack_Translate(p,a,b,c)    (p)->Translate(a,b,c)
+#define ID3DXMatrixStack_TranslateLocal(p,a,b,c)    (p)->TranslateLocal(a,b,c)
+#define ID3DXMatrixStack_GetTop(p)    (p)->GetTop()
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+HRESULT WINAPI D3DXCreateMatrixStack(DWORD flags, LPD3DXMATRIXSTACK* ppstack);
+
+#ifdef __cplusplus
+}
+#endif
+
 #ifdef ASSEMBLE_SHADER
 ///////////////////////////////////////////////////////////////////////////
 // ID3DXBuffer:
@@ -110,6 +195,9 @@
                                const SDL_Rect * rects, int count);
 static int D3D_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
                           const SDL_Rect * srcrect, const SDL_Rect * dstrect);
+static int D3D_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
+                          const SDL_Rect * srcrect, const SDL_Rect * dstrect,
+                          const double angle, const SDL_Point * center, const SDL_RendererFlip flip);
 static int D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                                 Uint32 format, void * pixels, int pitch);
 static void D3D_RenderPresent(SDL_Renderer * renderer);
@@ -141,6 +229,8 @@
     D3DTEXTUREFILTERTYPE scaleMode;
     IDirect3DSurface9 *defaultRenderTarget;
     IDirect3DSurface9 *currentRenderTarget;
+    void* d3dxDLL;
+    ID3DXMatrixStack *matrixStack;
 } D3D_RenderData;
 
 typedef struct
@@ -347,6 +437,8 @@
     int w, h;
     SDL_DisplayMode fullscreen_mode;
     D3DMATRIX matrix;
+    int d3dxVersion;
+	char d3dxDLLFile[50];
 
     renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer));
     if (!renderer) {
@@ -375,8 +467,28 @@
             SDL_UnloadObject(data->d3dDLL);
             data->d3dDLL = NULL;
         }
+
+        for (d3dxVersion=50;d3dxVersion>0;d3dxVersion--) {
+            SDL_snprintf(d3dxDLLFile, 49, "D3DX9_%02d.dll", d3dxVersion);
+            data->d3dxDLL = SDL_LoadObject(d3dxDLLFile);
+            if (data->d3dxDLL) {
+                HRESULT (WINAPI *D3DXCreateMatrixStack) (DWORD Flags, LPD3DXMATRIXSTACK*  ppStack);
+                D3DXCreateMatrixStack = (HRESULT (WINAPI *) (DWORD, LPD3DXMATRIXSTACK*)) SDL_LoadFunction(data->d3dxDLL, "D3DXCreateMatrixStack");
+                if (D3DXCreateMatrixStack) {
+                    D3DXCreateMatrixStack(0, &data->matrixStack);
+                    break;
+                }
+            }
+        }
+
+        if (!data->matrixStack) {
+            if (data->d3dxDLL) SDL_UnloadObject(data->d3dxDLL);
+        }
     }
-    if (!data->d3d) {
+
+
+    
+    if (!data->d3d || !data->matrixStack) {
         SDL_free(renderer);
         SDL_free(data);
         SDL_SetError("Unable to create Direct3D interface");
@@ -395,6 +507,7 @@
     renderer->RenderDrawLines = D3D_RenderDrawLines;
     renderer->RenderFillRects = D3D_RenderFillRects;
     renderer->RenderCopy = D3D_RenderCopy;
+    renderer->RenderCopyEx = D3D_RenderCopyEx;
     renderer->RenderReadPixels = D3D_RenderReadPixels;
     renderer->RenderPresent = D3D_RenderPresent;
     renderer->DestroyTexture = D3D_DestroyTexture;
@@ -1126,6 +1239,135 @@
     return 0;
 }
 
+
+static int
+D3D_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
+               const SDL_Rect * srcrect, const SDL_Rect * dstrect,
+               const double angle, const SDL_Point * center, const SDL_RendererFlip flip)
+{
+    D3D_RenderData *data = (D3D_RenderData *) renderer->driverdata;
+    D3D_TextureData *texturedata = (D3D_TextureData *) texture->driverdata;
+    LPDIRECT3DPIXELSHADER9 shader = NULL;
+    float minx, miny, maxx, maxy;
+    float minu, maxu, minv, maxv;
+    float centerx, centery;
+    DWORD color;
+    Vertex vertices[4];
+    HRESULT result;
+
+    if (D3D_ActivateRenderer(renderer) < 0) {
+        return -1;
+    }
+
+    centerx = (float)center->x;
+    centery = (float)center->y;
+
+    if (flip & SDL_FLIP_HORIZONTAL) {
+        minx = (float) dstrect->w - centerx - 0.5f;
+        maxx = (float) -centerx - 0.5f;
+    }
+    else {
+        minx = (float) -centerx - 0.5f;
+        maxx = (float) dstrect->w - centerx - 0.5f;
+    }
+
+    if (flip & SDL_FLIP_VERTICAL) {
+        miny = (float) dstrect->h - centery - 0.5f;
+        maxy = (float) -centery - 0.5f;
+    }
+    else {
+        miny = (float) -centery - 0.5f;
+        maxy = (float) dstrect->h - centery - 0.5f;
+    }
+
+    minu = (float) srcrect->x / texture->w;
+    maxu = (float) (srcrect->x + srcrect->w) / texture->w;
+    minv = (float) srcrect->y / texture->h;
+    maxv = (float) (srcrect->y + srcrect->h) / texture->h;
+
+    color = D3DCOLOR_ARGB(texture->a, texture->r, texture->g, texture->b);
+
+    vertices[0].x = minx;
+    vertices[0].y = miny;
+    vertices[0].z = 0.0f;
+    vertices[0].color = color;
+    vertices[0].u = minu;
+    vertices[0].v = minv;
+
+    vertices[1].x = maxx;
+    vertices[1].y = miny;
+    vertices[1].z = 0.0f;
+    vertices[1].color = color;
+    vertices[1].u = maxu;
+    vertices[1].v = minv;
+
+    vertices[2].x = maxx;
+    vertices[2].y = maxy;
+    vertices[2].z = 0.0f;
+    vertices[2].color = color;
+    vertices[2].u = maxu;
+    vertices[2].v = maxv;
+
+    vertices[3].x = minx;
+    vertices[3].y = maxy;
+    vertices[3].z = 0.0f;
+    vertices[3].color = color;
+    vertices[3].u = minu;
+    vertices[3].v = maxv;
+
+    D3D_SetBlendMode(data, texture->blendMode);
+
+    // Rotate and translate
+    ID3DXMatrixStack_Push(data->matrixStack);
+    ID3DXMatrixStack_LoadIdentity(data->matrixStack);
+    ID3DXMatrixStack_RotateYawPitchRoll(data->matrixStack, 0.0, 0.0, M_PI * (float) angle / 180.0f);
+    ID3DXMatrixStack_Translate(data->matrixStack, (float)dstrect->x + centerx, (float)dstrect->y + centery, (float)0.0);
+    IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*)ID3DXMatrixStack_GetTop(data->matrixStack));
+
+    if (texturedata->scaleMode != data->scaleMode) {
+        IDirect3DDevice9_SetSamplerState(data->device, 0, D3DSAMP_MINFILTER,
+                                         texturedata->scaleMode);
+        IDirect3DDevice9_SetSamplerState(data->device, 0, D3DSAMP_MAGFILTER,
+                                         texturedata->scaleMode);
+        data->scaleMode = texturedata->scaleMode;
+    }
+
+    result =
+        IDirect3DDevice9_SetTexture(data->device, 0, (IDirect3DBaseTexture9 *)
+                                    texturedata->texture);
+    if (FAILED(result)) {
+        D3D_SetError("SetTexture()", result);
+        return -1;
+    }
+    if (shader) {
+        result = IDirect3DDevice9_SetPixelShader(data->device, shader);
+        if (FAILED(result)) {
+            D3D_SetError("SetShader()", result);
+            return -1;
+        }
+    }
+    result =
+        IDirect3DDevice9_DrawPrimitiveUP(data->device, D3DPT_TRIANGLEFAN, 2,
+                                         vertices, sizeof(*vertices));
+    if (FAILED(result)) {
+        D3D_SetError("DrawPrimitiveUP()", result);
+        return -1;
+    }
+    if (shader) {
+        result = IDirect3DDevice9_SetPixelShader(data->device, NULL);
+        if (FAILED(result)) {
+            D3D_SetError("SetShader()", result);
+            return -1;
+        }
+    }
+    ID3DXMatrixStack_Pop(data->matrixStack);
+    ID3DXMatrixStack_Push(data->matrixStack);
+    ID3DXMatrixStack_LoadIdentity(data->matrixStack);
+    IDirect3DDevice9_SetTransform(data->device, D3DTS_VIEW, (D3DMATRIX*)ID3DXMatrixStack_GetTop(data->matrixStack));
+    ID3DXMatrixStack_Pop(data->matrixStack);
+    return 0;
+}
+
 static int
 D3D_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                      Uint32 format, void * pixels, int pitch)
--- a/src/render/opengl/SDL_glfuncs.h	Fri Jun 01 19:42:15 2012 -0300
+++ b/src/render/opengl/SDL_glfuncs.h	Fri Jun 01 19:51:08 2012 -0300
@@ -279,14 +279,14 @@
 SDL_PROC_UNUSED(void, glPolygonStipple, (const GLubyte * mask))
 SDL_PROC_UNUSED(void, glPopAttrib, (void))
 SDL_PROC_UNUSED(void, glPopClientAttrib, (void))
-SDL_PROC_UNUSED(void, glPopMatrix, (void))
+SDL_PROC(void, glPopMatrix, (void))
 SDL_PROC_UNUSED(void, glPopName, (void))
 SDL_PROC_UNUSED(void, glPrioritizeTextures,
                 (GLsizei n, const GLuint * textures,
                  const GLclampf * priorities))
 SDL_PROC_UNUSED(void, glPushAttrib, (GLbitfield mask))
 SDL_PROC_UNUSED(void, glPushClientAttrib, (GLbitfield mask))
-SDL_PROC_UNUSED(void, glPushMatrix, (void))
+SDL_PROC(void, glPushMatrix, (void))
 SDL_PROC_UNUSED(void, glPushName, (GLuint name))
 SDL_PROC_UNUSED(void, glRasterPos2d, (GLdouble x, GLdouble y))
 SDL_PROC_UNUSED(void, glRasterPos2dv, (const GLdouble * v))
@@ -331,7 +331,7 @@
                 (GLshort x1, GLshort y1, GLshort x2, GLshort y2))
 SDL_PROC_UNUSED(void, glRectsv, (const GLshort * v1, const GLshort * v2))
 SDL_PROC_UNUSED(GLint, glRenderMode, (GLenum mode))
-SDL_PROC_UNUSED(void, glRotated,
+SDL_PROC(void, glRotated,
                 (GLdouble angle, GLdouble x, GLdouble y, GLdouble z))
 SDL_PROC_UNUSED(void, glRotatef,
                 (GLfloat angle, GLfloat x, GLfloat y, GLfloat z))
@@ -419,7 +419,7 @@
           GLsizei width, GLsizei height, GLenum format, GLenum type,
           const GLvoid * pixels))
 SDL_PROC_UNUSED(void, glTranslated, (GLdouble x, GLdouble y, GLdouble z))
-SDL_PROC_UNUSED(void, glTranslatef, (GLfloat x, GLfloat y, GLfloat z))
+SDL_PROC(void, glTranslatef, (GLfloat x, GLfloat y, GLfloat z))
 SDL_PROC_UNUSED(void, glVertex2d, (GLdouble x, GLdouble y))
 SDL_PROC_UNUSED(void, glVertex2dv, (const GLdouble * v))
 SDL_PROC(void, glVertex2f, (GLfloat x, GLfloat y))
--- a/src/render/opengl/SDL_render_gl.c	Fri Jun 01 19:42:15 2012 -0300
+++ b/src/render/opengl/SDL_render_gl.c	Fri Jun 01 19:51:08 2012 -0300
@@ -65,6 +65,9 @@
                               const SDL_Rect * rects, int count);
 static int GL_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
                          const SDL_Rect * srcrect, const SDL_Rect * dstrect);
+static int GL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
+                         const SDL_Rect * srcrect, const SDL_Rect * dstrect,
+                         const double angle, const SDL_Point *center, const SDL_RendererFlip flip);
 static int GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                                Uint32 pixel_format, void * pixels, int pitch);
 static void GL_RenderPresent(SDL_Renderer * renderer);
@@ -314,6 +317,7 @@
     renderer->RenderDrawLines = GL_RenderDrawLines;
     renderer->RenderFillRects = GL_RenderFillRects;
     renderer->RenderCopy = GL_RenderCopy;
+    renderer->RenderCopyEx = GL_RenderCopyEx;
     renderer->RenderReadPixels = GL_RenderReadPixels;
     renderer->RenderPresent = GL_RenderPresent;
     renderer->DestroyTexture = GL_DestroyTexture;
@@ -1019,6 +1023,96 @@
 }
 
 static int
+GL_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
+              const SDL_Rect * srcrect, const SDL_Rect * dstrect,
+              const double angle, const SDL_Point *center, const SDL_RendererFlip flip)
+{
+    GL_RenderData *data = (GL_RenderData *) renderer->driverdata;
+    GL_TextureData *texturedata = (GL_TextureData *) texture->driverdata;
+    GLfloat minx, miny, maxx, maxy;
+    GLfloat centerx, centery;
+    GLfloat minu, maxu, minv, maxv;
+    GL_ActivateRenderer(renderer);
+
+    data->glEnable(texturedata->type);
+    if (texturedata->yuv) {
+        data->glActiveTextureARB(GL_TEXTURE2_ARB);
+        data->glBindTexture(texturedata->type, texturedata->vtexture);
+
+        data->glActiveTextureARB(GL_TEXTURE1_ARB);
+        data->glBindTexture(texturedata->type, texturedata->utexture);
+
+        data->glActiveTextureARB(GL_TEXTURE0_ARB);
+    }
+    data->glBindTexture(texturedata->type, texturedata->texture);
+
+    if (texture->modMode) {
+        GL_SetColor(data, texture->r, texture->g, texture->b, texture->a);
+    } else {
+        GL_SetColor(data, 255, 255, 255, 255);
+    }
+
+    GL_SetBlendMode(data, texture->blendMode);
+
+    if (texturedata->yuv) {
+        GL_SetShader(data, SHADER_YV12);
+    } else {
+        GL_SetShader(data, SHADER_RGB);
+    }
+
+    centerx = (GLfloat)center->x;
+    centery = (GLfloat)center->y;
+
+    if (flip & SDL_FLIP_HORIZONTAL) {
+        minx = (GLfloat) dstrect->w - centerx;
+        maxx = -centerx;
+    }
+    else {
+        minx = -centerx;
+        maxx = (GLfloat) dstrect->w - centerx;
+    }
+
+    if (flip & SDL_FLIP_VERTICAL) {
+        miny = (GLfloat) dstrect->h - centery;
+        maxy = -centery;
+    }
+    else {
+        miny = -centery;
+        maxy = (GLfloat) dstrect->h - centery;
+    }
+
+    minu = (GLfloat) srcrect->x / texture->w;
+    minu *= texturedata->texw;
+    maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
+    maxu *= texturedata->texw;
+    minv = (GLfloat) srcrect->y / texture->h;
+    minv *= texturedata->texh;
+    maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
+    maxv *= texturedata->texh;
+
+    // Translate to flip, rotate, translate to position
+    data->glPushMatrix();
+    data->glTranslatef((GLfloat)dstrect->x + centerx, (GLfloat)dstrect->y + centery, (GLfloat)0.0);    
+    data->glRotated(angle, (GLdouble)0.0, (GLdouble)0.0, (GLdouble)1.0);
+    
+    data->glBegin(GL_TRIANGLE_STRIP);
+    data->glTexCoord2f(minu, minv);
+    data->glVertex2f(minx, miny);
+    data->glTexCoord2f(maxu, minv);
+    data->glVertex2f(maxx, miny);
+    data->glTexCoord2f(minu, maxv);
+    data->glVertex2f(minx, maxy);
+    data->glTexCoord2f(maxu, maxv);
+    data->glVertex2f(maxx, maxy);
+    data->glEnd();
+    data->glPopMatrix();
+    
+    data->glDisable(texturedata->type);
+
+    return 0;
+}
+
+static int
 GL_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                     Uint32 pixel_format, void * pixels, int pitch)
 {
--- a/src/render/opengles/SDL_render_gles.c	Fri Jun 01 19:42:15 2012 -0300
+++ b/src/render/opengles/SDL_render_gles.c	Fri Jun 01 19:51:08 2012 -0300
@@ -71,6 +71,9 @@
                            const SDL_Rect * dstrect);
 static int GLES_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                     Uint32 pixel_format, void * pixels, int pitch);
+static int GLES_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
+                         const SDL_Rect * srcrect, const SDL_Rect * dstrect,
+                         const double angle, const SDL_Point *center, const SDL_RendererFlip flip);
 static void GLES_RenderPresent(SDL_Renderer * renderer);
 static void GLES_DestroyTexture(SDL_Renderer * renderer,
                                 SDL_Texture * texture);
@@ -304,6 +307,7 @@
     renderer->RenderFillRects = GLES_RenderFillRects;
     renderer->RenderCopy = GLES_RenderCopy;
     renderer->RenderReadPixels = GLES_RenderReadPixels;
+    renderer->RenderCopyEx = GLES_RenderCopyEx;
     renderer->RenderPresent = GLES_RenderPresent;
     renderer->DestroyTexture = GLES_DestroyTexture;
     renderer->DestroyRenderer = GLES_DestroyRenderer;
@@ -958,6 +962,98 @@
     return status;
 }
 
+static int
+GLES_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
+                const SDL_Rect * srcrect, const SDL_Rect * dstrect,
+                const double angle, const SDL_Point *center, const SDL_RendererFlip flip)
+{
+
+    GLES_RenderData *data = (GLES_RenderData *) renderer->driverdata;
+    GLES_TextureData *texturedata = (GLES_TextureData *) texture->driverdata;
+    int minx, miny, maxx, maxy;
+    GLfloat minu, maxu, minv, maxv;
+    GLfloat centerx, centery;
+    
+    GLES_ActivateRenderer(renderer);
+
+    data->glEnable(GL_TEXTURE_2D);
+
+    data->glBindTexture(texturedata->type, texturedata->texture);
+
+    if (texture->modMode) {
+        GLES_SetColor(data, texture->r, texture->g, texture->b, texture->a);
+    } else {
+        GLES_SetColor(data, 255, 255, 255, 255);
+    }
+
+    GLES_SetBlendMode(data, texture->blendMode);
+
+    GLES_SetTexCoords(data, SDL_TRUE);
+
+    centerx = (GLfloat)center->x;
+    centery = (GLfloat)center->y;
+
+    // Rotate and translate
+    data->glPushMatrix();
+    data->glTranslatef((GLfloat)dstrect->x + centerx, (GLfloat)dstrect->y + centery, (GLfloat)0.0);
+    data->glRotatef((GLfloat)angle, (GLfloat)0.0, (GLfloat)0.0, (GLfloat)1.0);
+
+    if (flip & SDL_FLIP_HORIZONTAL) {
+        minx = (GLfloat) dstrect->w - centerx;
+        maxx = -centerx;
+    }
+    else {
+        minx = -centerx;
+        maxx = dstrect->w - centerx;
+    }
+
+    if (flip & SDL_FLIP_VERTICAL) {
+        miny = dstrect->h - centery;
+        maxy = -centery;
+    }
+    else {
+        miny = -centery;
+        maxy = dstrect->h - centery;
+    }
+
+    minu = (GLfloat) srcrect->x / texture->w;
+    minu *= texturedata->texw;
+    maxu = (GLfloat) (srcrect->x + srcrect->w) / texture->w;
+    maxu *= texturedata->texw;
+    minv = (GLfloat) srcrect->y / texture->h;
+    minv *= texturedata->texh;
+    maxv = (GLfloat) (srcrect->y + srcrect->h) / texture->h;
+    maxv *= texturedata->texh;
+
+    GLshort vertices[8];
+    GLfloat texCoords[8];
+
+    vertices[0] = minx;
+    vertices[1] = miny;
+    vertices[2] = maxx;
+    vertices[3] = miny;
+    vertices[4] = minx;
+    vertices[5] = maxy;
+    vertices[6] = maxx;
+    vertices[7] = maxy;
+
+    texCoords[0] = minu;
+    texCoords[1] = minv;
+    texCoords[2] = maxu;
+    texCoords[3] = minv;
+    texCoords[4] = minu;
+    texCoords[5] = maxv;
+    texCoords[6] = maxu;
+    texCoords[7] = maxv;
+    data->glVertexPointer(2, GL_SHORT, 0, vertices);
+    data->glTexCoordPointer(2, GL_FLOAT, 0, texCoords);
+    data->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    data->glPopMatrix();
+    data->glDisable(GL_TEXTURE_2D);
+
+    return 0;
+}
+
 static void
 GLES_RenderPresent(SDL_Renderer * renderer)
 {
--- a/src/render/opengles2/SDL_render_gles2.c	Fri Jun 01 19:42:15 2012 -0300
+++ b/src/render/opengles2/SDL_render_gles2.c	Fri Jun 01 19:51:08 2012 -0300
@@ -112,7 +112,9 @@
 typedef enum
 {
     GLES2_ATTRIBUTE_POSITION = 0,
-    GLES2_ATTRIBUTE_TEXCOORD = 1
+    GLES2_ATTRIBUTE_TEXCOORD = 1,
+    GLES2_ATTRIBUTE_ANGLE = 2,
+    GLES2_ATTRIBUTE_CENTER = 3,
 } GLES2_Attribute;
 
 typedef enum
@@ -628,6 +630,8 @@
     rdata->glAttachShader(entry->id, fragment->id);
     rdata->glBindAttribLocation(entry->id, GLES2_ATTRIBUTE_POSITION, "a_position");
     rdata->glBindAttribLocation(entry->id, GLES2_ATTRIBUTE_TEXCOORD, "a_texCoord");
+    rdata->glBindAttribLocation(entry->id, GLES2_ATTRIBUTE_ANGLE, "a_angle");
+    rdata->glBindAttribLocation(entry->id, GLES2_ATTRIBUTE_CENTER, "a_center");
     rdata->glLinkProgram(entry->id);
     rdata->glGetProgramiv(entry->id, GL_LINK_STATUS, &linkSuccessful);
     if (rdata->glGetError() != GL_NO_ERROR || !linkSuccessful)
@@ -940,6 +944,9 @@
                             const SDL_Rect *dstrect);
 static int GLES2_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                     Uint32 pixel_format, void * pixels, int pitch);
+static int GLES2_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
+                         const SDL_Rect * srcrect, const SDL_Rect * dstrect,
+                         const double angle, const SDL_Point *center, const SDL_RendererFlip flip);
 static void GLES2_RenderPresent(SDL_Renderer *renderer);
 
 
@@ -1241,6 +1248,175 @@
     /* Select the target texture */
     locTexture = rdata->current_program->uniform_locations[GLES2_UNIFORM_TEXTURE];
     rdata->glGetError();
+	rdata->glActiveTexture(GL_TEXTURE0);
+    rdata->glBindTexture(tdata->texture_type, tdata->texture);
+    rdata->glUniform1i(locTexture, 0);
+
+    /* Configure color modulation */
+    locModulation = rdata->current_program->uniform_locations[GLES2_UNIFORM_MODULATION];
+    rdata->glUniform4f(locModulation,
+                texture->r * inv255f,
+                texture->g * inv255f,
+                texture->b * inv255f,
+                texture->a * inv255f);
+
+    /* Configure texture blending */
+    GLES2_SetBlendMode(rdata, blendMode);
+
+    GLES2_SetTexCoords(rdata, SDL_TRUE);
+
+    /* Emit the textured quad */
+    if (renderer->target) {
+        // Flip the texture vertically to compensate for the inversion it'll be subjected to later when it's rendered to the screen
+        vertices[0] = (GLfloat)dstrect->x;
+        vertices[1] = (GLfloat)renderer->viewport.h-dstrect->y;
+        vertices[2] = (GLfloat)(dstrect->x + dstrect->w);
+        vertices[3] = (GLfloat)renderer->viewport.h-dstrect->y;
+        vertices[4] = (GLfloat)dstrect->x;
+        vertices[5] = (GLfloat)renderer->viewport.h-(dstrect->y + dstrect->h);
+        vertices[6] = (GLfloat)(dstrect->x + dstrect->w);
+        vertices[7] = (GLfloat)renderer->viewport.h-(dstrect->y + dstrect->h);
+    }
+    else {
+        vertices[0] = (GLfloat)dstrect->x;
+        vertices[1] = (GLfloat)dstrect->y;
+        vertices[2] = (GLfloat)(dstrect->x + dstrect->w);
+        vertices[3] = (GLfloat)dstrect->y;
+        vertices[4] = (GLfloat)dstrect->x;
+        vertices[5] = (GLfloat)(dstrect->y + dstrect->h);
+        vertices[6] = (GLfloat)(dstrect->x + dstrect->w);
+        vertices[7] = (GLfloat)(dstrect->y + dstrect->h);
+    }
+    rdata->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
+    texCoords[0] = srcrect->x / (GLfloat)texture->w;
+    texCoords[1] = srcrect->y / (GLfloat)texture->h;
+    texCoords[2] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
+    texCoords[3] = srcrect->y / (GLfloat)texture->h;
+    texCoords[4] = srcrect->x / (GLfloat)texture->w;
+    texCoords[5] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
+    texCoords[6] = (srcrect->x + srcrect->w) / (GLfloat)texture->w;
+    texCoords[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
+    rdata->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
+    rdata->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    if (rdata->glGetError() != GL_NO_ERROR)
+    {
+        SDL_SetError("Failed to render texture");
+        return -1;
+    }
+    return 0;
+}
+
+static int
+GLES2_RenderCopyEx(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect,
+                 const SDL_Rect *dstrect, const double angle, const SDL_Point *center, const SDL_RendererFlip flip)
+{
+    GLES2_DriverContext *rdata = (GLES2_DriverContext *)renderer->driverdata;
+    GLES2_TextureData *tdata = (GLES2_TextureData *)texture->driverdata;
+    GLES2_ImageSource sourceType;
+    SDL_BlendMode blendMode;
+    GLfloat vertices[8];
+    GLfloat texCoords[8];
+    GLuint locTexture;
+    GLuint locModulation;
+    GLfloat translate[8];
+    GLfloat fAngle[4];
+    GLfloat tmp;
+
+    GLES2_ActivateRenderer(renderer);
+    
+    rdata->glEnableVertexAttribArray(GLES2_ATTRIBUTE_CENTER);
+    rdata->glEnableVertexAttribArray(GLES2_ATTRIBUTE_ANGLE);
+    fAngle[0] = fAngle[1] = fAngle[2] = fAngle[3] = (GLfloat)angle;
+    /* Calculate the center of rotation */
+    translate[0] = translate[2] = translate[4] = translate[6] = (GLfloat)(center->x + dstrect->x);
+    translate[1] = translate[3] = translate[5] = translate[7] = (GLfloat)(center->y + dstrect->y);
+
+    /* Activate an appropriate shader and set the projection matrix */
+    blendMode = texture->blendMode;
+    if (renderer->target) {
+        /* Check if we need to do color mapping between the source and render target textures */
+        if (renderer->target->format != texture->format) {
+            switch (texture->format)
+            {
+            case SDL_PIXELFORMAT_ABGR8888:
+                switch (renderer->target->format)
+                {
+                    case SDL_PIXELFORMAT_ARGB8888:
+                    case SDL_PIXELFORMAT_RGB888:
+                        sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
+                        break;
+                    case SDL_PIXELFORMAT_BGR888:
+                        sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR;
+                        break;
+                }
+                break;
+            case SDL_PIXELFORMAT_ARGB8888:
+                switch (renderer->target->format)
+                {
+                    case SDL_PIXELFORMAT_ABGR8888:
+                    case SDL_PIXELFORMAT_BGR888:
+                        sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
+                        break;
+                    case SDL_PIXELFORMAT_RGB888:
+                        sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR;
+                        break;
+                }
+                break;
+            case SDL_PIXELFORMAT_BGR888:
+                switch (renderer->target->format)
+                {
+                    case SDL_PIXELFORMAT_ABGR8888:
+                        sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR;
+                        break;
+                    case SDL_PIXELFORMAT_ARGB8888:
+                        sourceType = GLES2_IMAGESOURCE_TEXTURE_RGB;
+                        break;
+                    case SDL_PIXELFORMAT_RGB888:
+                        sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
+                        break;
+                }
+                break;
+            case SDL_PIXELFORMAT_RGB888:
+                switch (renderer->target->format)
+                {
+                    case SDL_PIXELFORMAT_ABGR8888:
+                        sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
+                        break;
+                    case SDL_PIXELFORMAT_ARGB8888:
+                        sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR;
+                        break;
+                    case SDL_PIXELFORMAT_BGR888:
+                        sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
+                        break;
+                }
+                break;
+            }
+        }
+        else sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR;   // Texture formats match, use the non color mapping shader (even if the formats are not ABGR)
+    }
+    else {
+        switch (texture->format)
+        {
+            case SDL_PIXELFORMAT_ABGR8888:
+                sourceType = GLES2_IMAGESOURCE_TEXTURE_ABGR;
+                break;
+            case SDL_PIXELFORMAT_ARGB8888:
+                sourceType = GLES2_IMAGESOURCE_TEXTURE_ARGB;
+                break;
+            case SDL_PIXELFORMAT_BGR888:
+                sourceType = GLES2_IMAGESOURCE_TEXTURE_BGR;
+                break;
+            case SDL_PIXELFORMAT_RGB888:
+                sourceType = GLES2_IMAGESOURCE_TEXTURE_RGB;
+                break;
+        }
+    }
+    if (GLES2_SelectProgram(renderer, sourceType, blendMode) < 0)
+        return -1;
+
+    /* Select the target texture */
+    locTexture = rdata->current_program->uniform_locations[GLES2_UNIFORM_TEXTURE];
+    rdata->glGetError();
     rdata->glActiveTexture(GL_TEXTURE0);
     rdata->glBindTexture(tdata->texture_type, tdata->texture);
     rdata->glUniform1i(locTexture, 0);
@@ -1280,6 +1456,19 @@
         vertices[6] = (GLfloat)(dstrect->x + dstrect->w);
         vertices[7] = (GLfloat)(dstrect->y + dstrect->h);
     }
+    if (flip & SDL_FLIP_HORIZONTAL) {
+        tmp = vertices[0];
+        vertices[0] = vertices[4] = vertices[2];
+        vertices[2] = vertices[6] = tmp;
+    }
+    if (flip & SDL_FLIP_VERTICAL) {
+        tmp = vertices[1];
+        vertices[1] = vertices[3] = vertices[5];
+        vertices[5] = vertices[7] = tmp;
+    }
+    
+    rdata->glVertexAttribPointer(GLES2_ATTRIBUTE_ANGLE, 1, GL_FLOAT, GL_FALSE, 0, &fAngle);
+    rdata->glVertexAttribPointer(GLES2_ATTRIBUTE_CENTER, 2, GL_FLOAT, GL_FALSE, 0, translate);
     rdata->glVertexAttribPointer(GLES2_ATTRIBUTE_POSITION, 2, GL_FLOAT, GL_FALSE, 0, vertices);
 
     texCoords[0] = srcrect->x / (GLfloat)texture->w;
@@ -1292,6 +1481,8 @@
     texCoords[7] = (srcrect->y + srcrect->h) / (GLfloat)texture->h;
     rdata->glVertexAttribPointer(GLES2_ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
     rdata->glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+    rdata->glDisableVertexAttribArray(GLES2_ATTRIBUTE_CENTER);
+    rdata->glDisableVertexAttribArray(GLES2_ATTRIBUTE_ANGLE);
     if (rdata->glGetError() != GL_NO_ERROR)
     {
         SDL_SetError("Failed to render texture");
@@ -1504,6 +1695,7 @@
     renderer->RenderFillRects     = &GLES2_RenderFillRects;
     renderer->RenderCopy          = &GLES2_RenderCopy;
     renderer->RenderReadPixels    = &GLES2_RenderReadPixels;
+    renderer->RenderCopyEx        = &GLES2_RenderCopyEx;
     renderer->RenderPresent       = &GLES2_RenderPresent;
     renderer->DestroyTexture      = &GLES2_DestroyTexture;
     renderer->DestroyRenderer     = &GLES2_DestroyRenderer;
--- a/src/render/opengles2/SDL_shaders_gles2.c	Fri Jun 01 19:42:15 2012 -0300
+++ b/src/render/opengles2/SDL_shaders_gles2.c	Fri Jun 01 19:51:08 2012 -0300
@@ -33,14 +33,21 @@
 
 static const Uint8 GLES2_VertexSrc_Default_[] = " \
     uniform mat4 u_projection; \
-    attribute vec4 a_position; \
+    attribute vec2 a_position; \
     attribute vec2 a_texCoord; \
+    attribute float a_angle; \
+    attribute vec2 a_center; \
     varying vec2 v_texCoord; \
     \
     void main() \
     { \
+        float angle = radians(a_angle); \
+        float c = cos(angle); \
+        float s = sin(angle); \
+        mat2 rotationMatrix = mat2(c, -s, s, c); \
+        vec2 position = rotationMatrix * (a_position - a_center) + a_center; \
         v_texCoord = a_texCoord; \
-        gl_Position = u_projection * a_position; \
+        gl_Position = u_projection * vec4(position, 0.0, 1.0);\
         gl_PointSize = 1.0; \
     } \
 ";
--- a/src/render/software/SDL_render_sw.c	Fri Jun 01 19:42:15 2012 -0300
+++ b/src/render/software/SDL_render_sw.c	Fri Jun 01 19:51:08 2012 -0300
@@ -24,6 +24,7 @@
 
 #include "../SDL_sysrender.h"
 #include "SDL_render_sw_c.h"
+#include "SDL_hints.h"
 
 #include "SDL_draw.h"
 #include "SDL_blendfillrect.h"
@@ -31,7 +32,7 @@
 #include "SDL_blendpoint.h"
 #include "SDL_drawline.h"
 #include "SDL_drawpoint.h"
-
+#include "SDL_rotate.h"
 
 /* SDL surface based renderer implementation */
 
@@ -62,6 +63,9 @@
                               const SDL_Rect * rects, int count);
 static int SW_RenderCopy(SDL_Renderer * renderer, SDL_Texture * texture,
                          const SDL_Rect * srcrect, const SDL_Rect * dstrect);
+static int SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
+                          const SDL_Rect * srcrect, const SDL_Rect * dstrect,
+                          const double angle, const SDL_Point * center, const SDL_RendererFlip flip);
 static int SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                                Uint32 format, void * pixels, int pitch);
 static void SW_RenderPresent(SDL_Renderer * renderer);
@@ -152,6 +156,7 @@
     renderer->RenderDrawLines = SW_RenderDrawLines;
     renderer->RenderFillRects = SW_RenderFillRects;
     renderer->RenderCopy = SW_RenderCopy;
+    renderer->RenderCopyEx = SW_RenderCopyEx;
     renderer->RenderReadPixels = SW_RenderReadPixels;
     renderer->RenderPresent = SW_RenderPresent;
     renderer->DestroyTexture = SW_DestroyTexture;
@@ -496,6 +501,102 @@
 }
 
 static int
+GetScaleQuality(void)
+{
+    const char *hint = SDL_GetHint(SDL_HINT_RENDER_SCALE_QUALITY);
+
+    if (!hint || *hint == '0' || SDL_strcasecmp(hint, "nearest") == 0) {
+        return 0;
+    } else {
+        return 1;
+    }
+}
+
+static int
+SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Texture * texture,
+                const SDL_Rect * srcrect, const SDL_Rect * dstrect,
+                const double angle, const SDL_Point * center, const SDL_RendererFlip flip)
+{
+    SDL_Surface *surface = SW_ActivateRenderer(renderer);
+    SDL_Surface *src = (SDL_Surface *) texture->driverdata;
+    SDL_Rect final_rect = *dstrect, tmp_rect;
+    SDL_Surface *surface_rotated, *surface_scaled;
+    SDL_Point final_rect_center;
+    Uint32 colorkey;
+    int retval, dstwidth, dstheight, abscenterx, abscentery;
+    double cangle, sangle, px, py, p1x, p1y, p2x, p2y, p3x, p3y, p4x, p4y;
+
+    if (!surface) {
+        return -1;
+    }
+
+    if (renderer->viewport.x || renderer->viewport.y) {
+        final_rect.x += renderer->viewport.x;
+        final_rect.y += renderer->viewport.y;
+    }
+
+    surface_scaled = SDL_CreateRGBSurface(SDL_SWSURFACE, final_rect.w, final_rect.h, src->format->BitsPerPixel,
+                                          src->format->Rmask, src->format->Gmask,
+                                          src->format->Bmask, src->format->Amask );
+    SDL_GetColorKey(src, &colorkey);
+    SDL_SetColorKey(surface_scaled, SDL_TRUE, colorkey);
+    tmp_rect = final_rect;
+    tmp_rect.x = 0;
+    tmp_rect.y = 0;
+    if (surface_scaled) {
+        retval = SDL_BlitScaled(src, srcrect, surface_scaled, &tmp_rect);
+        if (!retval) {
+            _rotozoomSurfaceSizeTrig(tmp_rect.w, tmp_rect.h, -angle, &dstwidth, &dstheight, &cangle, &sangle);
+            surface_rotated = _rotateSurface(surface_scaled, -angle, dstwidth/2, dstheight/2, GetScaleQuality(), flip & SDL_FLIP_HORIZONTAL, flip & SDL_FLIP_VERTICAL, dstwidth, dstheight, cangle, sangle);
+            if(surface_rotated) {
+                /* Find out where the new origin is by rotating the four final_rect points around the center and then taking the extremes */
+                abscenterx = final_rect.x + center->x;
+                abscentery = final_rect.y + center->y;
+                /* Compensate the angle inversion to match the behaviour of the other backends */
+                sangle = -sangle;
+
+                /* Top Left */
+                px = final_rect.x - abscenterx;
+                py = final_rect.y - abscentery;
+                p1x = px * cangle - py * sangle + abscenterx;
+                p1y = px * sangle + py * cangle + abscentery;
+
+                /* Top Right */
+                px = final_rect.x + final_rect.w - abscenterx;
+                py = final_rect.y - abscentery;
+                p2x = px * cangle - py * sangle + abscenterx;
+                p2y = px * sangle + py * cangle + abscentery;
+
+                /* Bottom Left */
+                px = final_rect.x - abscenterx;
+                py = final_rect.y + final_rect.h - abscentery;
+                p3x = px * cangle - py * sangle + abscenterx;
+                p3y = px * sangle + py * cangle + abscentery;
+
+                /* Bottom Right */
+                px = final_rect.x + final_rect.w - abscenterx;
+                py = final_rect.y + final_rect.h - abscentery;
+                p4x = px * cangle - py * sangle + abscenterx;
+                p4y = px * sangle + py * cangle + abscentery;
+
+                tmp_rect.x = (int)MIN(MIN(p1x, p2x), MIN(p3x, p4x));
+                tmp_rect.y = (int)MIN(MIN(p1y, p2y), MIN(p3y, p4y));
+                tmp_rect.w = dstwidth;
+                tmp_rect.h = dstheight;
+
+                retval = SDL_BlitSurface(surface_rotated, NULL, surface, &tmp_rect);
+                SDL_FreeSurface(surface_scaled);
+                SDL_FreeSurface(surface_rotated);
+                return retval;
+            }
+        }
+        return retval;
+    }
+
+    return -1;
+}
+
+static int
 SW_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect,
                     Uint32 format, void * pixels, int pitch)
 {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/render/software/SDL_rotate.c	Fri Jun 01 19:51:08 2012 -0300
@@ -0,0 +1,500 @@
+/*
+
+SDL_rotate.c: rotates 32bit or 8bit surfaces
+
+Shamelessly stolen from SDL_gfx by Andreas Schiffler. Original copyright follows:
+
+Copyright (C) 2001-2011  Andreas Schiffler
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+   1. The origin of this software must not be misrepresented; you must not
+   claim that you wrote the original software. If you use this software
+   in a product, an acknowledgment in the product documentation would be
+   appreciated but is not required.
+
+   2. Altered source versions must be plainly marked as such, and must not be
+   misrepresented as being the original software.
+
+   3. This notice may not be removed or altered from any source
+   distribution.
+
+Andreas Schiffler -- aschiffler at ferzkopp dot net
+
+*/
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "SDL.h"
+#include "SDL_rotate.h"
+
+/* ---- Internally used structures */
+
+/*!
+\brief A 32 bit RGBA pixel.
+*/
+typedef struct tColorRGBA {
+    Uint8 r;
+    Uint8 g;
+    Uint8 b;
+    Uint8 a;
+} tColorRGBA;
+
+/*!
+\brief A 8bit Y/palette pixel.
+*/
+typedef struct tColorY {
+    Uint8 y;
+} tColorY;
+
+/*!
+\brief Returns maximum of two numbers a and b.
+*/
+#define MAX(a,b)    (((a) > (b)) ? (a) : (b))
+
+/*!
+\brief Number of guard rows added to destination surfaces.
+
+This is a simple but effective workaround for observed issues.
+These rows allocate extra memory and are then hidden from the surface.
+Rows are added to the end of destination surfaces when they are allocated.
+This catches any potential overflows which seem to happen with
+just the right src image dimensions and scale/rotation and can lead
+to a situation where the program can segfault.
+*/
+#define GUARD_ROWS (2)
+
+/*!
+\brief Lower limit of absolute zoom factor or rotation degrees.
+*/
+#define VALUE_LIMIT 0.001
+
+/*!
+\brief Returns colorkey info for a surface
+*/
+Uint32 _colorkey(SDL_Surface *src)
+{
+    Uint32 key = 0;
+    SDL_GetColorKey(src, &key);
+    return key;
+}
+
+
+/*!
+\brief Internal target surface sizing function for rotations with trig result return.
+
+\param width The source surface width.
+\param height The source surface height.
+\param angle The angle to rotate in degrees.
+\param dstwidth The calculated width of the destination surface.
+\param dstheight The calculated height of the destination surface.
+\param cangle The sine of the angle
+\param sangle The cosine of the angle
+
+*/
+void _rotozoomSurfaceSizeTrig(int width, int height, double angle,
+                              int *dstwidth, int *dstheight,
+                              double *cangle, double *sangle)
+{
+    double x, y, cx, cy, sx, sy;
+    double radangle;
+    int dstwidthhalf, dstheighthalf;
+
+    /*
+    * Determine destination width and height by rotating a centered source box
+    */
+    radangle = angle * (M_PI / 180.0);
+    *sangle = SDL_sin(radangle);
+    *cangle = SDL_cos(radangle);
+    x = (double)(width / 2);
+    y = (double)(height / 2);
+    cx = *cangle * x;
+    cy = *cangle * y;
+    sx = *sangle * x;
+    sy = *sangle * y;
+
+    dstwidthhalf = MAX((int)
+        SDL_ceil(MAX(MAX(MAX(SDL_fabs(cx + sy), SDL_fabs(cx - sy)), SDL_fabs(-cx + sy)), SDL_fabs(-cx - sy))), 1);
+    dstheighthalf = MAX((int)
+        SDL_ceil(MAX(MAX(MAX(SDL_fabs(sx + cy), SDL_fabs(sx - cy)), SDL_fabs(-sx + cy)), SDL_fabs(-sx - cy))), 1);
+    *dstwidth = 2 * dstwidthhalf;
+    *dstheight = 2 * dstheighthalf;
+}
+
+
+/*!
+\brief Internal 32 bit rotozoomer with optional anti-aliasing.
+
+Rotates and zooms 32 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
+parameters by scanning the destination surface and applying optionally anti-aliasing
+by bilinear interpolation.
+Assumes src and dst surfaces are of 32 bit depth.
+Assumes dst surface was allocated with the correct dimensions.
+
+\param src Source surface.
+\param dst Destination surface.
+\param cx Horizontal center coordinate.
+\param cy Vertical center coordinate.
+\param isin Integer version of sine of angle.
+\param icos Integer version of cosine of angle.
+\param flipx Flag indicating horizontal mirroring should be applied.
+\param flipy Flag indicating vertical mirroring should be applied.
+\param smooth Flag indicating anti-aliasing should be used.
+*/
+void _transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy, int smooth)
+{
+    int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh;
+    tColorRGBA c00, c01, c10, c11, cswap;
+    tColorRGBA *pc, *sp;
+    int gap;
+
+    /*
+    * Variable setup
+    */
+    xd = ((src->w - dst->w) << 15);
+    yd = ((src->h - dst->h) << 15);
+    ax = (cx << 16) - (icos * cx);
+    ay = (cy << 16) - (isin * cx);
+    sw = src->w - 1;
+    sh = src->h - 1;
+    pc = (tColorRGBA*) dst->pixels;
+    gap = dst->pitch - dst->w * 4;
+
+    /*
+    * Switch between interpolating and non-interpolating code
+    */
+    if (smooth) {
+        for (y = 0; y < dst->h; y++) {
+            dy = cy - y;
+            sdx = (ax + (isin * dy)) + xd;
+            sdy = (ay - (icos * dy)) + yd;
+            for (x = 0; x < dst->w; x++) {
+                dx = (sdx >> 16);
+                dy = (sdy >> 16);
+                if (flipx) dx = sw - dx;
+                if (flipy) dy = sh - dy;
+                if ((dx > -1) && (dy > -1) && (dx < (src->w-1)) && (dy < (src->h-1))) {
+                    sp = (tColorRGBA *)src->pixels;;
+                    sp += ((src->pitch/4) * dy);
+                    sp += dx;
+                    c00 = *sp;
+                    sp += 1;
+                    c01 = *sp;
+                    sp += (src->pitch/4);
+                    c11 = *sp;
+                    sp -= 1;
+                    c10 = *sp;
+                    if (flipx) {
+                        cswap = c00; c00=c01; c01=cswap;
+                        cswap = c10; c10=c11; c11=cswap;
+                    }
+                    if (flipy) {
+                        cswap = c00; c00=c10; c10=cswap;
+                        cswap = c01; c01=c11; c11=cswap;
+                    }
+                    /*
+                    * Interpolate colors
+                    */
+                    ex = (sdx & 0xffff);
+                    ey = (sdy & 0xffff);
+                    t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff;
+                    t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff;
+                    pc->r = (((t2 - t1) * ey) >> 16) + t1;
+                    t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff;
+                    t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff;
+                    pc->g = (((t2 - t1) * ey) >> 16) + t1;
+                    t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff;
+                    t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff;
+                    pc->b = (((t2 - t1) * ey) >> 16) + t1;
+                    t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff;
+                    t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff;
+                    pc->a = (((t2 - t1) * ey) >> 16) + t1;
+                }
+                sdx += icos;
+                sdy += isin;
+                pc++;
+            }
+            pc = (tColorRGBA *) ((Uint8 *) pc + gap);
+        }
+    } else {
+        for (y = 0; y < dst->h; y++) {
+            dy = cy - y;
+            sdx = (ax + (isin * dy)) + xd;
+            sdy = (ay - (icos * dy)) + yd;
+            for (x = 0; x < dst->w; x++) {
+                dx = (short) (sdx >> 16);
+                dy = (short) (sdy >> 16);
+                if (flipx) dx = (src->w-1)-dx;
+                if (flipy) dy = (src->h-1)-dy;
+                if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
+                    sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy);
+                    sp += dx;
+                    *pc = *sp;
+                }
+                sdx += icos;
+                sdy += isin;
+                pc++;
+            }
+            pc = (tColorRGBA *) ((Uint8 *) pc + gap);
+        }
+    }
+}
+
+/*!
+
+\brief Rotates and zooms 8 bit palette/Y 'src' surface to 'dst' surface without smoothing.
+
+Rotates and zooms 8 bit RGBA/ABGR 'src' surface to 'dst' surface based on the control
+parameters by scanning the destination surface.
+Assumes src and dst surfaces are of 8 bit depth.
+Assumes dst surface was allocated with the correct dimensions.
+
+\param src Source surface.
+\param dst Destination surface.
+\param cx Horizontal center coordinate.
+\param cy Vertical center coordinate.
+\param isin Integer version of sine of angle.
+\param icos Integer version of cosine of angle.
+\param flipx Flag indicating horizontal mirroring should be applied.
+\param flipy Flag indicating vertical mirroring should be applied.
+*/
+void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy)
+{
+    int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh;
+    tColorY *pc, *sp;
+    int gap;
+
+    /*
+    * Variable setup
+    */
+    xd = ((src->w - dst->w) << 15);
+    yd = ((src->h - dst->h) << 15);
+    ax = (cx << 16) - (icos * cx);
+    ay = (cy << 16) - (isin * cx);
+    sw = src->w - 1;
+    sh = src->h - 1;
+    pc = (tColorY*) dst->pixels;
+    gap = dst->pitch - dst->w;
+    /*
+    * Clear surface to colorkey
+    */
+    SDL_memset(pc, (int)(_colorkey(src) & 0xff), dst->pitch * dst->h);
+    /*
+    * Iterate through destination surface
+    */
+    for (y = 0; y < dst->h; y++) {
+        dy = cy - y;
+        sdx = (ax + (isin * dy)) + xd;
+        sdy = (ay - (icos * dy)) + yd;
+        for (x = 0; x < dst->w; x++) {
+            dx = (short) (sdx >> 16);
+            dy = (short) (sdy >> 16);
+            if (flipx) dx = (src->w-1)-dx;
+            if (flipy) dy = (src->h-1)-dy;
+            if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) {
+                sp = (tColorY *) (src->pixels);
+                sp += (src->pitch * dy + dx);
+                *pc = *sp;
+            }
+            sdx += icos;
+            sdy += isin;
+            pc++;
+        }
+        pc += gap;
+    }
+}
+
+
+
+
+/*!
+\brief Rotates and zooms a surface with different horizontal and vertival scaling factors and optional anti-aliasing.
+
+Rotates a 32bit or 8bit 'src' surface to newly created 'dst' surface.
+'angle' is the rotation in degrees, 'centerx' and 'centery' the rotation center. If 'smooth' is set
+then the destination 32bit surface is anti-aliased. If the surface is not 8bit
+or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly.
+
+\param src The surface to rotozoom.
+\param angle The angle to rotate in degrees.
+\param centerx The horizontal coordinate of the center of rotation
+\param zoomy The vertical coordinate of the center of rotation
+\param smooth Antialiasing flag; set to SMOOTHING_ON to enable.
+\param flipx Set to 1 to flip the image horizontally
+\param flipy Set to 1 to flip the image vertically
+\param dstwidth The destination surface width
+\param dstheight The destination surface height
+\param cangle The angle cosine
+\param sangle The angle sine
+\return The new rotated surface.
+
+*/
+
+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)
+{
+    SDL_Surface *rz_src;
+    SDL_Surface *rz_dst;
+    int is32bit;
+    int i, src_converted;
+    Uint8 r,g,b;
+    Uint32 colorkey = 0;
+    int colorKeyAvailable = 0;
+    double sangleinv, cangleinv;
+
+    /*
+    * Sanity check
+    */
+    if (src == NULL)
+        return (NULL);
+
+    if (src->flags & SDL_TRUE/*SDL_SRCCOLORKEY*/)
+    {
+        colorkey = _colorkey(src);
+        SDL_GetRGB(colorkey, src->format, &r, &g, &b);
+        colorKeyAvailable = 1;
+    }
+    /*
+    * Determine if source surface is 32bit or 8bit
+    */
+    is32bit = (src->format->BitsPerPixel == 32);
+    if ((is32bit) || (src->format->BitsPerPixel == 8)) {
+        /*
+        * Use source surface 'as is'
+        */
+        rz_src = src;
+        src_converted = 0;
+    } else {
+        /*
+        * New source surface is 32bit with a defined RGBA ordering
+        */
+        rz_src =
+            SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32,
+#if SDL_BYTEORDER == SDL_LIL_ENDIAN
+            0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000
+#else
+            0xff000000,  0x00ff0000, 0x0000ff00, 0x000000ff
+#endif
+            );
+        if(colorKeyAvailable)
+            SDL_SetColorKey(src, 0, 0);
+
+        SDL_BlitSurface(src, NULL, rz_src, NULL);
+
+        if(colorKeyAvailable)
+            SDL_SetColorKey(src, SDL_TRUE /*SDL_SRCCOLORKEY*/, colorkey);
+        src_converted = 1;
+        is32bit = 1;
+    }
+
+
+    /* Determine target size */
+    //_rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, &dstwidth, &dstheight, &cangle, &sangle);
+
+    /*
+    * Calculate target factors from sin/cos and zoom
+    */
+    sangleinv = sangle*65536.0;
+    cangleinv = cangle*65536.0;
+
+    /*
+    * Alloc space to completely contain the rotated surface
+    */
+    rz_dst = NULL;
+    if (is32bit) {
+        /*
+        * Target surface is 32bit with source RGBA/ABGR ordering
+        */
+        rz_dst =
+            SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 32,
+            rz_src->format->Rmask, rz_src->format->Gmask,
+            rz_src->format->Bmask, rz_src->format->Amask);
+    } else {
+        /*
+        * Target surface is 8bit
+        */
+        rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight + GUARD_ROWS, 8, 0, 0, 0, 0);
+    }
+
+    /* Check target */
+    if (rz_dst == NULL)
+        return NULL;
+
+    /* Adjust for guard rows */
+    rz_dst->h = dstheight;
+
+    if (colorKeyAvailable == 1){
+        colorkey = SDL_MapRGB(rz_dst->format, r, g, b);
+
+        SDL_FillRect(rz_dst, NULL, colorkey );
+    }
+
+    /*
+    * Lock source surface
+    */
+    if (SDL_MUSTLOCK(rz_src)) {
+        SDL_LockSurface(rz_src);
+    }
+
+    /*
+    * Check which kind of surface we have
+    */
+    if (is32bit) {
+        /*
+        * Call the 32bit transformation routine to do the rotation (using alpha)
+        */
+        _transformSurfaceRGBA(rz_src, rz_dst, centerx, centery,
+            (int) (sangleinv), (int) (cangleinv),
+            flipx, flipy,
+            smooth);
+        /*
+        * Turn on source-alpha support
+        */
+        //SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255);
+        SDL_SetColorKey(rz_dst, /*SDL_SRCCOLORKEY*/ SDL_TRUE | SDL_RLEACCEL, _colorkey(rz_src));
+    } else {
+        /*
+        * Copy palette and colorkey info
+        */
+        for (i = 0; i < rz_src->format->palette->ncolors; i++) {
+            rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i];
+        }
+        rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors;
+        /*
+        * Call the 8bit transformation routine to do the rotation
+        */
+        transformSurfaceY(rz_src, rz_dst, centerx, centery,
+            (int) (sangleinv), (int) (cangleinv),
+            flipx, flipy);
+        SDL_SetColorKey(rz_dst, /*SDL_SRCCOLORKEY*/ SDL_TRUE | SDL_RLEACCEL, _colorkey(rz_src));
+    }
+    /*
+    * Unlock source surface
+    */
+    if (SDL_MUSTLOCK(rz_src)) {
+        SDL_UnlockSurface(rz_src);
+    }
+
+    /*
+    * Cleanup temp surface
+    */
+    if (src_converted) {
+        SDL_FreeSurface(rz_src);
+    }
+
+    /*
+    * Return destination surface
+    */
+    return (rz_dst);
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/render/software/SDL_rotate.h	Fri Jun 01 19:51:08 2012 -0300
@@ -0,0 +1,6 @@
+#ifndef MIN
+#define MIN(a,b)    (((a) < (b)) ? (a) : (b))
+#endif
+
+extern 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);
+extern void _rotozoomSurfaceSizeTrig(int width, int height, double angle, int *dstwidth, int *dstheight, double *cangle, double *sangle);
\ No newline at end of file
--- a/test/Makefile.in	Fri Jun 01 19:42:15 2012 -0300
+++ b/test/Makefile.in	Fri Jun 01 19:51:08 2012 -0300
@@ -45,6 +45,7 @@
 	testver$(EXE) \
 	testwm2$(EXE) \
 	torturethread$(EXE) \
+	testrendercopyex$(EXE) \
 
 all: Makefile $(TARGETS)
 
@@ -171,6 +172,9 @@
 torturethread$(EXE): $(srcdir)/torturethread.c
 	$(CC) -o $@ $? $(CFLAGS) $(LIBS)
 
+testrendercopyex$(EXE): $(srcdir)/testrendercopyex.c $(srcdir)/common.c
+	$(CC) -o $@ $(srcdir)/testrendercopyex.c $(srcdir)/common.c $(CFLAGS) $(LIBS) @MATHLIB@
+	
 clean:
 	rm -f $(TARGETS)
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/testrendercopyex.c	Fri Jun 01 19:51:08 2012 -0300
@@ -0,0 +1,208 @@
+/*
+  Copyright (C) 1997-2011 Sam Lantinga <slouken@libsdl.org>
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely.
+*/
+/* Simple program:  Move N sprites around on the screen as fast as possible */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <time.h>
+
+#include "SDL.h"
+#include "common.h"
+
+#define WINDOW_WIDTH    640
+#define WINDOW_HEIGHT   480
+
+static CommonState *state;
+
+typedef struct {
+    SDL_Window *window;
+    SDL_Renderer *renderer;
+    SDL_Texture *background;
+    SDL_Texture *sprite;
+    SDL_Rect sprite_rect;
+    int scale_direction;
+} DrawState;
+
+/* Call this instead of exit(), so we can clean up SDL: atexit() is evil. */
+static void
+quit(int rc)
+{
+    CommonQuit(state);
+    exit(rc);
+}
+
+SDL_Texture *
+LoadTexture(SDL_Renderer *renderer, char *file, SDL_bool transparent)
+{
+    SDL_Surface *temp;
+    SDL_Texture *texture;
+
+    /* Load the sprite image */
+    temp = SDL_LoadBMP(file);
+    if (temp == NULL) {
+        fprintf(stderr, "Couldn't load %s: %s", file, SDL_GetError());
+        return NULL;
+    }
+
+    /* Set transparent pixel as the pixel at (0,0) */
+    if (transparent) {
+        if (temp->format->palette) {
+            SDL_SetColorKey(temp, SDL_TRUE, *(Uint8 *) temp->pixels);
+        } else {
+            switch (temp->format->BitsPerPixel) {
+            case 15:
+                SDL_SetColorKey(temp, SDL_TRUE,
+                                (*(Uint16 *) temp->pixels) & 0x00007FFF);
+                break;
+            case 16:
+                SDL_SetColorKey(temp, SDL_TRUE, *(Uint16 *) temp->pixels);
+                break;
+            case 24:
+                SDL_SetColorKey(temp, SDL_TRUE,
+                                (*(Uint32 *) temp->pixels) & 0x00FFFFFF);
+                break;
+            case 32:
+                SDL_SetColorKey(temp, SDL_TRUE, *(Uint32 *) temp->pixels);
+                break;
+            }
+        }
+    }
+
+    /* Create textures from the image */
+    texture = SDL_CreateTextureFromSurface(renderer, temp);
+    if (!texture) {
+        fprintf(stderr, "Couldn't create texture: %s\n", SDL_GetError());
+        SDL_FreeSurface(temp);
+        return NULL;
+    }
+    SDL_FreeSurface(temp);
+
+    /* We're ready to roll. :) */
+    return texture;
+}
+
+void
+Draw(DrawState *s)
+{
+    SDL_Rect viewport;
+    SDL_Texture *target;
+    SDL_Point *center=NULL;
+    SDL_Point origin = {0,0};
+
+    SDL_RenderGetViewport(s->renderer, &viewport);
+
+    target = SDL_CreateTexture(s->renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, viewport.w, viewport.h);
+    SDL_SetRenderTarget(s->renderer, target);
+
+    /* Draw the background */
+    SDL_RenderCopy(s->renderer, s->background, NULL, NULL);
+
+    /* Scale and draw the sprite */
+    s->sprite_rect.w += s->scale_direction;
+    s->sprite_rect.h += s->scale_direction;
+    if (s->scale_direction > 0) {
+        center = &origin;
+        if (s->sprite_rect.w >= viewport.w || s->sprite_rect.h >= viewport.h) {
+            s->scale_direction = -1;
+        }
+    } else {
+        if (s->sprite_rect.w <= 1 || s->sprite_rect.h <= 1) {
+            s->scale_direction = 1;
+        }
+    }
+    s->sprite_rect.x = (viewport.w - s->sprite_rect.w) / 2;
+    s->sprite_rect.y = (viewport.h - s->sprite_rect.h) / 2;
+
+    SDL_RenderCopyEx(s->renderer, s->sprite, NULL, &s->sprite_rect, (double)s->sprite_rect.w, center, s->scale_direction);
+
+    SDL_SetRenderTarget(s->renderer, NULL);
+    SDL_RenderCopy(s->renderer, target, NULL, NULL);
+    SDL_DestroyTexture(target);
+
+    /* Update the screen! */
+    SDL_RenderPresent(s->renderer);
+    //SDL_Delay(10);
+}
+
+int
+main(int argc, char *argv[])
+{
+    DrawState *drawstates;
+    int i, done;
+    SDL_Event event;
+    int frames;
+    Uint32 then, now;
+
+    /* Initialize test framework */
+    state = CommonCreateState(argv, SDL_INIT_VIDEO);
+    if (!state) {
+        return 1;
+    }
+    for (i = 1; i < argc;) {
+        int consumed;
+
+        consumed = CommonArg(state, i);
+        if (consumed == 0) {
+            fprintf(stderr, "Usage: %s %s\n", argv[0], CommonUsage(state));
+            return 1;
+        }
+        i += consumed;
+    }
+    if (!CommonInit(state)) {
+        quit(2);
+    }
+
+    drawstates = SDL_stack_alloc(DrawState, state->num_windows);
+    for (i = 0; i < state->num_windows; ++i) {
+        DrawState *drawstate = &drawstates[i];
+
+        drawstate->window = state->windows[i];
+        drawstate->renderer = state->renderers[i];
+        drawstate->sprite = LoadTexture(drawstate->renderer, "icon.bmp", SDL_TRUE);
+        drawstate->background = LoadTexture(drawstate->renderer, "sample.bmp", SDL_FALSE);
+        if (!drawstate->sprite || !drawstate->background) {
+            quit(2);
+        }
+        SDL_QueryTexture(drawstate->sprite, NULL, NULL,
+                         &drawstate->sprite_rect.w, &drawstate->sprite_rect.h);
+        drawstate->scale_direction = 1;
+    }
+
+    /* Main render loop */
+    frames = 0;
+    then = SDL_GetTicks();
+    done = 0;
+    while (!done) {
+        /* Check for events */
+        ++frames;
+        while (SDL_PollEvent(&event)) {
+            CommonEvent(state, &event, &done);
+        }
+        for (i = 0; i < state->num_windows; ++i) {
+            Draw(&drawstates[i]);
+        }
+    }
+
+    /* Print out some timing information */
+    now = SDL_GetTicks();
+    if (now > then) {
+        double fps = ((double) frames * 1000) / (now - then);
+        printf("%2.2f frames per second\n", fps);
+    }
+
+    SDL_stack_free(drawstates);
+
+    quit(0);
+    return 0;
+}
+
+/* vi: set ts=4 sw=4 expandtab: */