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:
@@ -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).
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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(§ion.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);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user