SDL_image: ImageIO: set SDL_PROP_SURFACE_ROTATION_FLOAT appropriately for images

From 671035c63b520f191e5c739699c4282b73888c37 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 5 Jan 2026 15:09:38 -0800
Subject: [PATCH] ImageIO: set SDL_PROP_SURFACE_ROTATION_FLOAT appropriately
 for images

---
 src/IMG_ImageIO.m | 101 +++++++++++++++++++++++++++++-----------------
 1 file changed, 63 insertions(+), 38 deletions(-)

diff --git a/src/IMG_ImageIO.m b/src/IMG_ImageIO.m
index 65bc5983..b9a4a667 100644
--- a/src/IMG_ImageIO.m
+++ b/src/IMG_ImageIO.m
@@ -149,16 +149,14 @@ static CGImageRef CreateCGImageFromCGImageSource(CGImageSourceRef image_source)
 {
     CGImageRef image_ref = NULL;
 
-    if(NULL == image_source)
-    {
+    if (!image_source) {
         return NULL;
     }
 
     // Get the first item in the image source (some image formats may
     // contain multiple items).
     image_ref = CGImageSourceCreateImageAtIndex(image_source, 0, NULL);
-    if(NULL == image_ref)
-    {
+    if (!image_ref) {
         SDL_SetError("CGImageSourceCreateImageAtIndex() failed");
     }
     return image_ref;
@@ -344,14 +342,41 @@ static CFDictionaryRef CreateHintDictionary(CFStringRef uti_string_hint)
 
     return surface;
 }
-static SDL_Surface* Create_SDL_Surface_From_CGImage(CGImageRef image_ref)
+
+static SDL_Surface *Create_SDL_Surface_From_CGImage(CGImageRef image_ref, CFDictionaryRef properties)
 {
+    SDL_Surface *surface;
     CGColorSpaceRef color_space = CGImageGetColorSpace(image_ref);
     if (CGColorSpaceGetModel(color_space) == kCGColorSpaceModelIndexed) {
-        return Create_SDL_Surface_From_CGImage_Index(image_ref);
+        surface = Create_SDL_Surface_From_CGImage_Index(image_ref);
     } else {
-        return Create_SDL_Surface_From_CGImage_RGB(image_ref);
+        surface = Create_SDL_Surface_From_CGImage_RGB(image_ref);
+    }
+    if (surface && properties) {
+        CFNumberRef numval;
+        if (CFDictionaryGetValueIfPresent(properties, kCGImagePropertyOrientation, (const void **)&numval)) {
+            float rotation = 0.0f;
+            CGImagePropertyOrientation orientation;
+            CFNumberGetValue(numval, kCFNumberSInt32Type, &orientation);
+            switch (orientation) {
+            case kCGImagePropertyOrientationRight:
+                rotation = 90.0f;
+                break;
+            case kCGImagePropertyOrientationDown:
+                rotation = 180.0f;
+                break;
+            case kCGImagePropertyOrientationLeft:
+                rotation = 270.0f;
+                break;
+            default:
+                break;
+            }
+            if (rotation != 0.0f) {
+                SDL_SetFloatProperty(SDL_GetSurfaceProperties(surface), SDL_PROP_SURFACE_ROTATION_FLOAT, rotation);
+            }
+        }
     }
+    return surface;
 }
 
 
@@ -445,49 +470,49 @@ bool IMG_isTIF(SDL_IOStream *src)
     return Internal_isType(src, kUTTypeTIFF);
 }
 
-static SDL_Surface *LoadImageFromIOStream (SDL_IOStream *rw_ops, CFStringRef uti_string_hint)
+static SDL_Surface *LoadImageFromIOStream(SDL_IOStream *rw_ops, CFStringRef uti_string_hint)
 {
+    SDL_Surface *surface = NULL;
     CFDictionaryRef hint_dictionary = CreateHintDictionary(uti_string_hint);
     CGImageSourceRef image_source = CreateCGImageSourceFromIOStream(rw_ops, hint_dictionary);
 
-    if (hint_dictionary != NULL)
+    if (hint_dictionary) {
         CFRelease(hint_dictionary);
-
-    if (NULL == image_source) {
-        return NULL;
     }
 
-    CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
-    CFRelease(image_source);
-
-    if (NULL == image_ref) {
-        return NULL;
+    if (image_source) {
+        CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(image_source, 0, NULL);
+        CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
+        if (image_ref) {
+            surface = Create_SDL_Surface_From_CGImage(image_ref, properties);
+            CFRelease(image_ref);
+        }
+        if (properties) {
+            CFRelease(properties);
+        }
+        CFRelease(image_source);
     }
-    SDL_Surface *sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
-    CFRelease(image_ref);
-
-    return sdl_surface;
+    return surface;
 }
 
-static SDL_Surface* LoadImageFromFile (const char *file)
+static SDL_Surface *LoadImageFromFile(const char *file)
 {
-    CGImageSourceRef image_source = NULL;
-
-    image_source = CreateCGImageSourceFromFile(file);
-
-    if (NULL == image_source) {
-        return NULL;
-    }
-
-    CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
-    CFRelease(image_source);
-
-    if (NULL == image_ref) {
-        return NULL;
+    SDL_Surface *surface = NULL;
+    CGImageSourceRef image_source = CreateCGImageSourceFromFile(file);
+
+    if (image_source) {
+        CFDictionaryRef properties = CGImageSourceCopyPropertiesAtIndex(image_source, 0, NULL);
+        CGImageRef image_ref = CreateCGImageFromCGImageSource(image_source);
+        if (image_ref) {
+            surface = Create_SDL_Surface_From_CGImage(image_ref, properties);
+            CFRelease(image_ref);
+        }
+        if (properties) {
+            CFRelease(properties);
+        }
+        CFRelease(image_source);
     }
-    SDL_Surface *sdl_surface = Create_SDL_Surface_From_CGImage(image_ref);
-    CFRelease(image_ref);
-    return sdl_surface;
+    return surface;
 }
 
 #ifdef BMP_USES_IMAGEIO