SDL_ttf: Cache the glyph along with glyph positions

From 599071a0ebe5745d3bccd8d86b24c1f1cb3cc325 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 27 Jan 2025 11:56:01 -0800
Subject: [PATCH] Cache the glyph along with glyph positions

---
 src/SDL_ttf.c | 183 +++++++++++++++++++++++++-------------------------
 1 file changed, 93 insertions(+), 90 deletions(-)

diff --git a/src/SDL_ttf.c b/src/SDL_ttf.c
index d4dd9c7e..f5f628b6 100644
--- a/src/SDL_ttf.c
+++ b/src/SDL_ttf.c
@@ -220,6 +220,7 @@ typedef struct cached_glyph {
 typedef struct GlyphPosition {
     TTF_Font *font;
     FT_UInt index;
+    c_glyph *glyph;
     int x_offset;
     int y_offset;
     int x_advance;
@@ -1463,98 +1464,93 @@ static bool Render_Line_TextEngine(TTF_Font *font, TTF_Direction direction, int
         int x = pos->x;
         int y = pos->y;
         int offset = pos->offset;
-        c_glyph *glyph;
-
-        if (Find_GlyphByIndex(glyph_font, idx, 0, 0, 0, 0, 0, 0, &glyph, NULL)) {
-            int above_w, above_h;
-            int glyph_x = 0;
-            int glyph_y = 0;
-            int glyph_width = glyph->sz_width;
-            int glyph_rows = glyph->sz_rows;
-            TTF_DrawOperation *op;
-
-            // Position updated after glyph rendering
-            x = xstart + FT_FLOOR(x) + glyph->sz_left;
-            y = ystart + FT_FLOOR(y) - glyph->sz_top;
-
-            // Make sure glyph is inside text area
-            above_w = x + glyph_width - width;
-            above_h = y + glyph_rows  - height;
-
-            if (x < 0) {
-                int tmp = -x;
-                x = 0;
-                glyph_x += tmp;
-                glyph_width -= tmp;
-            }
-            if (above_w > 0) {
-                glyph_width -= above_w;
-            }
-            if (y < 0) {
-                int tmp = -y;
-                y = 0;
-                glyph_y += tmp;
-                glyph_rows -= tmp;
-            }
-            if (above_h > 0) {
-                glyph_rows -= above_h;
-            }
-
-            if (glyph_width > 0 && glyph_rows > 0) {
-                op = &ops[op_index++];
-                op->cmd = TTF_DRAW_COMMAND_COPY;
-                op->copy.text_offset = offset;
-                op->copy.glyph_font = glyph_font;
-                op->copy.glyph_index = idx;
-                op->copy.src.x = glyph_x;
-                op->copy.src.y = glyph_y;
-                op->copy.src.w = glyph_width;
-                op->copy.src.h = glyph_rows;
-                op->copy.dst.x = x;
-                op->copy.dst.y = y;
-                op->copy.dst.w = op->copy.src.w;
-                op->copy.dst.h = op->copy.src.h;
-            } else {
-                // Use the distance to the next glyph as our bounds width
-                glyph_width = FT_FLOOR(pos->x_advance);
-            }
-
-            bounds.x = x;
-            bounds.w = glyph_width;
-            if (offset != last_offset) {
-                cluster = &clusters[cluster_index++];
-                cluster->offset = cluster_offset + offset;
-                cluster->line_index = line_index;
-                if (direction == TTF_DIRECTION_INVALID) {
-                    if (last_offset == -1) {
-                        if (i < (font->positions->len - 1)) {
-                            GlyphPosition *next = &font->positions->pos[i + 1];
-                            if (offset < next->offset) {
-                                cluster->flags = TTF_DIRECTION_LTR;
-                            } else {
-                                cluster->flags = TTF_DIRECTION_RTL;
-                            }
-                        } else {
-                            cluster->flags = TTF_DIRECTION_INVALID;
-                        }
-                    } else {
-                        if (offset > last_offset) {
+        c_glyph *glyph = pos->glyph;
+        int above_w, above_h;
+        int glyph_x = 0;
+        int glyph_y = 0;
+        int glyph_width = glyph->sz_width;
+        int glyph_rows = glyph->sz_rows;
+        TTF_DrawOperation *op;
+
+        // Position updated after glyph rendering
+        x = xstart + FT_FLOOR(x) + glyph->sz_left;
+        y = ystart + FT_FLOOR(y) - glyph->sz_top;
+
+        // Make sure glyph is inside text area
+        above_w = x + glyph_width - width;
+        above_h = y + glyph_rows  - height;
+
+        if (x < 0) {
+            int tmp = -x;
+            x = 0;
+            glyph_x += tmp;
+            glyph_width -= tmp;
+        }
+        if (above_w > 0) {
+            glyph_width -= above_w;
+        }
+        if (y < 0) {
+            int tmp = -y;
+            y = 0;
+            glyph_y += tmp;
+            glyph_rows -= tmp;
+        }
+        if (above_h > 0) {
+            glyph_rows -= above_h;
+        }
+
+        if (glyph_width > 0 && glyph_rows > 0) {
+            op = &ops[op_index++];
+            op->cmd = TTF_DRAW_COMMAND_COPY;
+            op->copy.text_offset = offset;
+            op->copy.glyph_font = glyph_font;
+            op->copy.glyph_index = idx;
+            op->copy.src.x = glyph_x;
+            op->copy.src.y = glyph_y;
+            op->copy.src.w = glyph_width;
+            op->copy.src.h = glyph_rows;
+            op->copy.dst.x = x;
+            op->copy.dst.y = y;
+            op->copy.dst.w = op->copy.src.w;
+            op->copy.dst.h = op->copy.src.h;
+        } else {
+            // Use the distance to the next glyph as our bounds width
+            glyph_width = FT_FLOOR(pos->x_advance);
+        }
+
+        bounds.x = x;
+        bounds.w = glyph_width;
+        if (offset != last_offset) {
+            cluster = &clusters[cluster_index++];
+            cluster->offset = cluster_offset + offset;
+            cluster->line_index = line_index;
+            if (direction == TTF_DIRECTION_INVALID) {
+                if (last_offset == -1) {
+                    if (i < (font->positions->len - 1)) {
+                        GlyphPosition *next = &font->positions->pos[i + 1];
+                        if (offset < next->offset) {
                             cluster->flags = TTF_DIRECTION_LTR;
                         } else {
                             cluster->flags = TTF_DIRECTION_RTL;
                         }
+                    } else {
+                        cluster->flags = TTF_DIRECTION_INVALID;
                     }
                 } else {
-                    cluster->flags = direction;
+                    if (offset > last_offset) {
+                        cluster->flags = TTF_DIRECTION_LTR;
+                    } else {
+                        cluster->flags = TTF_DIRECTION_RTL;
+                    }
                 }
-                SDL_copyp(&cluster->rect, &bounds);
-
-                last_offset = offset;
-            } else if (cluster) {
-                SDL_GetRectUnion(&cluster->rect, &bounds, &cluster->rect);
+            } else {
+                cluster->flags = direction;
             }
-        } else {
-            return false;
+            SDL_copyp(&cluster->rect, &bounds);
+
+            last_offset = offset;
+        } else if (cluster) {
+            SDL_GetRectUnion(&cluster->rect, &bounds, &cluster->rect);
         }
     }
 
@@ -3255,6 +3251,9 @@ static bool CollectGlyphsFromFont(TTF_Font *font, const char *text, size_t lengt
         pos->x_offset = hb_glyph_position[i].x_offset;
         pos->y_offset = hb_glyph_position[i].y_offset;
         pos->offset = (int)hb_glyph_info[i].cluster;
+        if (!Find_GlyphByIndex(font, pos->index, 0, 0, 0, 0, 0, 0, &pos->glyph, NULL)) {
+            return SDL_SetError("Couldn't find glyph %u in font", pos->index);
+        }
     }
     hb_buffer_destroy(hb_buffer);
 
@@ -3302,6 +3301,7 @@ static bool CollectGlyphsFromFont(TTF_Font *font, const char *text, size_t lengt
         GlyphPosition *pos = &positions->pos[positions->len++];
         pos->font = font;
         pos->index = idx;
+        pos->glyph = glyph;
         pos->offset = offset;
         pos->x_advance = glyph->advance;
         pos->y_advance = 0;
@@ -3442,8 +3442,15 @@ static bool CollectGlyphs(TTF_Font *font, const char *text, size_t length, TTF_D
     // Make sure any missing characters use the tofu from the initial font
     for (int i = 0; i < positions->len; ++i) {
         GlyphPosition *pos = &positions->pos[i];
-        if (pos->index == 0) {
+        if (pos->index == 0 && pos->font != font) {
             pos->font = font;
+            if (!Find_GlyphByIndex(font, pos->index, 0, 0, 0, 0, 0, 0, &pos->glyph, NULL)) {
+                return SDL_SetError("Couldn't find glyph %u in font", pos->index);
+            }
+            pos->x_advance = pos->glyph->advance;
+            pos->y_advance = 0;
+            pos->x_offset = 0;
+            pos->y_offset = 0;
         }
     }
     return true;
@@ -3539,11 +3546,7 @@ static bool TTF_Size_Internal(TTF_Font *font, const char *text, size_t length, T
     int last_offset = -1;
     for (int i = 0; i < positions->len; ++i) {
         GlyphPosition *pos = &positions->pos[i];
-
-        c_glyph *glyph = NULL;
-        if (!Find_GlyphByIndex(pos->font, pos->index, 0, 0, 0, 0, 0, 0, &glyph, NULL)) {
-            return SDL_SetError("Couldn't find glyph %u in font", pos->index);
-        }
+        c_glyph *glyph = pos->glyph;
 
         // Compute positions
         pos_x = x + pos->x_offset;