Skip to content

Commit

Permalink
Merge pull request #2537 from iced-rs/feature/canvas-image-support
Browse files Browse the repository at this point in the history
`image` and `svg` support for `canvas`
  • Loading branch information
hecrj authored Aug 4, 2024
2 parents 9cccaeb + cc07690 commit 145c3dc
Show file tree
Hide file tree
Showing 31 changed files with 627 additions and 400 deletions.
85 changes: 75 additions & 10 deletions core/src/image.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,73 @@ use rustc_hash::FxHasher;
use std::hash::{Hash, Hasher};
use std::path::{Path, PathBuf};

/// A raster image that can be drawn.
#[derive(Debug, Clone, PartialEq)]
pub struct Image<H = Handle> {
/// The handle of the image.
pub handle: H,

/// The filter method of the image.
pub filter_method: FilterMethod,

/// The rotation to be applied to the image; on its center.
pub rotation: Radians,

/// The opacity of the image.
///
/// 0 means transparent. 1 means opaque.
pub opacity: f32,

/// If set to `true`, the image will be snapped to the pixel grid.
///
/// This can avoid graphical glitches, specially when using
/// [`FilterMethod::Nearest`].
pub snap: bool,
}

impl Image<Handle> {
/// Creates a new [`Image`] with the given handle.
pub fn new(handle: impl Into<Handle>) -> Self {
Self {
handle: handle.into(),
filter_method: FilterMethod::default(),
rotation: Radians(0.0),
opacity: 1.0,
snap: false,
}
}

/// Sets the filter method of the [`Image`].
pub fn filter_method(mut self, filter_method: FilterMethod) -> Self {
self.filter_method = filter_method;
self
}

/// Sets the rotation of the [`Image`].
pub fn rotation(mut self, rotation: impl Into<Radians>) -> Self {
self.rotation = rotation.into();
self
}

/// Sets the opacity of the [`Image`].
pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
self.opacity = opacity.into();
self
}

/// Sets whether the [`Image`] should be snapped to the pixel grid.
pub fn snap(mut self, snap: bool) -> Self {
self.snap = snap;
self
}
}

impl From<&Handle> for Image {
fn from(handle: &Handle) -> Self {
Image::new(handle.clone())
}
}

/// A handle of some image data.
#[derive(Clone, PartialEq, Eq)]
pub enum Handle {
Expand Down Expand Up @@ -101,6 +168,12 @@ where
}
}

impl From<&Handle> for Handle {
fn from(value: &Handle) -> Self {
value.clone()
}
}

impl std::fmt::Debug for Handle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Expand Down Expand Up @@ -166,14 +239,6 @@ pub trait Renderer: crate::Renderer {
/// Returns the dimensions of an image for the given [`Handle`].
fn measure_image(&self, handle: &Self::Handle) -> Size<u32>;

/// Draws an image with the given [`Handle`] and inside the provided
/// `bounds`.
fn draw_image(
&mut self,
handle: Self::Handle,
filter_method: FilterMethod,
bounds: Rectangle,
rotation: Radians,
opacity: f32,
);
/// Draws an [`Image`] inside the provided `bounds`.
fn draw_image(&mut self, image: Image<Self::Handle>, bounds: Rectangle);
}
2 changes: 2 additions & 0 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ pub use element::Element;
pub use event::Event;
pub use font::Font;
pub use gradient::Gradient;
pub use image::Image;
pub use layout::Layout;
pub use length::Length;
pub use overlay::Overlay;
Expand All @@ -69,6 +70,7 @@ pub use rotation::Rotation;
pub use shadow::Shadow;
pub use shell::Shell;
pub use size::Size;
pub use svg::Svg;
pub use text::Text;
pub use theme::Theme;
pub use transformation::Transformation;
Expand Down
56 changes: 56 additions & 0 deletions core/src/rectangle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,62 @@ impl Rectangle<f32> {
}
}

/// Creates a new square [`Rectangle`] with the center at the origin and
/// with the given radius.
pub fn with_radius(radius: f32) -> Self {
Self {
x: -radius,
y: -radius,
width: radius * 2.0,
height: radius * 2.0,
}
}

/// Creates a new axis-aligned [`Rectangle`] from the given vertices; returning the
/// rotation in [`Radians`] that must be applied to the axis-aligned [`Rectangle`]
/// to obtain the desired result.
pub fn with_vertices(
top_left: Point,
top_right: Point,
bottom_left: Point,
) -> (Rectangle, Radians) {
let width = (top_right.x - top_left.x).hypot(top_right.y - top_left.y);

let height =
(bottom_left.x - top_left.x).hypot(bottom_left.y - top_left.y);

let rotation =
(top_right.y - top_left.y).atan2(top_right.x - top_left.x);

let rotation = if rotation < 0.0 {
2.0 * std::f32::consts::PI + rotation
} else {
rotation
};

let position = {
let center = Point::new(
(top_right.x + bottom_left.x) / 2.0,
(top_right.y + bottom_left.y) / 2.0,
);

let rotation = -rotation - std::f32::consts::PI * 2.0;

Point::new(
center.x + (top_left.x - center.x) * rotation.cos()
- (top_left.y - center.y) * rotation.sin(),
center.y
+ (top_left.x - center.x) * rotation.sin()
+ (top_left.y - center.y) * rotation.cos(),
)
};

(
Rectangle::new(position, Size::new(width, height)),
Radians(rotation),
)
}

/// Returns the [`Point`] at the center of the [`Rectangle`].
pub fn center(&self) -> Point {
Point::new(self.center_x(), self.center_y())
Expand Down
27 changes: 5 additions & 22 deletions core/src/renderer/null.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
use crate::alignment;
use crate::image;
use crate::image::{self, Image};
use crate::renderer::{self, Renderer};
use crate::svg;
use crate::text::{self, Text};
use crate::{
Background, Color, Font, Pixels, Point, Radians, Rectangle, Size,
Transformation,
Background, Color, Font, Pixels, Point, Rectangle, Size, Transformation,
};

impl Renderer for () {
Expand Down Expand Up @@ -178,35 +177,19 @@ impl text::Editor for () {
}

impl image::Renderer for () {
type Handle = ();
type Handle = image::Handle;

fn measure_image(&self, _handle: &Self::Handle) -> Size<u32> {
Size::default()
}

fn draw_image(
&mut self,
_handle: Self::Handle,
_filter_method: image::FilterMethod,
_bounds: Rectangle,
_rotation: Radians,
_opacity: f32,
) {
}
fn draw_image(&mut self, _image: Image, _bounds: Rectangle) {}
}

impl svg::Renderer for () {
fn measure_svg(&self, _handle: &svg::Handle) -> Size<u32> {
Size::default()
}

fn draw_svg(
&mut self,
_handle: svg::Handle,
_color: Option<Color>,
_bounds: Rectangle,
_rotation: Radians,
_opacity: f32,
) {
}
fn draw_svg(&mut self, _svg: svg::Svg, _bounds: Rectangle) {}
}
69 changes: 61 additions & 8 deletions core/src/svg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,66 @@ use std::hash::{Hash, Hasher as _};
use std::path::PathBuf;
use std::sync::Arc;

/// A raster image that can be drawn.
#[derive(Debug, Clone, PartialEq)]
pub struct Svg<H = Handle> {
/// The handle of the [`Svg`].
pub handle: H,

/// The [`Color`] filter to be applied to the [`Svg`].
///
/// If some [`Color`] is set, the whole [`Svg`] will be
/// painted with it—ignoring any intrinsic colors.
///
/// This can be useful for coloring icons programmatically
/// (e.g. with a theme).
pub color: Option<Color>,

/// The rotation to be applied to the image; on its center.
pub rotation: Radians,

/// The opacity of the [`Svg`].
///
/// 0 means transparent. 1 means opaque.
pub opacity: f32,
}

impl Svg<Handle> {
/// Creates a new [`Svg`] with the given handle.
pub fn new(handle: impl Into<Handle>) -> Self {
Self {
handle: handle.into(),
color: None,
rotation: Radians(0.0),
opacity: 1.0,
}
}

/// Sets the [`Color`] filter of the [`Svg`].
pub fn color(mut self, color: impl Into<Color>) -> Self {
self.color = Some(color.into());
self
}

/// Sets the rotation of the [`Svg`].
pub fn rotation(mut self, rotation: impl Into<Radians>) -> Self {
self.rotation = rotation.into();
self
}

/// Sets the opacity of the [`Svg`].
pub fn opacity(mut self, opacity: impl Into<f32>) -> Self {
self.opacity = opacity.into();
self
}
}

impl From<&Handle> for Svg {
fn from(handle: &Handle) -> Self {
Svg::new(handle.clone())
}
}

/// A handle of Svg data.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Handle {
Expand Down Expand Up @@ -95,12 +155,5 @@ pub trait Renderer: crate::Renderer {
fn measure_svg(&self, handle: &Handle) -> Size<u32>;

/// Draws an SVG with the given [`Handle`], an optional [`Color`] filter, and inside the provided `bounds`.
fn draw_svg(
&mut self,
handle: Handle,
color: Option<Color>,
bounds: Rectangle,
rotation: Radians,
opacity: f32,
);
fn draw_svg(&mut self, svg: Svg, bounds: Rectangle);
}
Loading

0 comments on commit 145c3dc

Please sign in to comment.