SDL: Fixed bug #3070 - SDL_RenderCopyEx rotation and global scaling around the wrong way (software renderer)

From 18b76fcc5dca9c7f5c4ce1ac5eaee041e6d68bd3 Mon Sep 17 00:00:00 2001
From: Sylvain <[EMAIL REDACTED]>
Date: Tue, 15 Feb 2022 11:33:56 +0100
Subject: [PATCH] Fixed bug #3070 -  SDL_RenderCopyEx rotation and global
 scaling around the wrong way (software renderer)

---
 src/render/SDL_render.c             | 14 +++-------
 src/render/SDL_sysrender.h          |  2 +-
 src/render/software/SDL_render_sw.c | 42 ++++++++++++++++++++++++-----
 3 files changed, 39 insertions(+), 19 deletions(-)

diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index f9f01fb99b9..09e216dbefb 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -611,12 +611,12 @@ QueueCmdCopy(SDL_Renderer *renderer, SDL_Texture * texture, const SDL_Rect * src
 static int
 QueueCmdCopyEx(SDL_Renderer *renderer, SDL_Texture * texture,
                const SDL_Rect * srcquad, const SDL_FRect * dstrect,
-               const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
+               const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, float scale_x, float scale_y)
 {
     SDL_RenderCommand *cmd = PrepQueueCmdDraw(renderer, SDL_RENDERCMD_COPY_EX, texture);
     int retval = -1;
     if (cmd != NULL) {
-        retval = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip);
+        retval = renderer->QueueCopyEx(renderer, cmd, texture, srcquad, dstrect, angle, center, flip, scale_x, scale_y);
         if (retval < 0) {
             cmd->command = SDL_RENDERCMD_NO_OP;
         }
@@ -3734,15 +3734,7 @@ SDL_RenderCopyExF(SDL_Renderer * renderer, SDL_Texture * texture,
                 renderer->scale.x, renderer->scale.y);
     } else {
 
-        real_dstrect.x *= renderer->scale.x;
-        real_dstrect.y *= renderer->scale.y;
-        real_dstrect.w *= renderer->scale.x;
-        real_dstrect.h *= renderer->scale.y;
-
-        real_center.x *= renderer->scale.x;
-        real_center.y *= renderer->scale.y;
-
-        retval = QueueCmdCopyEx(renderer, texture, &real_srcrect, &real_dstrect, angle, &real_center, flip);
+        retval = QueueCmdCopyEx(renderer, texture, &real_srcrect, &real_dstrect, angle, &real_center, flip, renderer->scale.x, renderer->scale.y);
     }
     return retval < 0 ? retval : FlushRenderCommandsIfNotBatching(renderer);
 }
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index ff38ed0dd69..5d92f6a26c6 100644
--- a/src/render/SDL_sysrender.h
+++ b/src/render/SDL_sysrender.h
@@ -143,7 +143,7 @@ struct SDL_Renderer
                        const SDL_Rect * srcrect, const SDL_FRect * dstrect);
     int (*QueueCopyEx) (SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
                         const SDL_Rect * srcquad, const SDL_FRect * dstrect,
-                        const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip);
+                        const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, float scale_x, float scale_y);
     int (*QueueGeometry) (SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
                           const float *xy, int xy_stride, const SDL_Color *color, int color_stride, const float *uv, int uv_stride,
                           int num_vertices, const void *indices, int num_indices, int size_indices,
diff --git a/src/render/software/SDL_render_sw.c b/src/render/software/SDL_render_sw.c
index 961aea2f3d0..4fabcee48a8 100644
--- a/src/render/software/SDL_render_sw.c
+++ b/src/render/software/SDL_render_sw.c
@@ -273,12 +273,14 @@ typedef struct CopyExData
     double angle;
     SDL_FPoint center;
     SDL_RendererFlip flip;
+    float scale_x;
+    float scale_y;
 } CopyExData;
 
 static int
 SW_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture,
                const SDL_Rect * srcrect, const SDL_FRect * dstrect,
-               const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip)
+               const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip, float scale_x, float scale_y)
 {
     CopyExData *verts = (CopyExData *) SDL_AllocateRenderVertices(renderer, sizeof (CopyExData), 0, &cmd->data.draw.first);
 
@@ -297,14 +299,35 @@ SW_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * te
     verts->angle = angle;
     SDL_memcpy(&verts->center, center, sizeof (SDL_FPoint));
     verts->flip = flip;
+    verts->scale_x = scale_x;
+    verts->scale_y = scale_y;
 
     return 0;
 }
 
+static int
+Blit_to_Screen(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *surface, SDL_Rect *dstrect,
+        float scale_x, float scale_y, SDL_ScaleMode scaleMode)
+{
+    int retval;
+    /* Renderer scaling, if needed */
+    if (scale_x != 1.0f || scale_y != 1.0f) {
+        SDL_Rect r;
+        r.x = (int)((float) dstrect->x * scale_x);
+        r.y = (int)((float) dstrect->y * scale_y);
+        r.w = (int)((float) dstrect->w * scale_x);
+        r.h = (int)((float) dstrect->h * scale_y);
+        retval = SDL_PrivateUpperBlitScaled(src, srcrect, surface, &r, scaleMode);
+    } else {
+        retval = SDL_BlitSurface(src, srcrect, surface, dstrect);
+    }
+    return retval;
+}
+
 static int
 SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Surface *surface, SDL_Texture * texture,
                 const SDL_Rect * srcrect, const SDL_Rect * final_rect,
-                const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip)
+                const double angle, const SDL_FPoint * center, const SDL_RendererFlip flip, float scale_x, float scale_y)
 {
     SDL_Surface *src = (SDL_Surface *) texture->driverdata;
     SDL_Rect tmp_rect;
@@ -475,7 +498,8 @@ SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Surface *surface, SDL_Texture * tex
                     SDL_SetSurfaceAlphaMod(src_rotated, alphaMod);
                     SDL_SetSurfaceColorMod(src_rotated, rMod, gMod, bMod);
                 }
-                retval = SDL_BlitSurface(src_rotated, NULL, surface, &tmp_rect);
+                /* Renderer scaling, if needed */
+                retval = Blit_to_Screen(src_rotated, NULL, surface, &tmp_rect, scale_x, scale_y, texture->scaleMode);
             } else {
                 /* The NONE blend mode requires three steps to get the pixels onto the destination surface.
                  * First, the area where the rotated pixels will be blitted to get set to zero.
@@ -484,7 +508,8 @@ SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Surface *surface, SDL_Texture * tex
                  */
                 SDL_Rect mask_rect = tmp_rect;
                 SDL_SetSurfaceBlendMode(mask_rotated, SDL_BLENDMODE_NONE);
-                retval = SDL_BlitSurface(mask_rotated, NULL, surface, &mask_rect);
+                /* Renderer scaling, if needed */
+                retval = Blit_to_Screen(mask_rotated, NULL, surface, &mask_rect, scale_x, scale_y, texture->scaleMode);
                 if (!retval) {
                     /* The next step copies the alpha value. This is done with the BLEND blend mode and
                      * by modulating the source colors with 0. Since the destination is all zeros, this
@@ -492,7 +517,8 @@ SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Surface *surface, SDL_Texture * tex
                      */
                     SDL_SetSurfaceColorMod(src_rotated, 0, 0, 0);
                     mask_rect = tmp_rect;
-                    retval = SDL_BlitSurface(src_rotated, NULL, surface, &mask_rect);
+                    /* Renderer scaling, if needed */
+                    retval = Blit_to_Screen(src_rotated, NULL, surface, &mask_rect, scale_x, scale_y, texture->scaleMode);
                     if (!retval) {
                         /* The last step gets the color values in place. The ADD blend mode simply adds them to
                          * the destination (where the color values are all zero). However, because the ADD blend
@@ -508,7 +534,8 @@ SW_RenderCopyEx(SDL_Renderer * renderer, SDL_Surface *surface, SDL_Texture * tex
                             retval = -1;
                         } else {
                             SDL_SetSurfaceBlendMode(src_rotated_rgb, SDL_BLENDMODE_ADD);
-                            retval = SDL_BlitSurface(src_rotated_rgb, NULL, surface, &tmp_rect);
+                            /* Renderer scaling, if needed */
+                            retval = Blit_to_Screen(src_rotated_rgb, NULL, surface, &tmp_rect, scale_x, scale_y, texture->scaleMode);
                             SDL_FreeSurface(src_rotated_rgb);
                         }
                     }
@@ -879,7 +906,8 @@ SW_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertic
                 }
 
                 SW_RenderCopyEx(renderer, surface, cmd->data.draw.texture, &copydata->srcrect,
-                                &copydata->dstrect, copydata->angle, &copydata->center, copydata->flip);
+                                &copydata->dstrect, copydata->angle, &copydata->center, copydata->flip,
+                                copydata->scale_x, copydata->scale_y);
                 break;
             }