Replace confusing oc_text_bounding_box() with oc_font_text_metrics(), which returns both the logical metrics and ink bounding box of text
This commit is contained in:
parent
e6a3ac7694
commit
5cdded57b3
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
30
src/ui/ui.c
30
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;
|
||||
|
|
Loading…
Reference in New Issue