diff options
author | 2024-01-17 14:25:39 +0100 | |
---|---|---|
committer | 2024-01-17 14:25:39 +0100 | |
commit | dd032d9a7a73dc28c12802e1e702d0aebe92e261 (patch) | |
tree | e59415803449592de9869a47c7fd0791dfedfd75 /wgpu/src/geometry.rs | |
parent | d09f36e054b00cad206431654392fc68ba2b345b (diff) | |
download | iced-dd032d9a7a73dc28c12802e1e702d0aebe92e261.tar.gz iced-dd032d9a7a73dc28c12802e1e702d0aebe92e261.tar.bz2 iced-dd032d9a7a73dc28c12802e1e702d0aebe92e261.zip |
Implement vectorial text support for `iced_wgpu`
Diffstat (limited to 'wgpu/src/geometry.rs')
-rw-r--r-- | wgpu/src/geometry.rs | 225 |
1 files changed, 188 insertions, 37 deletions
diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index 04718441..a1583a07 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -1,6 +1,7 @@ //! Build and draw geometry. +use crate::core::alignment; use crate::core::text::LineHeight; -use crate::core::{Pixels, Point, Rectangle, Size, Vector}; +use crate::core::{Color, Pixels, Point, Rectangle, Size, Vector}; use crate::graphics::color; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::{ @@ -8,6 +9,7 @@ use crate::graphics::geometry::{ }; use crate::graphics::gradient::{self, Gradient}; use crate::graphics::mesh::{self, Mesh}; +use crate::graphics::text::{self, cosmic_text}; use crate::primitive::{self, Primitive}; use lyon::geom::euclid; @@ -123,8 +125,13 @@ impl Transform { self.0 == lyon::math::Transform::identity() } + fn is_scale_translation(&self) -> bool { + self.0.m12.abs() < 2.0 * f32::EPSILON + && self.0.m21.abs() < 2.0 * f32::EPSILON + } + fn scale(&self) -> (f32, f32) { - (self.0.m12, self.0.m22) + (self.0.m11, self.0.m22) } fn transform_point(&self, point: Point) -> Point { @@ -324,49 +331,193 @@ impl Frame { pub fn fill_text(&mut self, text: impl Into<Text>) { let text = text.into(); - let (position, size, line_height) = - if self.transforms.current.is_identity() { - (text.position, text.size, text.line_height) - } else { - let (_, scale_y) = self.transforms.current.scale(); + let (scale_x, scale_y) = self.transforms.current.scale(); + + if self.transforms.current.is_scale_translation() + && scale_x == scale_y + && scale_x > 0.0 + && scale_y > 0.0 + { + let (position, size, line_height) = + if self.transforms.current.is_identity() { + (text.position, text.size, text.line_height) + } else { + let position = + self.transforms.current.transform_point(text.position); + + let size = Pixels(text.size.0 * scale_y); + + let line_height = match text.line_height { + LineHeight::Absolute(size) => { + LineHeight::Absolute(Pixels(size.0 * scale_y)) + } + LineHeight::Relative(factor) => { + LineHeight::Relative(factor) + } + }; + + (position, size, line_height) + }; - let position = - self.transforms.current.transform_point(text.position); + let bounds = Rectangle { + x: position.x, + y: position.y, + width: f32::INFINITY, + height: f32::INFINITY, + }; - let size = Pixels(text.size.0 * scale_y); + // TODO: Honor layering! + self.primitives.push(Primitive::Text { + content: text.content, + bounds, + color: text.color, + size, + line_height, + font: text.font, + horizontal_alignment: text.horizontal_alignment, + vertical_alignment: text.vertical_alignment, + shaping: text.shaping, + clip_bounds: Rectangle::with_size(Size::INFINITY), + }); + } else { + let mut font_system = + text::font_system().write().expect("Write font system"); - let line_height = match text.line_height { - LineHeight::Absolute(size) => { - LineHeight::Absolute(Pixels(size.0 * scale_y)) - } - LineHeight::Relative(factor) => { - LineHeight::Relative(factor) + let mut buffer = cosmic_text::BufferLine::new( + &text.content, + cosmic_text::AttrsList::new(text::to_attributes(text.font)), + text::to_shaping(text.shaping), + ); + + let layout = buffer.layout( + font_system.raw(), + text.size.0, + f32::MAX, + cosmic_text::Wrap::None, + ); + + let translation_x = match text.horizontal_alignment { + alignment::Horizontal::Left => text.position.x, + alignment::Horizontal::Center + | alignment::Horizontal::Right => { + let mut line_width = 0.0f32; + + for line in layout.iter() { + line_width = line_width.max(line.w); } - }; - (position, size, line_height) + if text.horizontal_alignment + == alignment::Horizontal::Center + { + text.position.x - line_width / 2.0 + } else { + text.position.x - line_width + } + } }; - let bounds = Rectangle { - x: position.x, - y: position.y, - width: f32::INFINITY, - height: f32::INFINITY, - }; + let translation_y = { + let line_height = text.line_height.to_absolute(text.size); - // TODO: Use vectorial text instead of primitive - self.primitives.push(Primitive::Text { - content: text.content, - bounds, - color: text.color, - size, - line_height, - font: text.font, - horizontal_alignment: text.horizontal_alignment, - vertical_alignment: text.vertical_alignment, - shaping: text.shaping, - clip_bounds: Rectangle::with_size(Size::INFINITY), - }); + match text.vertical_alignment { + alignment::Vertical::Top => text.position.y, + alignment::Vertical::Center => { + text.position.y - line_height.0 / 2.0 + } + alignment::Vertical::Bottom => { + text.position.y - line_height.0 + } + } + }; + + let mut swash_cache = cosmic_text::SwashCache::new(); + + for run in layout.iter() { + for glyph in run.glyphs.iter() { + let physical_glyph = glyph.physical((0.0, 0.0), 1.0); + + let start_x = translation_x + glyph.x + glyph.x_offset; + let start_y = translation_y + glyph.y_offset + text.size.0; + let offset = Vector::new(start_x, start_y); + + if let Some(commands) = swash_cache.get_outline_commands( + font_system.raw(), + physical_glyph.cache_key, + ) { + let glyph = Path::new(|path| { + use cosmic_text::Command; + + for command in commands { + match command { + Command::MoveTo(p) => { + path.move_to( + Point::new(p.x, -p.y) + offset, + ); + } + Command::LineTo(p) => { + path.line_to( + Point::new(p.x, -p.y) + offset, + ); + } + Command::CurveTo( + control_a, + control_b, + to, + ) => { + path.bezier_curve_to( + Point::new( + control_a.x, + -control_a.y, + ) + offset, + Point::new( + control_b.x, + -control_b.y, + ) + offset, + Point::new(to.x, -to.y) + offset, + ); + } + Command::QuadTo(control, to) => { + path.quadratic_curve_to( + Point::new(control.x, -control.y) + + offset, + Point::new(to.x, -to.y) + offset, + ); + } + Command::Close => { + path.close(); + } + } + } + }); + + self.fill(&glyph, text.color); + } else { + // TODO: Raster image support for `Canvas` + let [r, g, b, a] = text.color.into_rgba8(); + + swash_cache.with_pixels( + font_system.raw(), + physical_glyph.cache_key, + cosmic_text::Color::rgba(r, g, b, a), + |x, y, color| { + self.fill( + &Path::rectangle( + Point::new(x as f32, y as f32) + offset, + Size::new(1.0, 1.0), + ), + Color::from_rgba8( + color.r(), + color.g(), + color.b(), + color.a() as f32 / 255.0, + ), + ); + }, + ) + } + } + } + } } /// Stores the current transform of the [`Frame`] and executes the given |