mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-08 02:08:22 -10:00
(Font) Recompute the derived metrics when a font is rebuilt
font_driver_reload_fonts() swaps a font's renderer state in place, so callers keep working - but the values derived from it do not follow. The menu drivers cache glyph_width, wideglyph_width, line_height, line_ascender and line_centre_offset in font_data_impl_t at font creation, and a rebuilt font has a different face and so different metrics. Left alone that gives the right glyphs at the wrong spacing. The signal already existed: font_driver_generation moves whenever a font is freed or rebuilt, and gfx_animation's ticker width cache has always watched it. The menu drivers just never did. font_driver_sync_impl() recomputes all five from font->metrics and two width queries, but only when the generation has moved - otherwise it is an integer compare and a return. font_flush() calls it, so the drivers that go through it pick up a rebuild before anything is drawn with the stale numbers. Recomputing wideglyph_width needs the string it was measured from, so font_data_impl_t keeps it. ozone and materialui set it where they already had it to hand. Two callers are not covered and are not made to look as though they are: xmb holds raw font_data_t pointers and caches its metrics separately, and gfx_widgets has its own font struct with its own copies. Both call renderer->flush directly rather than font_flush(), so neither reaches this hook. They need the same treatment before the language switch can rely on it everywhere. samples/gfx/font_lifecycle gains a case for this: that a first sync computes the values, that a second with nothing changed does not touch them, and that a sync after a rebuild recomputes them. Clean under ASan, UBSan, LeakSanitizer and TSan.
This commit is contained in:
1 parent
76a9aeeb1f
commit
7eddfc2058
5 files changed
+87
No files matched your search
@@ -688,12 +688,53 @@ void font_driver_bind_block(void *font_data, void *block)
|
||||
}
|
||||
|
||||
/* Flushing is slow - only do it if font has actually been used */
|
||||
void font_driver_sync_impl(font_data_impl_t *font_data)
|
||||
{
|
||||
int glyph_width;
|
||||
uint32_t gen = font_driver_get_generation();
|
||||
|
||||
if (!font_data || !font_data->font)
|
||||
return;
|
||||
if (font_data->metrics_generation == gen)
|
||||
return;
|
||||
|
||||
font_data->metrics_generation = gen;
|
||||
|
||||
if ((glyph_width = font_driver_get_message_width(
|
||||
font_data->font, "a", 1, 1.0f)) > 0)
|
||||
font_data->glyph_width = (unsigned)glyph_width;
|
||||
|
||||
font_data->wideglyph_width = 100;
|
||||
|
||||
if (font_data->wideglyph_str && glyph_width > 0)
|
||||
{
|
||||
int wide = font_driver_get_message_width(font_data->font,
|
||||
font_data->wideglyph_str,
|
||||
strlen(font_data->wideglyph_str), 1.0f);
|
||||
if (wide > 0)
|
||||
font_data->wideglyph_width = wide * 100 / glyph_width;
|
||||
}
|
||||
|
||||
font_data->line_height =
|
||||
(int)roundf(font_data->font->metrics.height);
|
||||
font_data->line_ascender =
|
||||
(int)roundf(font_data->font->metrics.ascender);
|
||||
font_data->line_centre_offset =
|
||||
(int)roundf((font_data->font->metrics.ascender
|
||||
- font_data->font->metrics.descender) * 0.5f);
|
||||
}
|
||||
|
||||
void font_flush(
|
||||
unsigned video_width,
|
||||
unsigned video_height,
|
||||
font_data_impl_t *font_data)
|
||||
{
|
||||
const font_renderer_t *renderer = font_data->font ? font_data->font->renderer : NULL;
|
||||
|
||||
/* A rebuilt font has different metrics; pick them up before
|
||||
* anything is drawn with the old ones. */
|
||||
font_driver_sync_impl(font_data);
|
||||
|
||||
if (font_data->raster_block.carr.coords.vertices == 0)
|
||||
return;
|
||||
if (renderer && renderer->flush)
|
||||
|
||||
@@ -112,6 +112,13 @@ typedef struct
|
||||
int line_height;
|
||||
int line_ascender;
|
||||
int line_centre_offset;
|
||||
/* The string wideglyph_width is measured from, kept so the derived
|
||||
* values above can be recomputed without the driver's help. */
|
||||
const char *wideglyph_str;
|
||||
/* font_driver generation these were computed at. When the font is
|
||||
* rebuilt underneath - switching menu language picks a different
|
||||
* face - the generation moves and they are recomputed. */
|
||||
uint32_t metrics_generation;
|
||||
} font_data_impl_t;
|
||||
|
||||
void font_driver_bind_block(void *font_data, void *block);
|
||||
@@ -148,6 +155,11 @@ unsigned font_driver_reload_fonts(void);
|
||||
* cached per-font derived data */
|
||||
uint32_t font_driver_get_generation(void);
|
||||
|
||||
/* Recompute the derived metrics above if the font has been rebuilt
|
||||
* since they were last worked out. Cheap when nothing has changed:
|
||||
* one integer compare. */
|
||||
void font_driver_sync_impl(font_data_impl_t *font_data);
|
||||
|
||||
void font_flush(
|
||||
unsigned video_width,
|
||||
unsigned video_height,
|
||||
|
||||
@@ -9369,6 +9369,10 @@ static void materialui_init_font(gfx_display_t *p_disp,
|
||||
font_data->glyph_width = (unsigned)char_width;
|
||||
|
||||
font_data->wideglyph_width = 100;
|
||||
/* Kept so the derived metrics can be recomputed if the font is
|
||||
* rebuilt - a language switch picks a different face. */
|
||||
font_data->wideglyph_str = wideglyph_str;
|
||||
font_data->metrics_generation = font_driver_get_generation();
|
||||
|
||||
if (wideglyph_str)
|
||||
{
|
||||
|
||||
@@ -9935,6 +9935,10 @@ static bool ozone_init_font(
|
||||
font_data->glyph_width = glyph_width;
|
||||
|
||||
font_data->wideglyph_width = 100;
|
||||
/* Kept so the derived metrics can be recomputed if the font is
|
||||
* rebuilt - a language switch picks a different face. */
|
||||
font_data->wideglyph_str = wideglyph_str;
|
||||
font_data->metrics_generation = font_driver_get_generation();
|
||||
|
||||
if (wideglyph_str)
|
||||
{
|
||||
|
||||
@@ -144,6 +144,32 @@ int main(void)
|
||||
}
|
||||
}
|
||||
|
||||
/* 7. derived metrics on an impl follow a rebuild */
|
||||
{
|
||||
font_data_impl_t impl;
|
||||
memset(&impl, 0, sizeof(impl));
|
||||
impl.font = a;
|
||||
impl.wideglyph_str = "WW";
|
||||
font_driver_sync_impl(&impl);
|
||||
{
|
||||
int h0 = impl.line_height;
|
||||
unsigned g0w = impl.glyph_width;
|
||||
CHECK(h0 > 0, "impl metrics computed on first sync");
|
||||
CHECK(g0w > 0, "impl glyph width computed");
|
||||
/* nothing changed: sync must be a no-op */
|
||||
impl.line_height = -1;
|
||||
font_driver_sync_impl(&impl);
|
||||
CHECK(impl.line_height == -1,
|
||||
"sync does nothing when the generation is unchanged");
|
||||
/* rebuild: the sync must notice and recompute */
|
||||
font_driver_reload_fonts();
|
||||
font_driver_sync_impl(&impl);
|
||||
CHECK(impl.line_height == h0,
|
||||
"impl metrics recomputed after a rebuild");
|
||||
CHECK(impl.wideglyph_width > 0, "wideglyph width recomputed");
|
||||
}
|
||||
}
|
||||
|
||||
font_driver_free(a);
|
||||
font_driver_free(c);
|
||||
CHECK(live_renderer_state == 0, "all renderer state released at teardown");
|
||||
|
||||
Reference in new issue
Block a user