SDL_ttf: Added TTF_GetTextProperties()

From 25b596c820a1377cdf7fccbcfc1e0399705d5e5c Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 30 Sep 2024 09:39:23 -0700
Subject: [PATCH] Added TTF_GetTextProperties()

Also made the internal fields of TTF_Text private, and added a debug label instead of always copying the text used to create the TTF_Text.
---
 include/SDL3_ttf/SDL_textengine.h |  8 +++++
 include/SDL3_ttf/SDL_ttf.h        | 22 +++++++++++--
 src/SDL_renderer_textengine.c     | 10 +++---
 src/SDL_surface_textengine.c      | 19 +++++------
 src/SDL_ttf.c                     | 53 +++++++++++++++++++++----------
 5 files changed, 78 insertions(+), 34 deletions(-)

diff --git a/include/SDL3_ttf/SDL_textengine.h b/include/SDL3_ttf/SDL_textengine.h
index b3e32ba4..59dbdee6 100644
--- a/include/SDL3_ttf/SDL_textengine.h
+++ b/include/SDL3_ttf/SDL_textengine.h
@@ -39,6 +39,14 @@ extern "C" {
 /* Text created with the text engine */
 typedef struct TTF_Text TTF_Text;
 
+/* Private data in TTF_Text, available to implementations */
+struct TTF_TextData
+{
+    TTF_TextEngine *engine; /**< The engine used to create this text, read-only. */
+    SDL_PropertiesID props; /**< Custom properties associated with this text, read-write. */
+    void *textrep;          /**< The implementation-specific representation of this text */
+};
+
 /**
  * A font atlas draw command.
  *
diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h
index 25982f55..83779c81 100644
--- a/include/SDL3_ttf/SDL_ttf.h
+++ b/include/SDL3_ttf/SDL_ttf.h
@@ -1228,6 +1228,9 @@ extern SDL_DECLSPEC SDL_Surface * SDLCALL TTF_RenderGlyph_LCD(TTF_Font *font, Ui
  */
 typedef struct TTF_TextEngine TTF_TextEngine;
 
+/* Internal data for TTF_Text */
+typedef struct TTF_TextData TTF_TextData;
+
 /**
  * Text created with TTF_CreateText()
  *
@@ -1235,6 +1238,7 @@ typedef struct TTF_TextEngine TTF_TextEngine;
  *
  * \sa TTF_CreateText
  * \sa TTF_CreateText_Wrapped
+ * \sa TTF_GetTextProperties
  * \sa TTF_DestroyText
  */
 typedef struct TTF_Text
@@ -1243,8 +1247,11 @@ typedef struct TTF_Text
     int w;                  /**< The width of this text, in pixels, read-only. */
     int h;                  /**< The height of this text, in pixels, read-only. */
     SDL_FColor color;       /**< The color of the text, read-write. You can change this anytime. */
-    TTF_TextEngine *engine; /**< The engine used to create this text, read-only. */
-    void *internal;         /**< The internal representation of this text, read-only */
+
+    int refcount;           /**< Application reference count, used when freeing surface */
+
+    TTF_TextData *internal; /**< Private */
+
 } TTF_Text;
 
 /**
@@ -1402,6 +1409,17 @@ extern SDL_DECLSPEC TTF_Text * SDLCALL TTF_CreateText(TTF_TextEngine *engine, TT
  */
 extern SDL_DECLSPEC TTF_Text * SDLCALL TTF_CreateText_Wrapped(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length, int wrapLength);
 
+/**
+ * Get the properties associated with a text object.
+ *
+ * \param text the TTF_Text to query.
+ * \returns a valid property ID on success or 0 on failure; call
+ *          SDL_GetError() for more information.
+ *
+ * \since This function is available since SDL 3.0.0.
+ */
+extern SDL_DECLSPEC SDL_PropertiesID SDLCALL TTF_GetTextProperties(TTF_Text *text);
+
 /**
  * Destroy a text object created by a text engine.
  *
diff --git a/src/SDL_renderer_textengine.c b/src/SDL_renderer_textengine.c
index 282949a8..4a74341b 100644
--- a/src/SDL_renderer_textengine.c
+++ b/src/SDL_renderer_textengine.c
@@ -262,13 +262,13 @@ static bool SDLCALL CreateText(void *userdata, TTF_Font *font, Uint32 font_gener
     if (!data) {
         return false;
     }
-    text->internal = data;
+    text->internal->textrep = data;
     return true;
 }
 
 static void SDLCALL DestroyText(void *userdata, TTF_Text *text)
 {
-    TTF_RendererTextEngineTextData *data = (TTF_RendererTextEngineTextData *)text->internal;
+    TTF_RendererTextEngineTextData *data = (TTF_RendererTextEngineTextData *)text->internal->textrep;
 
     (void)userdata;
     DestroyTextData(data);
@@ -333,12 +333,12 @@ bool TTF_DrawRendererText(TTF_Text *text, float x, float y)
     TTF_RendererTextEngineTextData *data;
     SDL_Renderer *renderer;
 
-    if (!text || !text->engine || text->engine->CreateText != CreateText) {
+    if (!text || !text->internal || text->internal->engine->CreateText != CreateText) {
         return SDL_InvalidParamError("text");
     }
 
-    renderer = ((TTF_RendererTextEngineData *)text->engine->userdata)->renderer;
-    data = (TTF_RendererTextEngineTextData *)text->internal;
+    renderer = ((TTF_RendererTextEngineData *)text->internal->engine->userdata)->renderer;
+    data = (TTF_RendererTextEngineTextData *)text->internal->textrep;
 
     for (int i = 0; i < data->num_ops; ++i) {
         const TTF_DrawOperation *op = &data->ops[i];
diff --git a/src/SDL_surface_textengine.c b/src/SDL_surface_textengine.c
index 06a290a4..0bc84342 100644
--- a/src/SDL_surface_textengine.c
+++ b/src/SDL_surface_textengine.c
@@ -251,13 +251,13 @@ static bool SDLCALL CreateText(void *userdata, TTF_Font *font, Uint32 font_gener
     if (!data) {
         return false;
     }
-    text->internal = data;
+    text->internal->textrep = data;
     return true;
 }
 
 static void SDLCALL DestroyText(void *userdata, TTF_Text *text)
 {
-    TTF_SurfaceTextEngineTextData *data = (TTF_SurfaceTextEngineTextData *)text->internal;
+    TTF_SurfaceTextEngineTextData *data = (TTF_SurfaceTextEngineTextData *)text->internal->textrep;
 
     (void)userdata;
     DestroyTextData(data);
@@ -290,10 +290,8 @@ static void UpdateColor(TTF_SurfaceTextEngineTextData *data, const SDL_FColor *c
     SDL_copyp(&data->fcolor, color);
 }
 
-static void DrawFill(TTF_Text *text, const TTF_FillOperation *op, int x, int y, SDL_Surface *surface)
+static void DrawFill(TTF_SurfaceTextEngineTextData *data, const TTF_FillOperation *op, int x, int y, SDL_Surface *surface)
 {
-    TTF_SurfaceTextEngineTextData *data = (TTF_SurfaceTextEngineTextData *)text->internal;
-
     Uint32 color = SDL_MapSurfaceRGBA(surface, data->color.r, data->color.g, data->color.b, data->color.a);
 
     SDL_Rect dst;
@@ -303,9 +301,8 @@ static void DrawFill(TTF_Text *text, const TTF_FillOperation *op, int x, int y,
     SDL_FillSurfaceRect(surface, &dst, color);
 }
 
-static void DrawCopy(TTF_Text *text, const TTF_CopyOperation *op, int x, int y, SDL_Surface *surface)
+static void DrawCopy(TTF_SurfaceTextEngineTextData *data, const TTF_CopyOperation *op, int x, int y, SDL_Surface *surface)
 {
-    TTF_SurfaceTextEngineTextData *data = (TTF_SurfaceTextEngineTextData *)text->internal;
     TTF_SurfaceTextEngineGlyphData *glyph = (TTF_SurfaceTextEngineGlyphData *)op->reserved;
 
     if (data->color.r != glyph->color.r ||
@@ -328,14 +325,14 @@ bool TTF_DrawSurfaceText(TTF_Text *text, int x, int y, SDL_Surface *surface)
 {
     TTF_SurfaceTextEngineTextData *data;
 
-    if (!text || !text->engine || text->engine->CreateText != CreateText) {
+    if (!text || !text->internal || text->internal->engine->CreateText != CreateText) {
         return SDL_InvalidParamError("text");
     }
     if (!surface) {
         return SDL_InvalidParamError("surface");
     }
 
-    data = (TTF_SurfaceTextEngineTextData *)text->internal;
+    data = (TTF_SurfaceTextEngineTextData *)text->internal->textrep;
 
     if (text->color.r != data->fcolor.r ||
         text->color.g != data->fcolor.g ||
@@ -348,10 +345,10 @@ bool TTF_DrawSurfaceText(TTF_Text *text, int x, int y, SDL_Surface *surface)
         const TTF_DrawOperation *op = &data->ops[i];
         switch (op->cmd) {
         case TTF_DRAW_COMMAND_FILL:
-            DrawFill(text, &op->fill, x, y, surface);
+            DrawFill(data, &op->fill, x, y, surface);
             break;
         case TTF_DRAW_COMMAND_COPY:
-            DrawCopy(text, &op->copy, x, y, surface);
+            DrawCopy(data, &op->copy, x, y, surface);
             break;
         default:
             break;
diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c
index 4224992a..0cc00de6 100644
--- a/src/SDL_ttf.c
+++ b/src/SDL_ttf.c
@@ -3547,21 +3547,29 @@ SDL_Surface* TTF_RenderText_LCD_Wrapped(TTF_Font *font, const char *text, size_t
     return TTF_Render_Wrapped_Internal(font, text, length, fg, bg, wrapLength, RENDER_LCD);
 }
 
-static TTF_Text *CreateText(TTF_TextEngine *engine, const char *text, int width, int height)
+typedef struct TTF_InternalText
 {
-    TTF_Text *result = (TTF_Text *)SDL_calloc(1, sizeof(*result));
-    if (!result) {
+    TTF_Text text;
+    TTF_TextData internal;
+} TTF_InternalText;
+
+static TTF_Text *CreateText(TTF_TextEngine *engine, int width, int height)
+{
+    TTF_InternalText *mem = (TTF_InternalText *)SDL_calloc(1, sizeof(*mem));
+    if (!mem) {
         return NULL;
     }
 
-    result->w = width;
-    result->h = height;
-    result->color.r = 1.0f;
-    result->color.g = 1.0f;
-    result->color.b = 1.0f;
-    result->color.a = 1.0f;
-    result->engine = engine;
-    return result;
+    TTF_Text *text = &mem->text;
+    text->internal = &mem->internal;
+    text->w = width;
+    text->h = height;
+    text->color.r = 1.0f;
+    text->color.g = 1.0f;
+    text->color.b = 1.0f;
+    text->color.a = 1.0f;
+    text->internal->engine = engine;
+    return text;
 }
 
 TTF_Text *TTF_CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *text, size_t length)
@@ -3620,7 +3628,7 @@ TTF_Text *TTF_CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *tex
         Draw_Line_TextEngine(font, width, height, 0, ystart + font->strikethrough_top_row, width, font->line_thickness, ops, &num_ops);
     }
 
-    result = CreateText(engine, text, width, height);
+    result = CreateText(engine, width, height);
     if (!result) {
         goto failure;
     }
@@ -3713,7 +3721,7 @@ TTF_Text *TTF_CreateText_Wrapped(TTF_TextEngine *engine, TTF_Font *font, const c
         }
     }
 
-    result = CreateText(engine, text, width, height);
+    result = CreateText(engine, width, height);
     if (!result) {
         goto failure;
     }
@@ -3733,14 +3741,27 @@ TTF_Text *TTF_CreateText_Wrapped(TTF_TextEngine *engine, TTF_Font *font, const c
     return NULL;
 }
 
+SDL_PropertiesID TTF_GetTextProperties(TTF_Text *text)
+{
+    TTF_CHECK_POINTER("text", text, 0);
+    TTF_CHECK_POINTER("text", text->internal, 0);
+
+    if (!text->internal->props) {
+        text->internal->props = SDL_CreateProperties();
+    }
+    return text->internal->props;
+}
+
 void TTF_DestroyText(TTF_Text *text)
 {
-    if (!text || !text->engine) {
+    if (!text || !text->internal) {
         return;
     }
 
-    text->engine->DestroyText(text->engine->userdata, text);
-    text->engine = NULL;
+    TTF_TextEngine *engine = text->internal->engine;
+    engine->DestroyText(engine->userdata, text);
+    SDL_DestroyProperties(text->internal->props);
+    text->internal = NULL;
     SDL_free(text->label);
     SDL_free(text);
 }