From 5467c19c80c992f03890264ed58156305a26b19a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 23 Oct 2023 03:13:28 +0200 Subject: Replace `Primitive::Translate` with `Transform` --- graphics/src/damage.rs | 18 ++++----- graphics/src/primitive.rs | 32 ++++++++++----- graphics/src/transformation.rs | 80 ++++++++++++++++++++++++++++++++++--- tiny_skia/src/backend.rs | 91 +++++++++++++++++++++--------------------- tiny_skia/src/geometry.rs | 6 +-- wgpu/src/geometry.rs | 11 ++--- wgpu/src/layer.rs | 72 ++++++++++++++------------------- wgpu/src/layer/mesh.rs | 18 ++++----- wgpu/src/layer/text.rs | 2 + wgpu/src/quad.rs | 2 +- wgpu/src/triangle.rs | 6 +-- 11 files changed, 205 insertions(+), 133 deletions(-) diff --git a/graphics/src/damage.rs b/graphics/src/damage.rs index ba9192ef..8edf69d7 100644 --- a/graphics/src/damage.rs +++ b/graphics/src/damage.rs @@ -102,10 +102,10 @@ impl Damage for Primitive { .fold(Rectangle::with_size(Size::ZERO), |a, b| { Rectangle::union(&a, &b) }), - Self::Translate { - translation, + Self::Transform { + transformation, content, - } => content.bounds() + *translation, + } => content.bounds() * *transformation, Self::Cache { content } => content.bounds(), Self::Custom(custom) => custom.bounds(), } @@ -144,19 +144,19 @@ fn regions(a: &Primitive, b: &Primitive) -> Vec { } } ( - Primitive::Translate { - translation: translation_a, + Primitive::Transform { + transformation: transformation_a, content: content_a, }, - Primitive::Translate { - translation: translation_b, + Primitive::Transform { + transformation: transformation_b, content: content_b, }, ) => { - if translation_a == translation_b { + if transformation_a == transformation_b { return regions(content_a, content_b) .into_iter() - .map(|r| r + *translation_a) + .map(|r| r * *transformation_a) .collect(); } } diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index aed59e1a..32008698 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -8,6 +8,7 @@ use crate::core::{ }; use crate::text::editor; use crate::text::paragraph; +use crate::Transformation; use std::sync::Arc; @@ -104,12 +105,12 @@ pub enum Primitive { /// The content of the clip content: Box>, }, - /// A primitive that applies a translation - Translate { - /// The translation vector - translation: Vector, + /// A primitive that applies a [`Transformation`] + Transform { + /// The [`Transformation`] + transformation: Transformation, - /// The primitive to translate + /// The primitive to transform content: Box>, }, /// A cached primitive. @@ -125,12 +126,12 @@ pub enum Primitive { } impl Primitive { - /// Creates a [`Primitive::Group`]. + /// Groups the current [`Primitive`]. pub fn group(primitives: Vec) -> Self { Self::Group { primitives } } - /// Creates a [`Primitive::Clip`]. + /// Clips the current [`Primitive`]. pub fn clip(self, bounds: Rectangle) -> Self { Self::Clip { bounds, @@ -138,10 +139,21 @@ impl Primitive { } } - /// Creates a [`Primitive::Translate`]. + /// Translates the current [`Primitive`]. pub fn translate(self, translation: Vector) -> Self { - Self::Translate { - translation, + Self::Transform { + transformation: Transformation::translate( + translation.x, + translation.y, + ), + content: Box::new(self), + } + } + + /// Transforms the current [`Primitive`]. + pub fn transform(self, transformation: Transformation) -> Self { + Self::Transform { + transformation, content: Box::new(self), } } diff --git a/graphics/src/transformation.rs b/graphics/src/transformation.rs index cf0457a4..1aeb691e 100644 --- a/graphics/src/transformation.rs +++ b/graphics/src/transformation.rs @@ -1,4 +1,6 @@ -use glam::{Mat4, Vec3}; +use crate::core::{Point, Rectangle, Size, Vector}; + +use glam::{Mat4, Vec3, Vec4}; use std::ops::Mul; /// A 2D transformation matrix. @@ -6,10 +8,8 @@ use std::ops::Mul; pub struct Transformation(Mat4); impl Transformation { - /// Get the identity transformation. - pub fn identity() -> Transformation { - Transformation(Mat4::IDENTITY) - } + /// A [`Transformation`] that preserves whatever is transformed. + pub const IDENTITY: Self = Self(Mat4::IDENTITY); /// Creates an orthographic projection. #[rustfmt::skip] @@ -30,6 +30,26 @@ impl Transformation { pub fn scale(x: f32, y: f32) -> Transformation { Transformation(Mat4::from_scale(Vec3::new(x, y, 1.0))) } + + /// The scale factor on the X axis. + pub fn scale_x(&self) -> f32 { + self.0.x_axis.x + } + + /// The scale factor on the Y axis. + pub fn scale_y(&self) -> f32 { + self.0.y_axis.y + } + + /// The translation on the X axis. + pub fn translation_x(&self) -> f32 { + self.0.w_axis.x + } + + /// The translation on the Y axis. + pub fn translation_y(&self) -> f32 { + self.0.w_axis.y + } } impl Mul for Transformation { @@ -40,6 +60,56 @@ impl Mul for Transformation { } } +impl Mul for Point { + type Output = Self; + + fn mul(self, transformation: Transformation) -> Self { + let point = transformation + .0 + .mul_vec4(Vec4::new(self.x, self.y, 1.0, 1.0)); + + Point::new(point.x, point.y) + } +} + +impl Mul for Vector { + type Output = Self; + + fn mul(self, transformation: Transformation) -> Self { + let new_vector = transformation + .0 + .mul_vec4(Vec4::new(self.x, self.y, 1.0, 0.0)); + + Vector::new(new_vector.x, new_vector.y) + } +} + +impl Mul for Size { + type Output = Self; + + fn mul(self, transformation: Transformation) -> Self { + let new_size = transformation.0.mul_vec4(Vec4::new( + self.width, + self.height, + 1.0, + 0.0, + )); + + Size::new(new_size.x, new_size.y) + } +} + +impl Mul for Rectangle { + type Output = Self; + + fn mul(self, transformation: Transformation) -> Self { + let position = self.position(); + let size = self.size(); + + Self::new(position * transformation, size * transformation) + } +} + impl AsRef<[f32; 16]> for Transformation { fn as_ref(&self) -> &[f32; 16] { self.0.as_ref() diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 468bcb7e..87007a51 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -3,7 +3,7 @@ use tiny_skia::Size; use crate::core::{Background, Color, Gradient, Rectangle, Vector}; use crate::graphics::backend; use crate::graphics::text; -use crate::graphics::Viewport; +use crate::graphics::{Transformation, Viewport}; use crate::primitive::{self, Primitive}; use std::borrow::Cow; @@ -106,7 +106,7 @@ impl Backend { clip_mask, region, scale_factor, - Vector::ZERO, + Transformation::IDENTITY, ); } @@ -146,7 +146,7 @@ impl Backend { clip_mask: &mut tiny_skia::Mask, clip_bounds: Rectangle, scale_factor: f32, - translation: Vector, + transformation: Transformation, ) { match primitive { Primitive::Quad { @@ -164,7 +164,7 @@ impl Backend { "Quad with non-normal height!" ); - let physical_bounds = (*bounds + translation) * scale_factor; + let physical_bounds = (*bounds * transformation) * scale_factor; if !clip_bounds.intersects(&physical_bounds) { return; @@ -173,11 +173,8 @@ impl Backend { let clip_mask = (!physical_bounds.is_within(&clip_bounds)) .then_some(clip_mask as &_); - let transform = tiny_skia::Transform::from_translate( - translation.x, - translation.y, - ) - .post_scale(scale_factor, scale_factor); + let transform = into_transform(transformation) + .post_scale(scale_factor, scale_factor); // Make sure the border radius is not larger than the bounds let border_width = border @@ -199,7 +196,7 @@ impl Backend { y: bounds.y + shadow.offset.y - shadow.blur_radius, width: bounds.width + shadow.blur_radius * 2.0, height: bounds.height + shadow.blur_radius * 2.0, - } + translation) + } * transformation) * scale_factor; let radii = fill_border_radius @@ -451,7 +448,7 @@ impl Backend { clip_bounds: text_clip_bounds, } => { let physical_bounds = - (*text_clip_bounds + translation) * scale_factor; + *text_clip_bounds * transformation * scale_factor; if !clip_bounds.intersects(&physical_bounds) { return; @@ -462,7 +459,7 @@ impl Backend { self.text_pipeline.draw_paragraph( paragraph, - *position + translation, + *position * transformation, *color, scale_factor, pixels, @@ -476,7 +473,7 @@ impl Backend { clip_bounds: text_clip_bounds, } => { let physical_bounds = - (*text_clip_bounds + translation) * scale_factor; + (*text_clip_bounds * transformation) * scale_factor; if !clip_bounds.intersects(&physical_bounds) { return; @@ -487,7 +484,7 @@ impl Backend { self.text_pipeline.draw_editor( editor, - *position + translation, + *position * transformation, *color, scale_factor, pixels, @@ -507,7 +504,7 @@ impl Backend { clip_bounds: text_clip_bounds, } => { let physical_bounds = - (*text_clip_bounds + translation) * scale_factor; + *text_clip_bounds * transformation * scale_factor; if !clip_bounds.intersects(&physical_bounds) { return; @@ -518,7 +515,7 @@ impl Backend { self.text_pipeline.draw_cached( content, - *bounds + translation, + *bounds * transformation, *color, *size, *line_height, @@ -542,7 +539,7 @@ impl Backend { }; let physical_bounds = - (*text_clip_bounds + translation) * scale_factor; + *text_clip_bounds * transformation * scale_factor; if !clip_bounds.intersects(&physical_bounds) { return; @@ -553,7 +550,7 @@ impl Backend { self.text_pipeline.draw_raw( &buffer, - *position + translation, + *position * transformation, *color, scale_factor, pixels, @@ -566,7 +563,7 @@ impl Backend { filter_method, bounds, } => { - let physical_bounds = (*bounds + translation) * scale_factor; + let physical_bounds = (*bounds * transformation) * scale_factor; if !clip_bounds.intersects(&physical_bounds) { return; @@ -575,11 +572,8 @@ impl Backend { let clip_mask = (!physical_bounds.is_within(&clip_bounds)) .then_some(clip_mask as &_); - let transform = tiny_skia::Transform::from_translate( - translation.x, - translation.y, - ) - .post_scale(scale_factor, scale_factor); + let transform = into_transform(transformation) + .post_scale(scale_factor, scale_factor); self.raster_pipeline.draw( handle, @@ -602,7 +596,7 @@ impl Backend { bounds, color, } => { - let physical_bounds = (*bounds + translation) * scale_factor; + let physical_bounds = (*bounds * transformation) * scale_factor; if !clip_bounds.intersects(&physical_bounds) { return; @@ -614,7 +608,7 @@ impl Backend { self.vector_pipeline.draw( handle, *color, - (*bounds + translation) * scale_factor, + (*bounds * transformation) * scale_factor, pixels, clip_mask, ); @@ -637,7 +631,7 @@ impl Backend { y: bounds.y(), width: bounds.width(), height: bounds.height(), - } + translation) + } * transformation) * scale_factor; if !clip_bounds.intersects(&physical_bounds) { @@ -651,11 +645,8 @@ impl Backend { path, paint, *rule, - tiny_skia::Transform::from_translate( - translation.x, - translation.y, - ) - .post_scale(scale_factor, scale_factor), + into_transform(transformation) + .post_scale(scale_factor, scale_factor), clip_mask, ); } @@ -671,7 +662,7 @@ impl Backend { y: bounds.y(), width: bounds.width(), height: bounds.height(), - } + translation) + } * transformation) * scale_factor; if !clip_bounds.intersects(&physical_bounds) { @@ -685,11 +676,8 @@ impl Backend { path, paint, stroke, - tiny_skia::Transform::from_translate( - translation.x, - translation.y, - ) - .post_scale(scale_factor, scale_factor), + into_transform(transformation) + .post_scale(scale_factor, scale_factor), clip_mask, ); } @@ -701,12 +689,12 @@ impl Backend { clip_mask, clip_bounds, scale_factor, - translation, + transformation, ); } } - Primitive::Translate { - translation: offset, + Primitive::Transform { + transformation: new_transformation, content, } => { self.draw_primitive( @@ -715,11 +703,11 @@ impl Backend { clip_mask, clip_bounds, scale_factor, - translation + *offset, + transformation * *new_transformation, ); } Primitive::Clip { bounds, content } => { - let bounds = (*bounds + translation) * scale_factor; + let bounds = (*bounds * transformation) * scale_factor; if bounds == clip_bounds { self.draw_primitive( @@ -728,7 +716,7 @@ impl Backend { clip_mask, bounds, scale_factor, - translation, + transformation, ); } else if let Some(bounds) = clip_bounds.intersection(&bounds) { if bounds.x + bounds.width <= 0.0 @@ -749,7 +737,7 @@ impl Backend { clip_mask, bounds, scale_factor, - translation, + transformation, ); adjust_clip_mask(clip_mask, clip_bounds); @@ -762,7 +750,7 @@ impl Backend { clip_mask, clip_bounds, scale_factor, - translation, + transformation, ); } } @@ -780,6 +768,17 @@ fn into_color(color: Color) -> tiny_skia::Color { .expect("Convert color from iced to tiny_skia") } +fn into_transform(transformation: Transformation) -> tiny_skia::Transform { + tiny_skia::Transform { + sx: transformation.scale_x(), + kx: 0.0, + ky: 0.0, + sy: transformation.scale_y(), + tx: transformation.translation_x(), + ty: transformation.translation_y(), + } +} + fn rounded_rectangle( bounds: Rectangle, border_radius: [f32; 4], diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index 74a08d38..579c153a 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -3,7 +3,7 @@ use crate::core::{Pixels, Point, Rectangle, Size, Vector}; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::stroke::{self, Stroke}; use crate::graphics::geometry::{Path, Style, Text}; -use crate::graphics::Gradient; +use crate::graphics::{Gradient, Transformation}; use crate::primitive::{self, Primitive}; pub struct Frame { @@ -181,8 +181,8 @@ impl Frame { } pub fn clip(&mut self, frame: Self, at: Point) { - self.primitives.push(Primitive::Translate { - translation: Vector::new(at.x, at.y), + self.primitives.push(Primitive::Transform { + transformation: Transformation::translate(at.x, at.y), content: Box::new(frame.into_primitive()), }); } diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index a4d4fb1f..d0660edc 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -8,6 +8,7 @@ use crate::graphics::geometry::{ }; use crate::graphics::gradient::{self, Gradient}; use crate::graphics::mesh::{self, Mesh}; +use crate::graphics::Transformation; use crate::primitive::{self, Primitive}; use lyon::geom::euclid; @@ -435,7 +436,7 @@ impl Frame { pub fn clip(&mut self, frame: Frame, at: Point) { let size = frame.size(); let primitives = frame.into_primitives(); - let translation = Vector::new(at.x, at.y); + let transformation = Transformation::translate(at.x, at.y); let (text, meshes) = primitives .into_iter() @@ -443,12 +444,12 @@ impl Frame { self.primitives.push(Primitive::Group { primitives: vec![ - Primitive::Translate { - translation, + Primitive::Transform { + transformation, content: Box::new(Primitive::Group { primitives: meshes }), }, - Primitive::Translate { - translation, + Primitive::Transform { + transformation, content: Box::new(Primitive::Clip { bounds: Rectangle::with_size(size), content: Box::new(Primitive::Group { diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index e213c95f..fd5f2345 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -15,7 +15,7 @@ use crate::core::alignment; use crate::core::{Color, Font, Pixels, Point, Rectangle, Size, Vector}; use crate::graphics; use crate::graphics::color; -use crate::graphics::Viewport; +use crate::graphics::{Transformation, Viewport}; use crate::primitive::{self, Primitive}; use crate::quad::{self, Quad}; @@ -104,7 +104,7 @@ impl<'a> Layer<'a> { for primitive in primitives { Self::process_primitive( &mut layers, - Vector::new(0.0, 0.0), + Transformation::IDENTITY, primitive, 0, ); @@ -115,7 +115,7 @@ impl<'a> Layer<'a> { fn process_primitive( layers: &mut Vec, - translation: Vector, + transformation: Transformation, primitive: &'a Primitive, current_layer: usize, ) { @@ -130,9 +130,10 @@ impl<'a> Layer<'a> { layer.text.push(Text::Paragraph { paragraph: paragraph.clone(), - position: *position + translation, + position: *position * transformation, color: *color, - clip_bounds: *clip_bounds + translation, + clip_bounds: *clip_bounds * transformation, + scale: transformation.scale_y(), }); } Primitive::Editor { @@ -145,9 +146,10 @@ impl<'a> Layer<'a> { layer.text.push(Text::Editor { editor: editor.clone(), - position: *position + translation, + position: *position * transformation, color: *color, - clip_bounds: *clip_bounds + translation, + clip_bounds: *clip_bounds * transformation, + scale: transformation.scale_y(), }); } Primitive::Text { @@ -166,7 +168,7 @@ impl<'a> Layer<'a> { layer.text.push(Text::Cached(text::Cached { content, - bounds: *bounds + translation, + bounds: *bounds * transformation, size: *size, line_height: *line_height, color: *color, @@ -174,7 +176,7 @@ impl<'a> Layer<'a> { horizontal_alignment: *horizontal_alignment, vertical_alignment: *vertical_alignment, shaping: *shaping, - clip_bounds: *clip_bounds + translation, + clip_bounds: *clip_bounds * transformation, })); } graphics::Primitive::RawText(graphics::text::Raw { @@ -187,9 +189,9 @@ impl<'a> Layer<'a> { layer.text.push(Text::Raw(graphics::text::Raw { buffer: buffer.clone(), - position: *position + translation, + position: *position * transformation, color: *color, - clip_bounds: *clip_bounds + translation, + clip_bounds: *clip_bounds * transformation, })); } Primitive::Quad { @@ -199,12 +201,10 @@ impl<'a> Layer<'a> { shadow, } => { let layer = &mut layers[current_layer]; + let bounds = *bounds * transformation; let quad = Quad { - position: [ - bounds.x + translation.x, - bounds.y + translation.y, - ], + position: [bounds.x, bounds.y], size: [bounds.width, bounds.height], border_color: color::pack(border.color), border_radius: border.radius.into(), @@ -226,7 +226,7 @@ impl<'a> Layer<'a> { layer.images.push(Image::Raster { handle: handle.clone(), filter_method: *filter_method, - bounds: *bounds + translation, + bounds: *bounds * transformation, }); } Primitive::Svg { @@ -239,7 +239,7 @@ impl<'a> Layer<'a> { layer.images.push(Image::Vector { handle: handle.clone(), color: *color, - bounds: *bounds + translation, + bounds: *bounds * transformation, }); } Primitive::Group { primitives } => { @@ -247,7 +247,7 @@ impl<'a> Layer<'a> { for primitive in primitives { Self::process_primitive( layers, - translation, + transformation, primitive, current_layer, ); @@ -255,7 +255,7 @@ impl<'a> Layer<'a> { } Primitive::Clip { bounds, content } => { let layer = &mut layers[current_layer]; - let translated_bounds = *bounds + translation; + let translated_bounds = *bounds * transformation; // Only draw visible content if let Some(clip_bounds) = @@ -266,19 +266,19 @@ impl<'a> Layer<'a> { Self::process_primitive( layers, - translation, + transformation, content, layers.len() - 1, ); } } - Primitive::Translate { - translation: new_translation, + Primitive::Transform { + transformation: new_transformation, content, } => { Self::process_primitive( layers, - translation + *new_translation, + transformation * *new_transformation, content, current_layer, ); @@ -286,7 +286,7 @@ impl<'a> Layer<'a> { Primitive::Cache { content } => { Self::process_primitive( layers, - translation, + transformation, content, current_layer, ); @@ -296,20 +296,15 @@ impl<'a> Layer<'a> { graphics::Mesh::Solid { buffers, size } => { let layer = &mut layers[current_layer]; - let bounds = Rectangle::new( - Point::new(translation.x, translation.y), - *size, - ); + let bounds = + Rectangle::with_size(*size) * transformation; // Only draw visible content if let Some(clip_bounds) = layer.bounds.intersection(&bounds) { layer.meshes.push(Mesh::Solid { - origin: Point::new( - translation.x, - translation.y, - ), + transformation, buffers, clip_bounds, }); @@ -318,20 +313,15 @@ impl<'a> Layer<'a> { graphics::Mesh::Gradient { buffers, size } => { let layer = &mut layers[current_layer]; - let bounds = Rectangle::new( - Point::new(translation.x, translation.y), - *size, - ); + let bounds = + Rectangle::with_size(*size) * transformation; // Only draw visible content if let Some(clip_bounds) = layer.bounds.intersection(&bounds) { layer.meshes.push(Mesh::Gradient { - origin: Point::new( - translation.x, - translation.y, - ), + transformation, buffers, clip_bounds, }); @@ -340,7 +330,7 @@ impl<'a> Layer<'a> { }, primitive::Custom::Pipeline(pipeline) => { let layer = &mut layers[current_layer]; - let bounds = pipeline.bounds + translation; + let bounds = pipeline.bounds * transformation; if let Some(clip_bounds) = layer.bounds.intersection(&bounds) diff --git a/wgpu/src/layer/mesh.rs b/wgpu/src/layer/mesh.rs index 7c6206cd..af3dc74a 100644 --- a/wgpu/src/layer/mesh.rs +++ b/wgpu/src/layer/mesh.rs @@ -1,14 +1,15 @@ //! A collection of triangle primitives. -use crate::core::{Point, Rectangle}; +use crate::core::Rectangle; use crate::graphics::mesh; +use crate::graphics::Transformation; /// A mesh of triangles. #[derive(Debug, Clone, Copy)] pub enum Mesh<'a> { /// A mesh of triangles with a solid color. Solid { - /// The origin of the vertices of the [`Mesh`]. - origin: Point, + /// The [`Transformation`] for the vertices of the [`Mesh`]. + transformation: Transformation, /// The vertex and index buffers of the [`Mesh`]. buffers: &'a mesh::Indexed, @@ -18,8 +19,8 @@ pub enum Mesh<'a> { }, /// A mesh of triangles with a gradient color. Gradient { - /// The origin of the vertices of the [`Mesh`]. - origin: Point, + /// The [`Transformation`] for the vertices of the [`Mesh`]. + transformation: Transformation, /// The vertex and index buffers of the [`Mesh`]. buffers: &'a mesh::Indexed, @@ -31,11 +32,10 @@ pub enum Mesh<'a> { impl Mesh<'_> { /// Returns the origin of the [`Mesh`]. - pub fn origin(&self) -> Point { + pub fn transformation(&self) -> Transformation { match self { - Self::Solid { origin, .. } | Self::Gradient { origin, .. } => { - *origin - } + Self::Solid { transformation, .. } + | Self::Gradient { transformation, .. } => *transformation, } } diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs index 37ee5247..2a09aecc 100644 --- a/wgpu/src/layer/text.rs +++ b/wgpu/src/layer/text.rs @@ -15,6 +15,7 @@ pub enum Text<'a> { position: Point, color: Color, clip_bounds: Rectangle, + scale: f32, }, /// An editor. #[allow(missing_docs)] @@ -23,6 +24,7 @@ pub enum Text<'a> { position: Point, color: Color, clip_bounds: Rectangle, + scale: f32, }, /// Some cached text. Cached(Cached<'a>), diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index cda1bec9..5a45187e 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -319,7 +319,7 @@ impl Uniforms { impl Default for Uniforms { fn default() -> Self { Self { - transform: *Transformation::identity().as_ref(), + transform: *Transformation::IDENTITY.as_ref(), scale: 1.0, _padding: [0.0; 3], } diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 69270a73..28a7b1b3 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -98,12 +98,10 @@ impl Layer { let mut index_offset = 0; for mesh in meshes { - let origin = mesh.origin(); let indices = mesh.indices(); - let uniforms = Uniforms::new( - transformation * Transformation::translate(origin.x, origin.y), - ); + let uniforms = + Uniforms::new(transformation * mesh.transformation()); index_offset += self.index_buffer.write(queue, index_offset, indices); -- cgit From 9b8614a4e2252f0b74d1a1b38b5e5bb55b1af995 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Mon, 23 Oct 2023 03:14:22 +0200 Subject: Implement `transform` method for `Geometry` --- examples/bezier_tool/Cargo.toml | 2 +- renderer/src/geometry.rs | 35 +++++++++++++++++++++++++---------- widget/src/canvas.rs | 1 + 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/examples/bezier_tool/Cargo.toml b/examples/bezier_tool/Cargo.toml index b2547ff1..e5624097 100644 --- a/examples/bezier_tool/Cargo.toml +++ b/examples/bezier_tool/Cargo.toml @@ -7,4 +7,4 @@ publish = false [dependencies] iced.workspace = true -iced.features = ["canvas"] +iced.features = ["canvas", "debug"] diff --git a/renderer/src/geometry.rs b/renderer/src/geometry.rs index cd2140dc..9cf506a8 100644 --- a/renderer/src/geometry.rs +++ b/renderer/src/geometry.rs @@ -4,12 +4,17 @@ pub use cache::Cache; use crate::core::{Point, Rectangle, Size, Vector}; use crate::graphics::geometry::{Fill, Path, Stroke, Text}; +use crate::graphics::Transformation; use crate::Renderer; -pub enum Frame { - TinySkia(iced_tiny_skia::geometry::Frame), - #[cfg(feature = "wgpu")] - Wgpu(iced_wgpu::geometry::Frame), +macro_rules! delegate { + ($frame:expr, $name:ident, $body:expr) => { + match $frame { + Self::TinySkia($name) => $body, + #[cfg(feature = "wgpu")] + Self::Wgpu($name) => $body, + } + }; } pub enum Geometry { @@ -18,14 +23,24 @@ pub enum Geometry { Wgpu(iced_wgpu::Primitive), } -macro_rules! delegate { - ($frame:expr, $name:ident, $body:expr) => { - match $frame { - Self::TinySkia($name) => $body, +impl Geometry { + pub fn transform(self, transformation: Transformation) -> Self { + match self { + Self::TinySkia(primitive) => { + Self::TinySkia(primitive.transform(transformation)) + } #[cfg(feature = "wgpu")] - Self::Wgpu($name) => $body, + Self::Wgpu(primitive) => { + Self::Wgpu(primitive.transform(transformation)) + } } - }; + } +} + +pub enum Frame { + TinySkia(iced_tiny_skia::geometry::Frame), + #[cfg(feature = "wgpu")] + Wgpu(iced_wgpu::geometry::Frame), } impl Frame { diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index b95e6206..3e4a0928 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -7,6 +7,7 @@ pub use event::Event; pub use program::Program; pub use crate::graphics::geometry::*; +pub use crate::graphics::Transformation; pub use crate::renderer::geometry::*; use crate::core; -- cgit From aa41d7656e734b5dae3c19dff87afbc74617a67f Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 24 Oct 2023 02:51:02 +0200 Subject: Apply `Transform` scaling to text primitives --- core/src/pixels.rs | 8 ++++++++ wgpu/src/layer.rs | 2 +- wgpu/src/text.rs | 9 ++++++++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/core/src/pixels.rs b/core/src/pixels.rs index 6a9e5c88..425c0028 100644 --- a/core/src/pixels.rs +++ b/core/src/pixels.rs @@ -26,3 +26,11 @@ impl From for f32 { pixels.0 } } + +impl std::ops::Mul for Pixels { + type Output = Pixels; + + fn mul(self, rhs: f32) -> Self { + Pixels(self.0 * rhs) + } +} diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index fd5f2345..82e8ba02 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -169,7 +169,7 @@ impl<'a> Layer<'a> { layer.text.push(Text::Cached(text::Cached { content, bounds: *bounds * transformation, - size: *size, + size: *size * transformation.scale_y(), line_height: *line_height, color: *color, font: *font, diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index dca09cb8..4a151073 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -124,11 +124,13 @@ impl Pipeline { vertical_alignment, color, clip_bounds, + scale, ) = match section { Text::Paragraph { position, color, clip_bounds, + scale, .. } => { use crate::core::text::Paragraph as _; @@ -145,12 +147,14 @@ impl Pipeline { paragraph.vertical_alignment(), *color, *clip_bounds, + *scale, ) } Text::Editor { position, color, clip_bounds, + scale, .. } => { use crate::core::text::Editor as _; @@ -167,6 +171,7 @@ impl Pipeline { alignment::Vertical::Top, *color, *clip_bounds, + *scale, ) } Text::Cached(text) => { @@ -186,6 +191,7 @@ impl Pipeline { text.vertical_alignment, text.color, text.clip_bounds, + 1.0, ) } Text::Raw(text) => { @@ -205,6 +211,7 @@ impl Pipeline { alignment::Vertical::Top, text.color, text.clip_bounds, + 1.0, ) } }; @@ -234,7 +241,7 @@ impl Pipeline { buffer, left, top, - scale: scale_factor, + scale: scale * scale_factor, bounds: glyphon::TextBounds { left: clip_bounds.x as i32, top: clip_bounds.y as i32, -- cgit From a6e91d13d5d43796d0e6bb570fb4f010cf27921a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 24 Oct 2023 03:18:03 +0200 Subject: Allow only uniform scaling in `Transformation` --- graphics/src/transformation.rs | 15 +++++---------- tiny_skia/src/backend.rs | 8 ++++---- wgpu/src/backend.rs | 8 ++++---- wgpu/src/layer.rs | 6 +++--- 4 files changed, 16 insertions(+), 21 deletions(-) diff --git a/graphics/src/transformation.rs b/graphics/src/transformation.rs index 1aeb691e..e2642980 100644 --- a/graphics/src/transformation.rs +++ b/graphics/src/transformation.rs @@ -26,21 +26,16 @@ impl Transformation { Transformation(Mat4::from_translation(Vec3::new(x, y, 0.0))) } - /// Creates a scale transformation. - pub fn scale(x: f32, y: f32) -> Transformation { - Transformation(Mat4::from_scale(Vec3::new(x, y, 1.0))) + /// Creates a uniform scaling transformation. + pub fn scale(scaling: f32) -> Transformation { + Transformation(Mat4::from_scale(Vec3::new(scaling, scaling, 1.0))) } - /// The scale factor on the X axis. - pub fn scale_x(&self) -> f32 { + /// The scale factor of the [`Transformation`]. + pub fn scale_factor(&self) -> f32 { self.0.x_axis.x } - /// The scale factor on the Y axis. - pub fn scale_y(&self) -> f32 { - self.0.y_axis.y - } - /// The translation on the X axis. pub fn translation_x(&self) -> f32 { self.0.w_axis.x diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 87007a51..9c03818f 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -461,7 +461,7 @@ impl Backend { paragraph, *position * transformation, *color, - scale_factor, + scale_factor * transformation.scale_factor(), pixels, clip_mask, ); @@ -523,7 +523,7 @@ impl Backend { *horizontal_alignment, *vertical_alignment, *shaping, - scale_factor, + scale_factor * transformation.scale_factor(), pixels, clip_mask, ); @@ -770,10 +770,10 @@ fn into_color(color: Color) -> tiny_skia::Color { fn into_transform(transformation: Transformation) -> tiny_skia::Transform { tiny_skia::Transform { - sx: transformation.scale_x(), + sx: transformation.scale_factor(), kx: 0.0, ky: 0.0, - sy: transformation.scale_y(), + sy: transformation.scale_factor(), tx: transformation.translation_x(), ty: transformation.translation_y(), } diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index 25134d68..e86e52c4 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -147,8 +147,8 @@ impl Backend { } if !layer.meshes.is_empty() { - let scaled = transformation - * Transformation::scale(scale_factor, scale_factor); + let scaled = + transformation * Transformation::scale(scale_factor); self.triangle_pipeline.prepare( device, @@ -161,8 +161,8 @@ impl Backend { #[cfg(any(feature = "image", feature = "svg"))] { if !layer.images.is_empty() { - let scaled = transformation - * Transformation::scale(scale_factor, scale_factor); + let scaled = + transformation * Transformation::scale(scale_factor); self.image_pipeline.prepare( device, diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 82e8ba02..4a2e72df 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -133,7 +133,7 @@ impl<'a> Layer<'a> { position: *position * transformation, color: *color, clip_bounds: *clip_bounds * transformation, - scale: transformation.scale_y(), + scale: transformation.scale_factor(), }); } Primitive::Editor { @@ -149,7 +149,7 @@ impl<'a> Layer<'a> { position: *position * transformation, color: *color, clip_bounds: *clip_bounds * transformation, - scale: transformation.scale_y(), + scale: transformation.scale_factor(), }); } Primitive::Text { @@ -169,7 +169,7 @@ impl<'a> Layer<'a> { layer.text.push(Text::Cached(text::Cached { content, bounds: *bounds * transformation, - size: *size * transformation.scale_y(), + size: *size * transformation.scale_factor(), line_height: *line_height, color: *color, font: *font, -- cgit From f4d66486016076bb339a338bc589645119962d1e Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 24 Oct 2023 05:34:03 +0200 Subject: Introduce `with_transformation` to `Renderer` trait --- core/Cargo.toml | 1 + core/src/lib.rs | 2 + core/src/mouse/interaction.rs | 1 + core/src/renderer.rs | 20 ++++++- core/src/renderer/null.rs | 8 ++- core/src/transformation.rs | 119 +++++++++++++++++++++++++++++++++++++++ graphics/Cargo.toml | 1 - graphics/src/lib.rs | 2 - graphics/src/primitive.rs | 4 +- graphics/src/renderer.rs | 18 +++--- graphics/src/transformation.rs | 124 ----------------------------------------- graphics/src/viewport.rs | 4 +- renderer/src/geometry.rs | 3 +- renderer/src/lib.rs | 16 +++--- src/lib.rs | 3 +- tiny_skia/src/backend.rs | 28 ++++++---- tiny_skia/src/geometry.rs | 4 +- tiny_skia/src/text.rs | 23 ++++++-- wgpu/src/backend.rs | 4 +- wgpu/src/geometry.rs | 3 +- wgpu/src/image.rs | 3 +- wgpu/src/layer.rs | 20 ++++--- wgpu/src/layer/mesh.rs | 3 +- wgpu/src/layer/text.rs | 6 +- wgpu/src/quad.rs | 4 +- wgpu/src/text.rs | 20 +++---- wgpu/src/triangle.rs | 4 +- widget/src/canvas.rs | 7 +-- widget/src/image/viewer.rs | 29 +++++----- widget/src/pane_grid.rs | 10 ++-- widget/src/qr_code.rs | 12 ++-- winit/src/conversion.rs | 1 + 32 files changed, 275 insertions(+), 232 deletions(-) create mode 100644 core/src/transformation.rs delete mode 100644 graphics/src/transformation.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 32dd3df2..2360e822 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -12,6 +12,7 @@ keywords.workspace = true [dependencies] bitflags.workspace = true +glam.workspace = true log.workspace = true num-traits.workspace = true smol_str.workspace = true diff --git a/core/src/lib.rs b/core/src/lib.rs index bbc973f0..002336ee 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -49,6 +49,7 @@ mod rectangle; mod shadow; mod shell; mod size; +mod transformation; mod vector; pub use alignment::Alignment; @@ -75,6 +76,7 @@ pub use shadow::Shadow; pub use shell::Shell; pub use size::Size; pub use text::Text; +pub use transformation::Transformation; pub use vector::Vector; pub use widget::Widget; diff --git a/core/src/mouse/interaction.rs b/core/src/mouse/interaction.rs index 072033fd..6ad66229 100644 --- a/core/src/mouse/interaction.rs +++ b/core/src/mouse/interaction.rs @@ -13,4 +13,5 @@ pub enum Interaction { ResizingHorizontally, ResizingVertically, NotAllowed, + ZoomIn, } diff --git a/core/src/renderer.rs b/core/src/renderer.rs index 0af74bb3..1139b41c 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -5,7 +5,9 @@ mod null; #[cfg(debug_assertions)] pub use null::Null; -use crate::{Background, Border, Color, Rectangle, Shadow, Size, Vector}; +use crate::{ + Background, Border, Color, Rectangle, Shadow, Size, Transformation, Vector, +}; /// A component that can be used by widgets to draw themselves on a screen. pub trait Renderer: Sized { @@ -14,12 +16,24 @@ pub trait Renderer: Sized { /// The layer will clip its contents to the provided `bounds`. fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)); - /// Applies a `translation` to the primitives recorded in the given closure. + /// Applies a [`Transformation`] to the primitives recorded in the given closure. + fn with_transformation( + &mut self, + transformation: Transformation, + f: impl FnOnce(&mut Self), + ); + + /// Applies a translation to the primitives recorded in the given closure. fn with_translation( &mut self, translation: Vector, f: impl FnOnce(&mut Self), - ); + ) { + self.with_transformation( + Transformation::translate(translation.x, translation.y), + f, + ); + } /// Fills a [`Quad`] with the provided [`Background`]. fn fill_quad(&mut self, quad: Quad, background: impl Into); diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 455daa42..75a3c8b6 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -1,7 +1,9 @@ use crate::alignment; use crate::renderer::{self, Renderer}; use crate::text::{self, Text}; -use crate::{Background, Color, Font, Pixels, Point, Rectangle, Size, Vector}; +use crate::{ + Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, +}; use std::borrow::Cow; @@ -21,9 +23,9 @@ impl Null { impl Renderer for Null { fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {} - fn with_translation( + fn with_transformation( &mut self, - _translation: Vector, + _transformation: Transformation, _f: impl FnOnce(&mut Self), ) { } diff --git a/core/src/transformation.rs b/core/src/transformation.rs new file mode 100644 index 00000000..b2c488b0 --- /dev/null +++ b/core/src/transformation.rs @@ -0,0 +1,119 @@ +use crate::{Point, Rectangle, Size, Vector}; + +use glam::{Mat4, Vec3, Vec4}; +use std::ops::Mul; + +/// A 2D transformation matrix. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Transformation(Mat4); + +impl Transformation { + /// A [`Transformation`] that preserves whatever is transformed. + pub const IDENTITY: Self = Self(Mat4::IDENTITY); + + /// Creates an orthographic projection. + #[rustfmt::skip] + pub fn orthographic(width: u32, height: u32) -> Transformation { + Transformation(Mat4::orthographic_rh_gl( + 0.0, width as f32, + height as f32, 0.0, + -1.0, 1.0 + )) + } + + /// Creates a translate transformation. + pub fn translate(x: f32, y: f32) -> Transformation { + Transformation(Mat4::from_translation(Vec3::new(x, y, 0.0))) + } + + /// Creates a uniform scaling transformation. + pub fn scale(scaling: f32) -> Transformation { + Transformation(Mat4::from_scale(Vec3::new(scaling, scaling, 1.0))) + } + + /// Returns the scale factor of the [`Transformation`]. + pub fn scale_factor(&self) -> f32 { + self.0.x_axis.x + } + + /// Returns the translation of the [`Transformation`]. + pub fn translation(&self) -> Vector { + Vector::new(self.0.w_axis.x, self.0.w_axis.y) + } +} + +impl Mul for Transformation { + type Output = Self; + + fn mul(self, rhs: Self) -> Self { + Transformation(self.0 * rhs.0) + } +} + +impl Mul for Point { + type Output = Self; + + fn mul(self, transformation: Transformation) -> Self { + let point = transformation + .0 + .mul_vec4(Vec4::new(self.x, self.y, 1.0, 1.0)); + + Point::new(point.x, point.y) + } +} + +impl Mul for Vector { + type Output = Self; + + fn mul(self, transformation: Transformation) -> Self { + let new_vector = transformation + .0 + .mul_vec4(Vec4::new(self.x, self.y, 1.0, 0.0)); + + Vector::new(new_vector.x, new_vector.y) + } +} + +impl Mul for Size { + type Output = Self; + + fn mul(self, transformation: Transformation) -> Self { + let new_size = transformation.0.mul_vec4(Vec4::new( + self.width, + self.height, + 1.0, + 0.0, + )); + + Size::new(new_size.x, new_size.y) + } +} + +impl Mul for Rectangle { + type Output = Self; + + fn mul(self, transformation: Transformation) -> Self { + let position = self.position(); + let size = self.size(); + + Self::new(position * transformation, size * transformation) + } +} + +impl AsRef<[f32; 16]> for Transformation { + fn as_ref(&self) -> &[f32; 16] { + self.0.as_ref() + } +} + +impl From for [f32; 16] { + fn from(t: Transformation) -> [f32; 16] { + *t.as_ref() + } +} + +impl From for Mat4 { + fn from(transformation: Transformation) -> Self { + transformation.0 + } +} diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index 4f323f9e..907f3705 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -26,7 +26,6 @@ iced_futures.workspace = true bitflags.workspace = true bytemuck.workspace = true cosmic-text.workspace = true -glam.workspace = true half.workspace = true log.workspace = true once_cell.workspace = true diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 76de56bf..aa9d00e8 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -19,7 +19,6 @@ mod antialiasing; mod error; mod primitive; -mod transformation; mod viewport; pub mod backend; @@ -46,7 +45,6 @@ pub use gradient::Gradient; pub use mesh::Mesh; pub use primitive::Primitive; pub use renderer::Renderer; -pub use transformation::Transformation; pub use viewport::Viewport; pub use iced_core as core; diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 32008698..6929b0a1 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -4,11 +4,11 @@ use crate::core::image; use crate::core::svg; use crate::core::text; use crate::core::{ - Background, Border, Color, Font, Pixels, Point, Rectangle, Shadow, Vector, + Background, Border, Color, Font, Pixels, Point, Rectangle, Shadow, + Transformation, Vector, }; use crate::text::editor; use crate::text::paragraph; -use crate::Transformation; use std::sync::Arc; diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index cb07c23b..143f348b 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -6,7 +6,7 @@ use crate::core::renderer; use crate::core::svg; use crate::core::text::Text; use crate::core::{ - Background, Color, Font, Pixels, Point, Rectangle, Size, Vector, + Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation, }; use crate::text; use crate::Primitive; @@ -73,20 +73,20 @@ impl Renderer { } /// Starts recording a translation. - pub fn start_translation(&mut self) -> Vec> { + pub fn start_transformation(&mut self) -> Vec> { std::mem::take(&mut self.primitives) } /// Ends the recording of a translation. - pub fn end_translation( + pub fn end_transformation( &mut self, primitives: Vec>, - translation: Vector, + transformation: Transformation, ) { let layer = std::mem::replace(&mut self.primitives, primitives); self.primitives - .push(Primitive::group(layer).translate(translation)); + .push(Primitive::group(layer).transform(transformation)); } } @@ -99,16 +99,16 @@ impl iced_core::Renderer for Renderer { self.end_layer(current, bounds); } - fn with_translation( + fn with_transformation( &mut self, - translation: Vector, + transformation: Transformation, f: impl FnOnce(&mut Self), ) { - let current = self.start_translation(); + let current = self.start_transformation(); f(self); - self.end_translation(current, translation); + self.end_transformation(current, transformation); } fn fill_quad( diff --git a/graphics/src/transformation.rs b/graphics/src/transformation.rs deleted file mode 100644 index e2642980..00000000 --- a/graphics/src/transformation.rs +++ /dev/null @@ -1,124 +0,0 @@ -use crate::core::{Point, Rectangle, Size, Vector}; - -use glam::{Mat4, Vec3, Vec4}; -use std::ops::Mul; - -/// A 2D transformation matrix. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct Transformation(Mat4); - -impl Transformation { - /// A [`Transformation`] that preserves whatever is transformed. - pub const IDENTITY: Self = Self(Mat4::IDENTITY); - - /// Creates an orthographic projection. - #[rustfmt::skip] - pub fn orthographic(width: u32, height: u32) -> Transformation { - Transformation(Mat4::orthographic_rh_gl( - 0.0, width as f32, - height as f32, 0.0, - -1.0, 1.0 - )) - } - - /// Creates a translate transformation. - pub fn translate(x: f32, y: f32) -> Transformation { - Transformation(Mat4::from_translation(Vec3::new(x, y, 0.0))) - } - - /// Creates a uniform scaling transformation. - pub fn scale(scaling: f32) -> Transformation { - Transformation(Mat4::from_scale(Vec3::new(scaling, scaling, 1.0))) - } - - /// The scale factor of the [`Transformation`]. - pub fn scale_factor(&self) -> f32 { - self.0.x_axis.x - } - - /// The translation on the X axis. - pub fn translation_x(&self) -> f32 { - self.0.w_axis.x - } - - /// The translation on the Y axis. - pub fn translation_y(&self) -> f32 { - self.0.w_axis.y - } -} - -impl Mul for Transformation { - type Output = Self; - - fn mul(self, rhs: Self) -> Self { - Transformation(self.0 * rhs.0) - } -} - -impl Mul for Point { - type Output = Self; - - fn mul(self, transformation: Transformation) -> Self { - let point = transformation - .0 - .mul_vec4(Vec4::new(self.x, self.y, 1.0, 1.0)); - - Point::new(point.x, point.y) - } -} - -impl Mul for Vector { - type Output = Self; - - fn mul(self, transformation: Transformation) -> Self { - let new_vector = transformation - .0 - .mul_vec4(Vec4::new(self.x, self.y, 1.0, 0.0)); - - Vector::new(new_vector.x, new_vector.y) - } -} - -impl Mul for Size { - type Output = Self; - - fn mul(self, transformation: Transformation) -> Self { - let new_size = transformation.0.mul_vec4(Vec4::new( - self.width, - self.height, - 1.0, - 0.0, - )); - - Size::new(new_size.x, new_size.y) - } -} - -impl Mul for Rectangle { - type Output = Self; - - fn mul(self, transformation: Transformation) -> Self { - let position = self.position(); - let size = self.size(); - - Self::new(position * transformation, size * transformation) - } -} - -impl AsRef<[f32; 16]> for Transformation { - fn as_ref(&self) -> &[f32; 16] { - self.0.as_ref() - } -} - -impl From for [f32; 16] { - fn from(t: Transformation) -> [f32; 16] { - *t.as_ref() - } -} - -impl From for Mat4 { - fn from(transformation: Transformation) -> Self { - transformation.0 - } -} diff --git a/graphics/src/viewport.rs b/graphics/src/viewport.rs index 5792555d..dc8e21d3 100644 --- a/graphics/src/viewport.rs +++ b/graphics/src/viewport.rs @@ -1,6 +1,4 @@ -use crate::Transformation; - -use iced_core::Size; +use crate::core::{Size, Transformation}; /// A viewing region for displaying computer graphics. #[derive(Debug, Clone)] diff --git a/renderer/src/geometry.rs b/renderer/src/geometry.rs index 9cf506a8..f09ccfbf 100644 --- a/renderer/src/geometry.rs +++ b/renderer/src/geometry.rs @@ -2,9 +2,8 @@ mod cache; pub use cache::Cache; -use crate::core::{Point, Rectangle, Size, Vector}; +use crate::core::{Point, Rectangle, Size, Transformation, Vector}; use crate::graphics::geometry::{Fill, Path, Stroke, Text}; -use crate::graphics::Transformation; use crate::Renderer; macro_rules! delegate { diff --git a/renderer/src/lib.rs b/renderer/src/lib.rs index a7df414b..757c264d 100644 --- a/renderer/src/lib.rs +++ b/renderer/src/lib.rs @@ -22,7 +22,9 @@ pub use geometry::Geometry; use crate::core::renderer; use crate::core::text::{self, Text}; -use crate::core::{Background, Color, Font, Pixels, Point, Rectangle, Vector}; +use crate::core::{ + Background, Color, Font, Pixels, Point, Rectangle, Transformation, +}; use crate::graphics::text::Editor; use crate::graphics::text::Paragraph; use crate::graphics::Mesh; @@ -97,20 +99,20 @@ impl core::Renderer for Renderer { } } - fn with_translation( + fn with_transformation( &mut self, - translation: Vector, + transformation: Transformation, f: impl FnOnce(&mut Self), ) { match self { Self::TinySkia(renderer) => { - let primitives = renderer.start_translation(); + let primitives = renderer.start_transformation(); f(self); match self { Self::TinySkia(renderer) => { - renderer.end_translation(primitives, translation); + renderer.end_transformation(primitives, transformation); } #[cfg(feature = "wgpu")] _ => unreachable!(), @@ -118,14 +120,14 @@ impl core::Renderer for Renderer { } #[cfg(feature = "wgpu")] Self::Wgpu(renderer) => { - let primitives = renderer.start_translation(); + let primitives = renderer.start_transformation(); f(self); match self { #[cfg(feature = "wgpu")] Self::Wgpu(renderer) => { - renderer.end_translation(primitives, translation); + renderer.end_transformation(primitives, transformation); } _ => unreachable!(), } diff --git a/src/lib.rs b/src/lib.rs index 86207d6e..a49259ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -193,7 +193,8 @@ pub use crate::core::color; pub use crate::core::gradient; pub use crate::core::{ Alignment, Background, Border, Color, ContentFit, Degrees, Gradient, - Length, Padding, Pixels, Point, Radians, Rectangle, Shadow, Size, Vector, + Length, Padding, Pixels, Point, Radians, Rectangle, Shadow, Size, + Transformation, Vector, }; pub mod clipboard { diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 9c03818f..44f5c151 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -1,9 +1,11 @@ use tiny_skia::Size; -use crate::core::{Background, Color, Gradient, Rectangle, Vector}; +use crate::core::{ + Background, Color, Gradient, Rectangle, Transformation, Vector, +}; use crate::graphics::backend; use crate::graphics::text; -use crate::graphics::{Transformation, Viewport}; +use crate::graphics::Viewport; use crate::primitive::{self, Primitive}; use std::borrow::Cow; @@ -459,11 +461,12 @@ impl Backend { self.text_pipeline.draw_paragraph( paragraph, - *position * transformation, + *position, *color, - scale_factor * transformation.scale_factor(), + scale_factor, pixels, clip_mask, + transformation, ); } Primitive::Editor { @@ -484,11 +487,12 @@ impl Backend { self.text_pipeline.draw_editor( editor, - *position * transformation, + *position, *color, scale_factor, pixels, clip_mask, + transformation, ); } Primitive::Text { @@ -515,7 +519,7 @@ impl Backend { self.text_pipeline.draw_cached( content, - *bounds * transformation, + *bounds, *color, *size, *line_height, @@ -523,9 +527,10 @@ impl Backend { *horizontal_alignment, *vertical_alignment, *shaping, - scale_factor * transformation.scale_factor(), + scale_factor, pixels, clip_mask, + transformation, ); } Primitive::RawText(text::Raw { @@ -550,11 +555,12 @@ impl Backend { self.text_pipeline.draw_raw( &buffer, - *position * transformation, + *position, *color, scale_factor, pixels, clip_mask, + transformation, ); } #[cfg(feature = "image")] @@ -769,13 +775,15 @@ fn into_color(color: Color) -> tiny_skia::Color { } fn into_transform(transformation: Transformation) -> tiny_skia::Transform { + let translation = transformation.translation(); + tiny_skia::Transform { sx: transformation.scale_factor(), kx: 0.0, ky: 0.0, sy: transformation.scale_factor(), - tx: transformation.translation_x(), - ty: transformation.translation_y(), + tx: translation.x, + ty: translation.y, } } diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index 579c153a..f7518731 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -1,9 +1,9 @@ use crate::core::text::LineHeight; -use crate::core::{Pixels, Point, Rectangle, Size, Vector}; +use crate::core::{Pixels, Point, Rectangle, Size, Transformation, Vector}; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::stroke::{self, Stroke}; use crate::graphics::geometry::{Path, Style, Text}; -use crate::graphics::{Gradient, Transformation}; +use crate::graphics::Gradient; use crate::primitive::{self, Primitive}; pub struct Frame { diff --git a/tiny_skia/src/text.rs b/tiny_skia/src/text.rs index 9413e311..c16037cf 100644 --- a/tiny_skia/src/text.rs +++ b/tiny_skia/src/text.rs @@ -1,6 +1,8 @@ use crate::core::alignment; use crate::core::text::{LineHeight, Shaping}; -use crate::core::{Color, Font, Pixels, Point, Rectangle, Size}; +use crate::core::{ + Color, Font, Pixels, Point, Rectangle, Size, Transformation, +}; use crate::graphics::text::cache::{self, Cache}; use crate::graphics::text::editor; use crate::graphics::text::font_system; @@ -42,6 +44,7 @@ impl Pipeline { scale_factor: f32, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, + transformation: Transformation, ) { use crate::core::text::Paragraph as _; @@ -62,6 +65,7 @@ impl Pipeline { scale_factor, pixels, clip_mask, + transformation, ); } @@ -73,6 +77,7 @@ impl Pipeline { scale_factor: f32, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, + transformation: Transformation, ) { use crate::core::text::Editor as _; @@ -93,6 +98,7 @@ impl Pipeline { scale_factor, pixels, clip_mask, + transformation, ); } @@ -110,6 +116,7 @@ impl Pipeline { scale_factor: f32, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, + transformation: Transformation, ) { let line_height = f32::from(line_height.to_absolute(size)); @@ -145,6 +152,7 @@ impl Pipeline { scale_factor, pixels, clip_mask, + transformation, ); } @@ -156,6 +164,7 @@ impl Pipeline { scale_factor: f32, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, + transformation: Transformation, ) { let mut font_system = font_system().write().expect("Write font system"); @@ -172,6 +181,7 @@ impl Pipeline { scale_factor, pixels, clip_mask, + transformation, ); } @@ -192,8 +202,9 @@ fn draw( scale_factor: f32, pixels: &mut tiny_skia::PixmapMut<'_>, clip_mask: Option<&tiny_skia::Mask>, + transformation: Transformation, ) { - let bounds = bounds * scale_factor; + let bounds = bounds * transformation * scale_factor; let x = match horizontal_alignment { alignment::Horizontal::Left => bounds.x, @@ -211,7 +222,8 @@ fn draw( for run in buffer.layout_runs() { for glyph in run.glyphs { - let physical_glyph = glyph.physical((x, y), scale_factor); + let physical_glyph = glyph + .physical((x, y), scale_factor * transformation.scale_factor()); if let Some((buffer, placement)) = glyph_cache.allocate( physical_glyph.cache_key, @@ -229,7 +241,10 @@ fn draw( pixels.draw_pixmap( physical_glyph.x + placement.left, physical_glyph.y - placement.top - + (run.line_y * scale_factor).round() as i32, + + (run.line_y + * scale_factor + * transformation.scale_factor()) + .round() as i32, pixmap, &tiny_skia::PixmapPaint::default(), tiny_skia::Transform::identity(), diff --git a/wgpu/src/backend.rs b/wgpu/src/backend.rs index e86e52c4..77b6fa83 100644 --- a/wgpu/src/backend.rs +++ b/wgpu/src/backend.rs @@ -1,7 +1,7 @@ -use crate::core::{Color, Size}; +use crate::core::{Color, Size, Transformation}; use crate::graphics::backend; use crate::graphics::color; -use crate::graphics::{Transformation, Viewport}; +use crate::graphics::Viewport; use crate::primitive::pipeline; use crate::primitive::{self, Primitive}; use crate::quad; diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index d0660edc..8cfcfff0 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -1,6 +1,6 @@ //! Build and draw geometry. use crate::core::text::LineHeight; -use crate::core::{Pixels, Point, Rectangle, Size, Vector}; +use crate::core::{Pixels, Point, Rectangle, Size, Transformation, Vector}; use crate::graphics::color; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::{ @@ -8,7 +8,6 @@ use crate::graphics::geometry::{ }; use crate::graphics::gradient::{self, Gradient}; use crate::graphics::mesh::{self, Mesh}; -use crate::graphics::Transformation; use crate::primitive::{self, Primitive}; use lyon::geom::euclid; diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index 1e5d3ee0..06c22870 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -8,8 +8,7 @@ mod vector; use atlas::Atlas; -use crate::core::{Rectangle, Size}; -use crate::graphics::Transformation; +use crate::core::{Rectangle, Size, Transformation}; use crate::layer; use crate::Buffer; diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 4a2e72df..12588849 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -12,10 +12,12 @@ pub use text::Text; use crate::core; use crate::core::alignment; -use crate::core::{Color, Font, Pixels, Point, Rectangle, Size, Vector}; +use crate::core::{ + Color, Font, Pixels, Point, Rectangle, Size, Transformation, Vector, +}; use crate::graphics; use crate::graphics::color; -use crate::graphics::{Transformation, Viewport}; +use crate::graphics::Viewport; use crate::primitive::{self, Primitive}; use crate::quad::{self, Quad}; @@ -130,10 +132,10 @@ impl<'a> Layer<'a> { layer.text.push(Text::Paragraph { paragraph: paragraph.clone(), - position: *position * transformation, + position: *position, color: *color, - clip_bounds: *clip_bounds * transformation, - scale: transformation.scale_factor(), + clip_bounds: *clip_bounds, + transformation, }); } Primitive::Editor { @@ -146,10 +148,10 @@ impl<'a> Layer<'a> { layer.text.push(Text::Editor { editor: editor.clone(), - position: *position * transformation, + position: *position, color: *color, - clip_bounds: *clip_bounds * transformation, - scale: transformation.scale_factor(), + clip_bounds: *clip_bounds, + transformation, }); } Primitive::Text { @@ -168,7 +170,7 @@ impl<'a> Layer<'a> { layer.text.push(Text::Cached(text::Cached { content, - bounds: *bounds * transformation, + bounds: *bounds + transformation.translation(), size: *size * transformation.scale_factor(), line_height: *line_height, color: *color, diff --git a/wgpu/src/layer/mesh.rs b/wgpu/src/layer/mesh.rs index af3dc74a..5ed7c654 100644 --- a/wgpu/src/layer/mesh.rs +++ b/wgpu/src/layer/mesh.rs @@ -1,7 +1,6 @@ //! A collection of triangle primitives. -use crate::core::Rectangle; +use crate::core::{Rectangle, Transformation}; use crate::graphics::mesh; -use crate::graphics::Transformation; /// A mesh of triangles. #[derive(Debug, Clone, Copy)] diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs index 2a09aecc..4c2b66a4 100644 --- a/wgpu/src/layer/text.rs +++ b/wgpu/src/layer/text.rs @@ -1,6 +1,6 @@ use crate::core::alignment; use crate::core::text; -use crate::core::{Color, Font, Pixels, Point, Rectangle}; +use crate::core::{Color, Font, Pixels, Point, Rectangle, Transformation}; use crate::graphics; use crate::graphics::text::editor; use crate::graphics::text::paragraph; @@ -15,7 +15,7 @@ pub enum Text<'a> { position: Point, color: Color, clip_bounds: Rectangle, - scale: f32, + transformation: Transformation, }, /// An editor. #[allow(missing_docs)] @@ -24,7 +24,7 @@ pub enum Text<'a> { position: Point, color: Color, clip_bounds: Rectangle, - scale: f32, + transformation: Transformation, }, /// Some cached text. Cached(Cached<'a>), diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index 5a45187e..b932f54f 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -4,9 +4,9 @@ mod solid; use gradient::Gradient; use solid::Solid; -use crate::core::{Background, Rectangle}; +use crate::core::{Background, Rectangle, Transformation}; +use crate::graphics; use crate::graphics::color; -use crate::graphics::{self, Transformation}; use bytemuck::{Pod, Zeroable}; diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 4a151073..cffe57cb 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -1,5 +1,5 @@ use crate::core::alignment; -use crate::core::{Rectangle, Size}; +use crate::core::{Rectangle, Size, Transformation}; use crate::graphics::color; use crate::graphics::text::cache::{self, Cache}; use crate::graphics::text::{font_system, to_color, Editor, Paragraph}; @@ -124,13 +124,13 @@ impl Pipeline { vertical_alignment, color, clip_bounds, - scale, + transformation, ) = match section { Text::Paragraph { position, color, clip_bounds, - scale, + transformation, .. } => { use crate::core::text::Paragraph as _; @@ -147,14 +147,14 @@ impl Pipeline { paragraph.vertical_alignment(), *color, *clip_bounds, - *scale, + *transformation, ) } Text::Editor { position, color, clip_bounds, - scale, + transformation, .. } => { use crate::core::text::Editor as _; @@ -171,7 +171,7 @@ impl Pipeline { alignment::Vertical::Top, *color, *clip_bounds, - *scale, + *transformation, ) } Text::Cached(text) => { @@ -191,7 +191,7 @@ impl Pipeline { text.vertical_alignment, text.color, text.clip_bounds, - 1.0, + Transformation::IDENTITY, ) } Text::Raw(text) => { @@ -211,12 +211,12 @@ impl Pipeline { alignment::Vertical::Top, text.color, text.clip_bounds, - 1.0, + Transformation::IDENTITY, ) } }; - let bounds = bounds * scale_factor; + let bounds = bounds * transformation * scale_factor; let left = match horizontal_alignment { alignment::Horizontal::Left => bounds.x, @@ -241,7 +241,7 @@ impl Pipeline { buffer, left, top, - scale: scale * scale_factor, + scale: scale_factor * transformation.scale_factor(), bounds: glyphon::TextBounds { left: clip_bounds.x as i32, top: clip_bounds.y as i32, diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 28a7b1b3..2bb6f307 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -1,8 +1,8 @@ //! Draw meshes of triangles. mod msaa; -use crate::core::Size; -use crate::graphics::{Antialiasing, Transformation}; +use crate::core::{Size, Transformation}; +use crate::graphics::Antialiasing; use crate::layer::mesh::{self, Mesh}; use crate::Buffer; diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 3e4a0928..0eda0191 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -7,7 +7,6 @@ pub use event::Event; pub use program::Program; pub use crate::graphics::geometry::*; -pub use crate::graphics::Transformation; pub use crate::renderer::geometry::*; use crate::core; @@ -16,7 +15,7 @@ use crate::core::mouse; use crate::core::renderer; use crate::core::widget::tree::{self, Tree}; use crate::core::{ - Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget, + Clipboard, Element, Length, Rectangle, Shell, Size, Transformation, Widget, }; use crate::graphics::geometry; @@ -208,8 +207,8 @@ where let state = tree.state.downcast_ref::(); - renderer.with_translation( - Vector::new(bounds.x, bounds.y), + renderer.with_transformation( + Transformation::translate(bounds.x, bounds.y), |renderer| { renderer.draw( self.program.draw(state, renderer, theme, bounds, cursor), diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index 9666ff9f..0006f978 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -7,7 +7,7 @@ use crate::core::renderer; use crate::core::widget::tree::{self, Tree}; use crate::core::{ Clipboard, Element, Layout, Length, Pixels, Point, Rectangle, Shell, Size, - Vector, Widget, + Transformation, Vector, Widget, }; use std::hash::Hash; @@ -328,18 +328,21 @@ where }; renderer.with_layer(bounds, |renderer| { - renderer.with_translation(translation, |renderer| { - image::Renderer::draw( - renderer, - self.handle.clone(), - self.filter_method, - Rectangle { - x: bounds.x, - y: bounds.y, - ..Rectangle::with_size(image_size) - }, - ); - }); + renderer.with_transformation( + Transformation::translate(translation.x, translation.y), + |renderer| { + image::Renderer::draw( + renderer, + self.handle.clone(), + self.filter_method, + Rectangle { + x: bounds.x, + y: bounds.y, + ..Rectangle::with_size(image_size) + }, + ); + }, + ); }); } } diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index f76c6088..24389462 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -43,7 +43,7 @@ use crate::core::widget; use crate::core::widget::tree::{self, Tree}; use crate::core::{ Clipboard, Element, Layout, Length, Pixels, Point, Rectangle, Shell, Size, - Vector, Widget, + Transformation, Vector, Widget, }; /// A collection of panes distributed using either vertical or horizontal splits @@ -962,9 +962,11 @@ pub fn draw( if let Some(cursor_position) = cursor.position() { let bounds = layout.bounds(); - renderer.with_translation( - cursor_position - - Point::new(bounds.x + origin.x, bounds.y + origin.y), + let translation = cursor_position + - Point::new(bounds.x + origin.x, bounds.y + origin.y); + + renderer.with_transformation( + Transformation::translate(translation.x, translation.y), |renderer| { renderer.with_layer(bounds, |renderer| { draw_pane( diff --git a/widget/src/qr_code.rs b/widget/src/qr_code.rs index 91c0a97b..da63d949 100644 --- a/widget/src/qr_code.rs +++ b/widget/src/qr_code.rs @@ -5,7 +5,8 @@ use crate::core::mouse; use crate::core::renderer::{self, Renderer as _}; use crate::core::widget::Tree; use crate::core::{ - Color, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget, + Color, Element, Layout, Length, Point, Rectangle, Size, Transformation, + Vector, Widget, }; use crate::graphics::geometry::Renderer as _; use crate::Renderer; @@ -121,9 +122,12 @@ impl<'a, Message, Theme> Widget for QRCode<'a> { let translation = Vector::new(bounds.x, bounds.y); - renderer.with_translation(translation, |renderer| { - renderer.draw(vec![geometry]); - }); + renderer.with_transformation( + Transformation::translate(translation.x, translation.y), + |renderer| { + renderer.draw(vec![geometry]); + }, + ); } } diff --git a/winit/src/conversion.rs b/winit/src/conversion.rs index eceb1604..ef789296 100644 --- a/winit/src/conversion.rs +++ b/winit/src/conversion.rs @@ -385,6 +385,7 @@ pub fn mouse_interaction( } Interaction::ResizingVertically => winit::window::CursorIcon::NsResize, Interaction::NotAllowed => winit::window::CursorIcon::NotAllowed, + Interaction::ZoomIn => winit::window::CursorIcon::ZoomIn, } } -- cgit From 7cba9a3e7fe609ca29066fb4b2e5dbf5a1dbd316 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 24 Oct 2023 05:34:24 +0200 Subject: Implement `loupe` example --- examples/loupe/Cargo.toml | 10 +++ examples/loupe/src/main.rs | 185 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+) create mode 100644 examples/loupe/Cargo.toml create mode 100644 examples/loupe/src/main.rs diff --git a/examples/loupe/Cargo.toml b/examples/loupe/Cargo.toml new file mode 100644 index 00000000..466905ba --- /dev/null +++ b/examples/loupe/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "loupe" +version = "0.1.0" +authors = ["Héctor Ramón Jiménez "] +edition = "2021" +publish = false + +[dependencies] +iced.workspace = true +iced.features = ["advanced", "debug"] \ No newline at end of file diff --git a/examples/loupe/src/main.rs b/examples/loupe/src/main.rs new file mode 100644 index 00000000..8602edb7 --- /dev/null +++ b/examples/loupe/src/main.rs @@ -0,0 +1,185 @@ +use iced::widget::{button, column, container, text}; +use iced::{Alignment, Element, Length, Sandbox, Settings}; + +use loupe::loupe; + +pub fn main() -> iced::Result { + Counter::run(Settings::default()) +} + +struct Counter { + value: i32, +} + +#[derive(Debug, Clone, Copy)] +enum Message { + IncrementPressed, + DecrementPressed, +} + +impl Sandbox for Counter { + type Message = Message; + + fn new() -> Self { + Self { value: 0 } + } + + fn title(&self) -> String { + String::from("Counter - Iced") + } + + fn update(&mut self, message: Message) { + match message { + Message::IncrementPressed => { + self.value += 1; + } + Message::DecrementPressed => { + self.value -= 1; + } + } + } + + fn view(&self) -> Element { + container(loupe( + 3.0, + column![ + button("Increment").on_press(Message::IncrementPressed), + text(self.value).size(50), + button("Decrement").on_press(Message::DecrementPressed) + ] + .padding(20) + .align_items(Alignment::Center), + )) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } +} + +mod loupe { + use iced::advanced::layout::{self, Layout}; + use iced::advanced::renderer; + use iced::advanced::widget::{self, Widget}; + use iced::advanced::Renderer as _; + use iced::mouse; + use iced::{ + Color, Element, Length, Rectangle, Renderer, Size, Theme, + Transformation, + }; + + pub fn loupe<'a, Message>( + zoom: f32, + content: impl Into>, + ) -> Loupe<'a, Message> + where + Message: 'static, + { + Loupe { + zoom, + content: content.into().explain(Color::BLACK), + } + } + + pub struct Loupe<'a, Message> { + zoom: f32, + content: Element<'a, Message>, + } + + impl<'a, Message> Widget for Loupe<'a, Message> { + fn tag(&self) -> widget::tree::Tag { + self.content.as_widget().tag() + } + + fn state(&self) -> widget::tree::State { + self.content.as_widget().state() + } + + fn children(&self) -> Vec { + self.content.as_widget().children() + } + + fn diff(&self, tree: &mut widget::Tree) { + self.content.as_widget().diff(tree); + } + + fn size(&self) -> Size { + self.content.as_widget().size() + } + + fn layout( + &self, + tree: &mut widget::Tree, + renderer: &Renderer, + limits: &layout::Limits, + ) -> layout::Node { + self.content.as_widget().layout(tree, renderer, limits) + } + + fn draw( + &self, + tree: &widget::Tree, + renderer: &mut Renderer, + theme: &Theme, + style: &renderer::Style, + layout: Layout<'_>, + cursor: mouse::Cursor, + viewport: &Rectangle, + ) { + let bounds = layout.bounds(); + + if let Some(position) = cursor.position_in(bounds) { + renderer.with_layer(bounds, |renderer| { + renderer.with_transformation( + Transformation::translate( + bounds.x + position.x * (1.0 - self.zoom), + bounds.y + position.y * (1.0 - self.zoom), + ) * Transformation::scale(self.zoom) + * Transformation::translate(-bounds.x, -bounds.y), + |renderer| { + self.content.as_widget().draw( + tree, + renderer, + theme, + style, + layout, + mouse::Cursor::Unavailable, + viewport, + ); + }, + ); + }); + } else { + self.content.as_widget().draw( + tree, renderer, theme, style, layout, cursor, viewport, + ); + } + } + + fn mouse_interaction( + &self, + _state: &widget::Tree, + layout: Layout<'_>, + cursor: mouse::Cursor, + _viewport: &Rectangle, + _renderer: &Renderer, + ) -> mouse::Interaction { + if cursor.is_over(layout.bounds()) { + mouse::Interaction::ZoomIn + } else { + mouse::Interaction::Idle + } + } + } + + impl<'a, Message> From> + for Element<'a, Message, Theme, Renderer> + where + Message: 'a, + { + fn from(loupe: Loupe<'a, Message>) -> Self { + Self::new(loupe) + } + } +} -- cgit From d8dffa411b8c1bbee538e3b51aae6f768e293789 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 2 Feb 2024 02:31:47 +0100 Subject: Transform `clip_bounds` in `wgpu::text` --- wgpu/src/text.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index cffe57cb..15179ff3 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -234,8 +234,9 @@ impl Pipeline { alignment::Vertical::Bottom => bounds.y - bounds.height, }; - let clip_bounds = - layer_bounds.intersection(&(clip_bounds * scale_factor))?; + let clip_bounds = layer_bounds.intersection( + &(clip_bounds * transformation * scale_factor), + )?; Some(glyphon::TextArea { buffer, -- cgit From a06682ff420678f7068265191738ab70ebe30b4c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 2 Feb 2024 14:31:17 +0100 Subject: Use `with_translation` wherever possible --- widget/src/image/viewer.rs | 29 +++++++++++++---------------- widget/src/pane_grid.rs | 29 +++++++++++++---------------- widget/src/qr_code.rs | 9 +++------ 3 files changed, 29 insertions(+), 38 deletions(-) diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index 0006f978..9666ff9f 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -7,7 +7,7 @@ use crate::core::renderer; use crate::core::widget::tree::{self, Tree}; use crate::core::{ Clipboard, Element, Layout, Length, Pixels, Point, Rectangle, Shell, Size, - Transformation, Vector, Widget, + Vector, Widget, }; use std::hash::Hash; @@ -328,21 +328,18 @@ where }; renderer.with_layer(bounds, |renderer| { - renderer.with_transformation( - Transformation::translate(translation.x, translation.y), - |renderer| { - image::Renderer::draw( - renderer, - self.handle.clone(), - self.filter_method, - Rectangle { - x: bounds.x, - y: bounds.y, - ..Rectangle::with_size(image_size) - }, - ); - }, - ); + renderer.with_translation(translation, |renderer| { + image::Renderer::draw( + renderer, + self.handle.clone(), + self.filter_method, + Rectangle { + x: bounds.x, + y: bounds.y, + ..Rectangle::with_size(image_size) + }, + ); + }); }); } } diff --git a/widget/src/pane_grid.rs b/widget/src/pane_grid.rs index 24389462..4f9a265a 100644 --- a/widget/src/pane_grid.rs +++ b/widget/src/pane_grid.rs @@ -43,7 +43,7 @@ use crate::core::widget; use crate::core::widget::tree::{self, Tree}; use crate::core::{ Clipboard, Element, Layout, Length, Pixels, Point, Rectangle, Shell, Size, - Transformation, Vector, Widget, + Vector, Widget, }; /// A collection of panes distributed using either vertical or horizontal splits @@ -965,21 +965,18 @@ pub fn draw( let translation = cursor_position - Point::new(bounds.x + origin.x, bounds.y + origin.y); - renderer.with_transformation( - Transformation::translate(translation.x, translation.y), - |renderer| { - renderer.with_layer(bounds, |renderer| { - draw_pane( - pane, - renderer, - default_style, - layout, - pane_cursor, - viewport, - ); - }); - }, - ); + renderer.with_translation(translation, |renderer| { + renderer.with_layer(bounds, |renderer| { + draw_pane( + pane, + renderer, + default_style, + layout, + pane_cursor, + viewport, + ); + }); + }); } } diff --git a/widget/src/qr_code.rs b/widget/src/qr_code.rs index da63d949..6a748e63 100644 --- a/widget/src/qr_code.rs +++ b/widget/src/qr_code.rs @@ -5,8 +5,7 @@ use crate::core::mouse; use crate::core::renderer::{self, Renderer as _}; use crate::core::widget::Tree; use crate::core::{ - Color, Element, Layout, Length, Point, Rectangle, Size, Transformation, - Vector, Widget, + Color, Element, Layout, Length, Point, Rectangle, Size, Vector, Widget, }; use crate::graphics::geometry::Renderer as _; use crate::Renderer; @@ -120,10 +119,8 @@ impl<'a, Message, Theme> Widget for QRCode<'a> { }); }); - let translation = Vector::new(bounds.x, bounds.y); - - renderer.with_transformation( - Transformation::translate(translation.x, translation.y), + renderer.with_translation( + bounds.position() - Point::ORIGIN, |renderer| { renderer.draw(vec![geometry]); }, -- cgit From b3adf3184594c9bf60e0548a0362d30c512f3966 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Fri, 2 Feb 2024 14:43:04 +0100 Subject: Apply `Transformation` to `RawText` primitives --- wgpu/src/layer.rs | 17 +++++------------ wgpu/src/layer/text.rs | 6 +++++- wgpu/src/text.rs | 17 +++++++++++------ 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 12588849..cc767c25 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -181,20 +181,13 @@ impl<'a> Layer<'a> { clip_bounds: *clip_bounds * transformation, })); } - graphics::Primitive::RawText(graphics::text::Raw { - buffer, - position, - color, - clip_bounds, - }) => { + graphics::Primitive::RawText(raw) => { let layer = &mut layers[current_layer]; - layer.text.push(Text::Raw(graphics::text::Raw { - buffer: buffer.clone(), - position: *position * transformation, - color: *color, - clip_bounds: *clip_bounds * transformation, - })); + layer.text.push(Text::Raw { + raw: raw.clone(), + transformation, + }); } Primitive::Quad { bounds, diff --git a/wgpu/src/layer/text.rs b/wgpu/src/layer/text.rs index 4c2b66a4..b3a00130 100644 --- a/wgpu/src/layer/text.rs +++ b/wgpu/src/layer/text.rs @@ -29,7 +29,11 @@ pub enum Text<'a> { /// Some cached text. Cached(Cached<'a>), /// Some raw text. - Raw(graphics::text::Raw), + #[allow(missing_docs)] + Raw { + raw: graphics::text::Raw, + transformation: Transformation, + }, } #[derive(Debug, Clone)] diff --git a/wgpu/src/text.rs b/wgpu/src/text.rs index 15179ff3..6fa1922d 100644 --- a/wgpu/src/text.rs +++ b/wgpu/src/text.rs @@ -109,7 +109,9 @@ impl Pipeline { Some(Allocation::Cache(key)) } - Text::Raw(text) => text.buffer.upgrade().map(Allocation::Raw), + Text::Raw { raw, .. } => { + raw.buffer.upgrade().map(Allocation::Raw) + } }) .collect(); @@ -194,7 +196,10 @@ impl Pipeline { Transformation::IDENTITY, ) } - Text::Raw(text) => { + Text::Raw { + raw, + transformation, + } => { let Some(Allocation::Raw(buffer)) = allocation else { return None; }; @@ -204,14 +209,14 @@ impl Pipeline { ( buffer.as_ref(), Rectangle::new( - text.position, + raw.position, Size::new(width, height), ), alignment::Horizontal::Left, alignment::Vertical::Top, - text.color, - text.clip_bounds, - Transformation::IDENTITY, + raw.color, + raw.clip_bounds, + *transformation, ) } }; -- cgit