From 837fc7fef7e6c32948a2aae9e65ec556a09aeb0a Mon Sep 17 00:00:00 2001 From: valadaptive Date: Fri, 4 Jul 2025 00:03:57 -0400 Subject: [PATCH] Remove more Arc> from font code By making `Font` a view type and indexing by font ID, we can avoid wrapping `FontImpl` and `TextureAtlas` in an `Arc>`. --- crates/egui/src/context.rs | 15 +- crates/egui_demo_lib/benches/benchmark.rs | 2 +- crates/epaint/src/text/font.rs | 163 ++++++++++------------ crates/epaint/src/text/fonts.rs | 152 +++++++++++++++----- crates/epaint/src/text/text_layout.rs | 32 ++--- 5 files changed, 208 insertions(+), 156 deletions(-) diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index ce2c7e167..83ef18b7e 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -1588,7 +1588,7 @@ impl Context { let font_id = TextStyle::Body.resolve(&self.style()); self.fonts_mut(|f| { - let font = f.fonts.font(&font_id.family); + let mut font = f.fonts.font(&font_id.family); font.has_glyphs(alt) && font.has_glyphs(ctrl) && font.has_glyphs(shift) @@ -2466,14 +2466,15 @@ impl ContextImpl { self.memory.end_pass(&viewport.this_pass.used_ids); - if let Some(fonts) = self.fonts.get(&pixels_per_point.into()) { + let num_font_envs = self.fonts.len(); + if let Some(fonts) = self.fonts.get_mut(&pixels_per_point.into()) { let tex_mngr = &mut self.tex_manager.0.write(); if let Some(font_image_delta) = fonts.font_image_delta() { // A partial font atlas update, e.g. a new glyph has been entered. tex_mngr.set(TextureId::default(), font_image_delta); } - if 1 < self.fonts.len() { + if 1 < num_font_envs { // We have multiple different `pixels_per_point`, // e.g. because we have many viewports spread across // monitors with different DPI scaling. @@ -2693,10 +2694,6 @@ impl Context { .1 .texture_atlas() }; - let (font_tex_size, prepared_discs) = { - let atlas = texture_atlas.lock(); - (atlas.size(), atlas.prepared_discs()) - }; let paint_stats = PaintStats::from_shapes(&shapes); let clipped_primitives = { @@ -2704,8 +2701,8 @@ impl Context { tessellator::Tessellator::new( pixels_per_point, tessellation_options, - font_tex_size, - prepared_discs, + texture_atlas.size(), + texture_atlas.prepared_discs(), ) .tessellate_shapes(shapes) }; diff --git a/crates/egui_demo_lib/benches/benchmark.rs b/crates/egui_demo_lib/benches/benchmark.rs index d3345c2e5..29b47b915 100644 --- a/crates/egui_demo_lib/benches/benchmark.rs +++ b/crates/egui_demo_lib/benches/benchmark.rs @@ -227,7 +227,7 @@ pub fn criterion_benchmark(c: &mut Criterion) { let galley = fonts.layout(LOREM_IPSUM_LONG.to_owned(), font_id, text_color, wrap_width); let font_image_size = fonts.font_image_size(); - let prepared_discs = fonts.texture_atlas().lock().prepared_discs(); + let prepared_discs = fonts.texture_atlas().prepared_discs(); let mut tessellator = egui::epaint::Tessellator::new( 1.0, Default::default(), diff --git a/crates/epaint/src/text/font.rs b/crates/epaint/src/text/font.rs index 30e7f4757..f128a6bc8 100644 --- a/crates/epaint/src/text/font.rs +++ b/crates/epaint/src/text/font.rs @@ -1,13 +1,14 @@ use std::collections::BTreeMap; -use std::sync::Arc; use ab_glyph::{Font as _, PxScaleFont, ScaleFont as _}; use emath::{GuiRounding as _, OrderedFloat, Vec2, vec2}; use crate::{ TextureAtlas, - mutex::{Mutex, RwLock}, - text::FontTweak, + text::{ + FontTweak, + fonts::{CachedFamily, FontFaceKey}, + }, }; // ---------------------------------------------------------------------------- @@ -84,9 +85,8 @@ pub struct FontImpl { name: String, ab_glyph_font: ab_glyph::FontArc, tweak: FontTweak, - glyph_info_cache: RwLock>, // TODO(emilk): standard Mutex - glyph_alloc_cache: RwLock), GlyphAllocation>>, // TODO(emilk): standard Mutex - atlas: Arc>, + glyph_info_cache: ahash::HashMap, + glyph_alloc_cache: ahash::HashMap<(GlyphInfo, OrderedFloat), GlyphAllocation>, } trait FontExt { @@ -116,19 +116,13 @@ where } impl FontImpl { - pub fn new( - atlas: Arc>, - name: String, - ab_glyph_font: ab_glyph::FontArc, - tweak: FontTweak, - ) -> Self { + pub fn new(name: String, ab_glyph_font: ab_glyph::FontArc, tweak: FontTweak) -> Self { Self { name, ab_glyph_font, tweak, glyph_info_cache: Default::default(), glyph_alloc_cache: Default::default(), - atlas, } } @@ -161,9 +155,9 @@ impl FontImpl { } /// `\n` will result in `None` - fn glyph_info(&self, c: char) -> Option { + fn glyph_info(&mut self, c: char) -> Option { { - if let Some(glyph_info) = self.glyph_info_cache.read().get(&c) { + if let Some(glyph_info) = self.glyph_info_cache.get(&c) { return Some(*glyph_info); } } @@ -180,7 +174,7 @@ impl FontImpl { .into(), ..space }; - self.glyph_info_cache.write().insert(c, glyph_info); + self.glyph_info_cache.insert(c, glyph_info); return Some(glyph_info); } } @@ -197,14 +191,14 @@ impl FontImpl { advance_width_unscaled: advance_width.into(), ..space }; - self.glyph_info_cache.write().insert(c, glyph_info); + self.glyph_info_cache.insert(c, glyph_info); return Some(glyph_info); } } if invisible_char(c) { let glyph_info = GlyphInfo::default(); - self.glyph_info_cache.write().insert(c, glyph_info); + self.glyph_info_cache.insert(c, glyph_info); return Some(glyph_info); } @@ -219,7 +213,7 @@ impl FontImpl { advance_width_unscaled: self.ab_glyph_font.h_advance_unscaled(glyph_id).into(), visible: true, }; - self.glyph_info_cache.write().insert(c, glyph_info); + self.glyph_info_cache.insert(c, glyph_info); Some(glyph_info) } } @@ -259,8 +253,9 @@ impl FontImpl { } pub fn allocate_glyph( - &self, + &mut self, glyph_info: GlyphInfo, + atlas: &mut TextureAtlas, font_size: f32, pixels_per_point: f32, ) -> GlyphAllocation { @@ -273,8 +268,7 @@ impl FontImpl { .ab_glyph_font .pt_scale_factor(font_size * self.tweak.scale * pixels_per_point) .round(); - let mut cache = self.glyph_alloc_cache.write(); - let entry = match cache.entry((glyph_info, scale.into())) { + let entry = match self.glyph_alloc_cache.entry((glyph_info, scale.into())) { std::collections::hash_map::Entry::Occupied(glyph_alloc) => { return *glyph_alloc.get(); } @@ -312,7 +306,6 @@ impl FontImpl { UvRect::default() } else { let glyph_pos = { - let atlas = &mut self.atlas.lock(); let text_alpha_from_coverage = atlas.text_alpha_from_coverage; let (glyph_pos, image) = atlas.allocate((glyph_width, glyph_height)); glyph.draw(|x, y, v| { @@ -352,56 +345,15 @@ impl FontImpl { } } -type FontIndex = usize; - // TODO(emilk): rename? /// Wrapper over multiple [`FontImpl`] (e.g. a primary + fallbacks for emojis) -pub struct Font { - fonts: Vec>, - - /// Lazily calculated. - characters: Option>>, - - replacement_glyph: (FontIndex, GlyphInfo), - glyph_info_cache: ahash::HashMap, +pub struct Font<'a> { + pub(super) fonts_by_id: &'a mut nohash_hasher::IntMap, + pub(super) cached_family: &'a mut CachedFamily, + pub(super) atlas: &'a mut TextureAtlas, } -impl Font { - pub fn new(fonts: Vec>) -> Self { - if fonts.is_empty() { - return Self { - fonts, - characters: None, - replacement_glyph: Default::default(), - glyph_info_cache: Default::default(), - }; - } - - let mut slf = Self { - fonts, - characters: None, - replacement_glyph: Default::default(), - glyph_info_cache: Default::default(), - }; - - const PRIMARY_REPLACEMENT_CHAR: char = '◻'; // white medium square - const FALLBACK_REPLACEMENT_CHAR: char = '?'; // fallback for the fallback - - let replacement_glyph = slf - .glyph_info_no_cache_or_fallback(PRIMARY_REPLACEMENT_CHAR) - .or_else(|| slf.glyph_info_no_cache_or_fallback(FALLBACK_REPLACEMENT_CHAR)) - .unwrap_or_else(|| { - #[cfg(feature = "log")] - log::warn!( - "Failed to find replacement characters {PRIMARY_REPLACEMENT_CHAR:?} or {FALLBACK_REPLACEMENT_CHAR:?}. Will use empty glyph." - ); - (0, GlyphInfo::default()) - }); - slf.replacement_glyph = replacement_glyph; - - slf - } - +impl Font<'_> { pub fn preload_characters(&mut self, s: &str) { for c in s.chars() { self.glyph_info(c); @@ -421,9 +373,10 @@ impl Font { /// All supported characters, and in which font they are available in. pub fn characters(&mut self) -> &BTreeMap> { - self.characters.get_or_insert_with(|| { + self.cached_family.characters.get_or_insert_with(|| { let mut characters: BTreeMap> = Default::default(); - for font in &self.fonts { + for font_id in &self.cached_family.fonts { + let font = self.fonts_by_id.get(font_id).expect("Nonexistent font ID"); for chr in font.characters() { characters.entry(chr).or_default().push(font.name.clone()); } @@ -437,20 +390,27 @@ impl Font { /// Returns a value rounded to [`emath::GUI_ROUNDING`]. #[inline(always)] pub fn row_height(&self, font_size: f32) -> f32 { - self.fonts[0].row_height(font_size) + let Some(first_font) = self.fonts_by_id.get(&self.cached_family.fonts[0]) else { + return 0.0; + }; + first_font.row_height(font_size) } /// Width of this character in points. pub fn glyph_width(&mut self, c: char, font_size: f32) -> f32 { - let (font_index, glyph_info) = self.glyph_info(c); - let font = &self.fonts[font_index].ab_glyph_font; + let (key, glyph_info) = self.glyph_info(c); + let font = &self + .fonts_by_id + .get(&key) + .expect("Nonexistent font ID") + .ab_glyph_font; glyph_info.advance_width_unscaled.0 * font.pt_scale_factor(font_size) / font.height_unscaled() } /// Can we display this glyph? pub fn has_glyph(&mut self, c: char) -> bool { - self.glyph_info(c) != self.replacement_glyph // TODO(emilk): this is a false negative if the user asks about the replacement character itself 🤦‍♂️ + self.glyph_info(c) != self.cached_family.replacement_glyph // TODO(emilk): this is a false negative if the user asks about the replacement character itself 🤦‍♂️ } /// Can we display all the glyphs in this text? @@ -459,24 +419,30 @@ impl Font { } /// `\n` will (intentionally) show up as the replacement character. - fn glyph_info(&mut self, c: char) -> (FontIndex, GlyphInfo) { - if let Some(font_index_glyph_info) = self.glyph_info_cache.get(&c) { + 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; } let font_index_glyph_info = self.glyph_info_no_cache_or_fallback(c); - let font_index_glyph_info = font_index_glyph_info.unwrap_or(self.replacement_glyph); - self.glyph_info_cache.insert(c, font_index_glyph_info); + let font_index_glyph_info = + font_index_glyph_info.unwrap_or(self.cached_family.replacement_glyph); + self.cached_family + .glyph_info_cache + .insert(c, font_index_glyph_info); font_index_glyph_info } #[inline] - pub(crate) fn font_impl_and_glyph_info(&mut self, c: char) -> (Option<&FontImpl>, GlyphInfo) { - if self.fonts.is_empty() { - return (None, self.replacement_glyph.1); + pub(crate) fn font_impl_and_glyph_info( + &mut self, + c: char, + ) -> (Option<&mut FontImpl>, GlyphInfo) { + if self.cached_family.fonts.is_empty() { + return (None, self.cached_family.replacement_glyph.1); } - let (font_index, glyph_info) = self.glyph_info(c); - let font_impl = &self.fonts[font_index]; + let (key, glyph_info) = self.glyph_info(c); + let font_impl = self.fonts_by_id.get_mut(&key).expect("Nonexistent font ID"); (Some(font_impl), glyph_info) } @@ -487,28 +453,39 @@ impl Font { font_size: f32, pixels_per_point: f32, ) -> (Option<&FontImpl>, GlyphAllocation) { - if self.fonts.is_empty() { + if self.cached_family.fonts.is_empty() { return (None, Default::default()); } - let (font_index, glyph_info) = self.glyph_info(c); - let font_impl = &self.fonts[font_index]; - let allocated_glyph = font_impl.allocate_glyph(glyph_info, font_size, pixels_per_point); + 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(glyph_info, self.atlas, font_size, pixels_per_point); (Some(font_impl), allocated_glyph) } pub(crate) fn ascent(&self, font_size: f32) -> f32 { - if let Some(first) = self.fonts.first() { + 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) } } - fn glyph_info_no_cache_or_fallback(&mut self, c: char) -> Option<(FontIndex, GlyphInfo)> { - for (font_index, font_impl) in self.fonts.iter().enumerate() { + pub(crate) fn glyph_info_no_cache_or_fallback( + &mut self, + c: char, + ) -> Option<(FontFaceKey, GlyphInfo)> { + for font_key in &self.cached_family.fonts { + let font_impl = self + .fonts_by_id + .get_mut(font_key) + .expect("Nonexistent font ID"); if let Some(glyph_info) = font_impl.glyph_info(c) { - self.glyph_info_cache.insert(c, (font_index, glyph_info)); - return Some((font_index, glyph_info)); + self.cached_family + .glyph_info_cache + .insert(c, (*font_key, glyph_info)); + return Some((*font_key, glyph_info)); } } None diff --git a/crates/epaint/src/text/fonts.rs b/crates/epaint/src/text/fonts.rs index e4eb30367..fefb61c46 100644 --- a/crates/epaint/src/text/fonts.rs +++ b/crates/epaint/src/text/fonts.rs @@ -1,11 +1,16 @@ -use std::{collections::BTreeMap, sync::Arc}; +use std::{ + collections::BTreeMap, + sync::{ + Arc, + atomic::{AtomicU64, Ordering}, + }, +}; use crate::{ AlphaFromCoverage, TextureAtlas, - mutex::Mutex, text::{ Galley, LayoutJob, LayoutSection, - font::{Font, FontImpl}, + font::{Font, FontImpl, GlyphInfo}, }, }; use emath::NumExt as _; @@ -399,6 +404,79 @@ impl FontDefinitions { } } +/// Unique ID for looking up a single font face/file. +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)] +pub(crate) struct FontFaceKey(pub u64); + +static KEY_COUNTER: AtomicU64 = AtomicU64::new(1); +impl FontFaceKey { + fn new() -> Self { + Self(KEY_COUNTER.fetch_add(1, Ordering::Relaxed)) + } +} + +impl nohash_hasher::IsEnabled for FontFaceKey {} + +/// Cached data for working with a font family (e.g. doing character lookups). +#[derive(Debug)] +pub(super) struct CachedFamily { + pub fonts: Vec, + + /// Lazily calculated. + pub characters: Option>>, + + pub replacement_glyph: (FontFaceKey, GlyphInfo), + + pub glyph_info_cache: ahash::HashMap, +} + +impl CachedFamily { + fn new( + fonts: Vec, + fonts_by_id: &mut nohash_hasher::IntMap, + atlas: &mut TextureAtlas, + ) -> Self { + if fonts.is_empty() { + return Self { + fonts, + characters: None, + replacement_glyph: Default::default(), + glyph_info_cache: Default::default(), + }; + } + + let mut slf = Self { + fonts, + characters: None, + replacement_glyph: Default::default(), + glyph_info_cache: Default::default(), + }; + + const PRIMARY_REPLACEMENT_CHAR: char = '◻'; // white medium square + const FALLBACK_REPLACEMENT_CHAR: char = '?'; // fallback for the fallback + + let mut font = Font { + fonts_by_id, + cached_family: &mut slf, + atlas, + }; + + let replacement_glyph = font + .glyph_info_no_cache_or_fallback(PRIMARY_REPLACEMENT_CHAR) + .or_else(|| font.glyph_info_no_cache_or_fallback(FALLBACK_REPLACEMENT_CHAR)) + .unwrap_or_else(|| { + #[cfg(feature = "log")] + log::warn!( + "Failed to find replacement characters {PRIMARY_REPLACEMENT_CHAR:?} or {FALLBACK_REPLACEMENT_CHAR:?}. Will use empty glyph." + ); + (FontFaceKey::default(), GlyphInfo::default()) + }); + slf.replacement_glyph = replacement_glyph; + + slf + } +} + // ---------------------------------------------------------------------------- /// The collection of fonts used by `epaint`. @@ -454,8 +532,8 @@ impl Fonts { let pixels_per_point_changed = self.fonts.pixels_per_point != pixels_per_point; let max_texture_side_changed = self.fonts.max_texture_side != max_texture_side; let text_alpha_from_coverage_changed = - self.fonts.atlas.lock().text_alpha_from_coverage != text_alpha_from_coverage; - let font_atlas_almost_full = self.fonts.atlas.lock().fill_ratio() > 0.8; + self.fonts.atlas.text_alpha_from_coverage != text_alpha_from_coverage; + let font_atlas_almost_full = self.fonts.atlas.fill_ratio() > 0.8; let needs_recreate = pixels_per_point_changed || max_texture_side_changed || text_alpha_from_coverage_changed @@ -479,8 +557,8 @@ impl Fonts { } /// Call at the end of each frame (before painting) to get the change to the font texture since last call. - pub fn font_image_delta(&self) -> Option { - self.fonts.atlas.lock().take_delta() + pub fn font_image_delta(&mut self) -> Option { + self.fonts.atlas.take_delta() } #[inline] @@ -495,20 +573,20 @@ impl Fonts { /// The font atlas. /// Pass this to [`crate::Tessellator`]. - pub fn texture_atlas(&self) -> Arc> { - self.fonts.atlas.clone() + pub fn texture_atlas(&self) -> &TextureAtlas { + &self.fonts.atlas } /// The full font atlas image. #[inline] pub fn image(&self) -> crate::ColorImage { - self.fonts.atlas.lock().image().clone() + self.fonts.atlas.image().clone() } /// Current size of the font image. /// Pass this to [`crate::Tessellator`]. pub fn font_image_size(&self) -> [usize; 2] { - self.fonts.atlas.lock().size() + self.fonts.atlas.size() } /// Width of this character in points. @@ -564,7 +642,7 @@ impl Fonts { /// This increases as new fonts and/or glyphs are used, /// but can also decrease in a call to [`Self::begin_pass`]. pub fn font_atlas_fill_ratio(&self) -> f32 { - self.fonts.atlas.lock().fill_ratio() + self.fonts.atlas.fill_ratio() } /// Will wrap text at the given width and line break at `\n`. @@ -618,10 +696,10 @@ pub struct FontsImpl { pixels_per_point: f32, max_texture_side: usize, definitions: FontDefinitions, - atlas: Arc>, - //font_impl_cache: FontImplCache, - font_impls: ahash::HashMap>, - family_cache: ahash::HashMap, + atlas: TextureAtlas, + fonts_by_id: nohash_hasher::IntMap, + font_impls: ahash::HashMap, + family_cache: ahash::HashMap, } impl FontsImpl { @@ -642,24 +720,23 @@ impl FontsImpl { let initial_height = 32; // Keep initial font atlas small, so it is fast to upload to GPU. This will expand as needed anyways. let atlas = TextureAtlas::new([texture_width, initial_height], text_alpha_from_coverage); - let atlas = Arc::new(Mutex::new(atlas)); - - let font_impls = definitions - .font_data - .iter() - .map(|(name, font_data)| { - let tweak = font_data.tweak; - let ab_glyph = ab_glyph_font_from_font_data(name, font_data); - let font_impl = FontImpl::new(atlas.clone(), name.clone(), ab_glyph, tweak); - (name.clone(), Arc::new(font_impl)) - }) - .collect(); + let mut fonts_by_id: nohash_hasher::IntMap = Default::default(); + let mut font_impls: ahash::HashMap = Default::default(); + for (name, font_data) in &definitions.font_data { + let tweak = font_data.tweak; + let ab_glyph = ab_glyph_font_from_font_data(name, font_data); + let font_impl = FontImpl::new(name.clone(), ab_glyph, tweak); + let key = FontFaceKey::new(); + fonts_by_id.insert(key, font_impl); + font_impls.insert(name.clone(), key); + } Self { pixels_per_point, max_texture_side, definitions, atlas, + fonts_by_id, font_impls, family_cache: Default::default(), } @@ -676,24 +753,29 @@ impl FontsImpl { } /// Get the right font implementation from [`FontFamily`]. - pub fn font(&mut self, family: &FontFamily) -> &mut Font { - self.family_cache.entry(family.clone()).or_insert_with(|| { + pub fn font(&mut self, family: &FontFamily) -> Font<'_> { + let cached_family = self.family_cache.entry(family.clone()).or_insert_with(|| { let fonts = &self.definitions.families.get(family); let fonts = fonts.unwrap_or_else(|| panic!("FontFamily::{family:?} is not bound to any fonts")); - let fonts: Vec> = fonts + let fonts: Vec = fonts .iter() .map(|font_name| { - self.font_impls + *self + .font_impls .get(font_name) .unwrap_or_else(|| panic!("No font data found for {font_name:?}")) - .clone() }) .collect(); - Font::new(fonts) - }) + CachedFamily::new(fonts, &mut self.fonts_by_id, &mut self.atlas) + }); + Font { + fonts_by_id: &mut self.fonts_by_id, + cached_family, + atlas: &mut self.atlas, + } } /// Width of this character in points. diff --git a/crates/epaint/src/text/text_layout.rs b/crates/epaint/src/text/text_layout.rs index cba38c450..8b938b56c 100644 --- a/crates/epaint/src/text/text_layout.rs +++ b/crates/epaint/src/text/text_layout.rs @@ -144,7 +144,7 @@ fn layout_section( format, } = section; let pixels_per_point = fonts.pixels_per_point(); - let font = fonts.font(&format.font_id.family); + let mut font = fonts.font(&format.font_id.family); let font_size = format.font_id.size; let line_height = section .format @@ -416,7 +416,7 @@ fn replace_last_glyph_with_overflow_character( } } - fn row_height(section: &LayoutSection, font: &Font, font_size: f32) -> f32 { + fn row_height(section: &LayoutSection, font: &Font<'_>, font_size: f32) -> f32 { section .format .line_height @@ -433,9 +433,9 @@ fn replace_last_glyph_with_overflow_character( if let Some(last_glyph) = row.glyphs.last() { let section_index = last_glyph.section_index; let section = &job.sections[section_index as usize]; - let font = fonts.font(§ion.format.font_id.family); + let mut font = fonts.font(§ion.format.font_id.family); let font_size = section.format.font_id.size; - let line_height = row_height(section, font, font_size); + let line_height = row_height(section, &font, font_size); let (_, last_glyph_info) = font.font_impl_and_glyph_info(last_glyph.chr); @@ -470,29 +470,25 @@ fn replace_last_glyph_with_overflow_character( } else { let section_index = row.section_index_at_start; let section = &job.sections[section_index as usize]; - let font = fonts.font(§ion.format.font_id.family); + let mut font = fonts.font(§ion.format.font_id.family); let font_size = section.format.font_id.size; - let line_height = row_height(section, font, font_size); + let line_height = row_height(section, &font, font_size); let x = 0.0; // TODO(emilk): heed paragraph leading_space 😬 - let (font_impl, replacement_glyph_info) = font.font_impl_and_glyph_info(overflow_character); - let glyph_alloc = if let Some(font_impl) = font_impl { - font_impl.allocate_glyph(replacement_glyph_info, font_size, pixels_per_point) - } else { - Default::default() - }; + let (mut font_impl, replacement_glyph_alloc) = + font.font_impl_and_glyph_alloc(overflow_character, font_size, pixels_per_point); row.glyphs.push(Glyph { chr: overflow_character, pos: pos2(x, f32::NAN), - advance_width: glyph_alloc.advance_width, + advance_width: replacement_glyph_alloc.advance_width, line_height, - font_impl_height: font_impl.map_or(0.0, |f| f.row_height(font_size)), - font_impl_ascent: font_impl.map_or(0.0, |f| f.ascent(font_size)), + font_impl_height: font_impl.as_mut().map_or(0.0, |f| f.row_height(font_size)), + font_impl_ascent: font_impl.as_mut().map_or(0.0, |f| f.ascent(font_size)), font_height: font.row_height(font_size), font_ascent: font.ascent(font_size), - uv_rect: glyph_alloc.uv_rect, + uv_rect: replacement_glyph_alloc.uv_rect, section_index, }); } @@ -519,7 +515,7 @@ fn replace_last_glyph_with_overflow_character( let section = &job.sections[last_glyph.section_index as usize]; let extra_letter_spacing = section.format.extra_letter_spacing; let pixels_per_point = fonts.pixels_per_point(); - let font = fonts.font(§ion.format.font_id.family); + let mut font = fonts.font(§ion.format.font_id.family); let font_size = section.format.font_id.size; if let Some(prev_glyph) = prev_glyph { @@ -573,7 +569,7 @@ fn replace_last_glyph_with_overflow_character( return; }; let pixels_per_point = fonts.pixels_per_point(); - let font = fonts.font(§ion.format.font_id.family); + let mut font = fonts.font(§ion.format.font_id.family); let font_size = section.format.font_id.size; // Just replace and be done with it. last_glyph.chr = overflow_character;