From 5cdded57b351026ada11eba20b782ca67e22ea92 Mon Sep 17 00:00:00 2001 From: Martin Fouilleul Date: Sun, 10 Sep 2023 19:25:41 +0200 Subject: [PATCH] Replace confusing oc_text_bounding_box() with oc_font_text_metrics(), which returns both the logical metrics and ink bounding box of text --- samples/breakout/src/main.c | 11 +- samples/clock/src/main.c | 8 +- sketches/perf_text/main.c | 6 +- src/graphics/graphics.h | 48 ++++--- src/graphics/graphics_common.c | 229 ++++++++++++++++++++------------- src/ui/ui.c | 30 ++--- 6 files changed, 191 insertions(+), 141 deletions(-) diff --git a/samples/breakout/src/main.c b/samples/breakout/src/main.c index 801b08a..b5eb93f 100644 --- a/samples/breakout/src/main.c +++ b/samples/breakout/src/main.c @@ -416,11 +416,11 @@ ORCA_EXPORT void oc_on_frame_refresh(void) int fontSize = 18; oc_str8 text = oc_str8_pushf(oc_scratch(), "%d", blockHealth[i]); - oc_rect textRect = oc_text_bounding_box(font, fontSize, text); + oc_rect textRect = oc_font_text_metrics(font, fontSize, text).ink; oc_vec2 textPos = { - r.x + r.w / 2 - textRect.w / 2, - r.y + 9, // TODO: oc_text_bounding_box is returning extremely wack results for height. + r.x + r.w / 2 - textRect.w / 2 - textRect.x, + r.y + r.h / 2 - textRect.h / 2 - textRect.y - textRect.h, //NOTE: we render with y-up so we need to flip bounding box coordinates. }; oc_set_color_rgba(0.9, 0.9, 0.9, 1); @@ -449,10 +449,9 @@ ORCA_EXPORT void oc_on_frame_refresh(void) // draw score text { - oc_move_to(10, 10); + oc_move_to(20, 20); oc_str8 text = oc_str8_pushf(oc_scratch(), "Destroy all %d blocks to win! Current score: %d", NUM_BLOCKS_TO_WIN, score); - oc_rect textRect = oc_text_bounding_box(font, 20, text); - oc_vec2 textPos = { 10, 10 }; + oc_vec2 textPos = { 20, 20 }; oc_matrix_push(flip_y_at(textPos)); { oc_set_color_rgba(0.9, 0.9, 0.9, 1); diff --git a/samples/clock/src/main.c b/samples/clock/src/main.c index b8b9aef..f1033d6 100644 --- a/samples/clock/src/main.c +++ b/samples/clock/src/main.c @@ -94,11 +94,13 @@ ORCA_EXPORT void oc_on_frame_refresh(void) // clock face for(int i = 0; i < oc_array_size(clockNumberStrings); ++i) { - oc_rect textRect = oc_text_bounding_box(font, fontSize, clockNumberStrings[i]); - textRect.h -= 10 * uiScale; // oc_text_bounding_box height doesn't seem to be a tight fit around the glyph + oc_rect textRect = oc_font_text_metrics(font, fontSize, clockNumberStrings[i]).ink; const f32 angle = i * ((M_PI * 2) / 12.0f) - (M_PI / 2); - oc_mat2x3 transform = mat_transform(centerX - (textRect.w / 2), centerY + (textRect.h / 2), angle); + oc_mat2x3 transform = mat_transform(centerX - (textRect.w / 2) - textRect.x, + centerY - (textRect.h / 2) - textRect.y, + angle); + oc_vec2 pos = oc_mat2x3_mul(transform, (oc_vec2){ clockRadius * 0.8f, 0 }); oc_set_color_rgba(0.2, 0.2, 0.2, 1); diff --git a/sketches/perf_text/main.c b/sketches/perf_text/main.c index e739963..47fa169 100644 --- a/sketches/perf_text/main.c +++ b/sketches/perf_text/main.c @@ -118,15 +118,15 @@ int main() create_font("../../resources/CMUSerif-Roman.ttf"), create_font("../../resources/Courier.ttf") }; - oc_font_extents extents[FONT_COUNT]; + oc_font_metrics extents[FONT_COUNT]; f32 fontScales[FONT_COUNT]; f32 lineHeights[FONT_COUNT]; for(int i = 0; i < FONT_COUNT; i++) { - extents[i] = oc_font_get_extents(fonts[i]); + extents[i] = oc_font_get_metrics_unscaled(fonts[i]); fontScales[i] = oc_font_get_scale_for_em_pixels(fonts[i], 14); - lineHeights[i] = fontScales[i] * (extents[i].ascent + extents[i].descent + extents[i].leading); + lineHeights[i] = fontScales[i] * (extents[i].ascent + extents[i].descent + extents[i].lineGap); } int codePointCount = oc_utf8_codepoint_count_for_string(OC_STR8((char*)TEST_STRING)); diff --git a/src/graphics/graphics.h b/src/graphics/graphics.h index 9f7d6d7..4786019 100644 --- a/src/graphics/graphics.h +++ b/src/graphics/graphics.h @@ -185,27 +185,30 @@ typedef enum OC_CAP_SQUARE } oc_cap_type; -typedef struct oc_font_extents +typedef struct oc_font_metrics { f32 ascent; // the extent above the baseline (by convention a positive value extends above the baseline) f32 descent; // the extent below the baseline (by convention, positive value extends below the baseline) - f32 leading; // spacing between one row's descent and the next row's ascent + f32 lineGap; // spacing between one row's descent and the next row's ascent f32 xHeight; // height of the lower case letter 'x' f32 capHeight; // height of the upper case letter 'M' f32 width; // maximum width of the font -} oc_font_extents; +} oc_font_metrics; -typedef struct oc_text_extents +typedef struct oc_glyph_metrics { - f32 xBearing; - f32 yBearing; - f32 width; - f32 height; - f32 xAdvance; - f32 yAdvance; + oc_rect ink; + oc_vec2 advance; +} oc_glyph_metrics; -} oc_text_extents; +typedef struct oc_text_metrics +{ + oc_rect ink; + oc_rect logical; + oc_vec2 advance; + +} oc_text_metrics; //------------------------------------------------------------------------------------------ //SECTION: graphics canvas @@ -222,32 +225,25 @@ ORCA_API void oc_render(oc_canvas canvas); //DOC: renders all canvas //SECTION: fonts //------------------------------------------------------------------------------------------ ORCA_API oc_font oc_font_nil(void); +ORCA_API bool oc_font_is_nil(oc_font font); + ORCA_API oc_font oc_font_create_from_memory(oc_str8 mem, u32 rangeCount, oc_unicode_range* ranges); ORCA_API oc_font oc_font_create_from_file(oc_file file, u32 rangeCount, oc_unicode_range* ranges); ORCA_API oc_font oc_font_create_from_path(oc_str8 path, u32 rangeCount, oc_unicode_range* ranges); ORCA_API void oc_font_destroy(oc_font font); -//NOTE(martin): the following int valued functions return -1 if font is invalid or codepoint is not present in font// -//TODO(martin): add enum error codes - -ORCA_API oc_font_extents oc_font_get_extents(oc_font font); -ORCA_API oc_font_extents oc_font_get_scaled_extents(oc_font font, f32 emSize); -ORCA_API f32 oc_font_get_scale_for_em_pixels(oc_font font, f32 emSize); - -//NOTE(martin): if you need to process more than one codepoint, first convert your codepoints to glyph indices, then use the -// glyph index versions of the functions, which can take an array of glyph indices. - ORCA_API oc_str32 oc_font_get_glyph_indices(oc_font font, oc_str32 codePoints, oc_str32 backing); ORCA_API oc_str32 oc_font_push_glyph_indices(oc_font font, oc_arena* arena, oc_str32 codePoints); ORCA_API u32 oc_font_get_glyph_index(oc_font font, oc_utf32 codePoint); -ORCA_API int oc_font_get_codepoint_extents(oc_font font, oc_utf32 codePoint, oc_text_extents* outExtents); +// metrics +ORCA_API oc_font_metrics oc_font_get_metrics(oc_font font, f32 emSize); +ORCA_API oc_font_metrics oc_font_get_metrics_unscaled(oc_font font); +ORCA_API f32 oc_font_get_scale_for_em_pixels(oc_font font, f32 emSize); -ORCA_API int oc_font_get_glyph_extents(oc_font font, oc_str32 glyphIndices, oc_text_extents* outExtents); - -ORCA_API oc_rect oc_text_bounding_box_utf32(oc_font font, f32 fontSize, oc_str32 text); -ORCA_API oc_rect oc_text_bounding_box(oc_font font, f32 fontSize, oc_str8 text); +ORCA_API oc_text_metrics oc_font_text_metrics_utf32(oc_font font, f32 fontSize, oc_str32 codepoints); +ORCA_API oc_text_metrics oc_font_text_metrics(oc_font font, f32 fontSize, oc_str8 text); //------------------------------------------------------------------------------------------ //SECTION: images diff --git a/src/graphics/graphics_common.c b/src/graphics/graphics_common.c index a9e4db2..a84e9d3 100644 --- a/src/graphics/graphics_common.c +++ b/src/graphics/graphics_common.c @@ -37,7 +37,7 @@ typedef struct oc_glyph_data bool exists; oc_utf32 codePoint; oc_path_descriptor pathDescriptor; - oc_text_extents extents; + oc_glyph_metrics metrics; //... } oc_glyph_data; @@ -62,7 +62,7 @@ typedef struct oc_font_data oc_path_elt* outlines; f32 unitsPerEm; - oc_font_extents extents; + oc_font_metrics metrics; } oc_font_data; @@ -398,16 +398,16 @@ oc_font oc_font_create_from_memory(oc_str8 mem, u32 rangeCount, oc_unicode_range stbtt_GetFontVMetrics(&stbttFontInfo, &ascent, &descent, &lineGap); stbtt_GetFontBoundingBox(&stbttFontInfo, &x0, &y0, &x1, &y1); - font->extents.ascent = ascent; - font->extents.descent = -descent; - font->extents.leading = lineGap; - font->extents.width = x1 - x0; + font->metrics.ascent = ascent; + font->metrics.descent = -descent; + font->metrics.lineGap = lineGap; + font->metrics.width = x1 - x0; stbtt_GetCodepointBox(&stbttFontInfo, 'x', &x0, &y0, &x1, &y1); - font->extents.xHeight = y1 - y0; + font->metrics.xHeight = y1 - y0; stbtt_GetCodepointBox(&stbttFontInfo, 'M', &x0, &y0, &x1, &y1); - font->extents.capHeight = y1 - y0; + font->metrics.capHeight = y1 - y0; //NOTE(martin): load codepoint ranges font->rangeCount = rangeCount; @@ -483,13 +483,16 @@ oc_font oc_font_create_from_memory(oc_str8 mem, u32 rangeCount, oc_unicode_range stbtt_GetGlyphHMetrics(&stbttFontInfo, stbttGlyphIndex, &xAdvance, &xBearing); stbtt_GetGlyphBox(&stbttFontInfo, stbttGlyphIndex, &x0, &y0, &x1, &y1); - glyph->extents.xAdvance = (f32)xAdvance; - glyph->extents.yAdvance = 0; - glyph->extents.xBearing = (f32)xBearing; - glyph->extents.yBearing = y0; + //NOTE(martin): stb stbtt_GetGlyphBox returns bottom left and top right corners, with y up, + // so we have to set .y = -y1 + glyph->metrics.ink = (oc_rect){ + .x = x0, + .y = -y1, + .w = x1 - x0, + .h = y1 - y0 + }; - glyph->extents.width = x1 - x0; - glyph->extents.height = y1 - y0; + glyph->metrics.advance = (oc_vec2){ xAdvance, 0 }; //NOTE(martin): load glyph outlines @@ -673,34 +676,34 @@ oc_glyph_data* oc_font_get_glyph_data(oc_font_data* fontData, u32 glyphIndex) return (&(fontData->glyphs[glyphIndex - 1])); } -oc_font_extents oc_font_get_extents(oc_font font) +oc_font_metrics oc_font_get_metrics_unscaled(oc_font font) { oc_font_data* fontData = oc_font_data_from_handle(font); if(!fontData) { - return ((oc_font_extents){ 0 }); + return ((oc_font_metrics){ 0 }); } - return (fontData->extents); + return (fontData->metrics); } -oc_font_extents oc_font_get_scaled_extents(oc_font font, f32 emSize) +oc_font_metrics oc_font_get_metrics(oc_font font, f32 emSize) { oc_font_data* fontData = oc_font_data_from_handle(font); if(!fontData) { - return ((oc_font_extents){ 0 }); + return ((oc_font_metrics){ 0 }); } f32 scale = emSize / fontData->unitsPerEm; - oc_font_extents extents = fontData->extents; + oc_font_metrics metrics = fontData->metrics; - extents.ascent *= scale; - extents.descent *= scale; - extents.leading *= scale; - extents.xHeight *= scale; - extents.capHeight *= scale; - extents.width *= scale; + metrics.ascent *= scale; + metrics.descent *= scale; + metrics.lineGap *= scale; + metrics.xHeight *= scale; + metrics.capHeight *= scale; + metrics.width *= scale; - return (extents); + return (metrics); } f32 oc_font_get_scale_for_em_pixels(oc_font font, f32 emSize) @@ -713,9 +716,10 @@ f32 oc_font_get_scale_for_em_pixels(oc_font font, f32 emSize) return (emSize / fontData->unitsPerEm); } -void oc_font_get_glyph_extents_from_font_data(oc_font_data* fontData, +////////////////////////////////////////////////////////////////////////////////////////////:: +void oc_font_get_glyph_metrics_from_font_data(oc_font_data* fontData, oc_str32 glyphIndices, - oc_text_extents* outExtents) + oc_glyph_metrics* outMetrics) { for(int i = 0; i < glyphIndices.len; i++) { @@ -724,22 +728,23 @@ void oc_font_get_glyph_extents_from_font_data(oc_font_data* fontData, continue; } oc_glyph_data* glyph = oc_font_get_glyph_data(fontData, glyphIndices.ptr[i]); - outExtents[i] = glyph->extents; + outMetrics[i] = glyph->metrics; } } -int oc_font_get_glyph_extents(oc_font font, oc_str32 glyphIndices, oc_text_extents* outExtents) +/* +int oc_font_get_glyph_metrics(oc_font font, oc_str32 glyphIndices, oc_text_metrics* outMetrics) { oc_font_data* fontData = oc_font_data_from_handle(font); if(!fontData) { return (-1); } - oc_font_get_glyph_extents_from_font_data(fontData, glyphIndices, outExtents); + oc_font_get_glyph_metrics_from_font_data(fontData, glyphIndices, outMetrics); return (0); } -int oc_font_get_codepoint_extents(oc_font font, oc_utf32 codePoint, oc_text_extents* outExtents) +int oc_font_get_codepoint_metrics(oc_font font, oc_utf32 codePoint, oc_text_metrics* outMetrics) { oc_font_data* fontData = oc_font_data_from_handle(font); if(!fontData) @@ -750,21 +755,23 @@ int oc_font_get_codepoint_extents(oc_font font, oc_utf32 codePoint, oc_text_exte oc_str32 codePoints = { 1, &codePoint }; oc_str32 backing = { 1, &glyphIndex }; oc_str32 glyphs = oc_font_get_glyph_indices_from_font_data(fontData, codePoints, backing); - oc_font_get_glyph_extents_from_font_data(fontData, glyphs, outExtents); + oc_font_get_glyph_metrics_from_font_data(fontData, glyphs, outMetrics); return (0); } +*/ +///////////////////////////////////////////////// -oc_rect oc_text_bounding_box_utf32(oc_font font, f32 fontSize, oc_str32 codePoints) +oc_text_metrics oc_font_text_metrics_utf32(oc_font font, f32 fontSize, oc_str32 codePoints) { if(!codePoints.len || !codePoints.ptr) { - return ((oc_rect){ 0 }); + return ((oc_text_metrics){ 0 }); } oc_font_data* fontData = oc_font_data_from_handle(font); if(!fontData) { - return ((oc_rect){ 0 }); + return ((oc_text_metrics){ 0 }); } oc_arena_scope scratch = oc_scratch_begin(); @@ -772,86 +779,123 @@ oc_rect oc_text_bounding_box_utf32(oc_font font, f32 fontSize, oc_str32 codePoin //NOTE(martin): find width of missing character //TODO(martin): should cache that at font creation... - oc_text_extents missingGlyphExtents; + oc_glyph_metrics missingGlyphMetrics = { 0 }; u32 missingGlyphIndex = oc_font_get_glyph_index_from_font_data(fontData, 0xfffd); if(missingGlyphIndex) { - oc_font_get_glyph_extents_from_font_data(fontData, (oc_str32){ 1, &missingGlyphIndex }, &missingGlyphExtents); + oc_font_get_glyph_metrics_from_font_data(fontData, (oc_str32){ 1, &missingGlyphIndex }, &missingGlyphMetrics); } else { - //NOTE(martin): could not find replacement glyph, try to get an 'x' to get a somewhat correct width + //NOTE(martin): could not find replacement glyph, try to get an 'X' to get a somewhat correct dimensions // to render an empty rectangle. Otherwise just render with the max font width - f32 boxWidth = fontData->extents.width * 0.8; - f32 xBearing = fontData->extents.width * 0.1; - f32 xAdvance = fontData->extents.width; - missingGlyphIndex = oc_font_get_glyph_index_from_font_data(fontData, 'x'); + missingGlyphIndex = oc_font_get_glyph_index_from_font_data(fontData, 'X'); if(missingGlyphIndex) { - oc_font_get_glyph_extents_from_font_data(fontData, (oc_str32){ 1, &missingGlyphIndex }, &missingGlyphExtents); + oc_font_get_glyph_metrics_from_font_data(fontData, (oc_str32){ 1, &missingGlyphIndex }, &missingGlyphMetrics); } else { - missingGlyphExtents.xBearing = fontData->extents.width * 0.1; - missingGlyphExtents.yBearing = 0; - missingGlyphExtents.width = fontData->extents.width * 0.8; - missingGlyphExtents.xAdvance = fontData->extents.width; - missingGlyphExtents.yAdvance = 0; + missingGlyphMetrics = (oc_glyph_metrics){ + .ink = { + .x = fontData->metrics.width * 0.1, + .y = -fontData->metrics.ascent, + .w = fontData->metrics.width * 0.8, + .h = fontData->metrics.ascent, + }, + .advance = { .x = fontData->metrics.width, .y = 0 }, + }; } } //NOTE(martin): accumulate text extents - f32 width = 0; - f32 x = 0; - f32 y = 0; - f32 lineHeight = fontData->extents.descent + fontData->extents.ascent; + oc_text_metrics metrics = { 0 }; + f32 lineHeight = fontData->metrics.descent + fontData->metrics.ascent + fontData->metrics.lineGap; + + if(glyphIndices.len) + { + metrics.logical.y = -(fontData->metrics.ascent + fontData->metrics.lineGap); + metrics.logical.h += lineHeight + fontData->metrics.lineGap; + } + + f32 inkX0 = 0, inkX1 = 0, inkY0 = 0, inkY1 = 0; for(int i = 0; i < glyphIndices.len; i++) { //TODO(martin): make it failsafe for fonts that don't have a glyph for the line-feed codepoint ? oc_glyph_data* glyph = 0; - oc_text_extents extents; + oc_glyph_metrics glyphMetrics; if(!glyphIndices.ptr[i] || glyphIndices.ptr[i] >= fontData->glyphCount) { - extents = missingGlyphExtents; + glyphMetrics = missingGlyphMetrics; } else { glyph = oc_font_get_glyph_data(fontData, glyphIndices.ptr[i]); - extents = glyph->extents; + glyphMetrics = glyph->metrics; } - x += extents.xAdvance; - y += extents.yAdvance; + + inkX0 = oc_min(inkX0, metrics.advance.x + glyphMetrics.ink.x); + inkX1 = oc_max(inkX1, metrics.advance.x + glyphMetrics.ink.x + glyphMetrics.ink.w); + + inkY0 = oc_min(inkY0, metrics.advance.y + glyphMetrics.ink.y); + inkY1 = oc_max(inkY1, metrics.advance.y + glyphMetrics.ink.y + glyphMetrics.ink.h); + + metrics.advance.x += glyphMetrics.advance.x; + metrics.advance.y += glyphMetrics.advance.y; + + metrics.logical.w = oc_max(metrics.logical.w, metrics.advance.x); if(glyph && glyph->codePoint == '\n') { - width = oc_max(width, x); - x = 0; - y += lineHeight + fontData->extents.leading; + metrics.advance.y += lineHeight; + metrics.advance.x = 0; + + if(i < glyphIndices.len - 1) + { + metrics.logical.h += lineHeight; + } } } - width = oc_max(width, x); + metrics.ink = (oc_rect){ + inkX0, + inkY0, + inkX1 - inkX0, + inkY1 - inkY0 + }; + + OC_ASSERT(metrics.ink.y <= 0); f32 fontScale = oc_font_get_scale_for_em_pixels(font, fontSize); - oc_rect rect = { 0, -fontData->extents.ascent * fontScale, width * fontScale, (y + lineHeight) * fontScale }; + + metrics.ink.x *= fontScale; + metrics.ink.y *= fontScale; + metrics.ink.w *= fontScale; + metrics.ink.h *= fontScale; + metrics.logical.x *= fontScale; + metrics.logical.y *= fontScale; + metrics.logical.w *= fontScale; + metrics.logical.h *= fontScale; + metrics.advance.x *= fontScale; + metrics.advance.y *= fontScale; oc_scratch_end(scratch); - return (rect); + return (metrics); } -oc_rect oc_text_bounding_box(oc_font font, f32 fontSize, oc_str8 text) +oc_text_metrics oc_font_text_metrics(oc_font font, f32 fontSize, oc_str8 text) { if(!text.len || !text.ptr) { - return ((oc_rect){ 0 }); + return ((oc_text_metrics){ 0 }); } oc_arena_scope scratch = oc_scratch_begin(); oc_str32 codePoints = oc_utf8_push_to_codepoints(scratch.arena, text); - oc_rect result = oc_text_bounding_box_utf32(font, fontSize, codePoints); + oc_text_metrics result = oc_font_text_metrics_utf32(font, fontSize, codePoints); oc_scratch_end(scratch); return (result); } @@ -1377,31 +1421,40 @@ oc_rect oc_glyph_outlines_from_font_data(oc_font_data* fontData, oc_str32 glyphI glyphIndex = oc_font_get_glyph_index_from_font_data(fontData, 0xfffd); if(!glyphIndex) { - //NOTE(martin): could not find replacement glyph, try to get an 'x' to get a somewhat correct width + //NOTE(martin): could not find replacement glyph, try to get an 'X' to get a somewhat correct dimensions // to render an empty rectangle. Otherwise just render with the max font width - f32 boxWidth = fontData->extents.width * 0.8; - f32 xBearing = fontData->extents.width * 0.1; - f32 xAdvance = fontData->extents.width; - glyphIndex = oc_font_get_glyph_index_from_font_data(fontData, 'x'); - if(glyphIndex) + oc_glyph_metrics missingGlyphMetrics = { 0 }; + u32 missingGlyphIndex = oc_font_get_glyph_index_from_font_data(fontData, 'X'); + + if(missingGlyphIndex) { - oc_glyph_data* glyph = &(fontData->glyphs[glyphIndex]); - boxWidth = glyph->extents.width; - xBearing = glyph->extents.xBearing; - xAdvance = glyph->extents.xAdvance; + oc_font_get_glyph_metrics_from_font_data(fontData, (oc_str32){ .len = 1, .ptr = &missingGlyphIndex }, &missingGlyphMetrics); } + else + { + missingGlyphMetrics = (oc_glyph_metrics){ + .ink = { + .x = fontData->metrics.width * 0.1, + .y = -fontData->metrics.ascent, + .w = fontData->metrics.width * 0.8, + .h = fontData->metrics.ascent, + }, + .advance = { .x = fontData->metrics.width, .y = 0 }, + }; + } + f32 oldStrokeWidth = canvas->attributes.width; - oc_set_width(boxWidth * 0.005); - oc_rectangle_stroke(xOffset + xBearing * scale, - yOffset, - boxWidth * scale * flip, - fontData->extents.capHeight * scale); + oc_set_width(missingGlyphMetrics.ink.w * 0.005); + oc_rectangle_stroke(xOffset + missingGlyphMetrics.ink.x * scale, + yOffset + missingGlyphMetrics.ink.y * scale, + missingGlyphMetrics.ink.w * scale * flip, + missingGlyphMetrics.ink.h * scale); oc_set_width(oldStrokeWidth); - oc_move_to(xOffset + xAdvance * scale, yOffset); - maxWidth = oc_max(maxWidth, xOffset + xAdvance * scale - startX); + oc_move_to(xOffset + missingGlyphMetrics.advance.x * scale, yOffset); + maxWidth = oc_max(maxWidth, xOffset + missingGlyphMetrics.advance.x * scale - startX); continue; } } @@ -1419,11 +1472,11 @@ oc_rect oc_glyph_outlines_from_font_data(oc_font_data* fontData, oc_str32 glyphI elements[eltIndex].p[pIndex].y = elements[eltIndex].p[pIndex].y * scale * flip + yOffset; } } - oc_move_to(xOffset + scale * glyph->extents.xAdvance, yOffset); + oc_move_to(xOffset + scale * glyph->metrics.advance.x, yOffset); - maxWidth = oc_max(maxWidth, xOffset + scale * glyph->extents.xAdvance - startX); + maxWidth = oc_max(maxWidth, xOffset + scale * glyph->metrics.advance.x - startX); } - f32 lineHeight = (fontData->extents.ascent + fontData->extents.descent) * scale; + f32 lineHeight = (fontData->metrics.ascent + fontData->metrics.descent) * scale; oc_rect box = { startX, startY, maxWidth, canvas->subPathLastPoint.y - startY + lineHeight }; return (box); } diff --git a/src/ui/ui.c b/src/ui/ui.c index 7e43523..426dab6 100644 --- a/src/ui/ui.c +++ b/src/ui/ui.c @@ -964,7 +964,7 @@ void oc_ui_styling_prepass(oc_ui_context* ui, oc_ui_box* box, oc_list* before, o if(desiredSize[OC_UI_AXIS_X].kind == OC_UI_SIZE_TEXT || desiredSize[OC_UI_AXIS_Y].kind == OC_UI_SIZE_TEXT) { - textBox = oc_text_bounding_box(style->font, style->fontSize, box->string); + textBox = oc_font_text_metrics(style->font, style->fontSize, box->string).logical; } for(int i = 0; i < OC_UI_AXIS_COUNT; i++) @@ -1362,7 +1362,7 @@ void oc_ui_draw_box(oc_ui_box* box) if(draw && (box->flags & OC_UI_FLAG_DRAW_TEXT)) { - oc_rect textBox = oc_text_bounding_box(style->font, style->fontSize, box->string); + oc_rect textBox = oc_font_text_metrics(style->font, style->fontSize, box->string).logical; f32 x = 0; f32 y = 0; @@ -2508,7 +2508,7 @@ oc_ui_select_popup_info oc_ui_select_popup(const char* name, oc_ui_select_popup_ oc_rect bbox = { 0 }; for(int i = 0; i < info->optionCount; i++) { - bbox = oc_text_bounding_box(button->style.font, button->style.fontSize, info->options[i]); + bbox = oc_font_text_metrics(button->style.font, button->style.fontSize, info->options[i]).logical; maxOptionWidth = oc_max(maxOptionWidth, bbox.w); } f32 buttonWidth = maxOptionWidth + 2 * button->style.layout.margin.x + button->rect.h; @@ -3305,11 +3305,11 @@ void oc_ui_text_box_render(oc_ui_box* box, void* data) } oc_ui_style* style = &box->style; - oc_font_extents extents = oc_font_get_scaled_extents(style->font, style->fontSize); + oc_font_metrics extents = oc_font_get_metrics(style->font, style->fontSize); f32 lineHeight = extents.ascent + extents.descent; oc_str32 before = oc_str32_slice(codepoints, 0, firstDisplayedChar); - oc_rect beforeBox = oc_text_bounding_box_utf32(style->font, style->fontSize, before); + oc_rect beforeBox = oc_font_text_metrics_utf32(style->font, style->fontSize, before).logical; f32 textX = box->rect.x - beforeBox.w; f32 textTop = box->rect.y + 0.5 * (box->rect.h - lineHeight); @@ -3321,7 +3321,7 @@ void oc_ui_text_box_render(oc_ui_box* box, void* data) u32 selectEnd = oc_max(ui->editCursor, ui->editMark); oc_str32 beforeSelect = oc_str32_slice(codepoints, 0, selectStart); - oc_rect beforeSelectBox = oc_text_bounding_box_utf32(style->font, style->fontSize, beforeSelect); + oc_rect beforeSelectBox = oc_font_text_metrics_utf32(style->font, style->fontSize, beforeSelect).logical; beforeSelectBox.x += textX; beforeSelectBox.y += textY; @@ -3329,8 +3329,8 @@ void oc_ui_text_box_render(oc_ui_box* box, void* data) { oc_str32 select = oc_str32_slice(codepoints, selectStart, selectEnd); oc_str32 afterSelect = oc_str32_slice(codepoints, selectEnd, codepoints.len); - oc_rect selectBox = oc_text_bounding_box_utf32(style->font, style->fontSize, select); - oc_rect afterSelectBox = oc_text_bounding_box_utf32(style->font, style->fontSize, afterSelect); + oc_rect selectBox = oc_font_text_metrics_utf32(style->font, style->fontSize, select).logical; + oc_rect afterSelectBox = oc_font_text_metrics_utf32(style->font, style->fontSize, afterSelect).logical; selectBox.x += beforeSelectBox.x + beforeSelectBox.w; selectBox.y += textY; @@ -3432,7 +3432,7 @@ oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8 oc_ui_box* textBox = oc_ui_box_make("text", OC_UI_FLAG_CLIP | OC_UI_FLAG_DRAW_PROC); oc_ui_tag_box(textBox, "text"); - oc_font_extents extents = oc_font_get_scaled_extents(font, fontSize); + oc_font_metrics extents = oc_font_get_metrics(font, fontSize); oc_ui_sig sig = oc_ui_box_sig(frame); @@ -3464,7 +3464,7 @@ oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8 f32 x = 0; for(int i = ui->editFirstDisplayedChar; i < codepoints.len; i++) { - oc_rect bbox = oc_text_bounding_box_utf32(font, fontSize, oc_str32_slice(codepoints, i, i + 1)); + oc_rect bbox = oc_font_text_metrics_utf32(font, fontSize, oc_str32_slice(codepoints, i, i + 1)).logical; if(x < cursorX) { hoveredChar = i; @@ -3520,7 +3520,7 @@ oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8 } else if(ui->editSelectionMode == OC_UI_EDIT_MOVE_LINE) { - oc_rect bbox = oc_text_bounding_box_utf32(font, fontSize, codepoints); + oc_rect bbox = oc_font_text_metrics_utf32(font, fontSize, codepoints).logical; if(fabsf(bbox.w - cursorX) < fabsf(cursorX)) { ui->editCursor = codepoints.len; @@ -3537,8 +3537,8 @@ oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8 if(oc_min(ui->editCursor, ui->editMark) == oc_min(ui->editWordSelectionInitialCursor, ui->editWordSelectionInitialMark) && oc_max(ui->editCursor, ui->editMark) == oc_max(ui->editWordSelectionInitialCursor, ui->editWordSelectionInitialMark)) { - oc_rect editCursorPrefixBbox = oc_text_bounding_box_utf32(font, fontSize, oc_str32_slice(codepoints, 0, ui->editCursor)); - oc_rect editMarkPrefixBbox = oc_text_bounding_box_utf32(font, fontSize, oc_str32_slice(codepoints, 0, ui->editMark)); + oc_rect editCursorPrefixBbox = oc_font_text_metrics_utf32(font, fontSize, oc_str32_slice(codepoints, 0, ui->editCursor)).logical; + oc_rect editMarkPrefixBbox = oc_font_text_metrics_utf32(font, fontSize, oc_str32_slice(codepoints, 0, ui->editMark)).logical; f32 editCursorX = editCursorPrefixBbox.w; f32 editMarkX = editMarkPrefixBbox.w; if(fabsf(cursorX - editMarkX) < fabsf(cursorX - editCursorX)) @@ -3665,13 +3665,13 @@ oc_ui_text_box_result oc_ui_text_box(const char* name, oc_arena* arena, oc_str8 { i32 firstDisplayedChar = ui->editFirstDisplayedChar; oc_str32 firstToCursor = oc_str32_slice(codepoints, firstDisplayedChar, ui->editCursor); - oc_rect firstToCursorBox = oc_text_bounding_box_utf32(font, fontSize, firstToCursor); + oc_rect firstToCursorBox = oc_font_text_metrics_utf32(font, fontSize, firstToCursor).logical; while(firstToCursorBox.w > textBox->rect.w) { firstDisplayedChar++; firstToCursor = oc_str32_slice(codepoints, firstDisplayedChar, ui->editCursor); - firstToCursorBox = oc_text_bounding_box_utf32(font, fontSize, firstToCursor); + firstToCursorBox = oc_font_text_metrics_utf32(font, fontSize, firstToCursor).logical; } ui->editFirstDisplayedChar = firstDisplayedChar;