From 8129e2c208d9b13dbd32a309058b0547c723dede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Sun, 3 Sep 2023 08:08:27 +0200 Subject: [PATCH] Implement `draw_paragraph` in `iced_tiny_skia` --- tiny_skia/src/backend.rs | 2 +- tiny_skia/src/text.rs | 158 +++++++++++++++++++++++++-------------- wgpu/src/text.rs | 25 +++---- 3 files changed, 116 insertions(+), 69 deletions(-) diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index ef587bac5e..c721d96eaf 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -405,7 +405,7 @@ impl Backend { let clip_mask = (!physical_bounds.is_within(&clip_bounds)) .then_some(clip_mask as &_); - self.text_pipeline.draw( + self.text_pipeline.draw_cached( content, *bounds + translation, *color, diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 6047a82657..3f57f9b1c7 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -41,16 +41,34 @@ impl Pipeline { pub fn draw_paragraph( &mut self, - _paragraph: ¶graph::Weak, - _position: Point, - _color: Color, - _scale_factor: f32, - _pixels: &mut tiny_skia::PixmapMut<'_>, - _clip_mask: Option<&tiny_skia::Mask>, + paragraph: ¶graph::Weak, + position: Point, + color: Color, + scale_factor: f32, + pixels: &mut tiny_skia::PixmapMut<'_>, + clip_mask: Option<&tiny_skia::Mask>, ) { + use crate::core::text::Paragraph as _; + + let Some(paragraph) = paragraph.upgrade() else { + return; + }; + + draw( + &mut self.font_system.get_mut(), + &mut self.glyph_cache, + paragraph.buffer(), + Rectangle::new(position, paragraph.min_bounds()), + color, + paragraph.horizontal_alignment(), + paragraph.vertical_alignment(), + scale_factor, + pixels, + clip_mask, + ); } - pub fn draw( + pub fn draw_cached( &mut self, content: &str, bounds: Rectangle, @@ -79,54 +97,25 @@ impl Pipeline { let (_, entry) = self.cache.get_mut().allocate(font_system, key); - let max_width = entry.min_bounds.width * scale_factor; - let total_height = entry.min_bounds.height * scale_factor; - - let bounds = bounds * scale_factor; - - 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 entry.buffer.layout_runs() { - for glyph in run.glyphs { - let physical_glyph = glyph.physical((x, y), scale_factor); - - if let Some((buffer, placement)) = self.glyph_cache.allocate( - physical_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( - physical_glyph.x + placement.left, - physical_glyph.y - placement.top - + (run.line_y * scale_factor).round() as i32, - pixmap, - &tiny_skia::PixmapPaint::default(), - tiny_skia::Transform::identity(), - clip_mask, - ); - } - } - } + let width = entry.min_bounds.width; + let height = entry.min_bounds.height; + + draw( + font_system, + &mut self.glyph_cache, + &entry.buffer, + Rectangle { + width, + height, + ..bounds + }, + color, + horizontal_alignment, + vertical_alignment, + scale_factor, + pixels, + clip_mask, + ); } pub fn trim_cache(&mut self) { @@ -135,6 +124,65 @@ impl Pipeline { } } +fn draw( + font_system: &mut cosmic_text::FontSystem, + glyph_cache: &mut GlyphCache, + buffer: &cosmic_text::Buffer, + bounds: Rectangle, + color: Color, + horizontal_alignment: alignment::Horizontal, + vertical_alignment: alignment::Vertical, + scale_factor: f32, + pixels: &mut tiny_skia::PixmapMut<'_>, + clip_mask: Option<&tiny_skia::Mask>, +) { + let bounds = bounds * scale_factor; + + let x = match horizontal_alignment { + alignment::Horizontal::Left => bounds.x, + alignment::Horizontal::Center => bounds.x - bounds.width / 2.0, + alignment::Horizontal::Right => bounds.x - bounds.width, + }; + + let y = match vertical_alignment { + alignment::Vertical::Top => bounds.y, + alignment::Vertical::Center => bounds.y - bounds.height / 2.0, + alignment::Vertical::Bottom => bounds.y - bounds.height, + }; + + let mut swash = cosmic_text::SwashCache::new(); + + for run in buffer.layout_runs() { + for glyph in run.glyphs { + let physical_glyph = glyph.physical((x, y), scale_factor); + + if let Some((buffer, placement)) = glyph_cache.allocate( + physical_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( + physical_glyph.x + placement.left, + physical_glyph.y - placement.top + + (run.line_y * scale_factor).round() as i32, + pixmap, + &tiny_skia::PixmapPaint::default(), + tiny_skia::Transform::identity(), + clip_mask, + ); + } + } + } +} + #[derive(Debug, Clone, Default)] struct GlyphCache { entries: FxHashMap< diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index ee3523682c..a1ec511b1f 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -159,29 +159,28 @@ impl Pipeline { } }; - let x = bounds.x * scale_factor; - let y = bounds.y * scale_factor; - - let max_width = bounds.width * scale_factor; - let total_height = bounds.height * scale_factor; + let bounds = bounds * scale_factor; let left = match horizontal_alignment { - alignment::Horizontal::Left => x, - alignment::Horizontal::Center => x - max_width / 2.0, - alignment::Horizontal::Right => x - max_width, + alignment::Horizontal::Left => bounds.x, + alignment::Horizontal::Center => { + bounds.x - bounds.width / 2.0 + } + alignment::Horizontal::Right => bounds.x - bounds.width, }; let top = match vertical_alignment { - alignment::Vertical::Top => y, - alignment::Vertical::Center => y - total_height / 2.0, - alignment::Vertical::Bottom => y - total_height, + alignment::Vertical::Top => bounds.y, + alignment::Vertical::Center => { + bounds.y - bounds.height / 2.0 + } + alignment::Vertical::Bottom => bounds.y - bounds.height, }; let section_bounds = Rectangle { x: left, y: top, - width: max_width, - height: total_height, + ..bounds }; let clip_bounds = layer_bounds.intersection(§ion_bounds)?;