summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2023-02-27 16:28:19 +0100
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2023-02-27 16:28:19 +0100
commitc1ff803b8f98beb2a73bb4252b34921110aa6cf0 (patch)
tree9c30672431198613ad71b5cc23f590e89d6057a1
parent3105ad2e0036e101e66b5e6ab437b570f2d923a3 (diff)
downloadiced-c1ff803b8f98beb2a73bb4252b34921110aa6cf0.tar.gz
iced-c1ff803b8f98beb2a73bb4252b34921110aa6cf0.tar.bz2
iced-c1ff803b8f98beb2a73bb4252b34921110aa6cf0.zip
Implement basic glyph cache in `iced_tiny_skia`
-rw-r--r--tiny_skia/Cargo.toml2
-rw-r--r--tiny_skia/src/text.rs183
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>,