From fb3a7c1a1ce4c4a0d1865551760c80041659f138 Mon Sep 17 00:00:00 2001 From: tigregalis Date: Sat, 15 Mar 2025 11:42:58 +0800 Subject: [PATCH] Reuse fallbacks --- src/font/fallback/mod.rs | 77 ++++++++++++++++++++++++++-------------- src/font/system.rs | 14 +++++--- 2 files changed, 61 insertions(+), 30 deletions(-) diff --git a/src/font/fallback/mod.rs b/src/font/fallback/mod.rs index 3df3ee3..cedea48 100644 --- a/src/font/fallback/mod.rs +++ b/src/font/fallback/mod.rs @@ -2,14 +2,12 @@ use alloc::sync::Arc; use alloc::vec::Vec; -use core::ops::Range; +use core::{mem, ops::Range}; use fontdb::Family; use unicode_script::Script; use crate::{BuildHasher, Font, FontMatchKey, FontSystem, HashMap, ShapeBuffer}; -use self::platform::*; - #[cfg(not(any(all(unix, not(target_os = "android")), target_os = "windows")))] #[path = "other.rs"] mod platform; @@ -37,24 +35,18 @@ pub trait Fallback { fn script_fallback(&self, script: Script, locale: &str) -> &[&'static str]; } -#[derive(Debug)] +#[derive(Debug, Default)] pub struct Fallbacks { lists: Vec<&'static str>, common_fallback_range: Range, forbidden_fallback_range: Range, // PERF: Consider using NoHashHasher since Script is just an integer script_fallback_ranges: HashMap>, + locale: String, } impl Fallbacks { pub(crate) fn new(fallbacks: &dyn Fallback, scripts: &[Script], locale: &str) -> Self { - let mut index = 0; - let mut new_range = |lists: &Vec<&str>| { - let old_index = index; - index = lists.len(); - old_index..index - }; - let common_fallback = fallbacks.common_fallback(); let forbidden_fallback = fallbacks.forbidden_fallback(); @@ -62,6 +54,13 @@ impl Fallbacks { let mut lists = Vec::with_capacity(common_fallback.len() + forbidden_fallback.len() + scripts.len()); + let mut index = lists.len(); + let mut new_range = |lists: &Vec<&str>| { + let old_index = index; + index = lists.len(); + old_index..index + }; + lists.extend_from_slice(common_fallback); let common_fallback_range = new_range(&lists); @@ -77,11 +76,34 @@ impl Fallbacks { script_fallback_ranges.insert(script, script_fallback_range); } + let locale = locale.to_owned(); Self { lists, common_fallback_range, forbidden_fallback_range, script_fallback_ranges, + locale, + } + } + + pub(crate) fn extend(&mut self, fallbacks: &dyn Fallback, scripts: &[Script]) { + self.lists.reserve(scripts.len()); + + let mut index = self.lists.len(); + let mut new_range = |lists: &Vec<&str>| { + let old_index = index; + index = lists.len(); + old_index..index + }; + + for &script in scripts { + self.script_fallback_ranges + .entry(script) + .or_insert_with_key(|&script| { + let script_fallback = fallbacks.script_fallback(script, &self.locale); + self.lists.extend_from_slice(script_fallback); + new_range(&self.lists) + }); } } @@ -120,7 +142,6 @@ pub(crate) struct MonospaceFallbackInfo { pub struct FontFallbackIter<'a> { font_system: &'a mut FontSystem, - fallbacks: Fallbacks, font_match_keys: &'a [FontMatchKey], default_families: &'a [&'a Family<'a>], default_i: usize, @@ -140,15 +161,12 @@ impl<'a> FontFallbackIter<'a> { scripts: &'a [Script], word: &'a str, ) -> Self { - let fallbacks = Fallbacks::new( - font_system.fallbacks.as_ref(), - scripts, - font_system.locale(), - ); + font_system + .fallbacks + .extend(font_system.dyn_fallback.as_ref(), scripts); font_system.monospace_fallbacks_buffer.clear(); Self { font_system, - fallbacks, font_match_keys, default_families, default_i: 0, @@ -178,7 +196,7 @@ impl<'a> FontFallbackIter<'a> { word ); } else if !self.scripts.is_empty() && self.common_i > 0 { - let family = self.fallbacks.common_fallback()[self.common_i - 1]; + let family = self.font_system.fallbacks.common_fallback()[self.common_i - 1]; missing_warn!( "Failed to find script fallback for {:?} locale '{}', used '{}': '{}'", self.scripts, @@ -222,11 +240,8 @@ impl<'a> FontFallbackIter<'a> { .filter(|m_key| m_key.font_weight_diff == 0) .find(|m_key| self.face_contains_family(m_key.id, default_family_name)) } -} -impl Iterator for FontFallbackIter<'_> { - type Item = Arc; - fn next(&mut self) -> Option { + fn next_item(&mut self, fallbacks: &Fallbacks) -> Option<::Item> { if let Some(fallback_info) = self.font_system.monospace_fallbacks_buffer.pop_first() { if let Some(font) = self.font_system.get_font(fallback_info.id) { return Some(font); @@ -346,7 +361,7 @@ impl Iterator for FontFallbackIter<'_> { while self.script_i.0 < self.scripts.len() { let script = self.scripts[self.script_i.0]; - let script_families = self.fallbacks.script_fallback(script); + let script_families = fallbacks.script_fallback(script); while self.script_i.1 < script_families.len() { let script_family = script_families[self.script_i.1]; @@ -370,7 +385,7 @@ impl Iterator for FontFallbackIter<'_> { self.script_i.1 = 0; } - let common_families = self.fallbacks.common_fallback(); + let common_families = fallbacks.common_fallback(); while self.common_i < common_families.len() { let common_family = common_families[self.common_i]; self.common_i += 1; @@ -386,7 +401,7 @@ impl Iterator for FontFallbackIter<'_> { //TODO: do we need to do this? //TODO: do not evaluate fonts more than once! - let forbidden_families = self.fallbacks.forbidden_fallback(); + let forbidden_families = fallbacks.forbidden_fallback(); while self.other_i < self.font_match_keys.len() { let id = self.font_match_keys[self.other_i].id; self.other_i += 1; @@ -404,3 +419,13 @@ impl Iterator for FontFallbackIter<'_> { None } } + +impl Iterator for FontFallbackIter<'_> { + type Item = Arc; + fn next(&mut self) -> Option { + let mut fallbacks = mem::take(&mut self.font_system.fallbacks); + let item = self.next_item(&fallbacks); + mem::swap(&mut fallbacks, &mut self.font_system.fallbacks); + item + } +} diff --git a/src/font/system.rs b/src/font/system.rs index 13e128c..d52c1ad 100644 --- a/src/font/system.rs +++ b/src/font/system.rs @@ -11,7 +11,7 @@ use core::ops::{Deref, DerefMut}; pub use fontdb; pub use rustybuzz; -use super::fallback::{Fallback, MonospaceFallbackInfo, PlatformFallback}; +use super::fallback::{Fallback, Fallbacks, MonospaceFallbackInfo, PlatformFallback}; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub struct FontMatchKey { @@ -115,7 +115,10 @@ pub struct FontSystem { pub shape_run_cache: crate::ShapeRunCache, /// List of fallbacks - pub(crate) fallbacks: Box, + pub(crate) dyn_fallback: Box, + + /// List of fallbacks + pub(crate) fallbacks: Fallbacks, } impl fmt::Debug for FontSystem { @@ -161,7 +164,7 @@ impl FontSystem { pub fn new_with_locale_and_db_and_fallback( locale: String, db: fontdb::Database, - fallbacks: impl Fallback + 'static, + impl_fallback: impl Fallback + 'static, ) -> Self { let mut monospace_font_ids = db .faces() @@ -200,6 +203,8 @@ impl FontSystem { .map(|(k, v)| (k, Vec::from_iter(v))) .collect(); + let fallbacks = Fallbacks::new(&impl_fallback, &[], &locale); + Self { locale, db, @@ -212,7 +217,8 @@ impl FontSystem { #[cfg(feature = "shape-run-cache")] shape_run_cache: crate::ShapeRunCache::default(), shape_buffer: ShapeBuffer::default(), - fallbacks: Box::new(fallbacks), + dyn_fallback: Box::new(impl_fallback), + fallbacks, } }