diff --git a/src/renderer/caching_shaper.rs b/src/renderer/caching_shaper.rs index 2fa952b..eb05bc8 100644 --- a/src/renderer/caching_shaper.rs +++ b/src/renderer/caching_shaper.rs @@ -1,42 +1,100 @@ use lru::LruCache; -use skulpin::skia_safe::{Shaper, TextBlob, Font, Point}; +use skulpin::skia_safe::{Shaper, TextBlob, Font, Point, TextBlobBuilder}; +use font_kit::source::SystemSource; +use skribo::{ + layout, layout_run, make_layout, FontCollection, FontFamily, FontRef, Layout, LayoutSession, + TextStyle +}; #[derive(new, Clone, Hash, PartialEq, Eq)] -struct ShapeKey { - pub text: String, +struct FontKey { + pub name: String, pub scale: u16, pub bold: bool, pub italic: bool } +#[derive(new, Clone, Hash, PartialEq, Eq)] +struct ShapeKey { + pub text: String, + pub font_key: FontKey +} + pub struct CachingShaper { - shaper: Shaper, - cache: LruCache + font_cache: LruCache, + blob_cache: LruCache } impl CachingShaper { pub fn new() -> CachingShaper { CachingShaper { - shaper: Shaper::new(None), - cache: LruCache::new(10000) + font_cache: LruCache::new(100), + blob_cache: LruCache::new(10000) } } - pub fn shape(&self, text: &str, font: &Font) -> TextBlob { - let (blob, _) = self.shaper.shape_text_blob(text, font, true, 1000000.0, Point::default()).unwrap(); - blob + fn get_font(&self, font_key: &FontKey) -> &FontRef { + if !self.font_cache.contains(font_key) { + let source = SystemSource::new(); + let font_name = font_key.name.clone(); + let font = source + .select_family_by_name(&font_name) + .expect("Failed to load by postscript name") + .fonts()[0] + .load() + .unwrap(); + self.font_cache.put(key.clone(), FontRef::new(font)); + } + + self.font_cache.get(&key).unwrap() + } + + pub fn shape(&self, text: &str, font_name: &str, scale: u16, bold: bool, italic: bool, font: &Font) -> TextBlob { + let font_key = FontKey::new(font_name.to_string(), scale, bold, italic); + let font_ref = self.get_font(&font_key); + + let style = TextStyle { size: font_size }; + let layout = layout_run(&style, &font_ref, standard_character_string); + + let blob_builder = TextBlobBuilder::new(); + + unsafe { + let count = layout.glyphs.count(); + let buffer = blob_builder + .native_mut() + .allocRunPosH(font.native(), count.try_into().unwrap(), 0, None); + let mut glyphs = slice::from_raw_parts_mut((*buffer).glyphs, count); + for (glyph_id, i) in layout.glyphs.iter().map(|glyph| glyph.glyph_id as u16).enumerate() { + glyphs[i] = glyph_id; + } + let mut positions = slice::from_raw_parts_mut((*buffer).pos, count); + for (offset, i) in layout.glyphs.iter().map(|glyph| glyph.offset.x as f32).enumerate() { + positions[i] = offset; + } + } + + blob_builder.make() + // TextBlob::from_pos_text_h(text.as_bytes(), layout.glyphs.iter(). + // let (mut glyphs, mut points) = blob_builder.alloc_run_pos( + // // let glyph_offsets: Vec = layout.glyphs.iter().map(|glyph| glyph.offset.x).collect(); + // // let glyph_advances: Vec = glyph_offsets.windows(2).map(|pair| pair[1] - pair[0]).collect(); + + // let (blob, _) = self.shaper.shape_text_blob(text, font, true, 1000000.0, Point::default()).unwrap(); + // blob } - pub fn shape_cached(&mut self, text: String, scale: u16, bold: bool, italic: bool, font: &Font) -> &TextBlob { - let key = ShapeKey::new(text.clone(), scale, bold, italic); - if !self.cache.contains(&key) { - self.cache.put(key.clone(), self.shape(&text, &font)); + pub fn shape_cached(&mut self, text: &str, font_name: &str, scale: u16, bold: bool, italic: bool, font: &Font) -> &TextBlob { + let font_key = FontKey::new(font_name.to_string(), scale, bold, italic); + let key = ShapeKey::new(text.to_string(), font_key); + if !self.blob_cache.contains(&key) { + self.blob_cache.put(key.clone(), self.shape(text, font_name, scale, bold, italic, &font)); } - self.cache.get(&key).unwrap() + self.blob_cache.get(&key).unwrap() } pub fn clear(&mut self) { - self.cache.clear(); + self.font_cache.clear(); + self.blob_cache.clear(); } } diff --git a/src/renderer/fonts.rs b/src/renderer/fonts.rs index 1d71cbe..fd5124c 100644 --- a/src/renderer/fonts.rs +++ b/src/renderer/fonts.rs @@ -7,7 +7,7 @@ use skribo::{ }; use crate::editor::Style; -const standard_character_string: &'static str = "XXXX"; +const standard_character_string: &'static str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"; pub struct Fonts { pub name: String, @@ -106,11 +106,17 @@ impl FontLookup { let font_ref = FontRef::new(font); let style = TextStyle { size: font_size }; let layout = layout_run(&style, &font_ref, standard_character_string); - let font_width = layout.advance.x / standard_character_string.len() as f32; - // let glyph_offsets: Vec = layout.glyphs.iter().map(|glyph| glyph.offset.x).collect(); - // let glyph_advances: Vec = glyph_offsets.windows(2).map(|pair| pair[1] - pair[0]).collect(); - // dbg!(&glyph_advances); - // let font_width = glyph_advances.iter().cloned().fold(0.0, f32::max); + let glyph_offsets: Vec = layout.glyphs.iter().map(|glyph| glyph.offset.x).collect(); + let glyph_advances: Vec = glyph_offsets.windows(2).map(|pair| pair[1] - pair[0]).collect(); + + let mut amounts = HashMap::new(); + for advance in glyph_advances.iter() { + amounts.entry(advance.to_string()) + .and_modify(|e| *e += 1) + .or_insert(1); + } + let (font_width, _) = amounts.into_iter().max_by_key(|(_, count)| count.clone()).unwrap(); + let font_width = font_width.parse::().unwrap(); (font_width, font_height) }