Call get_font lazily

This commit is contained in:
Edgar Geier 2023-03-14 00:39:50 +01:00
parent f86acd325c
commit d297a6a48a
No known key found for this signature in database
GPG key ID: B022ECD3278A265C
5 changed files with 97 additions and 99 deletions

View file

@ -3,9 +3,10 @@
use alloc::sync::Arc; use alloc::sync::Arc;
#[cfg(not(feature = "std"))] #[cfg(not(feature = "std"))]
use alloc::vec::Vec; use alloc::vec::Vec;
use fontdb::Family;
use unicode_script::Script; use unicode_script::Script;
use crate::Font; use crate::{Font, FontSystem};
use self::platform::*; use self::platform::*;
@ -26,11 +27,11 @@ mod platform;
mod platform; mod platform;
pub struct FontFallbackIter<'a> { pub struct FontFallbackIter<'a> {
fonts: &'a [Arc<Font>], font_system: &'a mut FontSystem,
default_families: &'a [&'a str], font_ids: &'a [fontdb::ID],
default_families: &'a [&'a Family<'a>],
default_i: usize, default_i: usize,
scripts: Vec<Script>, scripts: Vec<Script>,
locale: &'a str,
script_i: (usize, usize), script_i: (usize, usize),
common_i: usize, common_i: usize,
other_i: usize, other_i: usize,
@ -39,17 +40,17 @@ pub struct FontFallbackIter<'a> {
impl<'a> FontFallbackIter<'a> { impl<'a> FontFallbackIter<'a> {
pub fn new( pub fn new(
fonts: &'a [Arc<Font>], font_system: &'a mut FontSystem,
default_families: &'a [&'a str], font_ids: &'a [fontdb::ID],
default_families: &'a [&'a Family<'a>],
scripts: Vec<Script>, scripts: Vec<Script>,
locale: &'a str,
) -> Self { ) -> Self {
Self { Self {
fonts, font_system,
font_ids,
default_families, default_families,
default_i: 0, default_i: 0,
scripts, scripts,
locale,
script_i: (0, 0), script_i: (0, 0),
common_i: 0, common_i: 0,
other_i: 0, other_i: 0,
@ -57,21 +58,20 @@ impl<'a> FontFallbackIter<'a> {
} }
} }
pub fn check_missing(&self, word: &str) { pub fn check_missing(&mut self, word: &str) {
if self.end { if self.end {
log::debug!( log::debug!(
"Failed to find any fallback for {:?} locale '{}': '{}'", "Failed to find any fallback for {:?} locale '{}': '{}'",
self.scripts, self.scripts,
self.locale, self.font_system.locale(),
word word
); );
} else if self.other_i > 0 { } else if self.other_i > 0 {
let font = &self.fonts[self.other_i - 1];
log::debug!( log::debug!(
"Failed to find preset fallback for {:?} locale '{}', used '{}': '{}'", "Failed to find preset fallback for {:?} locale '{}', used '{}': '{}'",
self.scripts, self.scripts,
self.locale, self.font_system.locale(),
font.name(), self.face_name(self.font_ids[self.other_i - 1]),
word word
); );
} else if !self.scripts.is_empty() && self.common_i > 0 { } else if !self.scripts.is_empty() && self.common_i > 0 {
@ -79,24 +79,48 @@ impl<'a> FontFallbackIter<'a> {
log::debug!( log::debug!(
"Failed to find script fallback for {:?} locale '{}', used '{}': '{}'", "Failed to find script fallback for {:?} locale '{}', used '{}': '{}'",
self.scripts, self.scripts,
self.locale, self.font_system.locale(),
family, family,
word word
); );
} }
} }
pub fn face_name(&self, id: fontdb::ID) -> &str {
if let Some(face) = self.font_system.db().face(id) {
if let Some((name, _)) = face.families.first() {
name
} else {
&face.post_script_name
}
} else {
"invalid font id"
}
}
fn face_contains_family(&self, id: fontdb::ID, family_name: &str) -> bool {
if let Some(face) = self.font_system.db().face(id) {
face.families.iter().any(|(name, _)| name == family_name)
} else {
false
}
}
} }
impl<'a> Iterator for FontFallbackIter<'a> { impl<'a> Iterator for FontFallbackIter<'a> {
type Item = &'a Arc<Font>; type Item = Arc<Font>;
fn next(&mut self) -> Option<Self::Item> { fn next(&mut self) -> Option<Self::Item> {
while self.default_i < self.default_families.len() { while self.default_i < self.default_families.len() {
let default_family = self.default_families[self.default_i];
self.default_i += 1; self.default_i += 1;
for id in self.font_ids.iter() {
for font in self.fonts.iter() { let default_family = self
if font.contains_family(default_family) { .font_system
return Some(font); .db()
.family_name(self.default_families[self.default_i - 1]);
if self.face_contains_family(*id, default_family) {
if let Some(font) = self.font_system.get_font(*id) {
return Some(font);
}
} }
} }
} }
@ -104,20 +128,22 @@ impl<'a> Iterator for FontFallbackIter<'a> {
while self.script_i.0 < self.scripts.len() { while self.script_i.0 < self.scripts.len() {
let script = self.scripts[self.script_i.0]; let script = self.scripts[self.script_i.0];
let script_families = script_fallback(script, self.locale); let script_families = script_fallback(script, self.font_system.locale());
while self.script_i.1 < script_families.len() { while self.script_i.1 < script_families.len() {
let script_family = script_families[self.script_i.1]; let script_family = script_families[self.script_i.1];
self.script_i.1 += 1; self.script_i.1 += 1;
for font in self.fonts.iter() { for id in self.font_ids.iter() {
if font.contains_family(script_family) { if self.face_contains_family(*id, script_family) {
return Some(font); if let Some(font) = self.font_system.get_font(*id) {
return Some(font);
}
} }
} }
log::debug!( log::debug!(
"failed to find family '{}' for script {:?} and locale '{}'", "failed to find family '{}' for script {:?} and locale '{}'",
script_family, script_family,
script, script,
self.locale self.font_system.locale(),
); );
} }
@ -129,9 +155,11 @@ impl<'a> Iterator for FontFallbackIter<'a> {
while self.common_i < common_families.len() { while self.common_i < common_families.len() {
let common_family = common_families[self.common_i]; let common_family = common_families[self.common_i];
self.common_i += 1; self.common_i += 1;
for font in self.fonts.iter() { for id in self.font_ids.iter() {
if font.contains_family(common_family) { if self.face_contains_family(*id, common_family) {
return Some(font); if let Some(font) = self.font_system.get_font(*id) {
return Some(font);
}
} }
} }
log::debug!("failed to find family '{}'", common_family); log::debug!("failed to find family '{}'", common_family);
@ -140,14 +168,16 @@ impl<'a> Iterator for FontFallbackIter<'a> {
//TODO: do we need to do this? //TODO: do we need to do this?
//TODO: do not evaluate fonts more than once! //TODO: do not evaluate fonts more than once!
let forbidden_families = forbidden_fallback(); let forbidden_families = forbidden_fallback();
while self.other_i < self.fonts.len() { while self.other_i < self.font_ids.len() {
let font = &self.fonts[self.other_i]; let id = self.font_ids[self.other_i];
self.other_i += 1; self.other_i += 1;
if forbidden_families if forbidden_families
.iter() .iter()
.all(|family| !font.contains_family(family)) .all(|family_name| !self.face_contains_family(id, family_name))
{ {
return Some(font); if let Some(font) = self.font_system.get_font(id) {
return Some(font);
}
} }
} }

View file

@ -1,7 +1,7 @@
// SPDX-License-Identifier: MIT OR Apache-2.0 // SPDX-License-Identifier: MIT OR Apache-2.0
pub(crate) mod fallback; pub(crate) mod fallback;
use fontdb::FaceInfo; use alloc::sync::Arc;
pub use self::system::*; pub use self::system::*;
mod system; mod system;
@ -12,8 +12,9 @@ pub struct Font(FontInner);
#[ouroboros::self_referencing] #[ouroboros::self_referencing]
#[allow(dead_code)] #[allow(dead_code)]
struct FontInner { struct FontInner {
info: fontdb::FaceInfo, id: fontdb::ID,
#[borrows(info)] data: Arc<dyn AsRef<[u8]> + Send + Sync>,
#[borrows(data)]
#[covariant] #[covariant]
rustybuzz: rustybuzz::Face<'this>, rustybuzz: rustybuzz::Face<'this>,
// workaround, since ouroboros does not work with #[cfg(feature = "swash")] // workaround, since ouroboros does not work with #[cfg(feature = "swash")]
@ -30,18 +31,18 @@ impl Font {
pub fn new(info: &fontdb::FaceInfo) -> Option<Self> { pub fn new(info: &fontdb::FaceInfo) -> Option<Self> {
#[allow(unused_variables)] #[allow(unused_variables)]
let data = match &info.source { let data = match &info.source {
fontdb::Source::Binary(data) => (**data).as_ref(), fontdb::Source::Binary(data) => Arc::clone(data),
#[cfg(feature = "std")] #[cfg(feature = "std")]
fontdb::Source::File(path) => { fontdb::Source::File(path) => {
log::warn!("Unsupported fontdb Source::File('{}')", path.display()); log::warn!("Unsupported fontdb Source::File('{}')", path.display());
return None; return None;
} }
#[cfg(feature = "std")] #[cfg(feature = "std")]
fontdb::Source::SharedFile(_path, data) => (**data).as_ref(), fontdb::Source::SharedFile(_path, data) => Arc::clone(data),
}; };
Some(Self( Some(Self(
FontInnerTryBuilder { FontInnerTryBuilder {
info: info.clone(), id: info.id,
swash: { swash: {
#[cfg(feature = "swash")] #[cfg(feature = "swash")]
let swash = { let swash = {
@ -53,8 +54,9 @@ impl Font {
let swash = (); let swash = ();
swash swash
}, },
rustybuzz_builder: |info| { data,
rustybuzz::Face::from_slice(get_data(info), info.index).ok_or(()) rustybuzz_builder: |data| {
rustybuzz::Face::from_slice((**data).as_ref(), info.index).ok_or(())
}, },
} }
.try_build() .try_build()
@ -62,36 +64,23 @@ impl Font {
)) ))
} }
pub fn info(&self) -> &FaceInfo { pub fn id(&self) -> fontdb::ID {
self.0.borrow_info() *self.0.borrow_id()
} }
pub fn data(&self) -> &[u8] { pub fn data(&self) -> &[u8] {
get_data(self.0.borrow_info()) (**self.0.borrow_data()).as_ref()
} }
pub fn rustybuzz(&self) -> &rustybuzz::Face { pub fn rustybuzz(&self) -> &rustybuzz::Face {
self.0.borrow_rustybuzz() self.0.borrow_rustybuzz()
} }
pub fn name(&self) -> &str {
if let Some((name, _)) = self.info().families.first() {
name
} else {
&self.info().post_script_name
}
}
pub fn contains_family(&self, family: &str) -> bool {
self.info().families.iter().any(|(name, _)| name == family)
}
#[cfg(feature = "swash")] #[cfg(feature = "swash")]
pub fn as_swash(&self) -> swash::FontRef { pub fn as_swash(&self) -> swash::FontRef {
let info = self.0.borrow_info();
let swash = self.0.borrow_swash(); let swash = self.0.borrow_swash();
swash::FontRef { swash::FontRef {
data: get_data(info), data: self.data(),
offset: swash.0, offset: swash.0,
key: swash.1, key: swash.1,
} }
@ -104,16 +93,3 @@ impl Font {
self.0.borrow_swash(); self.0.borrow_swash();
} }
} }
fn get_data(info: &FaceInfo) -> &[u8] {
match &info.source {
fontdb::Source::Binary(data) => (**data).as_ref(),
#[cfg(feature = "std")]
fontdb::Source::File(path) => {
// This should never happen, because `Font::new` verified the source isn't a file
panic!("Unsupported fontdb Source::File('{}')", path.display());
}
#[cfg(feature = "std")]
fontdb::Source::SharedFile(_path, data) => (**data).as_ref(),
}
}

View file

@ -51,19 +51,15 @@ impl FontSystem {
get_font(&self.db, id) get_font(&self.db, id)
} }
pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc<Vec<Arc<Font>>> { pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc<Vec<fontdb::ID>> {
let mut fonts = Vec::new(); let ids = self
for face in self.db.faces() { .db
if !attrs.matches(face) { .faces()
continue; .filter(|face| attrs.matches(face))
} .map(|face| face.id)
.collect::<Vec<_>>();
if let Some(font) = get_font(&self.db, face.id) { Arc::new(ids)
fonts.push(font);
}
}
Arc::new(fonts)
} }
} }

View file

@ -9,7 +9,7 @@ pub struct FontSystem {
locale: String, locale: String,
db: fontdb::Database, db: fontdb::Database,
font_cache: HashMap<fontdb::ID, Option<Arc<Font>>>, font_cache: HashMap<fontdb::ID, Option<Arc<Font>>>,
font_matches_cache: HashMap<AttrsOwned, Arc<Vec<Arc<Font>>>>, font_matches_cache: HashMap<AttrsOwned, Arc<Vec<fontdb::ID>>>,
} }
impl FontSystem { impl FontSystem {
@ -92,7 +92,7 @@ impl FontSystem {
get_font(&mut self.font_cache, &mut self.db, id) get_font(&mut self.font_cache, &mut self.db, id)
} }
pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc<Vec<Arc<Font>>> { pub fn get_font_matches(&mut self, attrs: Attrs) -> Arc<Vec<fontdb::ID>> {
self.font_matches_cache self.font_matches_cache
//TODO: do not create AttrsOwned unless entry does not already exist //TODO: do not create AttrsOwned unless entry does not already exist
.entry(AttrsOwned::new(attrs)) .entry(AttrsOwned::new(attrs))
@ -106,10 +106,6 @@ impl FontSystem {
.filter(|face| attrs.matches(face)) .filter(|face| attrs.matches(face))
.map(|face| face.id) .map(|face| face.id)
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let fonts = ids
.into_iter()
.filter_map(|id| get_font(&mut self.font_cache, &mut self.db, id))
.collect();
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
{ {
@ -117,7 +113,7 @@ impl FontSystem {
log::debug!("font matches for {:?} in {:?}", attrs, elapsed); log::debug!("font matches for {:?} in {:?}", attrs, elapsed);
} }
Arc::new(fonts) Arc::new(ids)
}) })
.clone() .clone()
} }

View file

@ -62,7 +62,7 @@ fn shape_fallback(
y_advance, y_advance,
x_offset, x_offset,
y_offset, y_offset,
font_id: font.info().id, font_id: font.id(),
glyph_id: info.glyph_id.try_into().expect("failed to cast glyph ID"), glyph_id: info.glyph_id.try_into().expect("failed to cast glyph ID"),
//TODO: color should not be related to shaping //TODO: color should not be related to shaping
color_opt: attrs.color_opt, color_opt: attrs.color_opt,
@ -125,16 +125,13 @@ fn shape_run(
let fonts = font_system.get_font_matches(attrs); let fonts = font_system.get_font_matches(attrs);
let db = font_system.db(); let default_families = [&attrs.family];
let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, scripts);
let default_families = [db.family_name(&attrs.family)];
let mut font_iter =
FontFallbackIter::new(&fonts, &default_families, scripts, font_system.locale());
let font = font_iter.next().expect("no default font found"); let font = font_iter.next().expect("no default font found");
let (mut glyphs, mut missing) = let (mut glyphs, mut missing) =
shape_fallback(font, line, attrs_list, start_run, end_run, span_rtl); shape_fallback(&font, line, attrs_list, start_run, end_run, span_rtl);
//TODO: improve performance! //TODO: improve performance!
while !missing.is_empty() { while !missing.is_empty() {
@ -143,9 +140,12 @@ fn shape_run(
None => break, None => break,
}; };
log::trace!("Evaluating fallback with font '{}'", font.name()); log::trace!(
"Evaluating fallback with font '{}'",
font_iter.face_name(font.id())
);
let (mut fb_glyphs, fb_missing) = let (mut fb_glyphs, fb_missing) =
shape_fallback(font, line, attrs_list, start_run, end_run, span_rtl); shape_fallback(&font, line, attrs_list, start_run, end_run, span_rtl);
// Insert all matching glyphs // Insert all matching glyphs
let mut fb_i = 0; let mut fb_i = 0;