From 64fb722dfe8769d4a92edb0133f1863383ecfd86 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 26 Feb 2023 23:40:17 +0100 Subject: Draft text support in `iced_tiny_skia` --- tiny_skia/src/text.rs | 350 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 350 insertions(+) create mode 100644 tiny_skia/src/text.rs (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs new file mode 100644 index 00000000..5bd6eff1 --- /dev/null +++ b/tiny_skia/src/text.rs @@ -0,0 +1,350 @@ +pub use iced_native::text::Hit; + +use iced_native::alignment; +use iced_native::{Color, Font, Rectangle, Size}; + +use rustc_hash::{FxHashMap, FxHashSet}; +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::hash_map; +use std::hash::{BuildHasher, Hash, Hasher}; +use std::sync::Arc; + +#[allow(missing_debug_implementations)] +pub struct Pipeline { + system: Option, +} + +#[ouroboros::self_referencing] +struct System { + fonts: cosmic_text::FontSystem, + + #[borrows(fonts)] + #[not_covariant] + measurement_cache: RefCell>, + + #[borrows(fonts)] + #[not_covariant] + render_cache: Cache<'this>, +} + +impl Pipeline { + pub fn new() -> Self { + Pipeline { + system: Some( + SystemBuilder { + fonts: cosmic_text::FontSystem::new_with_fonts( + [cosmic_text::fontdb::Source::Binary(Arc::new( + include_bytes!("../../wgpu/fonts/Iced-Icons.ttf") + .as_slice(), + ))] + .into_iter(), + ), + measurement_cache_builder: |_| RefCell::new(Cache::new()), + render_cache_builder: |_| Cache::new(), + } + .build(), + ), + } + } + + pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) { + let heads = self.system.take().unwrap().into_heads(); + + let (locale, mut db) = heads.fonts.into_locale_and_db(); + + db.load_font_source(cosmic_text::fontdb::Source::Binary(Arc::new( + bytes.into_owned(), + ))); + + self.system = Some( + SystemBuilder { + fonts: cosmic_text::FontSystem::new_with_locale_and_db( + locale, db, + ), + measurement_cache_builder: |_| RefCell::new(Cache::new()), + render_cache_builder: |_| Cache::new(), + } + .build(), + ); + } + + pub fn draw( + &mut self, + content: &str, + bounds: Rectangle, + color: Color, + size: f32, + font: Font, + _horizontal_alignment: alignment::Horizontal, // TODO + _vertical_alignment: alignment::Vertical, // TODO + pixels: &mut tiny_skia::PixmapMut<'_>, + clip_mask: Option<&tiny_skia::ClipMask>, + ) { + self.system.as_mut().unwrap().with_mut(|fields| { + let key = Key { + bounds: bounds.size(), + content, + font, + size, + }; + + let (_, buffer) = fields.render_cache.allocate(&fields.fonts, key); + + let mut swash = cosmic_text::SwashCache::new(&fields.fonts); + + 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] = (image.data[i + 3] as u32) + << 24 + | (image.data[i + 2] as u32) << 16 + | (image.data[i + 1] as u32) << 8 + | image.data[i] as u32; + + i += 1; + } + } + } + cosmic_text::SwashContent::SubpixelMask => { + // TODO + } + } + + let pixmap = tiny_skia::PixmapRef::from_bytes( + bytemuck::cast_slice(&buffer), + image.placement.width, + image.placement.height, + ) + .expect("Create glyph pixel map"); + + pixels.draw_pixmap( + bounds.x as i32 + + glyph.x_int + + image.placement.left, + bounds.y as i32 - glyph.y_int - image.placement.top + + run.line_y as i32, + pixmap, + &tiny_skia::PixmapPaint::default(), + tiny_skia::Transform::identity(), + clip_mask, + ); + } + } + } + }); + } + + pub fn end_frame(&mut self) { + self.system + .as_mut() + .unwrap() + .with_render_cache_mut(|cache| cache.trim()); + } + + pub fn measure( + &self, + content: &str, + size: f32, + font: Font, + bounds: Size, + ) -> (f32, f32) { + self.system.as_ref().unwrap().with(|fields| { + let mut measurement_cache = fields.measurement_cache.borrow_mut(); + + let (_, paragraph) = measurement_cache.allocate( + fields.fonts, + Key { + content, + size, + font, + bounds, + }, + ); + + let (total_lines, max_width) = paragraph + .layout_runs() + .enumerate() + .fold((0, 0.0), |(_, max), (i, buffer)| { + (i + 1, buffer.line_w.max(max)) + }); + + (max_width, size * 1.2 * total_lines as f32) + }) + } + + pub fn hit_test( + &self, + content: &str, + size: f32, + font: iced_native::Font, + bounds: iced_native::Size, + point: iced_native::Point, + _nearest_only: bool, + ) -> Option { + self.system.as_ref().unwrap().with(|fields| { + let mut measurement_cache = fields.measurement_cache.borrow_mut(); + + let (_, paragraph) = measurement_cache.allocate( + fields.fonts, + Key { + content, + size, + font, + bounds, + }, + ); + + let cursor = paragraph.hit(point.x, point.y)?; + + Some(Hit::CharOffset(cursor.index)) + }) + } + + pub fn trim_measurement_cache(&mut self) { + self.system + .as_mut() + .unwrap() + .with_measurement_cache_mut(|cache| cache.borrow_mut().trim()); + } +} + +fn to_family(font: Font) -> cosmic_text::Family<'static> { + match font { + Font::Name(name) => cosmic_text::Family::Name(name), + Font::SansSerif => cosmic_text::Family::SansSerif, + Font::Serif => cosmic_text::Family::Serif, + Font::Cursive => cosmic_text::Family::Cursive, + Font::Fantasy => cosmic_text::Family::Fantasy, + Font::Monospace => cosmic_text::Family::Monospace, + } +} + +struct Cache<'a> { + entries: FxHashMap>, + recently_used: FxHashSet, + hasher: HashBuilder, + trim_count: usize, +} + +#[cfg(not(target_arch = "wasm32"))] +type HashBuilder = twox_hash::RandomXxHashBuilder64; + +#[cfg(target_arch = "wasm32")] +type HashBuilder = std::hash::BuildHasherDefault; + +impl<'a> Cache<'a> { + const TRIM_INTERVAL: usize = 300; + + fn new() -> Self { + Self { + entries: FxHashMap::default(), + recently_used: FxHashSet::default(), + hasher: HashBuilder::default(), + trim_count: 0, + } + } + + fn allocate( + &mut self, + fonts: &'a cosmic_text::FontSystem, + key: Key<'_>, + ) -> (KeyHash, &mut cosmic_text::Buffer<'a>) { + let hash = { + let mut hasher = self.hasher.build_hasher(); + + key.content.hash(&mut hasher); + key.size.to_bits().hash(&mut hasher); + key.font.hash(&mut hasher); + key.bounds.width.to_bits().hash(&mut hasher); + key.bounds.height.to_bits().hash(&mut hasher); + + hasher.finish() + }; + + if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) { + let metrics = cosmic_text::Metrics::new(key.size, key.size * 1.2); + let mut buffer = cosmic_text::Buffer::new(fonts, metrics); + + buffer.set_size( + key.bounds.width, + key.bounds.height.max(key.size * 1.2), + ); + buffer.set_text( + key.content, + cosmic_text::Attrs::new() + .family(to_family(key.font)) + .monospaced(matches!(key.font, Font::Monospace)), + ); + + let _ = entry.insert(buffer); + } + + let _ = self.recently_used.insert(hash); + + (hash, self.entries.get_mut(&hash).unwrap()) + } + + fn trim(&mut self) { + if self.trim_count >= Self::TRIM_INTERVAL { + self.entries + .retain(|key, _| self.recently_used.contains(key)); + + self.recently_used.clear(); + + self.trim_count = 0; + } else { + self.trim_count += 1; + } + } +} + +#[derive(Debug, Clone, Copy)] +struct Key<'a> { + content: &'a str, + size: f32, + font: Font, + bounds: Size, +} + +type KeyHash = u64; -- cgit From 3386402f5a3e75cdacd230f5e76cd54f4868d87d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 26 Feb 2023 23:44:50 +0100 Subject: Implement text alignment support in `iced_tiny_skia` --- tiny_skia/src/text.rs | 31 +++++++++++++++++++++++++------ 1 file changed, 25 insertions(+), 6 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 5bd6eff1..64f31aae 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -76,8 +76,8 @@ impl Pipeline { color: Color, size: f32, font: Font, - _horizontal_alignment: alignment::Horizontal, // TODO - _vertical_alignment: alignment::Vertical, // TODO + horizontal_alignment: alignment::Horizontal, + vertical_alignment: alignment::Vertical, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::ClipMask>, ) { @@ -91,6 +91,27 @@ impl Pipeline { let (_, buffer) = fields.render_cache.allocate(&fields.fonts, key); + let (total_lines, max_width) = buffer + .layout_runs() + .enumerate() + .fold((0, 0.0), |(_, max), (i, buffer)| { + (i + 1, buffer.line_w.max(max)) + }); + + let total_height = total_lines as f32 * size * 1.2; + + let x = match horizontal_alignment { + alignment::Horizontal::Left => bounds.x, + alignment::Horizontal::Center => bounds.x - max_width / 2.0, + alignment::Horizontal::Right => bounds.x - max_width, + }; + + let y = match vertical_alignment { + alignment::Vertical::Top => bounds.y, + alignment::Vertical::Center => bounds.y - total_height / 2.0, + alignment::Vertical::Bottom => bounds.y - total_height, + }; + let mut swash = cosmic_text::SwashCache::new(&fields.fonts); for run in buffer.layout_runs() { @@ -159,10 +180,8 @@ impl Pipeline { .expect("Create glyph pixel map"); pixels.draw_pixmap( - bounds.x as i32 - + glyph.x_int - + image.placement.left, - bounds.y as i32 - glyph.y_int - image.placement.top + x as i32 + glyph.x_int + image.placement.left, + y as i32 - glyph.y_int - image.placement.top + run.line_y as i32, pixmap, &tiny_skia::PixmapPaint::default(), -- cgit From 4067c427db19eb59c4ec6c8c6d6658a9643df580 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 26 Feb 2023 23:49:58 +0100 Subject: Fix glyphs with color mask in `iced_tiny_skia` --- tiny_skia/src/text.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 64f31aae..fc26703b 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -157,13 +157,17 @@ impl Pipeline { for _y in 0..image.placement.height { for _x in 0..image.placement.width { // TODO: Blend alpha - buffer[i] = (image.data[i + 3] as u32) - << 24 - | (image.data[i + 2] as u32) << 16 - | (image.data[i + 1] as u32) << 8 - | image.data[i] as u32; + 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 += 1; + i += 4; } } } -- cgit From 4e615a65cab9dfc5fa4a17a72580c573c1c040d9 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 27 Feb 2023 01:12:06 +0100 Subject: Fix `clippy` lints --- tiny_skia/src/text.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index fc26703b..6d4cfe96 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -89,7 +89,7 @@ impl Pipeline { size, }; - let (_, buffer) = fields.render_cache.allocate(&fields.fonts, key); + let (_, buffer) = fields.render_cache.allocate(fields.fonts, key); let (total_lines, max_width) = buffer .layout_runs() @@ -112,7 +112,7 @@ impl Pipeline { alignment::Vertical::Bottom => bounds.y - total_height, }; - let mut swash = cosmic_text::SwashCache::new(&fields.fonts); + let mut swash = cosmic_text::SwashCache::new(fields.fonts); for run in buffer.layout_runs() { for glyph in run.glyphs { -- cgit From c1ff803b8f98beb2a73bb4252b34921110aa6cf0 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 27 Feb 2023 16:28:19 +0100 Subject: Implement basic glyph cache in `iced_tiny_skia` --- tiny_skia/src/text.rs | 183 ++++++++++++++++++++++++++++++++------------------ 1 file changed, 119 insertions(+), 64 deletions(-) (limited to 'tiny_skia/src/text.rs') 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, + 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, 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>, recently_used: FxHashSet, -- cgit From 151daf95b70d5a53007496e58f49fc618c1a22e4 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 27 Feb 2023 16:30:54 +0100 Subject: Remove unnecessary `cast_slice` in `iced_tiny_skia` --- tiny_skia/src/text.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index cd7eb1ed..37b0fe47 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -123,7 +123,7 @@ impl Pipeline { .allocate(glyph.cache_key, color, &mut swash) { let pixmap = tiny_skia::PixmapRef::from_bytes( - bytemuck::cast_slice(&buffer), + buffer, placement.width, placement.height, ) @@ -266,7 +266,6 @@ impl GlyphCache { return None; } - // TODO: Cache glyph rasterization let mut buffer = vec![0u32; glyph_size]; match image.content { -- cgit From fd06de5d9c5afa05c5b11cda730b0769aef92caa Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 28 Feb 2023 03:48:34 +0100 Subject: Use `get_image_uncached` in `iced_tiny_skia` ... since we are not reusing the `SwashCache` --- tiny_skia/src/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 37b0fe47..da3a06bf 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -257,7 +257,7 @@ impl GlyphCache { if let hash_map::Entry::Vacant(entry) = self.entries.entry(key) { // TODO: Outline support - let image = swash.get_image(cache_key).as_ref()?; + let image = swash.get_image_uncached(cache_key)?; let glyph_size = image.placement.width as usize * image.placement.height as usize; -- cgit From 3a0d34c0240f4421737a6a08761f99d6f8140d02 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 4 Mar 2023 05:37:11 +0100 Subject: Create `iced_widget` subcrate and re-organize the whole codebase --- tiny_skia/src/text.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index da3a06bf..7a5034c2 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -1,7 +1,6 @@ -pub use iced_native::text::Hit; - -use iced_native::alignment; -use iced_native::{Color, Font, Rectangle, Size}; +use crate::core::alignment; +use crate::core::text::Hit; +use crate::core::{Color, Font, Point, Rectangle, Size}; use rustc_hash::{FxHashMap, FxHashSet}; use std::borrow::Cow; @@ -189,8 +188,8 @@ impl Pipeline { content: &str, size: f32, font: iced_native::Font, - bounds: iced_native::Size, - point: iced_native::Point, + bounds: Size, + point: Point, _nearest_only: bool, ) -> Option { self.system.as_ref().unwrap().with(|fields| { -- cgit From 99e0a71504456976ba88040f5d1d3bbc347694ea Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 5 Mar 2023 06:35:20 +0100 Subject: Rename `iced_native` to `iced_runtime` --- tiny_skia/src/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 7a5034c2..c4edadb3 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -187,7 +187,7 @@ impl Pipeline { &self, content: &str, size: f32, - font: iced_native::Font, + font: Font, bounds: Size, point: Point, _nearest_only: bool, -- cgit From 5b3977daf6df624ca5d5e1a21ce282161234b22d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Mar 2023 06:09:51 +0100 Subject: Implement `vector` pipeline in `iced_tiny_skia` --- tiny_skia/src/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index c4edadb3..bfe5da9d 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -143,7 +143,7 @@ impl Pipeline { }); } - pub fn end_frame(&mut self) { + pub fn trim_cache(&mut self) { self.system .as_mut() .unwrap() -- cgit From 0850f52d8c06bd4c5ee80609758197a093939d2d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 7 Mar 2023 06:23:09 +0100 Subject: Use `ceil` to avoid cut text in `iced_tiny_skia` This won't be necessary once we reuse the buffers from layouting by leveraging layout linearity. --- tiny_skia/src/text.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index bfe5da9d..f2935efa 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -84,7 +84,12 @@ impl Pipeline { ) { self.system.as_mut().unwrap().with_mut(|fields| { let key = Key { - bounds: bounds.size(), + bounds: { + let size = bounds.size(); + + // TODO: Reuse buffers from layouting + Size::new(size.width.ceil(), size.height.ceil()) + }, content, font, size, -- cgit From ea50ec8df1431c9c6aa8077cd1578c4698dc0314 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 17 Mar 2023 19:58:42 +0100 Subject: Trim text `Buffer` cache every frame in `iced_wgpu` and `iced_tiny_skia` --- tiny_skia/src/text.rs | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index f2935efa..714695b9 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -246,6 +246,8 @@ struct GlyphCache { } impl GlyphCache { + const TRIM_INTERVAL: usize = 300; + fn new() -> Self { GlyphCache::default() } @@ -328,7 +330,7 @@ impl GlyphCache { } pub fn trim(&mut self) { - if self.trim_count > 300 { + if self.trim_count > Self::TRIM_INTERVAL { self.entries .retain(|key, _| self.recently_used.contains(key)); @@ -345,7 +347,6 @@ struct Cache<'a> { entries: FxHashMap>, recently_used: FxHashSet, hasher: HashBuilder, - trim_count: usize, } #[cfg(not(target_arch = "wasm32"))] @@ -355,14 +356,11 @@ type HashBuilder = twox_hash::RandomXxHashBuilder64; type HashBuilder = std::hash::BuildHasherDefault; impl<'a> Cache<'a> { - const TRIM_INTERVAL: usize = 300; - fn new() -> Self { Self { entries: FxHashMap::default(), recently_used: FxHashSet::default(), hasher: HashBuilder::default(), - trim_count: 0, } } @@ -407,16 +405,10 @@ impl<'a> Cache<'a> { } fn trim(&mut self) { - if self.trim_count >= Self::TRIM_INTERVAL { - self.entries - .retain(|key, _| self.recently_used.contains(key)); + self.entries + .retain(|key, _| self.recently_used.contains(key)); - self.recently_used.clear(); - - self.trim_count = 0; - } else { - self.trim_count += 1; - } + self.recently_used.clear(); } } -- cgit From 5f9e7f6cb99467363d691086cb697b2390793afd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 19 Mar 2023 14:52:30 +0100 Subject: Update `cosmic-text` to latest :tada: --- tiny_skia/src/text.rs | 280 ++++++++++++++++++++++---------------------------- 1 file changed, 121 insertions(+), 159 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 714695b9..8391571c 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -11,62 +11,31 @@ use std::sync::Arc; #[allow(missing_debug_implementations)] pub struct Pipeline { - system: Option, + font_system: RefCell, glyph_cache: GlyphCache, -} - -#[ouroboros::self_referencing] -struct System { - fonts: cosmic_text::FontSystem, - - #[borrows(fonts)] - #[not_covariant] - measurement_cache: RefCell>, - - #[borrows(fonts)] - #[not_covariant] - render_cache: Cache<'this>, + measurement_cache: RefCell, + render_cache: Cache, } impl Pipeline { pub fn new() -> Self { Pipeline { - system: Some( - SystemBuilder { - fonts: cosmic_text::FontSystem::new_with_fonts( - [cosmic_text::fontdb::Source::Binary(Arc::new( - include_bytes!("../../wgpu/fonts/Iced-Icons.ttf") - .as_slice(), - ))] - .into_iter(), - ), - measurement_cache_builder: |_| RefCell::new(Cache::new()), - render_cache_builder: |_| Cache::new(), - } - .build(), - ), + font_system: RefCell::new(cosmic_text::FontSystem::new_with_fonts( + [cosmic_text::fontdb::Source::Binary(Arc::new( + include_bytes!("../../wgpu/fonts/Iced-Icons.ttf") + .as_slice(), + ))] + .into_iter(), + )), glyph_cache: GlyphCache::new(), + measurement_cache: RefCell::new(Cache::new()), + render_cache: Cache::new(), } } pub fn load_font(&mut self, bytes: Cow<'static, [u8]>) { - let heads = self.system.take().unwrap().into_heads(); - - let (locale, mut db) = heads.fonts.into_locale_and_db(); - - db.load_font_source(cosmic_text::fontdb::Source::Binary(Arc::new( - bytes.into_owned(), - ))); - - self.system = Some( - SystemBuilder { - fonts: cosmic_text::FontSystem::new_with_locale_and_db( - locale, db, - ), - measurement_cache_builder: |_| RefCell::new(Cache::new()), - render_cache_builder: |_| Cache::new(), - } - .build(), + self.font_system.get_mut().db_mut().load_font_source( + cosmic_text::fontdb::Source::Binary(Arc::new(bytes.into_owned())), ); } @@ -82,78 +51,75 @@ impl Pipeline { pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::ClipMask>, ) { - self.system.as_mut().unwrap().with_mut(|fields| { - let key = Key { - bounds: { - let size = bounds.size(); - - // TODO: Reuse buffers from layouting - Size::new(size.width.ceil(), size.height.ceil()) - }, - content, - font, - size, - }; - - let (_, buffer) = fields.render_cache.allocate(fields.fonts, key); - - let (total_lines, max_width) = buffer - .layout_runs() - .enumerate() - .fold((0, 0.0), |(_, max), (i, buffer)| { - (i + 1, buffer.line_w.max(max)) - }); - - let total_height = total_lines as f32 * size * 1.2; - - let x = match horizontal_alignment { - alignment::Horizontal::Left => bounds.x, - alignment::Horizontal::Center => bounds.x - max_width / 2.0, - alignment::Horizontal::Right => bounds.x - max_width, - }; - - let y = match vertical_alignment { - alignment::Vertical::Top => bounds.y, - alignment::Vertical::Center => bounds.y - total_height / 2.0, - alignment::Vertical::Bottom => bounds.y - total_height, - }; - - let mut swash = cosmic_text::SwashCache::new(fields.fonts); - - for run in buffer.layout_runs() { - for glyph in run.glyphs { - if let Some((buffer, placement)) = self - .glyph_cache - .allocate(glyph.cache_key, color, &mut swash) - { - let pixmap = tiny_skia::PixmapRef::from_bytes( - buffer, - placement.width, - placement.height, - ) - .expect("Create glyph pixel map"); - - pixels.draw_pixmap( - 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(), - tiny_skia::Transform::identity(), - clip_mask, - ); - } + let font_system = self.font_system.get_mut(); + let key = Key { + bounds: { + let size = bounds.size(); + + // TODO: Reuse buffers from layouting + Size::new(size.width.ceil(), size.height.ceil()) + }, + content, + font, + size, + }; + + let (_, buffer) = self.render_cache.allocate(font_system, key); + + let (total_lines, max_width) = buffer + .layout_runs() + .enumerate() + .fold((0, 0.0), |(_, max), (i, buffer)| { + (i + 1, buffer.line_w.max(max)) + }); + + let total_height = total_lines as f32 * size * 1.2; + + let x = match horizontal_alignment { + alignment::Horizontal::Left => bounds.x, + alignment::Horizontal::Center => bounds.x - max_width / 2.0, + alignment::Horizontal::Right => bounds.x - max_width, + }; + + let y = match vertical_alignment { + alignment::Vertical::Top => bounds.y, + alignment::Vertical::Center => bounds.y - total_height / 2.0, + alignment::Vertical::Bottom => bounds.y - total_height, + }; + + let mut swash = cosmic_text::SwashCache::new(); + + for run in buffer.layout_runs() { + for glyph in run.glyphs { + if let Some((buffer, placement)) = self.glyph_cache.allocate( + glyph.cache_key, + color, + font_system, + &mut swash, + ) { + let pixmap = tiny_skia::PixmapRef::from_bytes( + buffer, + placement.width, + placement.height, + ) + .expect("Create glyph pixel map"); + + pixels.draw_pixmap( + 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(), + tiny_skia::Transform::identity(), + clip_mask, + ); } } - }); + } } pub fn trim_cache(&mut self) { - self.system - .as_mut() - .unwrap() - .with_render_cache_mut(|cache| cache.trim()); - + self.render_cache.trim(); self.glyph_cache.trim(); } @@ -164,28 +130,26 @@ impl Pipeline { font: Font, bounds: Size, ) -> (f32, f32) { - self.system.as_ref().unwrap().with(|fields| { - let mut measurement_cache = fields.measurement_cache.borrow_mut(); - - let (_, paragraph) = measurement_cache.allocate( - fields.fonts, - Key { - content, - size, - font, - bounds, - }, - ); + let mut measurement_cache = self.measurement_cache.borrow_mut(); - let (total_lines, max_width) = paragraph - .layout_runs() - .enumerate() - .fold((0, 0.0), |(_, max), (i, buffer)| { - (i + 1, buffer.line_w.max(max)) - }); + let (_, paragraph) = measurement_cache.allocate( + &mut self.font_system.borrow_mut(), + Key { + content, + size, + font, + bounds, + }, + ); - (max_width, size * 1.2 * total_lines as f32) - }) + let (total_lines, max_width) = paragraph + .layout_runs() + .enumerate() + .fold((0, 0.0), |(_, max), (i, buffer)| { + (i + 1, buffer.line_w.max(max)) + }); + + (max_width, size * 1.2 * total_lines as f32) } pub fn hit_test( @@ -197,30 +161,25 @@ impl Pipeline { point: Point, _nearest_only: bool, ) -> Option { - self.system.as_ref().unwrap().with(|fields| { - let mut measurement_cache = fields.measurement_cache.borrow_mut(); - - let (_, paragraph) = measurement_cache.allocate( - fields.fonts, - Key { - content, - size, - font, - bounds, - }, - ); + let mut measurement_cache = self.measurement_cache.borrow_mut(); - let cursor = paragraph.hit(point.x, point.y)?; + let (_, paragraph) = measurement_cache.allocate( + &mut self.font_system.borrow_mut(), + Key { + content, + size, + font, + bounds, + }, + ); - Some(Hit::CharOffset(cursor.index)) - }) + let cursor = paragraph.hit(point.x, point.y)?; + + Some(Hit::CharOffset(cursor.index)) } pub fn trim_measurement_cache(&mut self) { - self.system - .as_mut() - .unwrap() - .with_measurement_cache_mut(|cache| cache.borrow_mut().trim()); + self.measurement_cache.borrow_mut().trim(); } } @@ -256,14 +215,15 @@ impl GlyphCache { &mut self, cache_key: cosmic_text::CacheKey, color: Color, - swash: &mut cosmic_text::SwashCache<'_>, + font_system: &mut cosmic_text::FontSystem, + 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_uncached(cache_key)?; + let image = swash.get_image_uncached(font_system, cache_key)?; let glyph_size = image.placement.width as usize * image.placement.height as usize; @@ -343,8 +303,8 @@ impl GlyphCache { } } -struct Cache<'a> { - entries: FxHashMap>, +struct Cache { + entries: FxHashMap, recently_used: FxHashSet, hasher: HashBuilder, } @@ -355,7 +315,7 @@ type HashBuilder = twox_hash::RandomXxHashBuilder64; #[cfg(target_arch = "wasm32")] type HashBuilder = std::hash::BuildHasherDefault; -impl<'a> Cache<'a> { +impl Cache { fn new() -> Self { Self { entries: FxHashMap::default(), @@ -366,9 +326,9 @@ impl<'a> Cache<'a> { fn allocate( &mut self, - fonts: &'a cosmic_text::FontSystem, + font_system: &mut cosmic_text::FontSystem, key: Key<'_>, - ) -> (KeyHash, &mut cosmic_text::Buffer<'a>) { + ) -> (KeyHash, &mut cosmic_text::Buffer) { let hash = { let mut hasher = self.hasher.build_hasher(); @@ -383,13 +343,15 @@ impl<'a> Cache<'a> { if let hash_map::Entry::Vacant(entry) = self.entries.entry(hash) { let metrics = cosmic_text::Metrics::new(key.size, key.size * 1.2); - let mut buffer = cosmic_text::Buffer::new(fonts, metrics); + let mut buffer = cosmic_text::Buffer::new(font_system, metrics); buffer.set_size( + font_system, key.bounds.width, key.bounds.height.max(key.size * 1.2), ); buffer.set_text( + font_system, key.content, cosmic_text::Attrs::new() .family(to_family(key.font)) -- cgit From 707de9d788dc3c49d4ac57a19afac1bb938b78d9 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 30 Mar 2023 00:56:00 +0200 Subject: Introduce support for `Font` attributes --- tiny_skia/src/text.rs | 44 +++++++++++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 11 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 8391571c..c9bb9873 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -1,6 +1,7 @@ use crate::core::alignment; +use crate::core::font::{self, Font}; use crate::core::text::Hit; -use crate::core::{Color, Font, Point, Rectangle, Size}; +use crate::core::{Color, Point, Rectangle, Size}; use rustc_hash::{FxHashMap, FxHashSet}; use std::borrow::Cow; @@ -183,14 +184,28 @@ impl Pipeline { } } -fn to_family(font: Font) -> cosmic_text::Family<'static> { - match font { - Font::Name(name) => cosmic_text::Family::Name(name), - Font::SansSerif => cosmic_text::Family::SansSerif, - Font::Serif => cosmic_text::Family::Serif, - Font::Cursive => cosmic_text::Family::Cursive, - Font::Fantasy => cosmic_text::Family::Fantasy, - Font::Monospace => cosmic_text::Family::Monospace, +fn to_family(family: font::Family) -> cosmic_text::Family<'static> { + match family { + font::Family::Name(name) => cosmic_text::Family::Name(name), + font::Family::SansSerif => cosmic_text::Family::SansSerif, + font::Family::Serif => cosmic_text::Family::Serif, + font::Family::Cursive => cosmic_text::Family::Cursive, + font::Family::Fantasy => cosmic_text::Family::Fantasy, + font::Family::Monospace => cosmic_text::Family::Monospace, + } +} + +fn to_weight(weight: font::Weight) -> cosmic_text::Weight { + match weight { + font::Weight::Thin => cosmic_text::Weight::THIN, + font::Weight::ExtraLight => cosmic_text::Weight::EXTRA_LIGHT, + font::Weight::Light => cosmic_text::Weight::LIGHT, + font::Weight::Normal => cosmic_text::Weight::NORMAL, + font::Weight::Medium => cosmic_text::Weight::MEDIUM, + font::Weight::Semibold => cosmic_text::Weight::SEMIBOLD, + font::Weight::Bold => cosmic_text::Weight::BOLD, + font::Weight::ExtraBold => cosmic_text::Weight::EXTRA_BOLD, + font::Weight::Black => cosmic_text::Weight::BLACK, } } @@ -354,8 +369,15 @@ impl Cache { font_system, key.content, cosmic_text::Attrs::new() - .family(to_family(key.font)) - .monospaced(matches!(key.font, Font::Monospace)), + .family(to_family(key.font.family)) + .weight(to_weight(key.font.weight)) + .monospaced( + key.font.monospaced + || matches!( + key.font.family, + font::Family::Monospace + ), + ), ); let _ = entry.insert(buffer); -- cgit From 0b459c8e240abf83bb62902a504c018194acdbb6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 30 Mar 2023 02:01:20 +0200 Subject: Introduce `font::Stretch` --- tiny_skia/src/text.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index c9bb9873..865132b4 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -209,6 +209,20 @@ fn to_weight(weight: font::Weight) -> cosmic_text::Weight { } } +fn to_stretch(stretch: font::Stretch) -> cosmic_text::Stretch { + match stretch { + font::Stretch::UltraCondensed => cosmic_text::Stretch::UltraCondensed, + font::Stretch::ExtraCondensed => cosmic_text::Stretch::ExtraCondensed, + font::Stretch::Condensed => cosmic_text::Stretch::Condensed, + font::Stretch::SemiCondensed => cosmic_text::Stretch::SemiCondensed, + font::Stretch::Normal => cosmic_text::Stretch::Normal, + font::Stretch::SemiExpanded => cosmic_text::Stretch::SemiExpanded, + font::Stretch::Expanded => cosmic_text::Stretch::Expanded, + font::Stretch::ExtraExpanded => cosmic_text::Stretch::ExtraExpanded, + font::Stretch::UltraExpanded => cosmic_text::Stretch::UltraExpanded, + } +} + #[derive(Debug, Clone, Default)] struct GlyphCache { entries: FxHashMap< @@ -371,6 +385,7 @@ impl Cache { cosmic_text::Attrs::new() .family(to_family(key.font.family)) .weight(to_weight(key.font.weight)) + .stretch(to_stretch(key.font.stretch)) .monospaced( key.font.monospaced || matches!( -- cgit From f8cd1faa286daaf34cc532bf6d34b932b32eb35a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 5 Apr 2023 04:10:00 +0200 Subject: Group damage regions by area increase --- tiny_skia/src/text.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 865132b4..512503e0 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -336,6 +336,7 @@ struct Cache { entries: FxHashMap, recently_used: FxHashSet, hasher: HashBuilder, + trim_count: usize, } #[cfg(not(target_arch = "wasm32"))] @@ -345,11 +346,14 @@ type HashBuilder = twox_hash::RandomXxHashBuilder64; type HashBuilder = std::hash::BuildHasherDefault; impl Cache { + const TRIM_INTERVAL: usize = 300; + fn new() -> Self { Self { entries: FxHashMap::default(), recently_used: FxHashSet::default(), hasher: HashBuilder::default(), + trim_count: 0, } } @@ -404,10 +408,16 @@ impl Cache { } fn trim(&mut self) { - self.entries - .retain(|key, _| self.recently_used.contains(key)); + if self.trim_count > Self::TRIM_INTERVAL { + self.entries + .retain(|key, _| self.recently_used.contains(key)); - self.recently_used.clear(); + self.recently_used.clear(); + + self.trim_count = 0; + } else { + self.trim_count += 1; + } } } -- cgit From c0431aedd3bbef4161456f2fa5f29866e8f17fc5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 8 Apr 2023 04:47:05 +0200 Subject: Update `wgpu` and `cosmic-text` --- tiny_skia/src/text.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 865132b4..e0e893bd 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -385,14 +385,7 @@ impl Cache { cosmic_text::Attrs::new() .family(to_family(key.font.family)) .weight(to_weight(key.font.weight)) - .stretch(to_stretch(key.font.stretch)) - .monospaced( - key.font.monospaced - || matches!( - key.font.family, - font::Family::Monospace - ), - ), + .stretch(to_stretch(key.font.stretch)), ); let _ = entry.insert(buffer); -- cgit From 9c63eb7df559e58b14188b4096e9bd206444bbf3 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 26 Apr 2023 16:46:27 +0200 Subject: Update `tiny-skia` and `resvg` --- tiny_skia/src/text.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index f5994d09..1246bbd5 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -50,7 +50,7 @@ impl Pipeline { horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, pixels: &mut tiny_skia::PixmapMut<'_>, - clip_mask: Option<&tiny_skia::ClipMask>, + clip_mask: Option<&tiny_skia::Mask>, ) { let font_system = self.font_system.get_mut(); let key = Key { -- cgit From 33b5a900197e2798a393d6d9a0834039666eddbb Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 19 Apr 2023 01:19:56 +0200 Subject: Make basic text shaping the default shaping strategy --- tiny_skia/src/text.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 1246bbd5..603a3e16 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -49,6 +49,7 @@ impl Pipeline { font: Font, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, + advanced_shape: bool, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, ) { @@ -63,6 +64,7 @@ impl Pipeline { content, font, size, + advanced_shape, }; let (_, buffer) = self.render_cache.allocate(font_system, key); @@ -130,6 +132,7 @@ impl Pipeline { size: f32, font: Font, bounds: Size, + advanced_shape: bool, ) -> (f32, f32) { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -140,6 +143,7 @@ impl Pipeline { size, font, bounds, + advanced_shape, }, ); @@ -161,6 +165,7 @@ impl Pipeline { bounds: Size, point: Point, _nearest_only: bool, + advanced_shape: bool, ) -> Option { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -171,6 +176,7 @@ impl Pipeline { size, font, bounds, + advanced_shape, }, ); @@ -390,6 +396,7 @@ impl Cache { .family(to_family(key.font.family)) .weight(to_weight(key.font.weight)) .stretch(to_stretch(key.font.stretch)), + !key.advanced_shape, ); let _ = entry.insert(buffer); @@ -420,6 +427,7 @@ struct Key<'a> { size: f32, font: Font, bounds: Size, + advanced_shape: bool, } type KeyHash = u64; -- cgit From 4bd290afe7d81d9aaf7467b3ce91491f6600261a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Wed, 19 Apr 2023 02:00:45 +0200 Subject: Introduce `text::Shaping` enum and replace magic boolean --- tiny_skia/src/text.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 603a3e16..a63da193 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -1,6 +1,6 @@ use crate::core::alignment; use crate::core::font::{self, Font}; -use crate::core::text::Hit; +use crate::core::text::{Hit, Shaping}; use crate::core::{Color, Point, Rectangle, Size}; use rustc_hash::{FxHashMap, FxHashSet}; @@ -49,7 +49,7 @@ impl Pipeline { font: Font, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, - advanced_shape: bool, + shaping: Shaping, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, ) { @@ -64,7 +64,7 @@ impl Pipeline { content, font, size, - advanced_shape, + shaping, }; let (_, buffer) = self.render_cache.allocate(font_system, key); @@ -132,7 +132,7 @@ impl Pipeline { size: f32, font: Font, bounds: Size, - advanced_shape: bool, + shaping: Shaping, ) -> (f32, f32) { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -143,7 +143,7 @@ impl Pipeline { size, font, bounds, - advanced_shape, + shaping, }, ); @@ -163,9 +163,9 @@ impl Pipeline { size: f32, font: Font, bounds: Size, + shaping: Shaping, point: Point, _nearest_only: bool, - advanced_shape: bool, ) -> Option { let mut measurement_cache = self.measurement_cache.borrow_mut(); @@ -176,7 +176,7 @@ impl Pipeline { size, font, bounds, - advanced_shape, + shaping, }, ); @@ -396,7 +396,7 @@ impl Cache { .family(to_family(key.font.family)) .weight(to_weight(key.font.weight)) .stretch(to_stretch(key.font.stretch)), - !key.advanced_shape, + matches!(key.shaping, Shaping::Basic), ); let _ = entry.insert(buffer); @@ -427,7 +427,7 @@ struct Key<'a> { size: f32, font: Font, bounds: Size, - advanced_shape: bool, + shaping: Shaping, } type KeyHash = u64; -- cgit From edf3432bf5176be13437b9fd5d25b890ec9dbe69 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 2 May 2023 00:58:33 +0200 Subject: Update `glyphon` and `cosmic-text` --- tiny_skia/src/text.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index a63da193..58079cc0 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -229,6 +229,13 @@ fn to_stretch(stretch: font::Stretch) -> cosmic_text::Stretch { } } +fn to_shaping(shaping: Shaping) -> cosmic_text::Shaping { + match shaping { + Shaping::Basic => cosmic_text::Shaping::Basic, + Shaping::Advanced => cosmic_text::Shaping::Advanced, + } +} + #[derive(Debug, Clone, Default)] struct GlyphCache { entries: FxHashMap< @@ -396,7 +403,7 @@ impl Cache { .family(to_family(key.font.family)) .weight(to_weight(key.font.weight)) .stretch(to_stretch(key.font.stretch)), - matches!(key.shaping, Shaping::Basic), + to_shaping(key.shaping), ); let _ = entry.insert(buffer); -- cgit From 9499a8f9e6f9971dedfae563cb133232aa3cebc2 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Thu, 4 May 2023 13:00:16 +0200 Subject: Support configurable `LineHeight` in text widgets --- tiny_skia/src/text.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 58079cc0..ba8a4e4b 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -1,7 +1,7 @@ use crate::core::alignment; use crate::core::font::{self, Font}; -use crate::core::text::{Hit, Shaping}; -use crate::core::{Color, Point, Rectangle, Size}; +use crate::core::text::{Hit, LineHeight, Shaping}; +use crate::core::{Color, Pixels, Point, Rectangle, Size}; use rustc_hash::{FxHashMap, FxHashSet}; use std::borrow::Cow; @@ -46,13 +46,21 @@ impl Pipeline { bounds: Rectangle, color: Color, size: f32, + line_height: LineHeight, font: Font, horizontal_alignment: alignment::Horizontal, vertical_alignment: alignment::Vertical, shaping: Shaping, + scale_factor: f32, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, ) { + let line_height = + f32::from(line_height.to_absolute(Pixels(size))) * scale_factor; + + let bounds = bounds * scale_factor; + let size = size * scale_factor; + let font_system = self.font_system.get_mut(); let key = Key { bounds: { @@ -64,6 +72,7 @@ impl Pipeline { content, font, size, + line_height, shaping, }; @@ -76,7 +85,7 @@ impl Pipeline { (i + 1, buffer.line_w.max(max)) }); - let total_height = total_lines as f32 * size * 1.2; + let total_height = total_lines as f32 * line_height; let x = match horizontal_alignment { alignment::Horizontal::Left => bounds.x, @@ -130,17 +139,21 @@ impl Pipeline { &self, content: &str, size: f32, + line_height: LineHeight, font: Font, bounds: Size, shaping: Shaping, ) -> (f32, f32) { let mut measurement_cache = self.measurement_cache.borrow_mut(); + let line_height = f32::from(line_height.to_absolute(Pixels(size))); + let (_, paragraph) = measurement_cache.allocate( &mut self.font_system.borrow_mut(), Key { content, size, + line_height, font, bounds, shaping, @@ -154,13 +167,14 @@ impl Pipeline { (i + 1, buffer.line_w.max(max)) }); - (max_width, size * 1.2 * total_lines as f32) + (max_width, line_height * total_lines as f32) } pub fn hit_test( &self, content: &str, size: f32, + line_height: LineHeight, font: Font, bounds: Size, shaping: Shaping, @@ -169,11 +183,14 @@ impl Pipeline { ) -> Option { let mut measurement_cache = self.measurement_cache.borrow_mut(); + let line_height = f32::from(line_height.to_absolute(Pixels(size))); + let (_, paragraph) = measurement_cache.allocate( &mut self.font_system.borrow_mut(), Key { content, size, + line_height, font, bounds, shaping, @@ -380,9 +397,11 @@ impl Cache { key.content.hash(&mut hasher); key.size.to_bits().hash(&mut hasher); + key.line_height.to_bits().hash(&mut hasher); key.font.hash(&mut hasher); key.bounds.width.to_bits().hash(&mut hasher); key.bounds.height.to_bits().hash(&mut hasher); + key.shaping.hash(&mut hasher); hasher.finish() }; @@ -432,6 +451,7 @@ impl Cache { struct Key<'a> { content: &'a str, size: f32, + line_height: f32, font: Font, bounds: Size, shaping: Shaping, -- cgit From c6d9221ee42b2838ca07b79df710d3d224e1c52a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 8 May 2023 16:20:05 +0200 Subject: Round paragraph position until we implement subpixel glyph positioning --- tiny_skia/src/text.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'tiny_skia/src/text.rs') diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index ba8a4e4b..a34c7317 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -99,6 +99,10 @@ impl Pipeline { alignment::Vertical::Bottom => bounds.y - total_height, }; + // TODO: Subpixel glyph positioning + let x = x.round() as i32; + let y = y.round() as i32; + let mut swash = cosmic_text::SwashCache::new(); for run in buffer.layout_runs() { @@ -117,9 +121,8 @@ impl Pipeline { .expect("Create glyph pixel map"); pixels.draw_pixmap( - x as i32 + glyph.x_int + placement.left, - y as i32 - glyph.y_int - placement.top - + run.line_y as i32, + x + glyph.x_int + placement.left, + y - glyph.y_int - placement.top + run.line_y as i32, pixmap, &tiny_skia::PixmapPaint::default(), tiny_skia::Transform::identity(), -- cgit