From 5e759ab4321e5c180f5f1fc90cf352041ac0d98c Mon Sep 17 00:00:00 2001 From: Mohammad AlSaleh Date: Sun, 10 Mar 2024 21:49:51 +0300 Subject: [PATCH] Skip trying monospace fallbacks if default font supports all codepoints Instead of blindly pushing default font to the start of the monospace fallbacks map. Actually collect codepoint support info for it, and if it supports all codepoints, skip collecting that info from other monospace fonts. If it doesn't, push it to the start of the map as before. This actually provides a big performance boost, while the sophisticated monospace fallback process is still done whenever needed. Signed-off-by: Mohammad AlSaleh --- src/font/fallback/mod.rs | 88 ++++++++++++++++++++++++++++------------ 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/src/font/fallback/mod.rs b/src/font/fallback/mod.rs index d090be50f9..c30dfc1b4b 100644 --- a/src/font/fallback/mod.rs +++ b/src/font/fallback/mod.rs @@ -130,6 +130,16 @@ impl<'a> FontFallbackIter<'a> { false } } + + fn default_font_match_key(&self) -> Option<&FontMatchKey> { + let default_family = self.default_families[self.default_i - 1]; + let default_family_name = self.font_system.db().family_name(default_family); + + self.font_match_keys + .iter() + .filter(|m_key| m_key.font_weight_diff == 0) + .find(|m_key| self.face_contains_family(m_key.id, default_family_name)) + } } impl<'a> Iterator for FontFallbackIter<'a> { @@ -147,9 +157,58 @@ impl<'a> Iterator for FontFallbackIter<'a> { .filter(move |m_key| m_key.font_weight_diff == 0 || is_mono) }; - while self.default_i < self.default_families.len() { + 'DEF_FAM: while self.default_i < self.default_families.len() { self.default_i += 1; let is_mono = self.default_families[self.default_i - 1] == &Family::Monospace; + let default_font_match_key = self.default_font_match_key().cloned(); + let word_chars_count = self.word.chars().count(); + + macro_rules! mk_mono_fallback_info { + ($m_key:expr) => {{ + let supported_cp_count_opt = self + .font_system + .get_font_supported_codepoints_in_word($m_key.id, self.word); + + supported_cp_count_opt.map(|supported_cp_count| { + let codepoint_non_matches = word_chars_count - supported_cp_count; + + MonospaceFallbackInfo { + font_weight_diff: Some($m_key.font_weight_diff), + codepoint_non_matches: Some(codepoint_non_matches), + font_weight: $m_key.font_weight, + id: $m_key.id, + } + }) + }}; + } + + match (is_mono, default_font_match_key.as_ref()) { + (false, None) => break 'DEF_FAM, + (false, Some(m_key)) => { + if let Some(font) = self.font_system.get_font(m_key.id) { + return Some(font); + } else { + break 'DEF_FAM; + } + } + (true, None) => (), + (true, Some(m_key)) => { + // Default Monospace font + if let Some(mut fallback_info) = mk_mono_fallback_info!(m_key) { + fallback_info.font_weight_diff = None; + + // Return early if default Monospace font supports all word codepoints. + // Otherewise, add to fallbacks set + if fallback_info.codepoint_non_matches == Some(0) { + if let Some(font) = self.font_system.get_font(m_key.id) { + return Some(font); + } + } else { + assert!(self.monospace_fallbacks.insert(fallback_info)); + } + } + } + }; let mono_ids_for_scripts = if is_mono && !self.scripts.is_empty() { let scripts = self.scripts.iter().filter_map(|script| { @@ -162,35 +221,14 @@ impl<'a> Iterator for FontFallbackIter<'a> { }; for m_key in font_match_keys_iter(is_mono) { - let default_family = self - .font_system - .db() - .family_name(self.default_families[self.default_i - 1]); - if self.face_contains_family(m_key.id, default_family) { - if let Some(font) = self.font_system.get_font(m_key.id) { - if !is_mono { - return Some(font); - } else if m_key.font_weight_diff == 0 { - // Default font - let fallback_info = MonospaceFallbackInfo { - font_weight_diff: None, - codepoint_non_matches: None, - font_weight: m_key.font_weight, - id: m_key.id, - }; - assert!(self.monospace_fallbacks.insert(fallback_info)); - } - } - } - // Set a monospace fallback if Monospace family is not found - if is_mono { - let include_mono_id = if mono_ids_for_scripts.is_empty() { + if Some(m_key.id) != default_font_match_key.as_ref().map(|m_key| m_key.id) { + let is_mono_id = if mono_ids_for_scripts.is_empty() { self.font_system.is_monospace(m_key.id) } else { mono_ids_for_scripts.binary_search(&m_key.id).is_ok() }; - if include_mono_id { + if is_mono_id { let supported_cp_count_opt = self .font_system .get_font_supported_codepoints_in_word(m_key.id, self.word);