SDL_image: Orientation can use properties instead of modifying the surface

From 0ecb3a7c3ac933d753e7e7bfe2ae339232d8dea3 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Thu, 8 Jan 2026 16:54:24 -0800
Subject: [PATCH] Orientation can use properties instead of modifying the
 surface

---
 examples/showimage.c | 53 ++++++++++++++++++++++----
 src/IMG.c            | 90 ++++++++++++++++++++++++--------------------
 2 files changed, 95 insertions(+), 48 deletions(-)

diff --git a/examples/showimage.c b/examples/showimage.c
index 9cf8da26..d620f976 100644
--- a/examples/showimage.c
+++ b/examples/showimage.c
@@ -23,6 +23,9 @@
 #include <SDL3/SDL_main.h>
 #include <SDL3_image/SDL_image.h>
 
+#ifndef SDL_PROP_SURFACE_FLIP_NUMBER
+#define SDL_PROP_SURFACE_FLIP_NUMBER    "SDL.surface.flip"
+#endif
 
 /* Draw a Gimpish background pattern to show transparency in the image */
 static void draw_background(SDL_Renderer *renderer)
@@ -79,7 +82,7 @@ static void set_cursor(const char *cursor_file)
     }
 }
 
-static SDL_Texture *load_image(SDL_Renderer *renderer, const char *file, const char *tonemap, float *rotation)
+static SDL_Texture *load_image(SDL_Renderer *renderer, const char *file, const char *tonemap, SDL_FlipMode *flip, float *rotation)
 {
     SDL_Texture *texture = NULL;
     SDL_Surface *surface = IMG_Load(get_file_path(file));
@@ -102,6 +105,7 @@ static SDL_Texture *load_image(SDL_Renderer *renderer, const char *file, const c
         surface = temp;
     }
 
+    *flip = SDL_GetNumberProperty(SDL_GetSurfaceProperties(surface), SDL_PROP_SURFACE_FLIP_NUMBER, SDL_FLIP_NONE);
     *rotation = SDL_GetFloatProperty(SDL_GetSurfaceProperties(surface), SDL_PROP_SURFACE_ROTATION_FLOAT, 0.0f);
 
     texture = SDL_CreateTextureFromSurface(renderer, surface);
@@ -114,6 +118,7 @@ int main(int argc, char *argv[])
     SDL_Window *window = NULL;
     SDL_Renderer *renderer = NULL;
     SDL_Texture *texture = NULL;
+    SDL_FlipMode flip = SDL_FLIP_NONE;
     float rotation = 0.0f;
     Uint32 flags;
     int i;
@@ -204,7 +209,7 @@ int main(int argc, char *argv[])
 
         /* Open the image file */
         ++attempted;
-        texture = load_image(renderer, argv[i], tonemap, &rotation);
+        texture = load_image(renderer, argv[i], tonemap, &flip, &rotation);
         if (!texture) {
             continue;
         }
@@ -225,7 +230,11 @@ int main(int argc, char *argv[])
 
         /* Show the window */
         SDL_SetWindowTitle(window, argv[i]);
-        SDL_SetWindowSize(window, texture->w, texture->h);
+        if (rotation == 90.0f || rotation == 270.0f) {
+            SDL_SetWindowSize(window, texture->h, texture->w);
+        } else {
+            SDL_SetWindowSize(window, texture->w, texture->h);
+        }
         SDL_ShowWindow(window);
 
         done = quit;
@@ -272,7 +281,20 @@ int main(int argc, char *argv[])
             draw_background(renderer);
 
             /* Display the image */
-            SDL_RenderTextureRotated(renderer, texture, NULL, NULL, rotation, NULL, SDL_FLIP_NONE);
+            SDL_FRect dst;
+            if (rotation == 90.0f || rotation == 270.0f) {
+                // Use a pre-rotated destination rectangle
+                dst.x = -(texture->w - texture->h) / 2.0f;
+                dst.y = (texture->w - texture->h) / 2.0f;
+                dst.w = (float)texture->w;
+                dst.h = (float)texture->h;
+            } else {
+                dst.x = 0.0f;
+                dst.y = 0.0f;
+                dst.w = (float)texture->w;
+                dst.h = (float)texture->h;
+            }
+            SDL_RenderTextureRotated(renderer, texture, NULL, &dst, rotation, NULL, flip);
             SDL_RenderPresent(renderer);
 
             SDL_Delay(100);
@@ -297,12 +319,16 @@ int main(int argc, char *argv[])
                             SDL_DestroyTexture(texture);
 
                             SDL_Log("Loading %s\n", file);
-                            texture = load_image(renderer, file, tonemap, &rotation);
+                            texture = load_image(renderer, file, tonemap, &flip, &rotation);
                             if (!texture) {
                                 break;
                             }
                             SDL_SetWindowTitle(window, file);
-                            SDL_SetWindowSize(window, texture->w, texture->h);
+                            if (rotation == 90.0f || rotation == 270.0f) {
+                                SDL_SetWindowSize(window, texture->h, texture->w);
+                            } else {
+                                SDL_SetWindowSize(window, texture->w, texture->h);
+                            }
                         }
                         break;
                     case SDL_EVENT_KEY_UP:
@@ -328,7 +354,20 @@ int main(int argc, char *argv[])
             draw_background(renderer);
 
             /* Display the image */
-            SDL_RenderTextureRotated(renderer, texture, NULL, NULL, rotation, NULL, SDL_FLIP_NONE);
+            SDL_FRect dst;
+            if (rotation == 90.0f || rotation == 270.0f) {
+                // Use a pre-rotated destination rectangle
+                dst.x = -(texture->w - texture->h) / 2.0f;
+                dst.y = (texture->w - texture->h) / 2.0f;
+                dst.w = (float)texture->w;
+                dst.h = (float)texture->h;
+            } else {
+                dst.x = 0.0f;
+                dst.y = 0.0f;
+                dst.w = (float)texture->w;
+                dst.h = (float)texture->h;
+            }
+            SDL_RenderTextureRotated(renderer, texture, NULL, &dst, rotation, NULL, flip);
             SDL_RenderPresent(renderer);
 
             SDL_Delay(100);
diff --git a/src/IMG.c b/src/IMG.c
index ca913e9b..cdd23eb9 100644
--- a/src/IMG.c
+++ b/src/IMG.c
@@ -27,6 +27,10 @@
 #include <emscripten/emscripten.h>
 #endif
 
+#ifndef SDL_PROP_SURFACE_FLIP_NUMBER
+#define SDL_PROP_SURFACE_FLIP_NUMBER    "SDL.surface.flip"
+#endif
+
 #if defined(SDL_BUILD_MAJOR_VERSION)
 SDL_COMPILE_TIME_ASSERT(SDL_BUILD_MAJOR_VERSION,
                         SDL_IMAGE_MAJOR_VERSION == SDL_BUILD_MAJOR_VERSION);
@@ -549,66 +553,70 @@ Uint64 IMG_TimebaseDuration(Uint64 pts, Uint64 duration, Uint64 src_numerator, U
 
 SDL_Surface *IMG_ApplyOrientation(SDL_Surface *surface, int orientation)
 {
-    SDL_Surface *tmp;
-    switch (orientation)
-    {
+    float rotation = 0.0f;
+    SDL_FlipMode flip = SDL_FLIP_NONE;
+    switch (orientation) {
     case 1:
         // Normal (no rotation required)
         break;
     case 2:
-        // Flipped horizontally
-        if (!SDL_FlipSurface(surface, SDL_FLIP_HORIZONTAL)) {
-            SDL_DestroySurface(surface);
-            return NULL;
-        }
+        // Mirror horizontal
+        flip = SDL_FLIP_HORIZONTAL;
         break;
     case 3:
-        // Upside-down (180 degrees rotation)
-        tmp = SDL_RotateSurface(surface, 180.0f);
-        SDL_DestroySurface(surface);
-        surface = tmp;
+        // Rotate 180
+        rotation = 180.0f;
         break;
     case 4:
-        // Flipped vertically
-        if (!SDL_FlipSurface(surface, SDL_FLIP_VERTICAL)) {
-            SDL_DestroySurface(surface);
-            return NULL;
-        }
+        // Mirror vertical
+        flip = SDL_FLIP_VERTICAL;
         break;
     case 5:
-        // Flip horizontally and rotate 90 degrees counterclockwise
-        if (!SDL_FlipSurface(surface, SDL_FLIP_HORIZONTAL)) {
-            SDL_DestroySurface(surface);
-            return NULL;
-        }
-        tmp = SDL_RotateSurface(surface, -90.0f);
-        SDL_DestroySurface(surface);
-        surface = tmp;
+        // Mirror horizontal and rotate 270 CW
+        flip = SDL_FLIP_HORIZONTAL;
+        rotation = 270.0f;
         break;
     case 6:
-        // Rotate 90 degrees clockwise
-        tmp = SDL_RotateSurface(surface, 90.0f);
-        SDL_DestroySurface(surface);
-        surface = tmp;
+        // Rotate 90 CW
+        rotation = 90.0f;
         break;
     case 7:
-        // Flip horizontally and rotate 90 degrees clockwise
-        if (!SDL_FlipSurface(surface, SDL_FLIP_HORIZONTAL)) {
-            SDL_DestroySurface(surface);
-            return NULL;
-        }
-        tmp = SDL_RotateSurface(surface, 90.0f);
-        SDL_DestroySurface(surface);
-        surface = tmp;
+        // Mirror horizontal and rotate 90 CW
+        flip = SDL_FLIP_HORIZONTAL;
+        rotation = 90.0f;
         break;
     case 8:
-        // Rotate 90 degrees counterclockwise
-        tmp = SDL_RotateSurface(surface, -90.0f);
-        SDL_DestroySurface(surface);
-        surface = tmp;
+        // Rotate 270 CW
+        rotation = 270.0f;
         break;
     default:
         break;
     }
+
+#ifdef ORIENTATION_USES_PROPERTIES
+    if (flip != SDL_FLIP_NONE) {
+        SDL_PropertiesID props = SDL_GetSurfaceProperties(surface);
+        SDL_SetNumberProperty(props, SDL_PROP_SURFACE_FLIP_NUMBER, flip);
+    }
+    if (rotation != 0.0f) {
+        SDL_PropertiesID props = SDL_GetSurfaceProperties(surface);
+        SDL_SetFloatProperty(props, SDL_PROP_SURFACE_ROTATION_FLOAT, rotation);
+    }
+#else
+    if (flip != SDL_FLIP_NONE) {
+        if (!SDL_FlipSurface(surface, flip)) {
+            SDL_DestroySurface(surface);
+            return NULL;
+        }
+    }
+    if (rotation != 0.0f) {
+        SDL_Surface *tmp = SDL_RotateSurface(surface, rotation);
+        SDL_DestroySurface(surface);
+        if (!tmp) {
+            return NULL;
+        }
+        surface = tmp;
+    }
+#endif
     return surface;
 }