Merge pull request #2120 from iced-rs/transform-primitive

`Transform` primitive
This commit is contained in:
Héctor Ramón 2024-02-02 14:54:56 +01:00 committed by GitHub
commit aea172543c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
36 changed files with 625 additions and 295 deletions

View file

@ -12,6 +12,7 @@ keywords.workspace = true
[dependencies] [dependencies]
bitflags.workspace = true bitflags.workspace = true
glam.workspace = true
log.workspace = true log.workspace = true
num-traits.workspace = true num-traits.workspace = true
smol_str.workspace = true smol_str.workspace = true

View file

@ -49,6 +49,7 @@ mod rectangle;
mod shadow; mod shadow;
mod shell; mod shell;
mod size; mod size;
mod transformation;
mod vector; mod vector;
pub use alignment::Alignment; pub use alignment::Alignment;
@ -75,6 +76,7 @@ pub use shadow::Shadow;
pub use shell::Shell; pub use shell::Shell;
pub use size::Size; pub use size::Size;
pub use text::Text; pub use text::Text;
pub use transformation::Transformation;
pub use vector::Vector; pub use vector::Vector;
pub use widget::Widget; pub use widget::Widget;

View file

@ -13,4 +13,5 @@ pub enum Interaction {
ResizingHorizontally, ResizingHorizontally,
ResizingVertically, ResizingVertically,
NotAllowed, NotAllowed,
ZoomIn,
} }

View file

@ -26,3 +26,11 @@ impl From<Pixels> for f32 {
pixels.0 pixels.0
} }
} }
impl std::ops::Mul<f32> for Pixels {
type Output = Pixels;
fn mul(self, rhs: f32) -> Self {
Pixels(self.0 * rhs)
}
}

View file

@ -5,7 +5,9 @@ mod null;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub use null::Null; 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. /// A component that can be used by widgets to draw themselves on a screen.
pub trait Renderer: Sized { pub trait Renderer: Sized {
@ -14,12 +16,24 @@ pub trait Renderer: Sized {
/// The layer will clip its contents to the provided `bounds`. /// The layer will clip its contents to the provided `bounds`.
fn with_layer(&mut self, bounds: Rectangle, f: impl FnOnce(&mut Self)); 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( fn with_translation(
&mut self, &mut self,
translation: Vector, translation: Vector,
f: impl FnOnce(&mut Self), f: impl FnOnce(&mut Self),
); ) {
self.with_transformation(
Transformation::translate(translation.x, translation.y),
f,
);
}
/// Fills a [`Quad`] with the provided [`Background`]. /// Fills a [`Quad`] with the provided [`Background`].
fn fill_quad(&mut self, quad: Quad, background: impl Into<Background>); fn fill_quad(&mut self, quad: Quad, background: impl Into<Background>);

View file

@ -1,7 +1,9 @@
use crate::alignment; use crate::alignment;
use crate::renderer::{self, Renderer}; use crate::renderer::{self, Renderer};
use crate::text::{self, Text}; 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; use std::borrow::Cow;
@ -21,9 +23,9 @@ impl Null {
impl Renderer for Null { impl Renderer for Null {
fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {} fn with_layer(&mut self, _bounds: Rectangle, _f: impl FnOnce(&mut Self)) {}
fn with_translation( fn with_transformation(
&mut self, &mut self,
_translation: Vector, _transformation: Transformation,
_f: impl FnOnce(&mut Self), _f: impl FnOnce(&mut Self),
) { ) {
} }

119
core/src/transformation.rs Normal file
View file

@ -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
}
}

View file

@ -7,4 +7,4 @@ publish = false
[dependencies] [dependencies]
iced.workspace = true iced.workspace = true
iced.features = ["canvas"] iced.features = ["canvas", "debug"]

10
examples/loupe/Cargo.toml Normal file
View file

@ -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"]

185
examples/loupe/src/main.rs Normal file
View file

@ -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)
}
}
}

View file

@ -26,7 +26,6 @@ iced_futures.workspace = true
bitflags.workspace = true bitflags.workspace = true
bytemuck.workspace = true bytemuck.workspace = true
cosmic-text.workspace = true cosmic-text.workspace = true
glam.workspace = true
half.workspace = true half.workspace = true
log.workspace = true log.workspace = true
once_cell.workspace = true once_cell.workspace = true

View file

@ -102,10 +102,10 @@ impl<T: Damage> Damage for Primitive<T> {
.fold(Rectangle::with_size(Size::ZERO), |a, b| { .fold(Rectangle::with_size(Size::ZERO), |a, b| {
Rectangle::union(&a, &b) Rectangle::union(&a, &b)
}), }),
Self::Translate { Self::Transform {
translation, transformation,
content, content,
} => content.bounds() + *translation, } => content.bounds() * *transformation,
Self::Cache { content } => content.bounds(), Self::Cache { content } => content.bounds(),
Self::Custom(custom) => custom.bounds(), Self::Custom(custom) => custom.bounds(),
} }
@ -144,19 +144,19 @@ fn regions<T: Damage>(a: &Primitive<T>, b: &Primitive<T>) -> Vec<Rectangle> {
} }
} }
( (
Primitive::Translate { Primitive::Transform {
translation: translation_a, transformation: transformation_a,
content: content_a, content: content_a,
}, },
Primitive::Translate { Primitive::Transform {
translation: translation_b, transformation: transformation_b,
content: content_b, content: content_b,
}, },
) => { ) => {
if translation_a == translation_b { if transformation_a == transformation_b {
return regions(content_a, content_b) return regions(content_a, content_b)
.into_iter() .into_iter()
.map(|r| r + *translation_a) .map(|r| r * *transformation_a)
.collect(); .collect();
} }
} }

View file

@ -19,7 +19,6 @@
mod antialiasing; mod antialiasing;
mod error; mod error;
mod primitive; mod primitive;
mod transformation;
mod viewport; mod viewport;
pub mod backend; pub mod backend;
@ -46,7 +45,6 @@ pub use gradient::Gradient;
pub use mesh::Mesh; pub use mesh::Mesh;
pub use primitive::Primitive; pub use primitive::Primitive;
pub use renderer::Renderer; pub use renderer::Renderer;
pub use transformation::Transformation;
pub use viewport::Viewport; pub use viewport::Viewport;
pub use iced_core as core; pub use iced_core as core;

View file

@ -4,7 +4,8 @@ use crate::core::image;
use crate::core::svg; use crate::core::svg;
use crate::core::text; use crate::core::text;
use crate::core::{ 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::editor;
use crate::text::paragraph; use crate::text::paragraph;
@ -104,12 +105,12 @@ pub enum Primitive<T> {
/// The content of the clip /// The content of the clip
content: Box<Primitive<T>>, content: Box<Primitive<T>>,
}, },
/// A primitive that applies a translation /// A primitive that applies a [`Transformation`]
Translate { Transform {
/// The translation vector /// The [`Transformation`]
translation: Vector, transformation: Transformation,
/// The primitive to translate /// The primitive to transform
content: Box<Primitive<T>>, content: Box<Primitive<T>>,
}, },
/// A cached primitive. /// A cached primitive.
@ -125,12 +126,12 @@ pub enum Primitive<T> {
} }
impl<T> Primitive<T> { impl<T> Primitive<T> {
/// Creates a [`Primitive::Group`]. /// Groups the current [`Primitive`].
pub fn group(primitives: Vec<Self>) -> Self { pub fn group(primitives: Vec<Self>) -> Self {
Self::Group { primitives } Self::Group { primitives }
} }
/// Creates a [`Primitive::Clip`]. /// Clips the current [`Primitive`].
pub fn clip(self, bounds: Rectangle) -> Self { pub fn clip(self, bounds: Rectangle) -> Self {
Self::Clip { Self::Clip {
bounds, bounds,
@ -138,10 +139,21 @@ impl<T> Primitive<T> {
} }
} }
/// Creates a [`Primitive::Translate`]. /// Translates the current [`Primitive`].
pub fn translate(self, translation: Vector) -> Self { pub fn translate(self, translation: Vector) -> Self {
Self::Translate { Self::Transform {
translation, 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), content: Box::new(self),
} }
} }

View file

@ -6,7 +6,7 @@ use crate::core::renderer;
use crate::core::svg; use crate::core::svg;
use crate::core::text::Text; use crate::core::text::Text;
use crate::core::{ use crate::core::{
Background, Color, Font, Pixels, Point, Rectangle, Size, Vector, Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation,
}; };
use crate::text; use crate::text;
use crate::Primitive; use crate::Primitive;
@ -73,20 +73,20 @@ impl<B: Backend> Renderer<B> {
} }
/// Starts recording a translation. /// 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) std::mem::take(&mut self.primitives)
} }
/// Ends the recording of a translation. /// Ends the recording of a translation.
pub fn end_translation( pub fn end_transformation(
&mut self, &mut self,
primitives: Vec<Primitive<B::Primitive>>, primitives: Vec<Primitive<B::Primitive>>,
translation: Vector, transformation: Transformation,
) { ) {
let layer = std::mem::replace(&mut self.primitives, primitives); let layer = std::mem::replace(&mut self.primitives, primitives);
self.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); self.end_layer(current, bounds);
} }
fn with_translation( fn with_transformation(
&mut self, &mut self,
translation: Vector, transformation: Transformation,
f: impl FnOnce(&mut Self), f: impl FnOnce(&mut Self),
) { ) {
let current = self.start_translation(); let current = self.start_transformation();
f(self); f(self);
self.end_translation(current, translation); self.end_transformation(current, transformation);
} }
fn fill_quad( fn fill_quad(

View file

@ -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
}
}

View file

@ -1,6 +1,4 @@
use crate::Transformation; use crate::core::{Size, Transformation};
use iced_core::Size;
/// A viewing region for displaying computer graphics. /// A viewing region for displaying computer graphics.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -2,22 +2,10 @@ mod cache;
pub use cache::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::geometry::{Fill, Path, Stroke, Text};
use crate::Renderer; use crate::Renderer;
pub enum Frame {
TinySkia(iced_tiny_skia::geometry::Frame),
#[cfg(feature = "wgpu")]
Wgpu(iced_wgpu::geometry::Frame),
}
pub enum Geometry {
TinySkia(iced_tiny_skia::Primitive),
#[cfg(feature = "wgpu")]
Wgpu(iced_wgpu::Primitive),
}
macro_rules! delegate { macro_rules! delegate {
($frame:expr, $name:ident, $body:expr) => { ($frame:expr, $name:ident, $body:expr) => {
match $frame { match $frame {
@ -28,6 +16,32 @@ macro_rules! delegate {
}; };
} }
pub enum Geometry {
TinySkia(iced_tiny_skia::Primitive),
#[cfg(feature = "wgpu")]
Wgpu(iced_wgpu::Primitive),
}
impl Geometry {
pub fn transform(self, transformation: Transformation) -> Self {
match self {
Self::TinySkia(primitive) => {
Self::TinySkia(primitive.transform(transformation))
}
#[cfg(feature = "wgpu")]
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 { impl Frame {
pub fn new(renderer: &Renderer, size: Size) -> Self { pub fn new(renderer: &Renderer, size: Size) -> Self {
match renderer { match renderer {

View file

@ -22,7 +22,9 @@ pub use geometry::Geometry;
use crate::core::renderer; use crate::core::renderer;
use crate::core::text::{self, Text}; 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::Editor;
use crate::graphics::text::Paragraph; use crate::graphics::text::Paragraph;
use crate::graphics::Mesh; use crate::graphics::Mesh;
@ -97,20 +99,20 @@ impl core::Renderer for Renderer {
} }
} }
fn with_translation( fn with_transformation(
&mut self, &mut self,
translation: Vector, transformation: Transformation,
f: impl FnOnce(&mut Self), f: impl FnOnce(&mut Self),
) { ) {
match self { match self {
Self::TinySkia(renderer) => { Self::TinySkia(renderer) => {
let primitives = renderer.start_translation(); let primitives = renderer.start_transformation();
f(self); f(self);
match self { match self {
Self::TinySkia(renderer) => { Self::TinySkia(renderer) => {
renderer.end_translation(primitives, translation); renderer.end_transformation(primitives, transformation);
} }
#[cfg(feature = "wgpu")] #[cfg(feature = "wgpu")]
_ => unreachable!(), _ => unreachable!(),
@ -118,14 +120,14 @@ impl core::Renderer for Renderer {
} }
#[cfg(feature = "wgpu")] #[cfg(feature = "wgpu")]
Self::Wgpu(renderer) => { Self::Wgpu(renderer) => {
let primitives = renderer.start_translation(); let primitives = renderer.start_transformation();
f(self); f(self);
match self { match self {
#[cfg(feature = "wgpu")] #[cfg(feature = "wgpu")]
Self::Wgpu(renderer) => { Self::Wgpu(renderer) => {
renderer.end_translation(primitives, translation); renderer.end_transformation(primitives, transformation);
} }
_ => unreachable!(), _ => unreachable!(),
} }

View file

@ -193,7 +193,8 @@ pub use crate::core::color;
pub use crate::core::gradient; pub use crate::core::gradient;
pub use crate::core::{ pub use crate::core::{
Alignment, Background, Border, Color, ContentFit, Degrees, Gradient, 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 { pub mod clipboard {

View file

@ -1,6 +1,8 @@
use tiny_skia::Size; 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::backend;
use crate::graphics::text; use crate::graphics::text;
use crate::graphics::Viewport; use crate::graphics::Viewport;
@ -106,7 +108,7 @@ impl Backend {
clip_mask, clip_mask,
region, region,
scale_factor, scale_factor,
Vector::ZERO, Transformation::IDENTITY,
); );
} }
@ -146,7 +148,7 @@ impl Backend {
clip_mask: &mut tiny_skia::Mask, clip_mask: &mut tiny_skia::Mask,
clip_bounds: Rectangle, clip_bounds: Rectangle,
scale_factor: f32, scale_factor: f32,
translation: Vector, transformation: Transformation,
) { ) {
match primitive { match primitive {
Primitive::Quad { Primitive::Quad {
@ -164,7 +166,7 @@ impl Backend {
"Quad with non-normal height!" "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) { if !clip_bounds.intersects(&physical_bounds) {
return; return;
@ -173,11 +175,8 @@ impl Backend {
let clip_mask = (!physical_bounds.is_within(&clip_bounds)) let clip_mask = (!physical_bounds.is_within(&clip_bounds))
.then_some(clip_mask as &_); .then_some(clip_mask as &_);
let transform = tiny_skia::Transform::from_translate( let transform = into_transform(transformation)
translation.x, .post_scale(scale_factor, scale_factor);
translation.y,
)
.post_scale(scale_factor, scale_factor);
// Make sure the border radius is not larger than the bounds // Make sure the border radius is not larger than the bounds
let border_width = border let border_width = border
@ -199,7 +198,7 @@ impl Backend {
y: bounds.y + shadow.offset.y - shadow.blur_radius, y: bounds.y + shadow.offset.y - shadow.blur_radius,
width: bounds.width + shadow.blur_radius * 2.0, width: bounds.width + shadow.blur_radius * 2.0,
height: bounds.height + shadow.blur_radius * 2.0, height: bounds.height + shadow.blur_radius * 2.0,
} + translation) } * transformation)
* scale_factor; * scale_factor;
let radii = fill_border_radius let radii = fill_border_radius
@ -451,7 +450,7 @@ impl Backend {
clip_bounds: text_clip_bounds, clip_bounds: text_clip_bounds,
} => { } => {
let physical_bounds = let physical_bounds =
(*text_clip_bounds + translation) * scale_factor; *text_clip_bounds * transformation * scale_factor;
if !clip_bounds.intersects(&physical_bounds) { if !clip_bounds.intersects(&physical_bounds) {
return; return;
@ -462,11 +461,12 @@ impl Backend {
self.text_pipeline.draw_paragraph( self.text_pipeline.draw_paragraph(
paragraph, paragraph,
*position + translation, *position,
*color, *color,
scale_factor, scale_factor,
pixels, pixels,
clip_mask, clip_mask,
transformation,
); );
} }
Primitive::Editor { Primitive::Editor {
@ -476,7 +476,7 @@ impl Backend {
clip_bounds: text_clip_bounds, clip_bounds: text_clip_bounds,
} => { } => {
let physical_bounds = let physical_bounds =
(*text_clip_bounds + translation) * scale_factor; (*text_clip_bounds * transformation) * scale_factor;
if !clip_bounds.intersects(&physical_bounds) { if !clip_bounds.intersects(&physical_bounds) {
return; return;
@ -487,11 +487,12 @@ impl Backend {
self.text_pipeline.draw_editor( self.text_pipeline.draw_editor(
editor, editor,
*position + translation, *position,
*color, *color,
scale_factor, scale_factor,
pixels, pixels,
clip_mask, clip_mask,
transformation,
); );
} }
Primitive::Text { Primitive::Text {
@ -507,7 +508,7 @@ impl Backend {
clip_bounds: text_clip_bounds, clip_bounds: text_clip_bounds,
} => { } => {
let physical_bounds = let physical_bounds =
(*text_clip_bounds + translation) * scale_factor; *text_clip_bounds * transformation * scale_factor;
if !clip_bounds.intersects(&physical_bounds) { if !clip_bounds.intersects(&physical_bounds) {
return; return;
@ -518,7 +519,7 @@ impl Backend {
self.text_pipeline.draw_cached( self.text_pipeline.draw_cached(
content, content,
*bounds + translation, *bounds,
*color, *color,
*size, *size,
*line_height, *line_height,
@ -529,6 +530,7 @@ impl Backend {
scale_factor, scale_factor,
pixels, pixels,
clip_mask, clip_mask,
transformation,
); );
} }
Primitive::RawText(text::Raw { Primitive::RawText(text::Raw {
@ -542,7 +544,7 @@ impl Backend {
}; };
let physical_bounds = let physical_bounds =
(*text_clip_bounds + translation) * scale_factor; *text_clip_bounds * transformation * scale_factor;
if !clip_bounds.intersects(&physical_bounds) { if !clip_bounds.intersects(&physical_bounds) {
return; return;
@ -553,11 +555,12 @@ impl Backend {
self.text_pipeline.draw_raw( self.text_pipeline.draw_raw(
&buffer, &buffer,
*position + translation, *position,
*color, *color,
scale_factor, scale_factor,
pixels, pixels,
clip_mask, clip_mask,
transformation,
); );
} }
#[cfg(feature = "image")] #[cfg(feature = "image")]
@ -566,7 +569,7 @@ impl Backend {
filter_method, filter_method,
bounds, bounds,
} => { } => {
let physical_bounds = (*bounds + translation) * scale_factor; let physical_bounds = (*bounds * transformation) * scale_factor;
if !clip_bounds.intersects(&physical_bounds) { if !clip_bounds.intersects(&physical_bounds) {
return; return;
@ -575,11 +578,8 @@ impl Backend {
let clip_mask = (!physical_bounds.is_within(&clip_bounds)) let clip_mask = (!physical_bounds.is_within(&clip_bounds))
.then_some(clip_mask as &_); .then_some(clip_mask as &_);
let transform = tiny_skia::Transform::from_translate( let transform = into_transform(transformation)
translation.x, .post_scale(scale_factor, scale_factor);
translation.y,
)
.post_scale(scale_factor, scale_factor);
self.raster_pipeline.draw( self.raster_pipeline.draw(
handle, handle,
@ -602,7 +602,7 @@ impl Backend {
bounds, bounds,
color, color,
} => { } => {
let physical_bounds = (*bounds + translation) * scale_factor; let physical_bounds = (*bounds * transformation) * scale_factor;
if !clip_bounds.intersects(&physical_bounds) { if !clip_bounds.intersects(&physical_bounds) {
return; return;
@ -614,7 +614,7 @@ impl Backend {
self.vector_pipeline.draw( self.vector_pipeline.draw(
handle, handle,
*color, *color,
(*bounds + translation) * scale_factor, (*bounds * transformation) * scale_factor,
pixels, pixels,
clip_mask, clip_mask,
); );
@ -637,7 +637,7 @@ impl Backend {
y: bounds.y(), y: bounds.y(),
width: bounds.width(), width: bounds.width(),
height: bounds.height(), height: bounds.height(),
} + translation) } * transformation)
* scale_factor; * scale_factor;
if !clip_bounds.intersects(&physical_bounds) { if !clip_bounds.intersects(&physical_bounds) {
@ -651,11 +651,8 @@ impl Backend {
path, path,
paint, paint,
*rule, *rule,
tiny_skia::Transform::from_translate( into_transform(transformation)
translation.x, .post_scale(scale_factor, scale_factor),
translation.y,
)
.post_scale(scale_factor, scale_factor),
clip_mask, clip_mask,
); );
} }
@ -671,7 +668,7 @@ impl Backend {
y: bounds.y(), y: bounds.y(),
width: bounds.width(), width: bounds.width(),
height: bounds.height(), height: bounds.height(),
} + translation) } * transformation)
* scale_factor; * scale_factor;
if !clip_bounds.intersects(&physical_bounds) { if !clip_bounds.intersects(&physical_bounds) {
@ -685,11 +682,8 @@ impl Backend {
path, path,
paint, paint,
stroke, stroke,
tiny_skia::Transform::from_translate( into_transform(transformation)
translation.x, .post_scale(scale_factor, scale_factor),
translation.y,
)
.post_scale(scale_factor, scale_factor),
clip_mask, clip_mask,
); );
} }
@ -701,12 +695,12 @@ impl Backend {
clip_mask, clip_mask,
clip_bounds, clip_bounds,
scale_factor, scale_factor,
translation, transformation,
); );
} }
} }
Primitive::Translate { Primitive::Transform {
translation: offset, transformation: new_transformation,
content, content,
} => { } => {
self.draw_primitive( self.draw_primitive(
@ -715,11 +709,11 @@ impl Backend {
clip_mask, clip_mask,
clip_bounds, clip_bounds,
scale_factor, scale_factor,
translation + *offset, transformation * *new_transformation,
); );
} }
Primitive::Clip { bounds, content } => { Primitive::Clip { bounds, content } => {
let bounds = (*bounds + translation) * scale_factor; let bounds = (*bounds * transformation) * scale_factor;
if bounds == clip_bounds { if bounds == clip_bounds {
self.draw_primitive( self.draw_primitive(
@ -728,7 +722,7 @@ impl Backend {
clip_mask, clip_mask,
bounds, bounds,
scale_factor, scale_factor,
translation, transformation,
); );
} else if let Some(bounds) = clip_bounds.intersection(&bounds) { } else if let Some(bounds) = clip_bounds.intersection(&bounds) {
if bounds.x + bounds.width <= 0.0 if bounds.x + bounds.width <= 0.0
@ -749,7 +743,7 @@ impl Backend {
clip_mask, clip_mask,
bounds, bounds,
scale_factor, scale_factor,
translation, transformation,
); );
adjust_clip_mask(clip_mask, clip_bounds); adjust_clip_mask(clip_mask, clip_bounds);
@ -762,7 +756,7 @@ impl Backend {
clip_mask, clip_mask,
clip_bounds, clip_bounds,
scale_factor, scale_factor,
translation, transformation,
); );
} }
} }
@ -780,6 +774,19 @@ fn into_color(color: Color) -> tiny_skia::Color {
.expect("Convert color from iced to tiny_skia") .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( fn rounded_rectangle(
bounds: Rectangle, bounds: Rectangle,
border_radius: [f32; 4], border_radius: [f32; 4],

View file

@ -1,5 +1,5 @@
use crate::core::text::LineHeight; 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::fill::{self, Fill};
use crate::graphics::geometry::stroke::{self, Stroke}; use crate::graphics::geometry::stroke::{self, Stroke};
use crate::graphics::geometry::{Path, Style, Text}; use crate::graphics::geometry::{Path, Style, Text};
@ -181,8 +181,8 @@ impl Frame {
} }
pub fn clip(&mut self, frame: Self, at: Point) { pub fn clip(&mut self, frame: Self, at: Point) {
self.primitives.push(Primitive::Translate { self.primitives.push(Primitive::Transform {
translation: Vector::new(at.x, at.y), transformation: Transformation::translate(at.x, at.y),
content: Box::new(frame.into_primitive()), content: Box::new(frame.into_primitive()),
}); });
} }

View file

@ -1,6 +1,8 @@
use crate::core::alignment; use crate::core::alignment;
use crate::core::text::{LineHeight, Shaping}; 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::cache::{self, Cache};
use crate::graphics::text::editor; use crate::graphics::text::editor;
use crate::graphics::text::font_system; use crate::graphics::text::font_system;
@ -42,6 +44,7 @@ impl Pipeline {
scale_factor: f32, scale_factor: f32,
pixels: &mut tiny_skia::PixmapMut<'_>, pixels: &mut tiny_skia::PixmapMut<'_>,
clip_mask: Option<&tiny_skia::Mask>, clip_mask: Option<&tiny_skia::Mask>,
transformation: Transformation,
) { ) {
use crate::core::text::Paragraph as _; use crate::core::text::Paragraph as _;
@ -62,6 +65,7 @@ impl Pipeline {
scale_factor, scale_factor,
pixels, pixels,
clip_mask, clip_mask,
transformation,
); );
} }
@ -73,6 +77,7 @@ impl Pipeline {
scale_factor: f32, scale_factor: f32,
pixels: &mut tiny_skia::PixmapMut<'_>, pixels: &mut tiny_skia::PixmapMut<'_>,
clip_mask: Option<&tiny_skia::Mask>, clip_mask: Option<&tiny_skia::Mask>,
transformation: Transformation,
) { ) {
use crate::core::text::Editor as _; use crate::core::text::Editor as _;
@ -93,6 +98,7 @@ impl Pipeline {
scale_factor, scale_factor,
pixels, pixels,
clip_mask, clip_mask,
transformation,
); );
} }
@ -110,6 +116,7 @@ impl Pipeline {
scale_factor: f32, scale_factor: f32,
pixels: &mut tiny_skia::PixmapMut<'_>, pixels: &mut tiny_skia::PixmapMut<'_>,
clip_mask: Option<&tiny_skia::Mask>, clip_mask: Option<&tiny_skia::Mask>,
transformation: Transformation,
) { ) {
let line_height = f32::from(line_height.to_absolute(size)); let line_height = f32::from(line_height.to_absolute(size));
@ -145,6 +152,7 @@ impl Pipeline {
scale_factor, scale_factor,
pixels, pixels,
clip_mask, clip_mask,
transformation,
); );
} }
@ -156,6 +164,7 @@ impl Pipeline {
scale_factor: f32, scale_factor: f32,
pixels: &mut tiny_skia::PixmapMut<'_>, pixels: &mut tiny_skia::PixmapMut<'_>,
clip_mask: Option<&tiny_skia::Mask>, clip_mask: Option<&tiny_skia::Mask>,
transformation: Transformation,
) { ) {
let mut font_system = font_system().write().expect("Write font system"); let mut font_system = font_system().write().expect("Write font system");
@ -172,6 +181,7 @@ impl Pipeline {
scale_factor, scale_factor,
pixels, pixels,
clip_mask, clip_mask,
transformation,
); );
} }
@ -192,8 +202,9 @@ fn draw(
scale_factor: f32, scale_factor: f32,
pixels: &mut tiny_skia::PixmapMut<'_>, pixels: &mut tiny_skia::PixmapMut<'_>,
clip_mask: Option<&tiny_skia::Mask>, clip_mask: Option<&tiny_skia::Mask>,
transformation: Transformation,
) { ) {
let bounds = bounds * scale_factor; let bounds = bounds * transformation * scale_factor;
let x = match horizontal_alignment { let x = match horizontal_alignment {
alignment::Horizontal::Left => bounds.x, alignment::Horizontal::Left => bounds.x,
@ -211,7 +222,8 @@ fn draw(
for run in buffer.layout_runs() { for run in buffer.layout_runs() {
for glyph in run.glyphs { 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( if let Some((buffer, placement)) = glyph_cache.allocate(
physical_glyph.cache_key, physical_glyph.cache_key,
@ -229,7 +241,10 @@ fn draw(
pixels.draw_pixmap( pixels.draw_pixmap(
physical_glyph.x + placement.left, physical_glyph.x + placement.left,
physical_glyph.y - placement.top 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, pixmap,
&tiny_skia::PixmapPaint::default(), &tiny_skia::PixmapPaint::default(),
tiny_skia::Transform::identity(), tiny_skia::Transform::identity(),

View file

@ -1,7 +1,7 @@
use crate::core::{Color, Size}; use crate::core::{Color, Size, Transformation};
use crate::graphics::backend; use crate::graphics::backend;
use crate::graphics::color; use crate::graphics::color;
use crate::graphics::{Transformation, Viewport}; use crate::graphics::Viewport;
use crate::primitive::pipeline; use crate::primitive::pipeline;
use crate::primitive::{self, Primitive}; use crate::primitive::{self, Primitive};
use crate::quad; use crate::quad;
@ -147,8 +147,8 @@ impl Backend {
} }
if !layer.meshes.is_empty() { if !layer.meshes.is_empty() {
let scaled = transformation let scaled =
* Transformation::scale(scale_factor, scale_factor); transformation * Transformation::scale(scale_factor);
self.triangle_pipeline.prepare( self.triangle_pipeline.prepare(
device, device,
@ -161,8 +161,8 @@ impl Backend {
#[cfg(any(feature = "image", feature = "svg"))] #[cfg(any(feature = "image", feature = "svg"))]
{ {
if !layer.images.is_empty() { if !layer.images.is_empty() {
let scaled = transformation let scaled =
* Transformation::scale(scale_factor, scale_factor); transformation * Transformation::scale(scale_factor);
self.image_pipeline.prepare( self.image_pipeline.prepare(
device, device,

View file

@ -1,6 +1,6 @@
//! Build and draw geometry. //! Build and draw geometry.
use crate::core::text::LineHeight; 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::color;
use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::fill::{self, Fill};
use crate::graphics::geometry::{ use crate::graphics::geometry::{
@ -435,7 +435,7 @@ impl Frame {
pub fn clip(&mut self, frame: Frame, at: Point) { pub fn clip(&mut self, frame: Frame, at: Point) {
let size = frame.size(); let size = frame.size();
let primitives = frame.into_primitives(); 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 let (text, meshes) = primitives
.into_iter() .into_iter()
@ -443,12 +443,12 @@ impl Frame {
self.primitives.push(Primitive::Group { self.primitives.push(Primitive::Group {
primitives: vec![ primitives: vec![
Primitive::Translate { Primitive::Transform {
translation, transformation,
content: Box::new(Primitive::Group { primitives: meshes }), content: Box::new(Primitive::Group { primitives: meshes }),
}, },
Primitive::Translate { Primitive::Transform {
translation, transformation,
content: Box::new(Primitive::Clip { content: Box::new(Primitive::Clip {
bounds: Rectangle::with_size(size), bounds: Rectangle::with_size(size),
content: Box::new(Primitive::Group { content: Box::new(Primitive::Group {

View file

@ -8,8 +8,7 @@ mod vector;
use atlas::Atlas; use atlas::Atlas;
use crate::core::{Rectangle, Size}; use crate::core::{Rectangle, Size, Transformation};
use crate::graphics::Transformation;
use crate::layer; use crate::layer;
use crate::Buffer; use crate::Buffer;

View file

@ -12,7 +12,9 @@ pub use text::Text;
use crate::core; use crate::core;
use crate::core::alignment; 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;
use crate::graphics::color; use crate::graphics::color;
use crate::graphics::Viewport; use crate::graphics::Viewport;
@ -104,7 +106,7 @@ impl<'a> Layer<'a> {
for primitive in primitives { for primitive in primitives {
Self::process_primitive( Self::process_primitive(
&mut layers, &mut layers,
Vector::new(0.0, 0.0), Transformation::IDENTITY,
primitive, primitive,
0, 0,
); );
@ -115,7 +117,7 @@ impl<'a> Layer<'a> {
fn process_primitive( fn process_primitive(
layers: &mut Vec<Self>, layers: &mut Vec<Self>,
translation: Vector, transformation: Transformation,
primitive: &'a Primitive, primitive: &'a Primitive,
current_layer: usize, current_layer: usize,
) { ) {
@ -130,9 +132,10 @@ impl<'a> Layer<'a> {
layer.text.push(Text::Paragraph { layer.text.push(Text::Paragraph {
paragraph: paragraph.clone(), paragraph: paragraph.clone(),
position: *position + translation, position: *position,
color: *color, color: *color,
clip_bounds: *clip_bounds + translation, clip_bounds: *clip_bounds,
transformation,
}); });
} }
Primitive::Editor { Primitive::Editor {
@ -145,9 +148,10 @@ impl<'a> Layer<'a> {
layer.text.push(Text::Editor { layer.text.push(Text::Editor {
editor: editor.clone(), editor: editor.clone(),
position: *position + translation, position: *position,
color: *color, color: *color,
clip_bounds: *clip_bounds + translation, clip_bounds: *clip_bounds,
transformation,
}); });
} }
Primitive::Text { Primitive::Text {
@ -166,31 +170,24 @@ impl<'a> Layer<'a> {
layer.text.push(Text::Cached(text::Cached { layer.text.push(Text::Cached(text::Cached {
content, content,
bounds: *bounds + translation, bounds: *bounds + transformation.translation(),
size: *size, size: *size * transformation.scale_factor(),
line_height: *line_height, line_height: *line_height,
color: *color, color: *color,
font: *font, font: *font,
horizontal_alignment: *horizontal_alignment, horizontal_alignment: *horizontal_alignment,
vertical_alignment: *vertical_alignment, vertical_alignment: *vertical_alignment,
shaping: *shaping, shaping: *shaping,
clip_bounds: *clip_bounds + translation, clip_bounds: *clip_bounds * transformation,
})); }));
} }
graphics::Primitive::RawText(graphics::text::Raw { graphics::Primitive::RawText(raw) => {
buffer,
position,
color,
clip_bounds,
}) => {
let layer = &mut layers[current_layer]; let layer = &mut layers[current_layer];
layer.text.push(Text::Raw(graphics::text::Raw { layer.text.push(Text::Raw {
buffer: buffer.clone(), raw: raw.clone(),
position: *position + translation, transformation,
color: *color, });
clip_bounds: *clip_bounds + translation,
}));
} }
Primitive::Quad { Primitive::Quad {
bounds, bounds,
@ -199,12 +196,10 @@ impl<'a> Layer<'a> {
shadow, shadow,
} => { } => {
let layer = &mut layers[current_layer]; let layer = &mut layers[current_layer];
let bounds = *bounds * transformation;
let quad = Quad { let quad = Quad {
position: [ position: [bounds.x, bounds.y],
bounds.x + translation.x,
bounds.y + translation.y,
],
size: [bounds.width, bounds.height], size: [bounds.width, bounds.height],
border_color: color::pack(border.color), border_color: color::pack(border.color),
border_radius: border.radius.into(), border_radius: border.radius.into(),
@ -226,7 +221,7 @@ impl<'a> Layer<'a> {
layer.images.push(Image::Raster { layer.images.push(Image::Raster {
handle: handle.clone(), handle: handle.clone(),
filter_method: *filter_method, filter_method: *filter_method,
bounds: *bounds + translation, bounds: *bounds * transformation,
}); });
} }
Primitive::Svg { Primitive::Svg {
@ -239,7 +234,7 @@ impl<'a> Layer<'a> {
layer.images.push(Image::Vector { layer.images.push(Image::Vector {
handle: handle.clone(), handle: handle.clone(),
color: *color, color: *color,
bounds: *bounds + translation, bounds: *bounds * transformation,
}); });
} }
Primitive::Group { primitives } => { Primitive::Group { primitives } => {
@ -247,7 +242,7 @@ impl<'a> Layer<'a> {
for primitive in primitives { for primitive in primitives {
Self::process_primitive( Self::process_primitive(
layers, layers,
translation, transformation,
primitive, primitive,
current_layer, current_layer,
); );
@ -255,7 +250,7 @@ impl<'a> Layer<'a> {
} }
Primitive::Clip { bounds, content } => { Primitive::Clip { bounds, content } => {
let layer = &mut layers[current_layer]; let layer = &mut layers[current_layer];
let translated_bounds = *bounds + translation; let translated_bounds = *bounds * transformation;
// Only draw visible content // Only draw visible content
if let Some(clip_bounds) = if let Some(clip_bounds) =
@ -266,19 +261,19 @@ impl<'a> Layer<'a> {
Self::process_primitive( Self::process_primitive(
layers, layers,
translation, transformation,
content, content,
layers.len() - 1, layers.len() - 1,
); );
} }
} }
Primitive::Translate { Primitive::Transform {
translation: new_translation, transformation: new_transformation,
content, content,
} => { } => {
Self::process_primitive( Self::process_primitive(
layers, layers,
translation + *new_translation, transformation * *new_transformation,
content, content,
current_layer, current_layer,
); );
@ -286,7 +281,7 @@ impl<'a> Layer<'a> {
Primitive::Cache { content } => { Primitive::Cache { content } => {
Self::process_primitive( Self::process_primitive(
layers, layers,
translation, transformation,
content, content,
current_layer, current_layer,
); );
@ -296,20 +291,15 @@ impl<'a> Layer<'a> {
graphics::Mesh::Solid { buffers, size } => { graphics::Mesh::Solid { buffers, size } => {
let layer = &mut layers[current_layer]; let layer = &mut layers[current_layer];
let bounds = Rectangle::new( let bounds =
Point::new(translation.x, translation.y), Rectangle::with_size(*size) * transformation;
*size,
);
// Only draw visible content // Only draw visible content
if let Some(clip_bounds) = if let Some(clip_bounds) =
layer.bounds.intersection(&bounds) layer.bounds.intersection(&bounds)
{ {
layer.meshes.push(Mesh::Solid { layer.meshes.push(Mesh::Solid {
origin: Point::new( transformation,
translation.x,
translation.y,
),
buffers, buffers,
clip_bounds, clip_bounds,
}); });
@ -318,20 +308,15 @@ impl<'a> Layer<'a> {
graphics::Mesh::Gradient { buffers, size } => { graphics::Mesh::Gradient { buffers, size } => {
let layer = &mut layers[current_layer]; let layer = &mut layers[current_layer];
let bounds = Rectangle::new( let bounds =
Point::new(translation.x, translation.y), Rectangle::with_size(*size) * transformation;
*size,
);
// Only draw visible content // Only draw visible content
if let Some(clip_bounds) = if let Some(clip_bounds) =
layer.bounds.intersection(&bounds) layer.bounds.intersection(&bounds)
{ {
layer.meshes.push(Mesh::Gradient { layer.meshes.push(Mesh::Gradient {
origin: Point::new( transformation,
translation.x,
translation.y,
),
buffers, buffers,
clip_bounds, clip_bounds,
}); });
@ -340,7 +325,7 @@ impl<'a> Layer<'a> {
}, },
primitive::Custom::Pipeline(pipeline) => { primitive::Custom::Pipeline(pipeline) => {
let layer = &mut layers[current_layer]; let layer = &mut layers[current_layer];
let bounds = pipeline.bounds + translation; let bounds = pipeline.bounds * transformation;
if let Some(clip_bounds) = if let Some(clip_bounds) =
layer.bounds.intersection(&bounds) layer.bounds.intersection(&bounds)

View file

@ -1,5 +1,5 @@
//! A collection of triangle primitives. //! A collection of triangle primitives.
use crate::core::{Point, Rectangle}; use crate::core::{Rectangle, Transformation};
use crate::graphics::mesh; use crate::graphics::mesh;
/// A mesh of triangles. /// A mesh of triangles.
@ -7,8 +7,8 @@ use crate::graphics::mesh;
pub enum Mesh<'a> { pub enum Mesh<'a> {
/// A mesh of triangles with a solid color. /// A mesh of triangles with a solid color.
Solid { Solid {
/// The origin of the vertices of the [`Mesh`]. /// The [`Transformation`] for the vertices of the [`Mesh`].
origin: Point, transformation: Transformation,
/// The vertex and index buffers of the [`Mesh`]. /// The vertex and index buffers of the [`Mesh`].
buffers: &'a mesh::Indexed<mesh::SolidVertex2D>, buffers: &'a mesh::Indexed<mesh::SolidVertex2D>,
@ -18,8 +18,8 @@ pub enum Mesh<'a> {
}, },
/// A mesh of triangles with a gradient color. /// A mesh of triangles with a gradient color.
Gradient { Gradient {
/// The origin of the vertices of the [`Mesh`]. /// The [`Transformation`] for the vertices of the [`Mesh`].
origin: Point, transformation: Transformation,
/// The vertex and index buffers of the [`Mesh`]. /// The vertex and index buffers of the [`Mesh`].
buffers: &'a mesh::Indexed<mesh::GradientVertex2D>, buffers: &'a mesh::Indexed<mesh::GradientVertex2D>,
@ -31,11 +31,10 @@ pub enum Mesh<'a> {
impl Mesh<'_> { impl Mesh<'_> {
/// Returns the origin of the [`Mesh`]. /// Returns the origin of the [`Mesh`].
pub fn origin(&self) -> Point { pub fn transformation(&self) -> Transformation {
match self { match self {
Self::Solid { origin, .. } | Self::Gradient { origin, .. } => { Self::Solid { transformation, .. }
*origin | Self::Gradient { transformation, .. } => *transformation,
}
} }
} }

View file

@ -1,6 +1,6 @@
use crate::core::alignment; use crate::core::alignment;
use crate::core::text; 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;
use crate::graphics::text::editor; use crate::graphics::text::editor;
use crate::graphics::text::paragraph; use crate::graphics::text::paragraph;
@ -15,6 +15,7 @@ pub enum Text<'a> {
position: Point, position: Point,
color: Color, color: Color,
clip_bounds: Rectangle, clip_bounds: Rectangle,
transformation: Transformation,
}, },
/// An editor. /// An editor.
#[allow(missing_docs)] #[allow(missing_docs)]
@ -23,11 +24,16 @@ pub enum Text<'a> {
position: Point, position: Point,
color: Color, color: Color,
clip_bounds: Rectangle, clip_bounds: Rectangle,
transformation: Transformation,
}, },
/// Some cached text. /// Some cached text.
Cached(Cached<'a>), Cached(Cached<'a>),
/// Some raw text. /// Some raw text.
Raw(graphics::text::Raw), #[allow(missing_docs)]
Raw {
raw: graphics::text::Raw,
transformation: Transformation,
},
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -4,9 +4,9 @@ mod solid;
use gradient::Gradient; use gradient::Gradient;
use solid::Solid; use solid::Solid;
use crate::core::{Background, Rectangle}; use crate::core::{Background, Rectangle, Transformation};
use crate::graphics;
use crate::graphics::color; use crate::graphics::color;
use crate::graphics::{self, Transformation};
use bytemuck::{Pod, Zeroable}; use bytemuck::{Pod, Zeroable};
@ -319,7 +319,7 @@ impl Uniforms {
impl Default for Uniforms { impl Default for Uniforms {
fn default() -> Self { fn default() -> Self {
Self { Self {
transform: *Transformation::identity().as_ref(), transform: *Transformation::IDENTITY.as_ref(),
scale: 1.0, scale: 1.0,
_padding: [0.0; 3], _padding: [0.0; 3],
} }

View file

@ -1,5 +1,5 @@
use crate::core::alignment; use crate::core::alignment;
use crate::core::{Rectangle, Size}; use crate::core::{Rectangle, Size, Transformation};
use crate::graphics::color; use crate::graphics::color;
use crate::graphics::text::cache::{self, Cache}; use crate::graphics::text::cache::{self, Cache};
use crate::graphics::text::{font_system, to_color, Editor, Paragraph}; use crate::graphics::text::{font_system, to_color, Editor, Paragraph};
@ -109,7 +109,9 @@ impl Pipeline {
Some(Allocation::Cache(key)) Some(Allocation::Cache(key))
} }
Text::Raw(text) => text.buffer.upgrade().map(Allocation::Raw), Text::Raw { raw, .. } => {
raw.buffer.upgrade().map(Allocation::Raw)
}
}) })
.collect(); .collect();
@ -124,11 +126,13 @@ impl Pipeline {
vertical_alignment, vertical_alignment,
color, color,
clip_bounds, clip_bounds,
transformation,
) = match section { ) = match section {
Text::Paragraph { Text::Paragraph {
position, position,
color, color,
clip_bounds, clip_bounds,
transformation,
.. ..
} => { } => {
use crate::core::text::Paragraph as _; use crate::core::text::Paragraph as _;
@ -145,12 +149,14 @@ impl Pipeline {
paragraph.vertical_alignment(), paragraph.vertical_alignment(),
*color, *color,
*clip_bounds, *clip_bounds,
*transformation,
) )
} }
Text::Editor { Text::Editor {
position, position,
color, color,
clip_bounds, clip_bounds,
transformation,
.. ..
} => { } => {
use crate::core::text::Editor as _; use crate::core::text::Editor as _;
@ -167,6 +173,7 @@ impl Pipeline {
alignment::Vertical::Top, alignment::Vertical::Top,
*color, *color,
*clip_bounds, *clip_bounds,
*transformation,
) )
} }
Text::Cached(text) => { Text::Cached(text) => {
@ -186,9 +193,13 @@ impl Pipeline {
text.vertical_alignment, text.vertical_alignment,
text.color, text.color,
text.clip_bounds, text.clip_bounds,
Transformation::IDENTITY,
) )
} }
Text::Raw(text) => { Text::Raw {
raw,
transformation,
} => {
let Some(Allocation::Raw(buffer)) = allocation else { let Some(Allocation::Raw(buffer)) = allocation else {
return None; return None;
}; };
@ -198,18 +209,19 @@ impl Pipeline {
( (
buffer.as_ref(), buffer.as_ref(),
Rectangle::new( Rectangle::new(
text.position, raw.position,
Size::new(width, height), Size::new(width, height),
), ),
alignment::Horizontal::Left, alignment::Horizontal::Left,
alignment::Vertical::Top, alignment::Vertical::Top,
text.color, raw.color,
text.clip_bounds, raw.clip_bounds,
*transformation,
) )
} }
}; };
let bounds = bounds * scale_factor; let bounds = bounds * transformation * scale_factor;
let left = match horizontal_alignment { let left = match horizontal_alignment {
alignment::Horizontal::Left => bounds.x, alignment::Horizontal::Left => bounds.x,
@ -227,14 +239,15 @@ impl Pipeline {
alignment::Vertical::Bottom => bounds.y - bounds.height, alignment::Vertical::Bottom => bounds.y - bounds.height,
}; };
let clip_bounds = let clip_bounds = layer_bounds.intersection(
layer_bounds.intersection(&(clip_bounds * scale_factor))?; &(clip_bounds * transformation * scale_factor),
)?;
Some(glyphon::TextArea { Some(glyphon::TextArea {
buffer, buffer,
left, left,
top, top,
scale: scale_factor, scale: scale_factor * transformation.scale_factor(),
bounds: glyphon::TextBounds { bounds: glyphon::TextBounds {
left: clip_bounds.x as i32, left: clip_bounds.x as i32,
top: clip_bounds.y as i32, top: clip_bounds.y as i32,

View file

@ -1,8 +1,8 @@
//! Draw meshes of triangles. //! Draw meshes of triangles.
mod msaa; mod msaa;
use crate::core::Size; use crate::core::{Size, Transformation};
use crate::graphics::{Antialiasing, Transformation}; use crate::graphics::Antialiasing;
use crate::layer::mesh::{self, Mesh}; use crate::layer::mesh::{self, Mesh};
use crate::Buffer; use crate::Buffer;
@ -98,12 +98,10 @@ impl Layer {
let mut index_offset = 0; let mut index_offset = 0;
for mesh in meshes { for mesh in meshes {
let origin = mesh.origin();
let indices = mesh.indices(); let indices = mesh.indices();
let uniforms = Uniforms::new( let uniforms =
transformation * Transformation::translate(origin.x, origin.y), Uniforms::new(transformation * mesh.transformation());
);
index_offset += index_offset +=
self.index_buffer.write(queue, index_offset, indices); self.index_buffer.write(queue, index_offset, indices);

View file

@ -15,7 +15,7 @@ use crate::core::mouse;
use crate::core::renderer; use crate::core::renderer;
use crate::core::widget::tree::{self, Tree}; use crate::core::widget::tree::{self, Tree};
use crate::core::{ use crate::core::{
Clipboard, Element, Length, Rectangle, Shell, Size, Vector, Widget, Clipboard, Element, Length, Rectangle, Shell, Size, Transformation, Widget,
}; };
use crate::graphics::geometry; use crate::graphics::geometry;
@ -207,8 +207,8 @@ where
let state = tree.state.downcast_ref::<P::State>(); let state = tree.state.downcast_ref::<P::State>();
renderer.with_translation( renderer.with_transformation(
Vector::new(bounds.x, bounds.y), Transformation::translate(bounds.x, bounds.y),
|renderer| { |renderer| {
renderer.draw( renderer.draw(
self.program.draw(state, renderer, theme, bounds, cursor), self.program.draw(state, renderer, theme, bounds, cursor),

View file

@ -962,22 +962,21 @@ pub fn draw<Theme, Renderer, T>(
if let Some(cursor_position) = cursor.position() { if let Some(cursor_position) = cursor.position() {
let bounds = layout.bounds(); let bounds = layout.bounds();
renderer.with_translation( let translation = cursor_position
cursor_position - Point::new(bounds.x + origin.x, bounds.y + origin.y);
- Point::new(bounds.x + origin.x, bounds.y + origin.y),
|renderer| { renderer.with_translation(translation, |renderer| {
renderer.with_layer(bounds, |renderer| { renderer.with_layer(bounds, |renderer| {
draw_pane( draw_pane(
pane, pane,
renderer, renderer,
default_style, default_style,
layout, layout,
pane_cursor, pane_cursor,
viewport, viewport,
); );
}); });
}, });
);
} }
} }

View file

@ -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(
bounds.position() - Point::ORIGIN,
renderer.with_translation(translation, |renderer| { |renderer| {
renderer.draw(vec![geometry]); renderer.draw(vec![geometry]);
}); },
);
} }
} }

View file

@ -385,6 +385,7 @@ pub fn mouse_interaction(
} }
Interaction::ResizingVertically => winit::window::CursorIcon::NsResize, Interaction::ResizingVertically => winit::window::CursorIcon::NsResize,
Interaction::NotAllowed => winit::window::CursorIcon::NotAllowed, Interaction::NotAllowed => winit::window::CursorIcon::NotAllowed,
Interaction::ZoomIn => winit::window::CursorIcon::ZoomIn,
} }
} }