1
0
mirror of https://github.com/emilk/egui.git synced 2026-06-26 22:53:14 -04:00

Cache scaled font metrics

This commit is contained in:
valadaptive
2025-09-07 18:23:34 -04:00
parent 3a6cb2e273
commit f560b6a9ff
3 changed files with 85 additions and 72 deletions

View File

@@ -229,22 +229,14 @@ impl FontImpl {
/ pixels_per_point
}
/// Height of one row of text in points.
///
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
#[inline(always)]
pub fn row_height(&self, font_size: f32) -> f32 {
pub fn scaled_metrics(&self, font_size: f32) -> ScaledMetrics {
let font = self.ab_glyph_font.pt_scaled(font_size);
font.ascent().round_ui() - font.descent().round_ui() + font.line_gap().round_ui()
}
/// This is the distance from the top to the baseline.
///
/// Unit: points.
#[inline(always)]
pub fn ascent(&self, font_size: f32) -> f32 {
self.ab_glyph_font.pt_scaled(font_size).ascent().round_ui()
ScaledMetrics {
ascent: font.ascent().round_ui(),
row_height: font.ascent().round_ui() - font.descent().round_ui()
+ font.line_gap().round_ui(),
}
}
pub fn allocate_glyph(
@@ -384,20 +376,13 @@ impl Font<'_> {
})
}
/// Height of one row of text. In points.
///
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
#[inline(always)]
pub fn row_height(&self, font_size: f32) -> f32 {
let Some(first_font) = self
.cached_family
pub fn scaled_metrics(&self, font_size: f32) -> ScaledMetrics {
self.cached_family
.fonts
.first()
.and_then(|key| self.fonts_by_id.get(key))
else {
return 0.0;
};
first_font.row_height(font_size)
.map(|font_impl| font_impl.scaled_metrics(font_size))
.unwrap_or_default()
}
/// Width of this character in points.
@@ -423,7 +408,7 @@ impl Font<'_> {
}
/// `\n` will (intentionally) show up as the replacement character.
fn glyph_info(&mut self, c: char) -> (FontFaceKey, GlyphInfo) {
pub(crate) fn glyph_info(&mut self, c: char) -> (FontFaceKey, GlyphInfo) {
if let Some(font_index_glyph_info) = self.cached_family.glyph_info_cache.get(&c) {
return *font_index_glyph_info;
}
@@ -438,32 +423,18 @@ impl Font<'_> {
.insert(c, font_index_glyph_info);
font_index_glyph_info
}
}
#[inline]
pub(crate) fn font_impl_and_glyph_alloc(
&mut self,
pixels_per_point: f32,
c: char,
font_size: f32,
) -> (Option<&mut FontImpl>, GlyphAllocation) {
if self.cached_family.fonts.is_empty() {
return (None, Default::default());
}
let (key, glyph_info) = self.glyph_info(c);
let font_impl = self.fonts_by_id.get_mut(&key).expect("Nonexistent font ID");
let allocated_glyph =
font_impl.allocate_glyph(self.atlas, pixels_per_point, glyph_info, font_size);
(Some(font_impl), allocated_glyph)
}
pub(crate) fn ascent(&self, font_size: f32) -> f32 {
if let Some(first) = self.cached_family.fonts.first() {
let first = self.fonts_by_id.get(first).expect("Nonexistent font ID");
first.ascent(font_size)
} else {
self.row_height(font_size)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Default)]
pub struct ScaledMetrics {
/// This is the distance from the top to the baseline.
///
/// Unit: points.
pub ascent: f32,
/// Height of one row of text in points.
///
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
pub row_height: f32,
}
/// Code points that will always be invisible (zero width).

View File

@@ -671,7 +671,10 @@ impl FontsView<'_> {
///
/// Returns a value rounded to [`emath::GUI_ROUNDING`].
pub fn row_height(&mut self, font_id: &FontId) -> f32 {
self.fonts.font(&font_id.family).row_height(font_id.size)
self.fonts
.font(&font_id.family)
.scaled_metrics(font_id.size)
.row_height
}
/// List of all known font families.

View File

@@ -2,7 +2,11 @@ use std::sync::Arc;
use emath::{Align, GuiRounding as _, NumExt as _, Pos2, Rect, Vec2, pos2, vec2};
use crate::{Color32, Mesh, Stroke, Vertex, stroke::PathStroke};
use crate::{
Color32, Mesh, Stroke, Vertex,
stroke::PathStroke,
text::{font::ScaledMetrics, fonts::FontFaceKey},
};
use super::{FontsImpl, Galley, Glyph, LayoutJob, LayoutSection, PlacedRow, Row, RowVisuals};
@@ -153,10 +157,11 @@ fn layout_section(
} = section;
let mut font = fonts.font(&format.font_id.family);
let font_size = format.font_id.size;
let font_metrics = font.scaled_metrics(font_size);
let line_height = section
.format
.line_height
.unwrap_or_else(|| font.row_height(font_size));
.unwrap_or(font_metrics.row_height);
let extra_letter_spacing = section.format.extra_letter_spacing;
let mut paragraph = out_paragraphs.last_mut().unwrap();
@@ -168,14 +173,33 @@ fn layout_section(
let mut last_glyph_id = None;
let mut last_font: Option<(FontFaceKey, Option<ScaledMetrics>)> = None;
for chr in job.text[byte_range.clone()].chars() {
if job.break_on_newline && chr == '\n' {
out_paragraphs.push(Paragraph::from_section_index(section_index));
paragraph = out_paragraphs.last_mut().unwrap();
paragraph.empty_paragraph_height = line_height; // TODO(emilk): replace this hack with actually including `\n` in the glyphs?
} else {
let (font_impl, glyph_alloc) =
font.font_impl_and_glyph_alloc(pixels_per_point, chr, font_size);
let (font_id, glyph_info) = font.glyph_info(chr);
let (mut font_impl, font_impl_metrics) = match last_font {
Some((last_font_id, last_font_metrics)) if last_font_id == font_id => {
(font.fonts_by_id.get_mut(&font_id), last_font_metrics)
}
_ => {
let font_impl = font.fonts_by_id.get_mut(&font_id);
let scaled_metrics = font_impl
.as_ref()
.map(|font_impl| font_impl.scaled_metrics(font_size));
last_font = Some((font_id, scaled_metrics));
(font_impl, scaled_metrics)
}
};
let glyph_alloc = match font_impl.as_mut() {
Some(font_impl) => {
font_impl.allocate_glyph(font.atlas, pixels_per_point, glyph_info, font_size)
}
None => Default::default(),
};
if let (Some(font_impl), Some(last_glyph_id)) = (&font_impl, last_glyph_id) {
paragraph.cursor_x += font_impl.pair_kerning(
@@ -192,10 +216,10 @@ fn layout_section(
pos: pos2(paragraph.cursor_x, f32::NAN),
advance_width: glyph_alloc.advance_width,
line_height,
font_impl_height: font_impl.as_ref().map_or(0.0, |f| f.row_height(font_size)),
font_impl_ascent: font_impl.as_ref().map_or(0.0, |f| f.ascent(font_size)),
font_height: font.row_height(font_size),
font_ascent: font.ascent(font_size),
font_impl_height: font_impl_metrics.map_or(0.0, |m| m.row_height),
font_impl_ascent: font_impl_metrics.map_or(0.0, |m| m.ascent),
font_height: font_metrics.row_height,
font_ascent: font_metrics.ascent,
uv_rect: glyph_alloc.uv_rect,
section_index,
});
@@ -431,8 +455,12 @@ fn replace_last_glyph_with_overflow_character(
let mut font = fonts.font(&section.format.font_id.family);
let font_size = section.format.font_id.size;
let (mut font_impl, replacement_glyph_alloc) =
font.font_impl_and_glyph_alloc(pixels_per_point, overflow_character, font_size);
let (font_id, glyph_info) = font.glyph_info(overflow_character);
let mut font_impl = font.fonts_by_id.get_mut(&font_id);
let replacement_glyph_alloc = font_impl
.as_mut()
.map(|f| f.allocate_glyph(font.atlas, pixels_per_point, glyph_info, font_size))
.unwrap_or_default();
let overflow_glyph_x = if let Some(prev_glyph) = row.glyphs.last() {
// Kern the overflow character properly
@@ -468,20 +496,25 @@ fn replace_last_glyph_with_overflow_character(
// We need to calculate these first since `font_impl` is mutably borrowed from `font`, which is later used
// to calculate the row height
let font_impl_height = font_impl.as_mut().map_or(0.0, |f| f.row_height(font_size));
let font_impl_ascent = font_impl.as_mut().map_or(0.0, |f| f.ascent(font_size));
let font_height = font.row_height(font_size);
let line_height = section.format.line_height.unwrap_or(font_height);
let font_impl_metrics = font_impl
.as_mut()
.map(|f| f.scaled_metrics(font_size))
.unwrap_or_default();
let font_metrics = font.scaled_metrics(font_size);
let line_height = section
.format
.line_height
.unwrap_or(font_metrics.row_height);
row.glyphs.push(Glyph {
chr: overflow_character,
pos: pos2(overflow_glyph_x, f32::NAN),
advance_width: replacement_glyph_alloc.advance_width,
line_height,
font_impl_height,
font_impl_ascent,
font_height,
font_ascent: font.ascent(font_size),
font_impl_height: font_impl_metrics.row_height,
font_impl_ascent: font_impl_metrics.ascent,
font_height: font_metrics.row_height,
font_ascent: font_metrics.ascent,
uv_rect: replacement_glyph_alloc.uv_rect,
section_index,
});
@@ -1170,7 +1203,10 @@ mod tests {
);
let font_id = FontId::default();
let font_height = fonts.font(&font_id.family).row_height(font_id.size);
let font_height = fonts
.font(&font_id.family)
.scaled_metrics(font_id.size)
.row_height;
let job = LayoutJob::simple(String::new(), font_id, Color32::WHITE, f32::INFINITY);
@@ -1204,7 +1240,10 @@ mod tests {
);
let font_id = FontId::default();
let font_height = fonts.font(&font_id.family).row_height(font_id.size);
let font_height = fonts
.font(&font_id.family)
.scaled_metrics(font_id.size)
.row_height;
let job = LayoutJob::simple("Hi!\n".to_owned(), font_id, Color32::WHITE, f32::INFINITY);