summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector@hecrj.dev>2024-02-02 14:54:56 +0100
committerLibravatar GitHub <noreply@github.com>2024-02-02 14:54:56 +0100
commitaea172543cb49f1f1e3625f60b49336f59e26c00 (patch)
treee99e7a55873678ac37fd695a0f46c1350b30dd2f
parent759f0e922598504705b543185bc7140a652b726a (diff)
parentb3adf3184594c9bf60e0548a0362d30c512f3966 (diff)
downloadiced-aea172543cb49f1f1e3625f60b49336f59e26c00.tar.gz
iced-aea172543cb49f1f1e3625f60b49336f59e26c00.tar.bz2
iced-aea172543cb49f1f1e3625f60b49336f59e26c00.zip
Merge pull request #2120 from iced-rs/transform-primitive
`Transform` primitive
Diffstat (limited to '')
-rw-r--r--core/Cargo.toml1
-rw-r--r--core/src/lib.rs2
-rw-r--r--core/src/mouse/interaction.rs1
-rw-r--r--core/src/pixels.rs8
-rw-r--r--core/src/renderer.rs20
-rw-r--r--core/src/renderer/null.rs8
-rw-r--r--core/src/transformation.rs119
-rw-r--r--examples/bezier_tool/Cargo.toml2
-rw-r--r--examples/loupe/Cargo.toml10
-rw-r--r--examples/loupe/src/main.rs185
-rw-r--r--graphics/Cargo.toml1
-rw-r--r--graphics/src/damage.rs18
-rw-r--r--graphics/src/lib.rs2
-rw-r--r--graphics/src/primitive.rs34
-rw-r--r--graphics/src/renderer.rs18
-rw-r--r--graphics/src/transformation.rs59
-rw-r--r--graphics/src/viewport.rs4
-rw-r--r--renderer/src/geometry.rs36
-rw-r--r--renderer/src/lib.rs16
-rw-r--r--src/lib.rs3
-rw-r--r--tiny_skia/src/backend.rs99
-rw-r--r--tiny_skia/src/geometry.rs6
-rw-r--r--tiny_skia/src/text.rs23
-rw-r--r--wgpu/src/backend.rs12
-rw-r--r--wgpu/src/geometry.rs12
-rw-r--r--wgpu/src/image.rs3
-rw-r--r--wgpu/src/layer.rs89
-rw-r--r--wgpu/src/layer/mesh.rs17
-rw-r--r--wgpu/src/layer/text.rs10
-rw-r--r--wgpu/src/quad.rs6
-rw-r--r--wgpu/src/text.rs33
-rw-r--r--wgpu/src/triangle.rs10
-rw-r--r--widget/src/canvas.rs6
-rw-r--r--widget/src/pane_grid.rs31
-rw-r--r--widget/src/qr_code.rs11
-rw-r--r--winit/src/conversion.rs1
36 files changed, 623 insertions, 293 deletions
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/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<Pixels> for f32 {
pixels.0
}
}
+
+impl std::ops::Mul<f32> for Pixels {
+ type Output = Pixels;
+
+ fn mul(self, rhs: f32) -> Self {
+ Pixels(self.0 * rhs)
+ }
+}
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<Background>);
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<Transformation> 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<Transformation> 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<Transformation> 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<Transformation> 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<Transformation> for [f32; 16] {
+ fn from(t: Transformation) -> [f32; 16] {
+ *t.as_ref()
+ }
+}
+
+impl From<Transformation> for Mat4 {
+ fn from(transformation: Transformation) -> Self {
+ transformation.0
+ }
+}
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/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 <hector0193@gmail.com>"]
+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<Message> {
+ 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<Element<'a, Message>>,
+ ) -> 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<Message, Theme, Renderer> 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<widget::Tree> {
+ self.content.as_widget().children()
+ }
+
+ fn diff(&self, tree: &mut widget::Tree) {
+ self.content.as_widget().diff(tree);
+ }
+
+ fn size(&self) -> Size<Length> {
+ 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<Loupe<'a, Message>>
+ for Element<'a, Message, Theme, Renderer>
+ where
+ Message: 'a,
+ {
+ fn from(loupe: Loupe<'a, Message>) -> Self {
+ Self::new(loupe)
+ }
+ }
+}
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/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<T: Damage> Damage for Primitive<T> {
.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<T: Damage>(a: &Primitive<T>, b: &Primitive<T>) -> Vec<Rectangle> {
}
}
(
- 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/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 aed59e1a..6929b0a1 100644
--- a/graphics/src/primitive.rs
+++ b/graphics/src/primitive.rs
@@ -4,7 +4,8 @@ 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;
@@ -104,12 +105,12 @@ pub enum Primitive<T> {
/// The content of the clip
content: Box<Primitive<T>>,
},
- /// 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<Primitive<T>>,
},
/// A cached primitive.
@@ -125,12 +126,12 @@ pub enum Primitive<T> {
}
impl<T> Primitive<T> {
- /// Creates a [`Primitive::Group`].
+ /// Groups the current [`Primitive`].
pub fn group(primitives: Vec<Self>) -> 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<T> Primitive<T> {
}
}
- /// 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/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<B: Backend> Renderer<B> {
}
/// Starts recording a translation.
- pub fn start_translation(&mut self) -> Vec<Primitive<B::Primitive>> {
+ pub fn start_transformation(&mut self) -> Vec<Primitive<B::Primitive>> {
std::mem::take(&mut self.primitives)
}
/// Ends the recording of a translation.
- pub fn end_translation(
+ pub fn end_transformation(
&mut self,
primitives: Vec<Primitive<B::Primitive>>,
- 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<B: Backend> iced_core::Renderer for Renderer<B> {
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 cf0457a4..00000000
--- a/graphics/src/transformation.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use glam::{Mat4, Vec3};
-use std::ops::Mul;
-
-/// A 2D transformation matrix.
-#[derive(Debug, Clone, Copy, PartialEq)]
-pub struct Transformation(Mat4);
-
-impl Transformation {
- /// Get the identity transformation.
- pub fn identity() -> Transformation {
- Transformation(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 scale transformation.
- pub fn scale(x: f32, y: f32) -> Transformation {
- Transformation(Mat4::from_scale(Vec3::new(x, y, 1.0)))
- }
-}
-
-impl Mul for Transformation {
- type Output = Self;
-
- fn mul(self, rhs: Self) -> Self {
- Transformation(self.0 * rhs.0)
- }
-}
-
-impl AsRef<[f32; 16]> for Transformation {
- fn as_ref(&self) -> &[f32; 16] {
- self.0.as_ref()
- }
-}
-
-impl From<Transformation> for [f32; 16] {
- fn from(t: Transformation) -> [f32; 16] {
- *t.as_ref()
- }
-}
-
-impl From<Transformation> 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 cd2140dc..f09ccfbf 100644
--- a/renderer/src/geometry.rs
+++ b/renderer/src/geometry.rs
@@ -2,14 +2,18 @@ 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::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 +22,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/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 468bcb7e..44f5c151 100644
--- a/tiny_skia/src/backend.rs
+++ b/tiny_skia/src/backend.rs
@@ -1,6 +1,8 @@
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::Viewport;
@@ -106,7 +108,7 @@ impl Backend {
clip_mask,
region,
scale_factor,
- Vector::ZERO,
+ Transformation::IDENTITY,
);
}
@@ -146,7 +148,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 +166,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 +175,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 +198,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 +450,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,11 +461,12 @@ impl Backend {
self.text_pipeline.draw_paragraph(
paragraph,
- *position + translation,
+ *position,
*color,
scale_factor,
pixels,
clip_mask,
+ transformation,
);
}
Primitive::Editor {
@@ -476,7 +476,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,11 +487,12 @@ impl Backend {
self.text_pipeline.draw_editor(
editor,
- *position + translation,
+ *position,
*color,
scale_factor,
pixels,
clip_mask,
+ transformation,
);
}
Primitive::Text {
@@ -507,7 +508,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 +519,7 @@ impl Backend {
self.text_pipeline.draw_cached(
content,
- *bounds + translation,
+ *bounds,
*color,
*size,
*line_height,
@@ -529,6 +530,7 @@ impl Backend {
scale_factor,
pixels,
clip_mask,
+ transformation,
);
}
Primitive::RawText(text::Raw {
@@ -542,7 +544,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,11 +555,12 @@ impl Backend {
self.text_pipeline.draw_raw(
&buffer,
- *position + translation,
+ *position,
*color,
scale_factor,
pixels,
clip_mask,
+ transformation,
);
}
#[cfg(feature = "image")]
@@ -566,7 +569,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 +578,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 +602,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 +614,7 @@ impl Backend {
self.vector_pipeline.draw(
handle,
*color,
- (*bounds + translation) * scale_factor,
+ (*bounds * transformation) * scale_factor,
pixels,
clip_mask,
);
@@ -637,7 +637,7 @@ impl Backend {
y: bounds.y(),
width: bounds.width(),
height: bounds.height(),
- } + translation)
+ } * transformation)
* scale_factor;
if !clip_bounds.intersects(&physical_bounds) {
@@ -651,11 +651,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 +668,7 @@ impl Backend {
y: bounds.y(),
width: bounds.width(),
height: bounds.height(),
- } + translation)
+ } * transformation)
* scale_factor;
if !clip_bounds.intersects(&physical_bounds) {
@@ -685,11 +682,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 +695,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 +709,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 +722,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 +743,7 @@ impl Backend {
clip_mask,
bounds,
scale_factor,
- translation,
+ transformation,
);
adjust_clip_mask(clip_mask, clip_bounds);
@@ -762,7 +756,7 @@ impl Backend {
clip_mask,
clip_bounds,
scale_factor,
- translation,
+ transformation,
);
}
}
@@ -780,6 +774,19 @@ fn into_color(color: Color) -> tiny_skia::Color {
.expect("Convert color from iced to tiny_skia")
}
+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: translation.x,
+ ty: 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..f7518731 100644
--- a/tiny_skia/src/geometry.rs
+++ b/tiny_skia/src/geometry.rs
@@ -1,5 +1,5 @@
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};
@@ -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/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 25134d68..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;
@@ -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/geometry.rs b/wgpu/src/geometry.rs
index a4d4fb1f..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::{
@@ -435,7 +435,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 +443,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/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 e213c95f..cc767c25 100644
--- a/wgpu/src/layer.rs
+++ b/wgpu/src/layer.rs
@@ -12,7 +12,9 @@ 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::Viewport;
@@ -104,7 +106,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 +117,7 @@ impl<'a> Layer<'a> {
fn process_primitive(
layers: &mut Vec<Self>,
- translation: Vector,
+ transformation: Transformation,
primitive: &'a Primitive,
current_layer: usize,
) {
@@ -130,9 +132,10 @@ impl<'a> Layer<'a> {
layer.text.push(Text::Paragraph {
paragraph: paragraph.clone(),
- position: *position + translation,
+ position: *position,
color: *color,
- clip_bounds: *clip_bounds + translation,
+ clip_bounds: *clip_bounds,
+ transformation,
});
}
Primitive::Editor {
@@ -145,9 +148,10 @@ impl<'a> Layer<'a> {
layer.text.push(Text::Editor {
editor: editor.clone(),
- position: *position + translation,
+ position: *position,
color: *color,
- clip_bounds: *clip_bounds + translation,
+ clip_bounds: *clip_bounds,
+ transformation,
});
}
Primitive::Text {
@@ -166,31 +170,24 @@ impl<'a> Layer<'a> {
layer.text.push(Text::Cached(text::Cached {
content,
- bounds: *bounds + translation,
- size: *size,
+ bounds: *bounds + transformation.translation(),
+ size: *size * transformation.scale_factor(),
line_height: *line_height,
color: *color,
font: *font,
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 {
- 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 + translation,
- color: *color,
- clip_bounds: *clip_bounds + translation,
- }));
+ layer.text.push(Text::Raw {
+ raw: raw.clone(),
+ transformation,
+ });
}
Primitive::Quad {
bounds,
@@ -199,12 +196,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 +221,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 +234,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 +242,7 @@ impl<'a> Layer<'a> {
for primitive in primitives {
Self::process_primitive(
layers,
- translation,
+ transformation,
primitive,
current_layer,
);
@@ -255,7 +250,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 +261,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 +281,7 @@ impl<'a> Layer<'a> {
Primitive::Cache { content } => {
Self::process_primitive(
layers,
- translation,
+ transformation,
content,
current_layer,
);
@@ -296,20 +291,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 +308,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 +325,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..5ed7c654 100644
--- a/wgpu/src/layer/mesh.rs
+++ b/wgpu/src/layer/mesh.rs
@@ -1,5 +1,5 @@
//! A collection of triangle primitives.
-use crate::core::{Point, Rectangle};
+use crate::core::{Rectangle, Transformation};
use crate::graphics::mesh;
/// A mesh of triangles.
@@ -7,8 +7,8 @@ use crate::graphics::mesh;
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<mesh::SolidVertex2D>,
@@ -18,8 +18,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<mesh::GradientVertex2D>,
@@ -31,11 +31,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..b3a00130 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,6 +15,7 @@ pub enum Text<'a> {
position: Point,
color: Color,
clip_bounds: Rectangle,
+ transformation: Transformation,
},
/// An editor.
#[allow(missing_docs)]
@@ -23,11 +24,16 @@ pub enum Text<'a> {
position: Point,
color: Color,
clip_bounds: Rectangle,
+ transformation: Transformation,
},
/// 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/quad.rs b/wgpu/src/quad.rs
index cda1bec9..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};
@@ -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/text.rs b/wgpu/src/text.rs
index dca09cb8..6fa1922d 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};
@@ -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();
@@ -124,11 +126,13 @@ impl Pipeline {
vertical_alignment,
color,
clip_bounds,
+ transformation,
) = match section {
Text::Paragraph {
position,
color,
clip_bounds,
+ transformation,
..
} => {
use crate::core::text::Paragraph as _;
@@ -145,12 +149,14 @@ impl Pipeline {
paragraph.vertical_alignment(),
*color,
*clip_bounds,
+ *transformation,
)
}
Text::Editor {
position,
color,
clip_bounds,
+ transformation,
..
} => {
use crate::core::text::Editor as _;
@@ -167,6 +173,7 @@ impl Pipeline {
alignment::Vertical::Top,
*color,
*clip_bounds,
+ *transformation,
)
}
Text::Cached(text) => {
@@ -186,9 +193,13 @@ impl Pipeline {
text.vertical_alignment,
text.color,
text.clip_bounds,
+ Transformation::IDENTITY,
)
}
- Text::Raw(text) => {
+ Text::Raw {
+ raw,
+ transformation,
+ } => {
let Some(Allocation::Raw(buffer)) = allocation else {
return None;
};
@@ -198,18 +209,19 @@ 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,
+ raw.color,
+ raw.clip_bounds,
+ *transformation,
)
}
};
- let bounds = bounds * scale_factor;
+ let bounds = bounds * transformation * scale_factor;
let left = match horizontal_alignment {
alignment::Horizontal::Left => bounds.x,
@@ -227,14 +239,15 @@ 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,
left,
top,
- 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 69270a73..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;
@@ -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);
diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs
index b95e6206..0eda0191 100644
--- a/widget/src/canvas.rs
+++ b/widget/src/canvas.rs
@@ -15,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;
@@ -207,8 +207,8 @@ where
let state = tree.state.downcast_ref::<P::State>();
- 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/pane_grid.rs b/widget/src/pane_grid.rs
index f76c6088..4f9a265a 100644
--- a/widget/src/pane_grid.rs
+++ b/widget/src/pane_grid.rs
@@ -962,22 +962,21 @@ pub fn draw<Theme, Renderer, T>(
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),
- |renderer| {
- renderer.with_layer(bounds, |renderer| {
- draw_pane(
- pane,
- renderer,
- default_style,
- layout,
- pane_cursor,
- viewport,
- );
- });
- },
- );
+ let translation = cursor_position
+ - Point::new(bounds.x + origin.x, bounds.y + origin.y);
+
+ 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 91c0a97b..6a748e63 100644
--- a/widget/src/qr_code.rs
+++ b/widget/src/qr_code.rs
@@ -119,11 +119,12 @@ impl<'a, Message, Theme> Widget<Message, Theme, Renderer> for QRCode<'a> {
});
});
- let translation = Vector::new(bounds.x, bounds.y);
-
- renderer.with_translation(translation, |renderer| {
- renderer.draw(vec![geometry]);
- });
+ renderer.with_translation(
+ bounds.position() - Point::ORIGIN,
+ |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,
}
}