Skip to content

Commit 8f3bec7

Browse files
committed
Add Image rotation support
1 parent ffa6614 commit 8f3bec7

19 files changed

Lines changed: 374 additions & 84 deletions

File tree

core/src/image.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,5 +173,7 @@ pub trait Renderer: crate::Renderer {
173173
handle: Self::Handle,
174174
filter_method: FilterMethod,
175175
bounds: Rectangle,
176+
rotation: f32,
177+
scale: Size,
176178
);
177179
}

core/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ mod padding;
3939
mod pixels;
4040
mod point;
4141
mod rectangle;
42+
mod rotation;
4243
mod shadow;
4344
mod shell;
4445
mod size;
@@ -64,6 +65,7 @@ pub use pixels::Pixels;
6465
pub use point::Point;
6566
pub use rectangle::Rectangle;
6667
pub use renderer::Renderer;
68+
pub use rotation::RotationLayout;
6769
pub use shadow::Shadow;
6870
pub use shell::Shell;
6971
pub use size::Size;

core/src/renderer/null.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,8 @@ impl image::Renderer for () {
171171
_handle: Self::Handle,
172172
_filter_method: image::FilterMethod,
173173
_bounds: Rectangle,
174+
_rotation: f32,
175+
_scale: Size,
174176
) {
175177
}
176178
}
@@ -185,6 +187,8 @@ impl svg::Renderer for () {
185187
_handle: svg::Handle,
186188
_color: Option<Color>,
187189
_bounds: Rectangle,
190+
_rotation: f32,
191+
_scale: Size,
188192
) {
189193
}
190194
}

core/src/rotation.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//! Control the rotation of some content (like an image) with the `RotationLayout` within a
2+
//! space.
3+
use crate::Size;
4+
5+
/// The strategy used to rotate the content.
6+
///
7+
/// This is used to control the behavior of the layout when the content is rotated
8+
/// by a certain angle.
9+
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
10+
pub enum RotationLayout {
11+
/// The layout is kept exactly as it was before the rotation.
12+
///
13+
/// This is especially useful when used for animations, as it will avoid the
14+
/// layout being shifted or resized when smoothly i.e. an icon.
15+
Keep,
16+
/// The layout is adjusted to fit the rotated content.
17+
///
18+
/// This allows you to rotate an image and have the layout adjust to fit the new
19+
/// size of the image.
20+
Change,
21+
}
22+
23+
impl RotationLayout {
24+
/// Applies the rotation to the layout while respecting the [`RotationLayout`] strategy.
25+
/// The rotation is given in radians.
26+
pub fn apply_to_size(&self, size: Size, rotation: f32) -> Size {
27+
match self {
28+
Self::Keep => size,
29+
Self::Change => Size {
30+
width: (size.width * rotation.cos()).abs()
31+
+ (size.height * rotation.sin()).abs(),
32+
height: (size.width * rotation.sin()).abs()
33+
+ (size.height * rotation.cos()).abs(),
34+
},
35+
}
36+
}
37+
}

core/src/svg.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,5 +100,7 @@ pub trait Renderer: crate::Renderer {
100100
handle: Handle,
101101
color: Option<Color>,
102102
bounds: Rectangle,
103+
rotation: f32,
104+
scale: Size,
103105
);
104106
}

graphics/src/image.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
#[cfg(feature = "image")]
33
pub use ::image as image_rs;
44

5-
use crate::core::image;
6-
use crate::core::svg;
7-
use crate::core::{Color, Rectangle};
5+
use crate::core::{image, svg, Color, Rectangle, Size};
86

97
/// A raster or vector image.
108
#[derive(Debug, Clone, PartialEq)]
@@ -19,6 +17,12 @@ pub enum Image {
1917

2018
/// The bounds of the image.
2119
bounds: Rectangle,
20+
21+
/// The rotation of the image in radians
22+
rotation: f32,
23+
24+
/// The scale of the image after rotation
25+
scale: Size,
2226
},
2327
/// A vector image.
2428
Vector {
@@ -30,6 +34,12 @@ pub enum Image {
3034

3135
/// The bounds of the image.
3236
bounds: Rectangle,
37+
38+
/// The rotation of the image in radians
39+
rotation: f32,
40+
41+
/// The scale of the image after rotation
42+
scale: Size,
3343
},
3444
}
3545

renderer/src/fallback.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,11 +154,13 @@ where
154154
handle: Self::Handle,
155155
filter_method: image::FilterMethod,
156156
bounds: Rectangle,
157+
rotation: f32,
158+
scale: Size,
157159
) {
158160
delegate!(
159161
self,
160162
renderer,
161-
renderer.draw_image(handle, filter_method, bounds)
163+
renderer.draw_image(handle, filter_method, bounds, rotation, scale)
162164
);
163165
}
164166
}
@@ -177,8 +179,14 @@ where
177179
handle: svg::Handle,
178180
color: Option<Color>,
179181
bounds: Rectangle,
182+
rotation: f32,
183+
scale: Size,
180184
) {
181-
delegate!(self, renderer, renderer.draw_svg(handle, color, bounds));
185+
delegate!(
186+
self,
187+
renderer,
188+
renderer.draw_svg(handle, color, bounds, rotation, scale)
189+
);
182190
}
183191
}
184192

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,8 @@ pub use crate::core::gradient;
200200
pub use crate::core::theme;
201201
pub use crate::core::{
202202
Alignment, Background, Border, Color, ContentFit, Degrees, Gradient,
203-
Length, Padding, Pixels, Point, Radians, Rectangle, Shadow, Size, Theme,
204-
Transformation, Vector,
203+
Length, Padding, Pixels, Point, Radians, Rectangle, RotationLayout, Shadow,
204+
Size, Theme, Transformation, Vector,
205205
};
206206

207207
pub mod clipboard {

tiny_skia/src/engine.rs

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -539,33 +539,42 @@ impl Engine {
539539
pub fn draw_image(
540540
&mut self,
541541
image: &Image,
542-
_transformation: Transformation,
543-
_pixels: &mut tiny_skia::PixmapMut<'_>,
544-
_clip_mask: &mut tiny_skia::Mask,
545-
_clip_bounds: Rectangle,
542+
transformation: Transformation,
543+
pixels: &mut tiny_skia::PixmapMut<'_>,
544+
clip_mask: &mut tiny_skia::Mask,
545+
clip_bounds: Rectangle,
546546
) {
547547
match image {
548548
#[cfg(feature = "image")]
549549
Image::Raster {
550550
handle,
551551
filter_method,
552552
bounds,
553+
rotation,
554+
scale,
553555
} => {
554-
let physical_bounds = *bounds * _transformation;
556+
let physical_bounds = *bounds * transformation;
555557

556-
if !_clip_bounds.intersects(&physical_bounds) {
558+
if !clip_bounds.intersects(&physical_bounds) {
557559
return;
558560
}
559561

560-
let clip_mask = (!physical_bounds.is_within(&_clip_bounds))
561-
.then_some(_clip_mask as &_);
562+
let clip_mask = (!physical_bounds.is_within(&clip_bounds))
563+
.then_some(clip_mask as &_);
564+
565+
let center = physical_bounds.center();
566+
let transform = into_transform(transformation)
567+
.post_rotate_at(rotation.to_degrees(), center.x, center.y)
568+
.post_translate(-center.x, -center.y)
569+
.post_scale(scale.width, scale.height)
570+
.post_translate(center.x, center.y);
562571

563572
self.raster_pipeline.draw(
564573
handle,
565574
*filter_method,
566575
*bounds,
567-
_pixels,
568-
into_transform(_transformation),
576+
pixels,
577+
transform,
569578
clip_mask,
570579
);
571580
}
@@ -574,21 +583,31 @@ impl Engine {
574583
handle,
575584
color,
576585
bounds,
586+
rotation,
587+
scale,
577588
} => {
578-
let physical_bounds = *bounds * _transformation;
589+
let physical_bounds = *bounds * transformation;
579590

580-
if !_clip_bounds.intersects(&physical_bounds) {
591+
if !clip_bounds.intersects(&physical_bounds) {
581592
return;
582593
}
583594

584-
let clip_mask = (!physical_bounds.is_within(&_clip_bounds))
585-
.then_some(_clip_mask as &_);
595+
let clip_mask = (!physical_bounds.is_within(&clip_bounds))
596+
.then_some(clip_mask as &_);
597+
598+
let center = physical_bounds.center();
599+
let transform = into_transform(transformation)
600+
.post_rotate_at(rotation.to_degrees(), center.x, center.y)
601+
.post_translate(-center.x, -center.y)
602+
.post_scale(scale.width, scale.height)
603+
.post_translate(center.x, center.y);
586604

587605
self.vector_pipeline.draw(
588606
handle,
589607
*color,
590608
physical_bounds,
591-
_pixels,
609+
pixels,
610+
transform,
592611
clip_mask,
593612
);
594613
}

tiny_skia/src/layer.rs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
use crate::core::image;
2-
use crate::core::renderer::Quad;
3-
use crate::core::svg;
4-
use crate::core::{Background, Color, Point, Rectangle, Transformation};
1+
use crate::core::{
2+
image, renderer::Quad, svg, Background, Color, Point, Rectangle, Size,
3+
Transformation,
4+
};
55
use crate::graphics::damage;
66
use crate::graphics::layer;
77
use crate::graphics::text::{Editor, Paragraph, Text};
@@ -121,11 +121,15 @@ impl Layer {
121121
filter_method: image::FilterMethod,
122122
bounds: Rectangle,
123123
transformation: Transformation,
124+
rotation: f32,
125+
scale: Size,
124126
) {
125127
let image = Image::Raster {
126128
handle,
127129
filter_method,
128130
bounds: bounds * transformation,
131+
rotation,
132+
scale,
129133
};
130134

131135
self.images.push(image);
@@ -137,11 +141,15 @@ impl Layer {
137141
color: Option<Color>,
138142
bounds: Rectangle,
139143
transformation: Transformation,
144+
rotation: f32,
145+
scale: Size,
140146
) {
141147
let svg = Image::Vector {
142148
handle,
143149
color,
144150
bounds: bounds * transformation,
151+
rotation,
152+
scale,
145153
};
146154

147155
self.images.push(svg);
@@ -256,6 +264,22 @@ impl Layer {
256264
Image::eq,
257265
);
258266

267+
// let center = bounds.center();
268+
// let rotated_size = RotationLayout::Change
269+
// .apply_to_size(bounds.size(), *rotation);
270+
//
271+
// let scaled_size = Size::new(
272+
// rotated_size.width * scale.width,
273+
// rotated_size.height * scale.height,
274+
// );
275+
//
276+
// let top_left = Point::new(
277+
// center.x - scaled_size.width / 2.0,
278+
// center.y - scaled_size.height / 2.0,
279+
// );
280+
//
281+
// Rectangle::new(top_left, scaled_size).expand(1.0)
282+
259283
damage.extend(text);
260284
damage.extend(primitives);
261285
damage.extend(images);

0 commit comments

Comments
 (0)