Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

image and svg support for canvas #2537

Merged
merged 13 commits into from
Aug 4, 2024
Merged
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
Loading