SDL: Add internal SDL_UpdateTextureFromSurface(), making SDL_CreateTextureFromSurface() lighter

From a57c566988e1611f83dd16ff1081cfcf17d600c1 Mon Sep 17 00:00:00 2001
From: Sylvain <[EMAIL REDACTED]>
Date: Wed, 7 Aug 2024 10:22:57 +0200
Subject: [PATCH] Add internal SDL_UpdateTextureFromSurface(), making
 SDL_CreateTextureFromSurface() lighter

---
 src/render/SDL_render.c | 158 ++++++++++++++++++++++++++--------------
 1 file changed, 105 insertions(+), 53 deletions(-)

diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 5886a286ec5c7..68682c274bf4e 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -1495,10 +1495,111 @@ SDL_Texture *SDL_CreateTexture(SDL_Renderer *renderer, SDL_PixelFormat format, S
     return texture;
 }
 
+static int SDL_UpdateTextureFromSurface(SDL_Texture *texture, SDL_Rect *rect, SDL_Surface *surface)
+{
+    int access;
+    SDL_bool direct_update;
+    SDL_PixelFormat tex_format;
+    SDL_PropertiesID surface_props;
+    SDL_PropertiesID tex_props;
+    SDL_Colorspace surface_colorspace = SDL_COLORSPACE_UNKNOWN;
+    SDL_Colorspace texture_colorspace = SDL_COLORSPACE_UNKNOWN;
+
+    if (texture == NULL || surface == NULL) {
+        return -1;
+    }
+
+    tex_props = SDL_GetTextureProperties(texture);
+    if (!tex_props) {
+        return -1;
+    }
+
+    surface_props = SDL_GetSurfaceProperties(surface);
+    if (!surface_props) {
+        return -1;
+    }
+
+    tex_format = SDL_GetNumberProperty(tex_props, SDL_PROP_TEXTURE_FORMAT_NUMBER, 0);
+    access = SDL_GetNumberProperty(tex_props, SDL_PROP_TEXTURE_ACCESS_NUMBER, 0);
+
+    if (access != SDL_TEXTUREACCESS_STATIC && access != SDL_TEXTUREACCESS_STREAMING) {
+        return -1;
+    }
+
+    surface_colorspace = SDL_GetSurfaceColorspace(surface);
+    texture_colorspace = surface_colorspace;
+
+    if (surface_colorspace == SDL_COLORSPACE_SRGB_LINEAR ||
+        SDL_COLORSPACETRANSFER(surface_colorspace) == SDL_TRANSFER_CHARACTERISTICS_PQ) {
+        if (SDL_ISPIXELFORMAT_FLOAT(tex_format)) {
+            texture_colorspace = SDL_COLORSPACE_SRGB_LINEAR;
+        } else if (SDL_ISPIXELFORMAT_10BIT(tex_format)) {
+            texture_colorspace = SDL_COLORSPACE_HDR10;
+        } else {
+            texture_colorspace = SDL_COLORSPACE_SRGB;
+        }
+    }
+
+    if (tex_format == surface->format && texture_colorspace == surface_colorspace) {
+        if (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) {
+            /* Surface and Renderer formats are identical.
+             * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */
+            direct_update = SDL_FALSE;
+        } else {
+            /* Update Texture directly */
+            direct_update = SDL_TRUE;
+        }
+    } else {
+        /* Surface and Renderer formats are different, it needs an intermediate conversion. */
+        direct_update = SDL_FALSE;
+    }
+
+    if (direct_update) {
+        if (SDL_MUSTLOCK(surface)) {
+            SDL_LockSurface(surface);
+            SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch);
+            SDL_UnlockSurface(surface);
+        } else {
+            SDL_UpdateTexture(texture, rect, surface->pixels, surface->pitch);
+        }
+    } else {
+        SDL_Surface *temp = NULL;
+
+        /* Set up a destination surface for the texture update */
+        temp = SDL_ConvertSurfaceAndColorspace(surface, tex_format, NULL, texture_colorspace, surface_props);
+        if (temp) {
+            SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
+            SDL_DestroySurface(temp);
+        } else {
+            return -1;
+        }
+    }
+
+    {
+        Uint8 r, g, b, a;
+        SDL_BlendMode blendMode;
+
+        SDL_GetSurfaceColorMod(surface, &r, &g, &b);
+        SDL_SetTextureColorMod(texture, r, g, b);
+
+        SDL_GetSurfaceAlphaMod(surface, &a);
+        SDL_SetTextureAlphaMod(texture, a);
+
+        if (SDL_SurfaceHasColorKey(surface)) {
+            /* We converted to a texture with alpha format */
+            SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
+        } else {
+            SDL_GetSurfaceBlendMode(surface, &blendMode);
+            SDL_SetTextureBlendMode(texture, blendMode);
+        }
+    }
+
+    return 0;
+}
+
 SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *surface)
 {
     SDL_bool needAlpha;
-    SDL_bool direct_update;
     int i;
     SDL_PixelFormat format = SDL_PIXELFORMAT_UNKNOWN;
     SDL_Palette *palette;
@@ -1605,20 +1706,6 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
         }
     }
 
-    if (format == surface->format && texture_colorspace == surface_colorspace) {
-        if (SDL_ISPIXELFORMAT_ALPHA(surface->format) && SDL_SurfaceHasColorKey(surface)) {
-            /* Surface and Renderer formats are identical.
-             * Intermediate conversion is needed to convert color key to alpha (SDL_ConvertColorkeyToAlpha()). */
-            direct_update = SDL_FALSE;
-        } else {
-            /* Update Texture directly */
-            direct_update = SDL_TRUE;
-        }
-    } else {
-        /* Surface and Renderer formats are different, it needs an intermediate conversion. */
-        direct_update = SDL_FALSE;
-    }
-
     props = SDL_CreateProperties();
     SDL_SetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, texture_colorspace);
     if (surface_colorspace == texture_colorspace) {
@@ -1637,46 +1724,11 @@ SDL_Texture *SDL_CreateTextureFromSurface(SDL_Renderer *renderer, SDL_Surface *s
         return NULL;
     }
 
-    if (direct_update) {
-        if (SDL_MUSTLOCK(surface)) {
-            SDL_LockSurface(surface);
-            SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
-            SDL_UnlockSurface(surface);
-        } else {
-            SDL_UpdateTexture(texture, NULL, surface->pixels, surface->pitch);
-        }
-    } else {
-        SDL_Surface *temp = NULL;
-
-        /* Set up a destination surface for the texture update */
-        temp = SDL_ConvertSurfaceAndColorspace(surface, format, NULL, texture_colorspace, surface->internal->props);
-        if (temp) {
-            SDL_UpdateTexture(texture, NULL, temp->pixels, temp->pitch);
-            SDL_DestroySurface(temp);
-        } else {
-            SDL_DestroyTexture(texture);
-            return NULL;
-        }
+    if (SDL_UpdateTextureFromSurface(texture, NULL, surface) < 0) {
+        SDL_DestroyTexture(texture);
+        return NULL;
     }
 
-    {
-        Uint8 r, g, b, a;
-        SDL_BlendMode blendMode;
-
-        SDL_GetSurfaceColorMod(surface, &r, &g, &b);
-        SDL_SetTextureColorMod(texture, r, g, b);
-
-        SDL_GetSurfaceAlphaMod(surface, &a);
-        SDL_SetTextureAlphaMod(texture, a);
-
-        if (SDL_SurfaceHasColorKey(surface)) {
-            /* We converted to a texture with alpha format */
-            SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
-        } else {
-            SDL_GetSurfaceBlendMode(surface, &blendMode);
-            SDL_SetTextureBlendMode(texture, blendMode);
-        }
-    }
     return texture;
 }