SDL_ttf: Added TTF_SetTextColor() and TTF_GetTextColor()

From cb0e12a6157556aad7e2aa229e9e8bc6bd386700 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Sat, 19 Oct 2024 20:32:57 -0700
Subject: [PATCH] Added TTF_SetTextColor() and TTF_GetTextColor()

People expected there to be an API function to set the text color. Voila!
---
 examples/editbox.c                |   4 +-
 examples/showfont.c               |   5 +-
 include/SDL3_ttf/SDL_textengine.h |   1 +
 include/SDL3_ttf/SDL_ttf.h        | 125 +++++++++++++++++++++++++++++-
 src/SDL_renderer_textengine.c     |   2 +-
 src/SDL_surface_textengine.c      |  10 +--
 src/SDL_ttf.c                     | 100 +++++++++++++++++++++++-
 src/SDL_ttf.sym                   |   4 +
 8 files changed, 235 insertions(+), 16 deletions(-)

diff --git a/examples/editbox.c b/examples/editbox.c
index 8cd239e9..f5f701a9 100644
--- a/examples/editbox.c
+++ b/examples/editbox.c
@@ -221,7 +221,9 @@ static void SaveCandidates(EditBox *edit, const SDL_Event *event)
     edit->candidates = TTF_CreateText(TTF_GetTextEngine(edit->text), edit->font, candidate_text, 0);
     SDL_free(candidate_text);
     if (edit->candidates) {
-        SDL_copyp(&edit->candidates->color, &edit->text->color);
+        float r, g, b, a;
+        TTF_GetTextColorFloat(edit->text, &r, &g, &b, &a);
+        TTF_SetTextColorFloat(edit->candidates, r, g, b, a);
     } else {
         ClearCandidates(edit);
     }
diff --git a/examples/showfont.c b/examples/showfont.c
index fda33db4..9ff9c6d0 100644
--- a/examples/showfont.c
+++ b/examples/showfont.c
@@ -550,10 +550,7 @@ int main(int argc, char *argv[])
         editRect.w -= 8.0f;
         scene.edit = EditBox_Create(scene.window, scene.renderer, engine, font, &editRect);
         if (scene.edit) {
-            scene.edit->text->color.r = forecol->r / 255.0f;
-            scene.edit->text->color.g = forecol->g / 255.0f;
-            scene.edit->text->color.b = forecol->b / 255.0f;
-            scene.edit->text->color.a = forecol->a / 255.0f;
+            TTF_SetTextColor(scene.edit->text, forecol->r, forecol->g, forecol->b, forecol->a);
 
             EditBox_Insert(scene.edit, message);
         }
diff --git a/include/SDL3_ttf/SDL_textengine.h b/include/SDL3_ttf/SDL_textengine.h
index 99222e95..0bf12399 100644
--- a/include/SDL3_ttf/SDL_textengine.h
+++ b/include/SDL3_ttf/SDL_textengine.h
@@ -106,6 +106,7 @@ typedef struct TTF_TextLayout TTF_TextLayout;
 struct TTF_TextData
 {
     TTF_Font *font;             /**< The font used by this text, read-only. */
+    SDL_FColor color;           /**< The color of the text, read-only. */
 
     bool needs_layout_update;   /**< True if the layout needs to be updated */
     TTF_TextLayout *layout;     /**< Cached layout information, read-only. */
diff --git a/include/SDL3_ttf/SDL_ttf.h b/include/SDL3_ttf/SDL_ttf.h
index f78f22fb..3dcce873 100644
--- a/include/SDL3_ttf/SDL_ttf.h
+++ b/include/SDL3_ttf/SDL_ttf.h
@@ -1341,7 +1341,6 @@ typedef struct TTF_TextData TTF_TextData;
 typedef struct TTF_Text
 {
     char *text;             /**< A copy of the text used to create this text object, useful for layout and debugging. This will be freed automatically when the object is destroyed. */
-    SDL_FColor color;       /**< The color of the text, read-write. You can change this anytime. */
     int num_lines;          /**< The number of lines in the text, 0 if it's empty */
 
     int refcount;           /**< Application reference count, used when freeing surface */
@@ -1549,6 +1548,8 @@ extern SDL_DECLSPEC SDL_PropertiesID SDLCALL TTF_GetTextProperties(TTF_Text *tex
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_GetTextEngine
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_SetTextEngine(TTF_Text *text, TTF_TextEngine *engine);
 
@@ -1563,6 +1564,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextEngine(TTF_Text *text, TTF_TextEngin
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_SetTextEngine
  */
 extern SDL_DECLSPEC TTF_TextEngine * SDLCALL TTF_GetTextEngine(TTF_Text *text);
 
@@ -1580,6 +1583,8 @@ extern SDL_DECLSPEC TTF_TextEngine * SDLCALL TTF_GetTextEngine(TTF_Text *text);
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_GetTextFont
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_SetTextFont(TTF_Text *text, TTF_Font *font);
 
@@ -1594,9 +1599,99 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextFont(TTF_Text *text, TTF_Font *font)
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_SetTextFont
  */
 extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_GetTextFont(TTF_Text *text);
 
+/**
+ * Set the color of a text object.
+ *
+ * The default text color is white (255, 255, 255, 255).
+ *
+ * \param text the TTF_Text to modify.
+ * \param r the red color value in the range of 0-255.
+ * \param g the green color value in the range of 0-255.
+ * \param b the blue color value in the range of 0-255.
+ * \param a the alpha value in the range of 0-255.
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ *          information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ *               text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_GetTextColor
+ * \sa TTF_SetTextColorFloat
+ */
+extern SDL_DECLSPEC bool SDLCALL TTF_SetTextColor(TTF_Text *text, Uint8 r, Uint8 g, Uint8 b, Uint8 a);
+
+/**
+ * Set the color of a text object.
+ *
+ * The default text color is white (1.0f, 1.0f, 1.0f, 1.0f).
+ *
+ * \param text the TTF_Text to modify.
+ * \param r the red color value, normally in the range of 0-1.
+ * \param g the green color value, normally in the range of 0-1.
+ * \param b the blue color value, normally in the range of 0-1.
+ * \param a the alpha value in the range of 0-1.
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ *          information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ *               text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_GetTextColorFloat
+ * \sa TTF_SetTextColor
+ */
+extern SDL_DECLSPEC bool SDLCALL TTF_SetTextColorFloat(TTF_Text *text, float r, float g, float b, float a);
+
+/**
+ * Get the color of a text object.
+ *
+ * \param text the TTF_Text to query.
+ * \param r a pointer filled in with the red color value in the range of 0-255, may be NULL.
+ * \param g a pointer filled in with the green color value in the range of 0-255, may be NULL.
+ * \param b a pointer filled in with the blue color value in the range of 0-255, may be NULL.
+ * \param a a pointer filled in with the alpha value in the range of 0-255, may be NULL.
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ *          information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ *               text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_GetTextColorFloat
+ * \sa TTF_SetTextColor
+ */
+extern SDL_DECLSPEC bool SDLCALL TTF_GetTextColor(TTF_Text *text, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a);
+
+/**
+ * Get the color of a text object.
+ *
+ * \param text the TTF_Text to query.
+ * \param r a pointer filled in with the red color value, normally in the range of 0-1, may be NULL.
+ * \param g a pointer filled in with the green color value, normally in the range of 0-1, may be NULL.
+ * \param b a pointer filled in with the blue color value, normally in the range of 0-1, may be NULL.
+ * \param a a pointer filled in with the alpha value in the range of 0-1, may be NULL.
+ * \returns true on success or false on failure; call SDL_GetError() for more
+ *          information.
+ *
+ * \threadsafety This function should be called on the thread that created the
+ *               text.
+ *
+ * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_GetTextColor
+ * \sa TTF_SetTextColorFloat
+ */
+extern SDL_DECLSPEC bool SDLCALL TTF_GetTextColorFloat(TTF_Text *text, float *r, float *g, float *b, float *a);
+
 /**
  * Set the position of a text object.
  *
@@ -1611,6 +1706,8 @@ extern SDL_DECLSPEC TTF_Font * SDLCALL TTF_GetTextFont(TTF_Text *text);
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_GetTextPosition
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_SetTextPosition(TTF_Text *text, int x, int y);
 
@@ -1627,6 +1724,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextPosition(TTF_Text *text, int x, int
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_SetTextPosition
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_GetTextPosition(TTF_Text *text, int *x, int *y);
 
@@ -1644,6 +1743,10 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextPosition(TTF_Text *text, int *x, int
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_AppendTextString
+ * \sa TTF_DeleteTextString
+ * \sa TTF_InsertTextString
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_SetTextString(TTF_Text *text, const char *string, size_t length);
 
@@ -1665,6 +1768,10 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextString(TTF_Text *text, const char *s
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_AppendTextString
+ * \sa TTF_DeleteTextString
+ * \sa TTF_SetTextString
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_InsertTextString(TTF_Text *text, int offset, const char *string, size_t length);
 
@@ -1682,6 +1789,10 @@ extern SDL_DECLSPEC bool SDLCALL TTF_InsertTextString(TTF_Text *text, int offset
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_DeleteTextString
+ * \sa TTF_InsertTextString
+ * \sa TTF_SetTextString
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_AppendTextString(TTF_Text *text, const char *string, size_t length);
 
@@ -1702,6 +1813,10 @@ extern SDL_DECLSPEC bool SDLCALL TTF_AppendTextString(TTF_Text *text, const char
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_AppendTextString
+ * \sa TTF_InsertTextString
+ * \sa TTF_SetTextString
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_DeleteTextString(TTF_Text *text, int offset, int length);
 
@@ -1718,6 +1833,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_DeleteTextString(TTF_Text *text, int offset
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_GetTextWrapping
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapping(TTF_Text *text, int wrapLength);
 
@@ -1734,6 +1851,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapping(TTF_Text *text, int wrapLen
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_SetTextWrapping
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, int *wrapLength);
 
@@ -1751,6 +1870,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_GetTextWrapping(TTF_Text *text, int *wrapLe
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_TextWrapWhitespaceVisible
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapWhitespaceVisible(TTF_Text *text, bool visible);
 
@@ -1764,6 +1885,8 @@ extern SDL_DECLSPEC bool SDLCALL TTF_SetTextWrapWhitespaceVisible(TTF_Text *text
  *               text.
  *
  * \since This function is available since SDL_ttf 3.0.0.
+ *
+ * \sa TTF_SetTextWrapWhitespaceVisible
  */
 extern SDL_DECLSPEC bool SDLCALL TTF_TextWrapWhitespaceVisible(TTF_Text *text);
 
diff --git a/src/SDL_renderer_textengine.c b/src/SDL_renderer_textengine.c
index 386597dc..d8e53663 100644
--- a/src/SDL_renderer_textengine.c
+++ b/src/SDL_renderer_textengine.c
@@ -887,7 +887,7 @@ bool TTF_DrawRendererText(TTF_Text *text, float x, float y)
         SDL_RenderGeometryRaw(renderer,
                               sequence->texture,
                               sequence->positions, 2 * sizeof(float),
-                              &text->color, 0,
+                              &text->internal->color, 0,
                               sequence->texcoords, 2 * sizeof(float),
                               sequence->num_rects * 4,
                               sequence->indices, sequence->num_rects * 6, sizeof(*sequence->indices));
diff --git a/src/SDL_surface_textengine.c b/src/SDL_surface_textengine.c
index f8a70754..8730a276 100644
--- a/src/SDL_surface_textengine.c
+++ b/src/SDL_surface_textengine.c
@@ -344,11 +344,11 @@ bool TTF_DrawSurfaceText(TTF_Text *text, int x, int y, SDL_Surface *surface)
         return true;
     }
 
-    if (text->color.r != data->fcolor.r ||
-        text->color.g != data->fcolor.g ||
-        text->color.b != data->fcolor.b ||
-        text->color.a != data->fcolor.a) {
-        UpdateColor(data, &text->color);
+    if (text->internal->color.r != data->fcolor.r ||
+        text->internal->color.g != data->fcolor.g ||
+        text->internal->color.b != data->fcolor.b ||
+        text->internal->color.a != data->fcolor.a) {
+        UpdateColor(data, &text->internal->color);
     }
 
     for (int i = 0; i < data->num_ops; ++i) {
diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c
index ec342eec..37b04bb3 100644
--- a/src/SDL_ttf.c
+++ b/src/SDL_ttf.c
@@ -3702,10 +3702,10 @@ static TTF_Text *CreateText(TTF_TextEngine *engine, TTF_Font *font, const char *
     result->internal = &mem->internal;
     result->internal->layout = &mem->layout;
     result->internal->font = font;
-    result->color.r = 1.0f;
-    result->color.g = 1.0f;
-    result->color.b = 1.0f;
-    result->color.a = 1.0f;
+    result->internal->color.r = 1.0f;
+    result->internal->color.g = 1.0f;
+    result->internal->color.b = 1.0f;
+    result->internal->color.a = 1.0f;
     result->internal->needs_layout_update = true;
     result->internal->engine = engine;
     result->internal->layout->wrap_length = wrapLength;
@@ -4064,6 +4064,98 @@ TTF_Font *TTF_GetTextFont(TTF_Text *text)
     return text->internal->font;
 }
 
+bool TTF_SetTextColor(TTF_Text *text, Uint8 r, Uint8 g, Uint8 b, Uint8 a)
+{
+    const float fR = (float)r / 255.0f;
+    const float fG = (float)g / 255.0f;
+    const float fB = (float)b / 255.0f;
+    const float fA = (float)a / 255.0f;
+
+    return TTF_SetTextColorFloat(text, fR, fG, fB, fA);
+}
+
+bool TTF_SetTextColorFloat(TTF_Text *text, float r, float g, float b, float a)
+{
+    TTF_CHECK_POINTER("text", text, false);
+
+    text->internal->color.r = r;
+    text->internal->color.g = g;
+    text->internal->color.b = b;
+    text->internal->color.a = a;
+    return true;
+}
+
+bool TTF_GetTextColor(TTF_Text *text, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a)
+{
+    float fR = 1.0f, fG = 1.0f, fB = 1.0f, fA = 1.0f;
+
+    if (!TTF_GetTextColorFloat(text, &fR, &fG, &fB, &fA)) {
+        if (r) {
+            *r = 255;
+        }
+        if (g) {
+            *g = 255;
+        }
+        if (b) {
+            *b = 255;
+        }
+        if (b) {
+            *b = 255;
+        }
+        return false;
+    }
+
+    if (r) {
+        *r = (Uint8)SDL_roundf(SDL_clamp(fR, 0.0f, 1.0f) * 255.0f);
+    }
+    if (g) {
+        *g = (Uint8)SDL_roundf(SDL_clamp(fG, 0.0f, 1.0f) * 255.0f);
+    }
+    if (b) {
+        *b = (Uint8)SDL_roundf(SDL_clamp(fB, 0.0f, 1.0f) * 255.0f);
+    }
+    if (a) {
+        *a = (Uint8)SDL_roundf(SDL_clamp(fA, 0.0f, 1.0f) * 255.0f);
+    }
+    return true;
+}
+
+bool TTF_GetTextColorFloat(TTF_Text *text, float *r, float *g, float *b, float *a)
+{
+    SDL_FColor color;
+
+    if (r) {
+        *r = 1.0f;
+    }
+    if (g) {
+        *g = 1.0f;
+    }
+    if (b) {
+        *b = 1.0f;
+    }
+    if (a) {
+        *a = 1.0f;
+    }
+
+    TTF_CHECK_POINTER("text", text, false);
+
+    color = text->internal->color;
+
+    if (r) {
+        *r = color.r;
+    }
+    if (g) {
+        *g = color.g;
+    }
+    if (b) {
+        *b = color.b;
+    }
+    if (a) {
+        *a = color.a;
+    }
+    return true;
+}
+
 bool TTF_SetTextPosition(TTF_Text *text, int x, int y)
 {
     TTF_CHECK_POINTER("text", text, false);
diff --git a/src/SDL_ttf.sym b/src/SDL_ttf.sym
index c5bb0e1b..4aab9e0f 100644
--- a/src/SDL_ttf.sym
+++ b/src/SDL_ttf.sym
@@ -43,6 +43,8 @@ SDL3_ttf_0.0.0 {
     TTF_GetPreviousTextSubString;
     TTF_GetStringSize;
     TTF_GetStringSizeWrapped;
+    TTF_GetTextColor;
+    TTF_GetTextColorFloat;
     TTF_GetTextEngine;
     TTF_GetTextFont;
     TTF_GetTextPosition;
@@ -80,6 +82,8 @@ SDL3_ttf_0.0.0 {
     TTF_SetFontSizeDPI;
     TTF_SetFontStyle;
     TTF_SetFontWrapAlignment;
+    TTF_SetTextColor;
+    TTF_SetTextColorFloat;
     TTF_SetTextEngine;
     TTF_SetTextFont;
     TTF_SetTextPosition;