SDL: Made texture size and format public in the API

From 1f3a0d12e657245bb7b72ba44b3835cece8bf351 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 30 Sep 2024 20:40:54 -0700
Subject: [PATCH] Made texture size and format public in the API

Also added refcount to textures so they can be retained by application code.
---
 include/SDL3/SDL_render.h    | 16 ++++++++++++++++
 src/SDL_internal.h           |  3 +++
 src/render/SDL_render.c      | 12 ++++++++----
 src/render/SDL_sysrender.h   | 31 ++++++++++++++++++-------------
 test/testautomation_render.c | 36 +++++++++++++++---------------------
 test/testcamera.c            | 13 +++++--------
 test/testnative.c            | 16 ++++++----------
 test/testviewport.c          | 15 +++++----------
 8 files changed, 76 insertions(+), 66 deletions(-)

diff --git a/include/SDL3/SDL_render.h b/include/SDL3/SDL_render.h
index 3096dc3b87219..1ea737e9868b3 100644
--- a/include/SDL3/SDL_render.h
+++ b/include/SDL3/SDL_render.h
@@ -118,11 +118,27 @@ typedef enum SDL_RendererLogicalPresentation
  */
 typedef struct SDL_Renderer SDL_Renderer;
 
+#ifndef SDL_INTERNAL
 /**
  * An efficient driver-specific representation of pixel data
  *
  * \since This struct is available since SDL 3.0.0.
+ *
+ * \sa SDL_CreateTexture
+ * \sa SDL_CreateTextureFromSurface
+ * \sa SDL_CreateTextureWithProperties
+ * \sa SDL_DestroyTexture
  */
+struct SDL_Texture
+{
+    SDL_PixelFormat format;     /**< The format of the texture, read-only */
+    int w;                      /**< The width of the texture, read-only. */
+    int h;                      /**< The height of the texture, read-only. */
+
+    int refcount;               /**< Application reference count, used when freeing texture */
+};
+#endif /* !SDL_INTERNAL */
+
 typedef struct SDL_Texture SDL_Texture;
 
 /* Function prototypes */
diff --git a/src/SDL_internal.h b/src/SDL_internal.h
index 4a83cf4c74722..498021849c4f8 100644
--- a/src/SDL_internal.h
+++ b/src/SDL_internal.h
@@ -222,6 +222,9 @@
 #define SDL_EndThreadFunction NULL
 #endif
 
+/* Enable internal definitions in SDL API headers */
+#define SDL_INTERNAL
+
 #include <SDL3/SDL.h>
 #include <SDL3/SDL_intrin.h>
 
diff --git a/src/render/SDL_render.c b/src/render/SDL_render.c
index 8663776a90987..8d69e1ce10e96 100644
--- a/src/render/SDL_render.c
+++ b/src/render/SDL_render.c
@@ -1362,6 +1362,7 @@ SDL_Texture *SDL_CreateTextureWithProperties(SDL_Renderer *renderer, SDL_Propert
     if (!texture) {
         return NULL;
     }
+    texture->refcount = 1;
     SDL_SetObjectValid(texture, SDL_OBJECT_TYPE_TEXTURE, true);
     texture->colorspace = (SDL_Colorspace)SDL_GetNumberProperty(props, SDL_PROP_TEXTURE_CREATE_COLORSPACE_NUMBER, default_colorspace);
     texture->format = format;
@@ -4990,12 +4991,10 @@ bool SDL_RenderPresent(SDL_Renderer *renderer)
     return true;
 }
 
-static bool SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying)
+static void SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying)
 {
     SDL_Renderer *renderer;
 
-    CHECK_TEXTURE_MAGIC(texture, false);
-
     SDL_DestroyProperties(texture->props);
 
     renderer = texture->renderer;
@@ -5036,11 +5035,16 @@ static bool SDL_DestroyTextureInternal(SDL_Texture *texture, bool is_destroying)
     texture->locked_surface = NULL;
 
     SDL_free(texture);
-    return true;
 }
 
 void SDL_DestroyTexture(SDL_Texture *texture)
 {
+    CHECK_TEXTURE_MAGIC(texture, );
+
+    if (--texture->refcount > 0) {
+        return;
+    }
+
     SDL_DestroyTextureInternal(texture, false /* is_destroying */);
 }
 
diff --git a/src/render/SDL_sysrender.h b/src/render/SDL_sysrender.h
index 0ae18fe980187..203c7b0f1cd0e 100644
--- a/src/render/SDL_sysrender.h
+++ b/src/render/SDL_sysrender.h
@@ -71,17 +71,22 @@ typedef struct SDL_RenderViewState
 // Define the SDL texture structure
 struct SDL_Texture
 {
-    SDL_Colorspace colorspace;  /**< The colorspace of the texture */
-    float SDR_white_point;      /**< The SDR white point for this content */
-    float HDR_headroom;         /**< The HDR headroom needed by this content */
-    SDL_PixelFormat format;     /**< The pixel format of the texture */
-    SDL_TextureAccess access;   /**< The texture access mode */
-    int w;                      /**< The width of the texture */
-    int h;                      /**< The height of the texture */
-    SDL_BlendMode blendMode;    /**< The texture blend mode */
-    SDL_ScaleMode scaleMode;    /**< The texture scale mode */
-    SDL_FColor color;           /**< Texture modulation values */
-    SDL_RenderViewState view;   /**< Target texture view state */
+    // Public API definition
+    SDL_PixelFormat format;     /**< The format of the texture, read-only */
+    int w;                      /**< The width of the texture, read-only. */
+    int h;                      /**< The height of the texture, read-only. */
+
+    int refcount;               /**< Application reference count, used when freeing texture */
+
+    // Private API definition
+    SDL_Colorspace colorspace;  // The colorspace of the texture
+    float SDR_white_point;      // The SDR white point for this content
+    float HDR_headroom;         // The HDR headroom needed by this content
+    SDL_TextureAccess access;   // The texture access mode
+    SDL_BlendMode blendMode;    // The texture blend mode
+    SDL_ScaleMode scaleMode;    // The texture scale mode
+    SDL_FColor color;           // Texture modulation values
+    SDL_RenderViewState view;   // Target texture view state
 
     SDL_Renderer *renderer;
 
@@ -91,13 +96,13 @@ struct SDL_Texture
     void *pixels;
     int pitch;
     SDL_Rect locked_rect;
-    SDL_Surface *locked_surface; /**< Locked region exposed as a SDL surface */
+    SDL_Surface *locked_surface; // Locked region exposed as a SDL surface
 
     Uint32 last_command_generation; // last command queue generation this texture was in.
 
     SDL_PropertiesID props;
 
-    void *internal; /**< Driver specific texture representation */
+    void *internal;             // Driver specific texture representation
 
     SDL_Texture *prev;
     SDL_Texture *next;
diff --git a/test/testautomation_render.c b/test/testautomation_render.c
index 5e75ad0ec5a87..9a74d7c1c757d 100644
--- a/test/testautomation_render.c
+++ b/test/testautomation_render.c
@@ -239,8 +239,7 @@ static int SDLCALL render_testBlit(void *arg)
     SDL_FRect rect;
     SDL_Texture *tface;
     SDL_Surface *referenceSurface = NULL;
-    float tw, th;
-    float i, j, ni, nj;
+    int i, j, ni, nj;
     int checkFailCount1;
 
     /* Clear surface. */
@@ -257,19 +256,18 @@ static int SDLCALL render_testBlit(void *arg)
     }
 
     /* Constant values. */
-    CHECK_FUNC(SDL_GetTextureSize, (tface, &tw, &th))
-    rect.w = tw;
-    rect.h = th;
-    ni = TESTRENDER_SCREEN_W - tw;
-    nj = TESTRENDER_SCREEN_H - th;
+    rect.w = (float)tface->w;
+    rect.h = (float)tface->h;
+    ni = TESTRENDER_SCREEN_W - tface->w;
+    nj = TESTRENDER_SCREEN_H - tface->h;
 
     /* Loop blit. */
     checkFailCount1 = 0;
     for (j = 0; j <= nj; j += 4) {
         for (i = 0; i <= ni; i += 4) {
             /* Blitting. */
-            rect.x = i;
-            rect.y = j;
+            rect.x = (float)i;
+            rect.y = (float)j;
             ret = SDL_RenderTexture(renderer, tface, NULL, &rect);
             if (!ret) {
                 checkFailCount1++;
@@ -631,7 +629,6 @@ static int SDLCALL render_testBlitColor(void *arg)
     SDL_FRect rect;
     SDL_Texture *tface;
     SDL_Surface *referenceSurface = NULL;
-    float tw, th;
     int i, j, ni, nj;
     int checkFailCount1;
     int checkFailCount2;
@@ -647,11 +644,10 @@ static int SDLCALL render_testBlitColor(void *arg)
     }
 
     /* Constant values. */
-    CHECK_FUNC(SDL_GetTextureSize, (tface, &tw, &th))
-    rect.w = tw;
-    rect.h = th;
-    ni = TESTRENDER_SCREEN_W - (int)tw;
-    nj = TESTRENDER_SCREEN_H - (int)th;
+    rect.w = (float)tface->w;
+    rect.h = (float)tface->h;
+    ni = TESTRENDER_SCREEN_W - tface->w;
+    nj = TESTRENDER_SCREEN_H - tface->h;
 
     /* Test blitting with color mod. */
     checkFailCount1 = 0;
@@ -1423,7 +1419,6 @@ static int SDLCALL render_testUVWrapping(void *arg)
     SDL_Vertex vertices[6];
     SDL_Vertex *verts = vertices;
     SDL_FColor color = { 1.0f, 1.0f, 1.0f, 1.0f };
-    float tw, th;
     SDL_FRect rect;
     float min_U = -0.5f;
     float max_U = 1.5f;
@@ -1442,11 +1437,10 @@ static int SDLCALL render_testUVWrapping(void *arg)
         return TEST_ABORTED;
     }
 
-    CHECK_FUNC(SDL_GetTextureSize, (tface, &tw, &th))
-    rect.w = tw * 2;
-    rect.h = th * 2;
-    rect.x = (TESTRENDER_SCREEN_W - rect.w) / 2;
-    rect.y = (TESTRENDER_SCREEN_H - rect.h) / 2;
+    rect.w = (float)tface->w * 2;
+    rect.h = (float)tface->h * 2;
+    rect.x = (float)(TESTRENDER_SCREEN_W - rect.w) / 2;
+    rect.y = (float)(TESTRENDER_SCREEN_H - rect.h) / 2;
 
     /*
      *   0--1
diff --git a/test/testcamera.c b/test/testcamera.c
index 893af67119220..3855b3b1aff8a 100644
--- a/test/testcamera.c
+++ b/test/testcamera.c
@@ -278,7 +278,6 @@ SDL_AppResult SDL_AppIterate(void *appstate)
     SDL_RenderClear(renderer);
 
     int win_w, win_h;
-    float tw, th;
     SDL_FRect d;
     Uint64 timestampNS = 0;
     SDL_Surface *frame_next = camera ? SDL_AcquireCameraFrame(camera, &timestampNS) : NULL;
@@ -305,8 +304,7 @@ SDL_AppResult SDL_AppIterate(void *appstate)
 
     if (frame_current) {
         if (!texture ||
-            !SDL_GetTextureSize(texture, &tw, &th) ||
-            (int)tw != frame_current->w || (int)th != frame_current->h) {
+            texture->w != frame_current->w || texture->h != frame_current->h) {
             /* Resize the window to match */
             SDL_SetWindowSize(window, frame_current->w, frame_current->h);
 
@@ -337,12 +335,11 @@ SDL_AppResult SDL_AppIterate(void *appstate)
             texture_updated = true;
         }
 
-        SDL_GetTextureSize(texture, &tw, &th);
         SDL_GetRenderOutputSize(renderer, &win_w, &win_h);
-        d.x = ((win_w - tw) / 2);
-        d.y = ((win_h - th) / 2);
-        d.w = tw;
-        d.h = th;
+        d.x = ((win_w - texture->w) / 2.0f);
+        d.y = ((win_h - texture->h) / 2.0f);
+        d.w = (float)texture->w;
+        d.h = (float)texture->h;
         SDL_RenderTexture(renderer, texture, NULL, &d);
     }
 
diff --git a/test/testnative.c b/test/testnative.c
index 95fb1c203f5cd..1a623841119ee 100644
--- a/test/testnative.c
+++ b/test/testnative.c
@@ -63,14 +63,12 @@ quit(int rc)
 
 static void MoveSprites(SDL_Renderer *renderer, SDL_Texture *sprite)
 {
-    float sprite_w, sprite_h;
     int i;
     SDL_Rect viewport;
     SDL_FRect *position, *velocity;
 
     /* Query the sizes */
     SDL_GetRenderViewport(renderer, &viewport);
-    SDL_GetTextureSize(sprite, &sprite_w, &sprite_h);
 
     /* Draw a gray background */
     SDL_SetRenderDrawColor(renderer, 0xA0, 0xA0, 0xA0, 0xFF);
@@ -81,12 +79,12 @@ static void MoveSprites(SDL_Renderer *renderer, SDL_Texture *sprite)
         position = &positions[i];
         velocity = &velocities[i];
         position->x += velocity->x;
-        if ((position->x < 0) || (position->x >= (viewport.w - sprite_w))) {
+        if ((position->x < 0) || (position->x >= (viewport.w - sprite->w))) {
             velocity->x = -velocity->x;
             position->x += velocity->x;
         }
         position->y += velocity->y;
-        if ((position->y < 0) || (position->y >= (viewport.h - sprite_h))) {
+        if ((position->y < 0) || (position->y >= (viewport.h - sprite->h))) {
             velocity->y = -velocity->y;
             position->y += velocity->y;
         }
@@ -108,7 +106,6 @@ int main(int argc, char *argv[])
     SDL_Renderer *renderer;
     SDL_Texture *sprite;
     int window_w, window_h;
-    float sprite_w, sprite_h;
     SDL_Event event;
 
     /* Initialize test framework */
@@ -178,7 +175,6 @@ int main(int argc, char *argv[])
 
     /* Allocate memory for the sprite info */
     SDL_GetWindowSize(window, &window_w, &window_h);
-    SDL_GetTextureSize(sprite, &sprite_w, &sprite_h);
     positions = (SDL_FRect *)SDL_malloc(NUM_SPRITES * sizeof(*positions));
     velocities = (SDL_FRect *)SDL_malloc(NUM_SPRITES * sizeof(*velocities));
     if (!positions || !velocities) {
@@ -186,10 +182,10 @@ int main(int argc, char *argv[])
         quit(2);
     }
     for (i = 0; i < NUM_SPRITES; ++i) {
-        positions[i].x = (float)(SDL_rand(window_w - (int)sprite_w));
-        positions[i].y = (float)(SDL_rand(window_h - (int)sprite_h));
-        positions[i].w = sprite_w;
-        positions[i].h = sprite_h;
+        positions[i].x = (float)SDL_rand(window_w - sprite->w);
+        positions[i].y = (float)SDL_rand(window_h - sprite->h);
+        positions[i].w = (float)sprite->w;
+        positions[i].h = (float)sprite->h;
         velocities[i].x = 0.0f;
         velocities[i].y = 0.0f;
         while (velocities[i].x == 0.f && velocities[i].y == 0.f) {
diff --git a/test/testviewport.c b/test/testviewport.c
index 6943145ac4b78..d4480b8605e68 100644
--- a/test/testviewport.c
+++ b/test/testviewport.c
@@ -48,7 +48,6 @@ static void DrawOnViewport(SDL_Renderer *renderer)
 {
     SDL_FRect rect;
     SDL_Rect cliprect;
-    float w, h;
 
     /* Set the viewport */
     SDL_SetRenderViewport(renderer, &viewport);
@@ -91,15 +90,11 @@ static void DrawOnViewport(SDL_Renderer *renderer)
     SDL_RenderFillRect(renderer, &rect);
 
     /* Add a clip rect and fill it with the sprite */
-    SDL_GetTextureSize(sprite, &w, &h);
-    rect.x = (viewport.w - w) / 2;
-    rect.y = (viewport.h - h) / 2;
-    rect.w = w;
-    rect.h = h;
-    cliprect.x = (int)rect.x;
-    cliprect.y = (int)rect.y;
-    cliprect.w = (int)rect.w;
-    cliprect.h = (int)rect.h;
+    cliprect.x = (viewport.w - sprite->w) / 2;
+    cliprect.y = (viewport.h - sprite->h) / 2;
+    cliprect.w = sprite->w;
+    cliprect.h = sprite->h;
+    SDL_RectToFRect(&cliprect, &rect);
     SDL_SetRenderClipRect(renderer, &cliprect);
     SDL_RenderTexture(renderer, sprite, NULL, &rect);
     SDL_SetRenderClipRect(renderer, NULL);