Reuse fallbacks

This commit is contained in:
tigregalis 2025-03-15 11:42:58 +08:00 committed by Jeremy Soller
parent 7fb685fa13
commit fb3a7c1a1c
2 changed files with 61 additions and 30 deletions

View file

@ -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<usize>,
forbidden_fallback_range: Range<usize>,
// PERF: Consider using NoHashHasher since Script is just an integer
script_fallback_ranges: HashMap<Script, Range<usize>>,
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<Font>;
fn next(&mut self) -> Option<Self::Item> {
fn next_item(&mut self, fallbacks: &Fallbacks) -> Option<<Self as Iterator>::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<Font>;
fn next(&mut self) -> Option<Self::Item> {
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
}
}

View file

@ -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<dyn Fallback>,
pub(crate) dyn_fallback: Box<dyn Fallback>,
/// 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,
}
}