From 2b46565a2e05f02fedc9b0c10ad5cb0fecacdf6e Mon Sep 17 00:00:00 2001 From: andrewmd5 <1297077+andrewmd5@users.noreply.github.com> Date: Fri, 8 Aug 2025 17:55:32 +0900 Subject: [PATCH] fix: text measurement/rendering width mismatch in sokol_clay The text bounding box width was incorrect because the measurement function was using logical pixels while the rendering function was using physical pixels (scaled by DPI). This caused a mismatch where: - sclay_measure_text() set font size and letter spacing without DPI scaling - sclay_render() applied DPI scaling to both font size and letter spacing - fontstash's fonsTextBounds() returned physical pixel measurements even when given logical pixel sizes, leading to incorrect width calculations The fix ensures consistent DPI scaling by: 1. Scaling font size and letter spacing by dpi_scale during measurement 2. Dividing the returned bounds by dpi_scale to convert back to logical pixels 3. This ensures Clay receives measurements in logical pixels that match what will actually be rendered This was particularly noticeable with long continuous text where the discrepancy accumulated across many characters. The issue was font-size dependent because different font sizes have different rounding behaviors in the underlying font rendering system. Also adds text alignment support to properly measure and render centered/right-aligned text. --- clay.h | 3 +++ renderers/sokol/sokol_clay.h | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/clay.h b/clay.h index 938683a..8971abe 100644 --- a/clay.h +++ b/clay.h @@ -566,6 +566,8 @@ typedef struct Clay_TextRenderData { uint16_t letterSpacing; // The height of the bounding box for this line of text. uint16_t lineHeight; + // Specifies the alignment of the text within its container. + Clay_TextAlignment textAlignment; } Clay_TextRenderData; // Render command data when commandType == CLAY_RENDER_COMMAND_TYPE_RECTANGLE @@ -2898,6 +2900,7 @@ void Clay__CalculateFinalLayout(void) { .fontSize = textElementConfig->fontSize, .letterSpacing = textElementConfig->letterSpacing, .lineHeight = textElementConfig->lineHeight, + .textAlignment = textElementConfig->textAlignment, }}, .userData = textElementConfig->userData, .id = Clay__HashNumber(lineIndex, currentElement->id).id, diff --git a/renderers/sokol/sokol_clay.h b/renderers/sokol/sokol_clay.h index 0436a50..9948502 100644 --- a/renderers/sokol/sokol_clay.h +++ b/renderers/sokol/sokol_clay.h @@ -227,13 +227,26 @@ Clay_Dimensions sclay_measure_text(Clay_StringSlice text, Clay_TextElementConfig sclay_font_t *fonts = (sclay_font_t *)userData; if(!fonts) return (Clay_Dimensions){ 0 }; fonsSetFont(_sclay.fonts, fonts[config->fontId]); - fonsSetSize(_sclay.fonts, config->fontSize); - fonsSetSpacing(_sclay.fonts, config->letterSpacing); + fonsSetSize(_sclay.fonts, config->fontSize * _sclay.dpi_scale); + fonsSetSpacing(_sclay.fonts, config->letterSpacing * _sclay.dpi_scale); + switch (config->textAlignment) { + case CLAY_TEXT_ALIGN_LEFT: + fonsSetAlign(_sclay.fonts, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); + break; + case CLAY_TEXT_ALIGN_CENTER: + fonsSetAlign(_sclay.fonts, FONS_ALIGN_CENTER | FONS_ALIGN_TOP); + break; + case CLAY_TEXT_ALIGN_RIGHT: + fonsSetAlign(_sclay.fonts, FONS_ALIGN_RIGHT | FONS_ALIGN_TOP); + break; + default: + fonsSetAlign(_sclay.fonts, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); + } float ascent, descent, lineh; fonsVertMetrics(_sclay.fonts, &ascent, &descent, &lineh); return (Clay_Dimensions) { - .width = fonsTextBounds(_sclay.fonts, 0, 0, text.chars, text.chars + text.length, NULL), - .height = ascent - descent + .width = fonsTextBounds(_sclay.fonts, 0, 0, text.chars, text.chars + text.length, NULL) / _sclay.dpi_scale, + .height = (ascent - descent) / _sclay.dpi_scale }; } @@ -366,6 +379,19 @@ void sclay_render(Clay_RenderCommandArray renderCommands, sclay_font_t *fonts) { config->textColor.a); fonsSetColor(_sclay.fonts, color); fonsSetSpacing(_sclay.fonts, config->letterSpacing * _sclay.dpi_scale); + switch (config->textAlignment) { + case CLAY_TEXT_ALIGN_LEFT: + fonsSetAlign(_sclay.fonts, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); + break; + case CLAY_TEXT_ALIGN_CENTER: + fonsSetAlign(_sclay.fonts, FONS_ALIGN_CENTER | FONS_ALIGN_TOP); + break; + case CLAY_TEXT_ALIGN_RIGHT: + fonsSetAlign(_sclay.fonts, FONS_ALIGN_RIGHT | FONS_ALIGN_TOP); + break; + default: + fonsSetAlign(_sclay.fonts, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); + } fonsSetAlign(_sclay.fonts, FONS_ALIGN_LEFT | FONS_ALIGN_TOP); fonsSetSize(_sclay.fonts, config->fontSize * _sclay.dpi_scale); sgl_matrix_mode_modelview();