sdl12-compat: SDL_UpdateRects: Only lock parts of texture we intend to update.

From e1da54fdfb721d5a615c25e1bd5063110a58b401 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Wed, 25 Aug 2021 10:49:36 -0400
Subject: [PATCH] SDL_UpdateRects: Only lock parts of texture we intend to
 update.

Previously we would lock the entire texture, but this is a misuse of SDL2's
API, as it doesn't promise to preserve locked portions that you don't write
to during the lock, and the Metal renderer definitely doesn't.

Fixes #130.
---
 src/SDL12_compat.c | 74 ++++++++++++++++++++++++----------------------
 1 file changed, 39 insertions(+), 35 deletions(-)

diff --git a/src/SDL12_compat.c b/src/SDL12_compat.c
index 14404e7..d672a9e 100644
--- a/src/SDL12_compat.c
+++ b/src/SDL12_compat.c
@@ -5021,52 +5021,56 @@ SDL_UpdateRects(SDL12_Surface *surface12, int numrects, SDL12_Rect *rects12)
      *  but in practice most apps never got a double-buffered surface and
      *  don't handle it correctly, so we have to work around it. */
     if (surface12 == VideoSurface12) {
+        SDL_Palette *logicalPal = surface12->surface20->format->palette;
+        const int pixsize = surface12->format->BytesPerPixel;
+        const int srcpitch = surface12->pitch;
         SDL_bool whole_screen = SDL_FALSE;
-        SDL_Rect rect20;
         void *pixels = NULL;
+        SDL_Rect rect20;
         int pitch = 0;
-        int i;
+        int i, j;
 
-        if (SDL20_LockTexture(VideoTexture20, NULL, &pixels, &pitch) < 0) {
-            return;  /* oh well */
-        }
+        for (i = 0; i < numrects; i++) {
+            UpdateRect12to20(surface12, &rects12[i], &rect20, &whole_screen);
+            if (!rect20.w || !rect20.h) {
+                continue;
+            } else if (SDL20_LockTexture(VideoTexture20, &rect20, &pixels, &pitch) < 0) {
+                continue;  /* oh well */
+            }
 
-        if (VideoConvertSurface20) {
-            SDL_Palette *logicalPal = surface12->surface20->format->palette;
-            surface12->surface20->format->palette = VideoPhysicalPalette20;
-            VideoConvertSurface20->pixels = pixels;
-            VideoConvertSurface20->pitch = pitch;
-            for (i = 0; i < numrects; i++) {
-                UpdateRect12to20(surface12, &rects12[i], &rect20, &whole_screen);
-                if (rect20.w && rect20.h) {
-                    SDL20_UpperBlit(VideoSurface12->surface20, &rect20, VideoConvertSurface20, &rect20);
+            if (VideoConvertSurface20) {
+                SDL_Rect dstrect20;  /* pretend that the subregion is just the top left of the convert surface. */
+                dstrect20.x = dstrect20.y = 0;
+                dstrect20.w = rect20.w;
+                dstrect20.h = rect20.h;
+                surface12->surface20->format->palette = VideoPhysicalPalette20;
+                VideoConvertSurface20->pixels = pixels;
+                VideoConvertSurface20->pitch = pitch;
+                VideoConvertSurface20->w = rect20.w;
+                VideoConvertSurface20->h = rect20.h;
+                SDL20_UpperBlit(VideoSurface12->surface20, &rect20, VideoConvertSurface20, &dstrect20);
+            } else {
+                const int cpy = rect20.w * pixsize;
+                char *dst = pixels;
+                const Uint8 *src = (((Uint8 *) surface12->pixels) + (rect20.y * srcpitch)) + (rect20.x * pixsize);
+                for (j = 0; j < rect20.h; j++) {
+                    SDL20_memcpy(dst, src, cpy);
+                    src += srcpitch;
+                    dst += pitch;
                 }
             }
+
+            SDL20_UnlockTexture(VideoTexture20);
+        }
+
+        if (VideoConvertSurface20) {  /* reset some state we messed with */
+            surface12->surface20->format->palette = logicalPal;
             VideoConvertSurface20->pixels = NULL;
             VideoConvertSurface20->pitch = 0;
-            surface12->surface20->format->palette = logicalPal;
-        } else {
-            const int srcpitch = surface12->pitch;
-            const int pixsize = surface12->format->BytesPerPixel;
-            for (i = 0; i < numrects; i++) {
-                UpdateRect12to20(surface12, &rects12[i], &rect20, &whole_screen);
-                if (rect20.w && rect20.h) {
-                    const int cpy = rect20.w * pixsize;
-                    const int h = surface12->h;
-                    char *dst = (((char *) pixels) + (rect20.y * pitch)) + (rect20.x * pixsize);
-                    char *src = (((char *) surface12->pixels) + (rect20.y * srcpitch)) + (rect20.x * pixsize);
-                    int j = 0;
-                    for (; j < h; j++) {
-                        SDL20_memcpy(dst, src, cpy);
-                        src += srcpitch;
-                        dst += pitch;
-                    }
-                }
-            }
+            VideoConvertSurface20->w = VideoSurface12->w;
+            VideoConvertSurface20->h = VideoSurface12->h;
         }
 
-        SDL20_UnlockTexture(VideoTexture20);
-
         if (whole_screen) {
             PresentScreen();  /* flip it now. */
         } else {