diff options
author | 2023-02-27 16:28:19 +0100 | |
---|---|---|
committer | 2023-02-27 16:28:19 +0100 | |
commit | c1ff803b8f98beb2a73bb4252b34921110aa6cf0 (patch) | |
tree | 9c30672431198613ad71b5cc23f590e89d6057a1 | |
parent | 3105ad2e0036e101e66b5e6ab437b570f2d923a3 (diff) | |
download | iced-c1ff803b8f98beb2a73bb4252b34921110aa6cf0.tar.gz iced-c1ff803b8f98beb2a73bb4252b34921110aa6cf0.tar.bz2 iced-c1ff803b8f98beb2a73bb4252b34921110aa6cf0.zip |
Implement basic glyph cache in `iced_tiny_skia`
-rw-r--r-- | tiny_skia/Cargo.toml | 2 | ||||
-rw-r--r-- | tiny_skia/src/text.rs | 183 |
2 files changed, 120 insertions, 65 deletions
diff --git a/tiny_skia/Cargo.toml b/tiny_skia/Cargo.toml index 7fee49cb..781e7d34 100644 --- a/tiny_skia/Cargo.toml +++ b/tiny_skia/Cargo.toml @@ -27,7 +27,7 @@ path = "../graphics" [dependencies.cosmic-text] features = ["std", "swash"] git = "https://github.com/hecrj/cosmic-text" -rev = "dc83efbf00a2efb4118403538e8a47bfd69c3e5e" +rev = "81080c1b9498933b43c1889601a7ea6a3d16e161" [dependencies.twox-hash] version = "1.6" diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 6d4cfe96..cd7eb1ed 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -13,6 +13,7 @@ use std::sync::Arc; #[allow(missing_debug_implementations)] pub struct Pipeline { system: Option<System>, + glyph_cache: GlyphCache, } #[ouroboros::self_referencing] @@ -45,6 +46,7 @@ impl Pipeline { } .build(), ), + glyph_cache: GlyphCache::new(), } } @@ -116,76 +118,20 @@ impl Pipeline { for run in buffer.layout_runs() { for glyph in run.glyphs { - // TODO: Outline support - if let Some(image) = swash.get_image(glyph.cache_key) { - let glyph_size = image.placement.width as usize - * image.placement.height as usize; - - if glyph_size == 0 { - continue; - } - - // TODO: Cache glyph rasterization - let mut buffer = vec![0u32; glyph_size]; - - match image.content { - cosmic_text::SwashContent::Mask => { - let mut i = 0; - - // TODO: Blend alpha - let [r, g, b, _a] = color.into_rgba8(); - - for _y in 0..image.placement.height { - for _x in 0..image.placement.width { - buffer[i] = - tiny_skia::ColorU8::from_rgba( - b, - g, - r, - image.data[i], - ) - .premultiply() - .get(); - - i += 1; - } - } - } - cosmic_text::SwashContent::Color => { - let mut i = 0; - - for _y in 0..image.placement.height { - for _x in 0..image.placement.width { - // TODO: Blend alpha - buffer[i >> 2] = - tiny_skia::ColorU8::from_rgba( - image.data[i + 2], - image.data[i + 1], - image.data[i], - image.data[i + 3], - ) - .premultiply() - .get(); - - i += 4; - } - } - } - cosmic_text::SwashContent::SubpixelMask => { - // TODO - } - } - + if let Some((buffer, placement)) = self + .glyph_cache + .allocate(glyph.cache_key, color, &mut swash) + { let pixmap = tiny_skia::PixmapRef::from_bytes( bytemuck::cast_slice(&buffer), - image.placement.width, - image.placement.height, + placement.width, + placement.height, ) .expect("Create glyph pixel map"); pixels.draw_pixmap( - x as i32 + glyph.x_int + image.placement.left, - y as i32 - glyph.y_int - image.placement.top + x as i32 + glyph.x_int + placement.left, + y as i32 - glyph.y_int - placement.top + run.line_y as i32, pixmap, &tiny_skia::PixmapPaint::default(), @@ -203,6 +149,8 @@ impl Pipeline { .as_mut() .unwrap() .with_render_cache_mut(|cache| cache.trim()); + + self.glyph_cache.trim(); } pub fn measure( @@ -283,6 +231,113 @@ fn to_family(font: Font) -> cosmic_text::Family<'static> { } } +#[derive(Debug, Clone, Default)] +struct GlyphCache { + entries: FxHashMap< + (cosmic_text::CacheKey, [u8; 3]), + (Vec<u32>, cosmic_text::Placement), + >, + recently_used: FxHashSet<(cosmic_text::CacheKey, [u8; 3])>, + trim_count: usize, +} + +impl GlyphCache { + fn new() -> Self { + GlyphCache::default() + } + + fn allocate( + &mut self, + cache_key: cosmic_text::CacheKey, + color: Color, + swash: &mut cosmic_text::SwashCache<'_>, + ) -> Option<(&[u8], cosmic_text::Placement)> { + let [r, g, b, _a] = color.into_rgba8(); + let key = (cache_key, [r, g, b]); + + if let hash_map::Entry::Vacant(entry) = self.entries.entry(key) { + // TODO: Outline support + let image = swash.get_image(cache_key).as_ref()?; + + let glyph_size = image.placement.width as usize + * image.placement.height as usize; + + if glyph_size == 0 { + return None; + } + + // TODO: Cache glyph rasterization + let mut buffer = vec![0u32; glyph_size]; + + match image.content { + cosmic_text::SwashContent::Mask => { + let mut i = 0; + + // TODO: Blend alpha + + for _y in 0..image.placement.height { + for _x in 0..image.placement.width { + buffer[i] = tiny_skia::ColorU8::from_rgba( + b, + g, + r, + image.data[i], + ) + .premultiply() + .get(); + + i += 1; + } + } + } + cosmic_text::SwashContent::Color => { + let mut i = 0; + + for _y in 0..image.placement.height { + for _x in 0..image.placement.width { + // TODO: Blend alpha + buffer[i >> 2] = tiny_skia::ColorU8::from_rgba( + image.data[i + 2], + image.data[i + 1], + image.data[i], + image.data[i + 3], + ) + .premultiply() + .get(); + + i += 4; + } + } + } + cosmic_text::SwashContent::SubpixelMask => { + // TODO + } + } + + entry.insert((buffer, image.placement)); + } + + self.recently_used.insert(key); + + self.entries.get(&key).map(|(buffer, placement)| { + (bytemuck::cast_slice(buffer.as_slice()), *placement) + }) + } + + pub fn trim(&mut self) { + if self.trim_count > 300 { + self.entries + .retain(|key, _| self.recently_used.contains(key)); + + self.recently_used.clear(); + + self.trim_count = 0; + } else { + self.trim_count += 1; + } + } +} + struct Cache<'a> { entries: FxHashMap<KeyHash, cosmic_text::Buffer<'a>>, recently_used: FxHashSet<KeyHash>, |