From e84070acef84f883ca42d965c577e54ce60c3f2a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 3 Aug 2024 16:20:12 +0200 Subject: Implement `From<&Handle>` for `image::Handle` --- core/src/image.rs | 6 ++++++ examples/screenshot/src/main.rs | 48 +++++++++++++++++++++++++---------------- widget/src/image.rs | 2 +- 3 files changed, 36 insertions(+), 20 deletions(-) diff --git a/core/src/image.rs b/core/src/image.rs index 82ecdd0f..77ff7500 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -101,6 +101,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 { diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 2d980dd9..5c105f6c 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -20,7 +20,7 @@ fn main() -> iced::Result { #[derive(Default)] struct Example { - screenshot: Option, + screenshot: Option<(Screenshot, image::Handle)>, saved_png_path: Option>, png_saving: bool, crop_error: Option, @@ -52,10 +52,17 @@ impl Example { .map(Message::Screenshotted); } Message::Screenshotted(screenshot) => { - self.screenshot = Some(screenshot); + self.screenshot = Some(( + screenshot.clone(), + image::Handle::from_rgba( + screenshot.size.width, + screenshot.size.height, + screenshot.bytes, + ), + )); } Message::Png => { - if let Some(screenshot) = &self.screenshot { + if let Some((screenshot, _handle)) = &self.screenshot { self.png_saving = true; return Task::perform( @@ -81,7 +88,7 @@ impl Example { self.height_input_value = new_value; } Message::Crop => { - if let Some(screenshot) = &self.screenshot { + if let Some((screenshot, _handle)) = &self.screenshot { let cropped = screenshot.crop(Rectangle:: { x: self.x_input_value.unwrap_or(0), y: self.y_input_value.unwrap_or(0), @@ -91,7 +98,14 @@ impl Example { match cropped { Ok(screenshot) => { - self.screenshot = Some(screenshot); + self.screenshot = Some(( + screenshot.clone(), + image::Handle::from_rgba( + screenshot.size.width, + screenshot.size.height, + screenshot.bytes, + ), + )); self.crop_error = None; } Err(crop_error) => { @@ -106,20 +120,16 @@ impl Example { } fn view(&self) -> Element<'_, Message> { - let image: Element = if let Some(screenshot) = &self.screenshot - { - image(image::Handle::from_rgba( - screenshot.size.width, - screenshot.size.height, - screenshot.clone(), - )) - .content_fit(ContentFit::Contain) - .width(Fill) - .height(Fill) - .into() - } else { - text("Press the button to take a screenshot!").into() - }; + let image: Element = + if let Some((_screenshot, handle)) = &self.screenshot { + image(handle) + .content_fit(ContentFit::Contain) + .width(Fill) + .height(Fill) + .into() + } else { + text("Press the button to take a screenshot!").into() + }; let image = container(image) .center_y(FillPortion(2)) diff --git a/widget/src/image.rs b/widget/src/image.rs index 80e17263..f1571400 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -43,7 +43,7 @@ pub struct Image { impl Image { /// Creates a new [`Image`] with the given path. - pub fn new>(handle: T) -> Self { + pub fn new(handle: impl Into) -> Self { Image { handle: handle.into(), width: Length::Shrink, -- cgit From 87a613edd186461f1a8d224394043527a372571c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 3 Aug 2024 16:23:30 +0200 Subject: Render text on top of images by default --- tiny_skia/src/lib.rs | 20 ++++++++++---------- wgpu/src/layer.rs | 2 +- wgpu/src/lib.rs | 48 ++++++++++++++++++++++++------------------------ 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index 1aabff00..6ec60158 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -178,6 +178,16 @@ impl Renderer { engine::adjust_clip_mask(clip_mask, clip_bounds); } + for image in &layer.images { + self.engine.draw_image( + image, + Transformation::scale(scale_factor), + pixels, + clip_mask, + clip_bounds, + ); + } + for group in &layer.text { for text in group.as_slice() { self.engine.draw_text( @@ -190,16 +200,6 @@ impl Renderer { ); } } - - for image in &layer.images { - self.engine.draw_image( - image, - Transformation::scale(scale_factor), - pixels, - clip_mask, - clip_bounds, - ); - } } if !overlay.is_empty() { diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 9551311d..df289e0e 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -20,8 +20,8 @@ pub struct Layer { pub quads: quad::Batch, pub triangles: triangle::Batch, pub primitives: primitive::Batch, - pub text: text::Batch, pub images: image::Batch, + pub text: text::Batch, pending_meshes: Vec, pending_text: Vec, } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index ad88ce3e..954340ec 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -182,19 +182,6 @@ impl Renderer { } } - if !layer.text.is_empty() { - engine.text_pipeline.prepare( - device, - queue, - &self.text_viewport, - encoder, - &mut self.text_storage, - &layer.text, - layer.bounds, - Transformation::scale(scale_factor), - ); - } - #[cfg(any(feature = "svg", feature = "image"))] if !layer.images.is_empty() { engine.image_pipeline.prepare( @@ -207,6 +194,19 @@ impl Renderer { scale_factor, ); } + + if !layer.text.is_empty() { + engine.text_pipeline.prepare( + device, + queue, + &self.text_viewport, + encoder, + &mut self.text_storage, + &layer.text, + layer.bounds, + Transformation::scale(scale_factor), + ); + } } } @@ -359,17 +359,6 @@ impl Renderer { )); } - if !layer.text.is_empty() { - text_layer += engine.text_pipeline.render( - &self.text_viewport, - &self.text_storage, - text_layer, - &layer.text, - scissor_rect, - &mut render_pass, - ); - } - #[cfg(any(feature = "svg", feature = "image"))] if !layer.images.is_empty() { engine.image_pipeline.render( @@ -381,6 +370,17 @@ impl Renderer { image_layer += 1; } + + if !layer.text.is_empty() { + text_layer += engine.text_pipeline.render( + &self.text_viewport, + &self.text_storage, + text_layer, + &layer.text, + scissor_rect, + &mut render_pass, + ); + } } let _ = ManuallyDrop::into_inner(render_pass); -- cgit From 0ceee1cf3ae49f5bd0e3f2b346a4b34076e4523a Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 4 Aug 2024 03:28:43 +0200 Subject: Implement image support for `canvas` widget --- core/src/rectangle.rs | 56 ++++++++++++++++++++++++++ graphics/Cargo.toml | 1 + graphics/src/geometry/frame.rs | 72 +++++++++++++++++++++++++++++---- graphics/src/image.rs | 6 +++ renderer/src/fallback.rs | 42 +++++++++++++++++-- tiny_skia/Cargo.toml | 2 +- tiny_skia/src/engine.rs | 1 + tiny_skia/src/geometry.rs | 86 +++++++++++++++++++++++++++++++++++++-- tiny_skia/src/layer.rs | 43 ++++++++++++++++++++ tiny_skia/src/lib.rs | 11 ++++- wgpu/Cargo.toml | 2 +- wgpu/src/geometry.rs | 91 +++++++++++++++++++++++++++++++++++++++--- wgpu/src/image/mod.rs | 24 +++++++++-- wgpu/src/layer.rs | 45 +++++++++++++++++++++ wgpu/src/lib.rs | 20 +++++++++- wgpu/src/shader/image.wgsl | 12 +++++- 16 files changed, 485 insertions(+), 29 deletions(-) diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index 1556e072..99c8d55d 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -47,6 +47,62 @@ impl Rectangle { } } + /// 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()) diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index e8d27d07..7e2d767b 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -20,6 +20,7 @@ all-features = true [features] geometry = ["lyon_path"] image = ["dep:image", "kamadak-exif"] +svg = [] web-colors = [] fira-sans = [] diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs index 377589d7..d53d1331 100644 --- a/graphics/src/geometry/frame.rs +++ b/graphics/src/geometry/frame.rs @@ -1,5 +1,7 @@ //! Draw and generate geometry. -use crate::core::{Point, Radians, Rectangle, Size, Vector}; +use crate::core::image; +use crate::core::svg; +use crate::core::{Color, Point, Radians, Rectangle, Size, Vector}; use crate::geometry::{self, Fill, Path, Stroke, Text}; /// The region of a surface that can be used to draw geometry. @@ -75,6 +77,25 @@ where self.raw.fill_text(text); } + /// Draws the given image on the [`Frame`] inside the given bounds. + #[cfg(feature = "image")] + pub fn draw_image( + &mut self, + handle: &image::Handle, + bounds: Rectangle, + filter_method: image::FilterMethod, + rotation: impl Into, + opacity: f32, + ) { + self.raw.draw_image( + handle, + bounds, + filter_method, + rotation.into(), + opacity, + ); + } + /// Stores the current transform of the [`Frame`] and executes the given /// drawing operations, restoring the transform afterwards. /// @@ -116,8 +137,7 @@ where let mut frame = self.draft(region); let result = f(&mut frame); - - self.paste(frame, Point::new(region.x, region.y)); + self.paste(frame); result } @@ -134,8 +154,8 @@ where } /// Draws the contents of the given [`Frame`] with origin at the given [`Point`]. - fn paste(&mut self, frame: Self, at: Point) { - self.raw.paste(frame.raw, at); + fn paste(&mut self, frame: Self) { + self.raw.paste(frame.raw); } /// Applies a translation to the current transform of the [`Frame`]. @@ -186,7 +206,7 @@ pub trait Backend: Sized { fn scale_nonuniform(&mut self, scale: impl Into); fn draft(&mut self, clip_bounds: Rectangle) -> Self; - fn paste(&mut self, frame: Self, at: Point); + fn paste(&mut self, frame: Self); fn stroke<'a>(&mut self, path: &Path, stroke: impl Into>); @@ -199,6 +219,24 @@ pub trait Backend: Sized { fill: impl Into, ); + fn draw_image( + &mut self, + handle: &image::Handle, + bounds: Rectangle, + filter_method: image::FilterMethod, + rotation: Radians, + opacity: f32, + ); + + fn draw_svg( + &mut self, + handle: &svg::Handle, + bounds: Rectangle, + color: Option, + rotation: Radians, + opacity: f32, + ); + fn into_geometry(self) -> Self::Geometry; } @@ -231,7 +269,7 @@ impl Backend for () { fn scale_nonuniform(&mut self, _scale: impl Into) {} fn draft(&mut self, _clip_bounds: Rectangle) -> Self {} - fn paste(&mut self, _frame: Self, _at: Point) {} + fn paste(&mut self, _frame: Self) {} fn stroke<'a>(&mut self, _path: &Path, _stroke: impl Into>) {} @@ -246,4 +284,24 @@ impl Backend for () { } fn into_geometry(self) -> Self::Geometry {} + + fn draw_image( + &mut self, + _handle: &image::Handle, + _bounds: Rectangle, + _filter_method: image::FilterMethod, + _rotation: Radians, + _opacity: f32, + ) { + } + + fn draw_svg( + &mut self, + _handle: &svg::Handle, + _bounds: Rectangle, + _color: Option, + _rotation: Radians, + _opacity: f32, + ) { + } } diff --git a/graphics/src/image.rs b/graphics/src/image.rs index 318592be..0e8f2fe3 100644 --- a/graphics/src/image.rs +++ b/graphics/src/image.rs @@ -23,6 +23,12 @@ pub enum Image { /// The opacity of the image. opacity: f32, + + /// If set to `true`, the image will be snapped to the pixel grid. + /// + /// This can avoid graphical glitches, specially when using a + /// [`image::FilterMethod::Nearest`]. + snap: bool, }, /// A vector image. Vector { diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs index 6a169692..ddf7fd95 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -572,6 +572,42 @@ mod geometry { delegate!(self, frame, frame.fill_text(text)); } + fn draw_image( + &mut self, + handle: &iced_wgpu::core::image::Handle, + bounds: Rectangle, + filter_method: iced_wgpu::core::image::FilterMethod, + rotation: Radians, + opacity: f32, + ) { + delegate!( + self, + frame, + frame.draw_image( + handle, + bounds, + filter_method, + rotation, + opacity + ) + ); + } + + fn draw_svg( + &mut self, + handle: &iced_wgpu::core::svg::Handle, + bounds: Rectangle, + color: Option, + rotation: Radians, + opacity: f32, + ) { + delegate!( + self, + frame, + frame.draw_svg(handle, bounds, color, rotation, opacity) + ); + } + fn push_transform(&mut self) { delegate!(self, frame, frame.push_transform()); } @@ -587,13 +623,13 @@ mod geometry { } } - fn paste(&mut self, frame: Self, at: Point) { + fn paste(&mut self, frame: Self) { match (self, frame) { (Self::Primary(target), Self::Primary(source)) => { - target.paste(source, at); + target.paste(source); } (Self::Secondary(target), Self::Secondary(source)) => { - target.paste(source, at); + target.paste(source); } _ => unreachable!(), } diff --git a/tiny_skia/Cargo.toml b/tiny_skia/Cargo.toml index 32ead3e0..323233f0 100644 --- a/tiny_skia/Cargo.toml +++ b/tiny_skia/Cargo.toml @@ -15,7 +15,7 @@ workspace = true [features] image = ["iced_graphics/image"] -svg = ["resvg"] +svg = ["iced_graphics/svg", "resvg"] geometry = ["iced_graphics/geometry"] [dependencies] diff --git a/tiny_skia/src/engine.rs b/tiny_skia/src/engine.rs index 898657c8..c5c4d494 100644 --- a/tiny_skia/src/engine.rs +++ b/tiny_skia/src/engine.rs @@ -556,6 +556,7 @@ impl Engine { bounds, rotation, opacity, + snap: _, } => { let physical_bounds = *bounds * _transformation; diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index 02b6e1b9..398b54f7 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -1,10 +1,12 @@ +use crate::core::image; +use crate::core::svg; use crate::core::text::LineHeight; -use crate::core::{Pixels, Point, Radians, Rectangle, Size, Vector}; +use crate::core::{Color, Pixels, Point, Radians, Rectangle, Size, Vector}; use crate::graphics::cache::{self, Cached}; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::stroke::{self, Stroke}; use crate::graphics::geometry::{self, Path, Style}; -use crate::graphics::{Gradient, Text}; +use crate::graphics::{Gradient, Image, Text}; use crate::Primitive; use std::rc::Rc; @@ -13,6 +15,7 @@ use std::rc::Rc; pub enum Geometry { Live { text: Vec, + images: Vec, primitives: Vec, clip_bounds: Rectangle, }, @@ -22,6 +25,7 @@ pub enum Geometry { #[derive(Debug, Clone)] pub struct Cache { pub text: Rc<[Text]>, + pub images: Rc<[Image]>, pub primitives: Rc<[Primitive]>, pub clip_bounds: Rectangle, } @@ -37,10 +41,12 @@ impl Cached for Geometry { match self { Self::Live { primitives, + images, text, clip_bounds, } => Cache { primitives: Rc::from(primitives), + images: Rc::from(images), text: Rc::from(text), clip_bounds, }, @@ -55,6 +61,7 @@ pub struct Frame { transform: tiny_skia::Transform, stack: Vec, primitives: Vec, + images: Vec, text: Vec, } @@ -68,6 +75,7 @@ impl Frame { clip_bounds, stack: Vec::new(), primitives: Vec::new(), + images: Vec::new(), text: Vec::new(), transform: tiny_skia::Transform::from_translate( clip_bounds.x, @@ -238,7 +246,7 @@ impl geometry::frame::Backend for Frame { Self::with_clip(clip_bounds) } - fn paste(&mut self, frame: Self, _at: Point) { + fn paste(&mut self, frame: Self) { self.primitives.extend(frame.primitives); self.text.extend(frame.text); } @@ -269,10 +277,82 @@ impl geometry::frame::Backend for Frame { fn into_geometry(self) -> Geometry { Geometry::Live { primitives: self.primitives, + images: self.images, text: self.text, clip_bounds: self.clip_bounds, } } + + fn draw_image( + &mut self, + handle: &image::Handle, + bounds: Rectangle, + filter_method: image::FilterMethod, + rotation: Radians, + opacity: f32, + ) { + let (bounds, external_rotation) = + transform_rectangle(bounds, self.transform); + + self.images.push(Image::Raster { + handle: handle.clone(), + filter_method, + bounds, + rotation: rotation + external_rotation, + opacity, + snap: false, + }); + } + + fn draw_svg( + &mut self, + handle: &svg::Handle, + bounds: Rectangle, + color: Option, + rotation: Radians, + opacity: f32, + ) { + let (bounds, external_rotation) = + transform_rectangle(bounds, self.transform); + + self.images.push(Image::Vector { + handle: handle.clone(), + bounds, + color, + rotation: rotation + external_rotation, + opacity, + }); + } +} + +fn transform_rectangle( + rectangle: Rectangle, + transform: tiny_skia::Transform, +) -> (Rectangle, Radians) { + let mut top_left = tiny_skia::Point { + x: rectangle.x, + y: rectangle.y, + }; + + let mut top_right = tiny_skia::Point { + x: rectangle.x + rectangle.width, + y: rectangle.y, + }; + + let mut bottom_left = tiny_skia::Point { + x: rectangle.x, + y: rectangle.y + rectangle.height, + }; + + transform.map_point(&mut top_left); + transform.map_point(&mut top_right); + transform.map_point(&mut bottom_left); + + Rectangle::with_vertices( + Point::new(top_left.x, top_left.y), + Point::new(top_right.x, top_right.y), + Point::new(bottom_left.x, bottom_left.y), + ) } fn convert_path(path: &Path) -> Option { diff --git a/tiny_skia/src/layer.rs b/tiny_skia/src/layer.rs index 48fca1d8..9a169f46 100644 --- a/tiny_skia/src/layer.rs +++ b/tiny_skia/src/layer.rs @@ -116,6 +116,48 @@ impl Layer { } pub fn draw_image( + &mut self, + image: &Image, + transformation: Transformation, + ) { + match image { + Image::Raster { + handle, + filter_method, + bounds, + rotation, + opacity, + snap: _, + } => { + self.draw_raster( + handle.clone(), + *filter_method, + *bounds, + transformation, + *rotation, + *opacity, + ); + } + Image::Vector { + handle, + color, + bounds, + rotation, + opacity, + } => { + self.draw_svg( + handle.clone(), + *color, + *bounds, + transformation, + *rotation, + *opacity, + ); + } + } + } + + pub fn draw_raster( &mut self, handle: image::Handle, filter_method: image::FilterMethod, @@ -130,6 +172,7 @@ impl Layer { bounds: bounds * transformation, rotation, opacity, + snap: false, }; self.images.push(image); diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index 6ec60158..f09e5aa3 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -330,6 +330,7 @@ impl graphics::geometry::Renderer for Renderer { match geometry { Geometry::Live { primitives, + images, text, clip_bounds, } => { @@ -339,6 +340,10 @@ impl graphics::geometry::Renderer for Renderer { transformation, ); + for image in images { + layer.draw_image(&image, transformation); + } + layer.draw_text_group(text, clip_bounds, transformation); } Geometry::Cache(cache) => { @@ -348,6 +353,10 @@ impl graphics::geometry::Renderer for Renderer { transformation, ); + for image in cache.images.iter() { + layer.draw_image(image, transformation); + } + layer.draw_text_cache( cache.text, cache.clip_bounds, @@ -381,7 +390,7 @@ impl core::image::Renderer for Renderer { opacity: f32, ) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_image( + layer.draw_raster( handle, filter_method, bounds, diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 30545fa2..b13ecb36 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -20,7 +20,7 @@ all-features = true [features] geometry = ["iced_graphics/geometry", "lyon"] image = ["iced_graphics/image"] -svg = ["resvg/text"] +svg = ["iced_graphics/svg", "resvg/text"] web-colors = ["iced_graphics/web-colors"] webgl = ["wgpu/webgl"] diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index f6213e1d..cb629b3e 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -1,7 +1,9 @@ //! Build and draw geometry. +use crate::core::image; +use crate::core::svg; use crate::core::text::LineHeight; use crate::core::{ - Pixels, Point, Radians, Rectangle, Size, Transformation, Vector, + Color, Pixels, Point, Radians, Rectangle, Size, Transformation, Vector, }; use crate::graphics::cache::{self, Cached}; use crate::graphics::color; @@ -11,7 +13,7 @@ use crate::graphics::geometry::{ }; use crate::graphics::gradient::{self, Gradient}; use crate::graphics::mesh::{self, Mesh}; -use crate::graphics::{self, Text}; +use crate::graphics::{self, Image, Text}; use crate::text; use crate::triangle; @@ -19,16 +21,22 @@ use lyon::geom::euclid; use lyon::tessellation; use std::borrow::Cow; +use std::sync::Arc; #[derive(Debug)] pub enum Geometry { - Live { meshes: Vec, text: Vec }, + Live { + meshes: Vec, + images: Vec, + text: Vec, + }, Cached(Cache), } #[derive(Debug, Clone)] pub struct Cache { pub meshes: Option, + pub images: Option>, pub text: Option, } @@ -45,7 +53,17 @@ impl Cached for Geometry { previous: Option, ) -> Self::Cache { match self { - Self::Live { meshes, text } => { + Self::Live { + meshes, + images, + text, + } => { + let images = if images.is_empty() { + None + } else { + Some(Arc::from(images)) + }; + if let Some(mut previous) = previous { if let Some(cache) = &mut previous.meshes { cache.update(meshes); @@ -59,10 +77,13 @@ impl Cached for Geometry { previous.text = text::Cache::new(group, text); } + previous.images = images; + previous } else { Cache { meshes: triangle::Cache::new(meshes), + images, text: text::Cache::new(group, text), } } @@ -78,6 +99,7 @@ pub struct Frame { clip_bounds: Rectangle, buffers: BufferStack, meshes: Vec, + images: Vec, text: Vec, transforms: Transforms, fill_tessellator: tessellation::FillTessellator, @@ -96,6 +118,7 @@ impl Frame { clip_bounds: bounds, buffers: BufferStack::new(), meshes: Vec::new(), + images: Vec::new(), text: Vec::new(), transforms: Transforms { previous: Vec::new(), @@ -335,10 +358,11 @@ impl geometry::frame::Backend for Frame { Frame::with_clip(clip_bounds) } - fn paste(&mut self, frame: Frame, _at: Point) { + fn paste(&mut self, frame: Frame) { self.meshes .extend(frame.buffers.into_meshes(frame.clip_bounds)); + self.images.extend(frame.images); self.text.extend(frame.text); } @@ -348,9 +372,51 @@ impl geometry::frame::Backend for Frame { Geometry::Live { meshes: self.meshes, + images: self.images, text: self.text, } } + + fn draw_image( + &mut self, + handle: &image::Handle, + bounds: Rectangle, + filter_method: image::FilterMethod, + rotation: Radians, + opacity: f32, + ) { + let (bounds, external_rotation) = + self.transforms.current.transform_rectangle(bounds); + + self.images.push(Image::Raster { + handle: handle.clone(), + filter_method, + bounds, + rotation: rotation + external_rotation, + opacity, + snap: false, + }); + } + + fn draw_svg( + &mut self, + handle: &svg::Handle, + bounds: Rectangle, + color: Option, + rotation: Radians, + opacity: f32, + ) { + let (bounds, external_rotation) = + self.transforms.current.transform_rectangle(bounds); + + self.images.push(Image::Vector { + handle: handle.clone(), + color, + bounds, + rotation: rotation + external_rotation, + opacity, + }); + } } enum Buffer { @@ -518,6 +584,21 @@ impl Transform { gradient } + + fn transform_rectangle( + &self, + rectangle: Rectangle, + ) -> (Rectangle, Radians) { + let top_left = self.transform_point(rectangle.position()); + let top_right = self.transform_point( + rectangle.position() + Vector::new(rectangle.width, 0.0), + ); + let bottom_left = self.transform_point( + rectangle.position() + Vector::new(0.0, rectangle.height), + ); + + Rectangle::with_vertices(top_left, top_right, bottom_left) + } } struct GradientVertex2DBuilder { gradient: gradient::Packed, diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index daa2fe16..ea34e4ec 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -149,6 +149,8 @@ impl Pipeline { 6 => Float32x2, // Layer 7 => Sint32, + // Snap + 8 => Uint32, ), }], }, @@ -212,8 +214,6 @@ impl Pipeline { transformation: Transformation, scale: f32, ) { - let transformation = transformation * Transformation::scale(scale); - let nearest_instances: &mut Vec = &mut Vec::new(); let linear_instances: &mut Vec = &mut Vec::new(); @@ -226,6 +226,7 @@ impl Pipeline { bounds, rotation, opacity, + snap, } => { if let Some(atlas_entry) = cache.upload_raster(device, encoder, handle) @@ -235,6 +236,7 @@ impl Pipeline { [bounds.width, bounds.height], f32::from(*rotation), *opacity, + *snap, atlas_entry, match filter_method { crate::core::image::FilterMethod::Nearest => { @@ -268,6 +270,7 @@ impl Pipeline { size, f32::from(*rotation), *opacity, + true, atlas_entry, nearest_instances, ); @@ -300,6 +303,7 @@ impl Pipeline { nearest_instances, linear_instances, transformation, + scale, ); self.prepare_layer += 1; @@ -375,9 +379,12 @@ impl Layer { nearest_instances: &[Instance], linear_instances: &[Instance], transformation: Transformation, + scale_factor: f32, ) { let uniforms = Uniforms { transform: transformation.into(), + scale_factor, + _padding: [0.0; 3], }; let bytes = bytemuck::bytes_of(&uniforms); @@ -492,6 +499,7 @@ struct Instance { _position_in_atlas: [f32; 2], _size_in_atlas: [f32; 2], _layer: u32, + _snap: u32, } impl Instance { @@ -502,6 +510,10 @@ impl Instance { #[derive(Debug, Clone, Copy, Zeroable, Pod)] struct Uniforms { transform: [f32; 16], + scale_factor: f32, + // Uniforms must be aligned to their largest member, + // this uses a mat4x4 which aligns to 16, so align to that + _padding: [f32; 3], } fn add_instances( @@ -509,6 +521,7 @@ fn add_instances( image_size: [f32; 2], rotation: f32, opacity: f32, + snap: bool, entry: &atlas::Entry, instances: &mut Vec, ) { @@ -525,6 +538,7 @@ fn add_instances( image_size, rotation, opacity, + snap, allocation, instances, ); @@ -554,8 +568,8 @@ fn add_instances( ]; add_instance( - position, center, size, rotation, opacity, allocation, - instances, + position, center, size, rotation, opacity, snap, + allocation, instances, ); } } @@ -569,6 +583,7 @@ fn add_instance( size: [f32; 2], rotation: f32, opacity: f32, + snap: bool, allocation: &atlas::Allocation, instances: &mut Vec, ) { @@ -591,6 +606,7 @@ fn add_instance( (height as f32 - 1.0) / atlas::SIZE as f32, ], _layer: layer as u32, + _snap: snap as u32, }; instances.push(instance); diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index df289e0e..e714e281 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -113,6 +113,49 @@ impl Layer { } pub fn draw_image( + &mut self, + image: &Image, + transformation: Transformation, + ) { + match image { + Image::Raster { + handle, + filter_method, + bounds, + rotation, + opacity, + snap, + } => { + self.draw_raster( + handle.clone(), + *filter_method, + *bounds, + transformation, + *rotation, + *opacity, + *snap, + ); + } + Image::Vector { + handle, + color, + bounds, + rotation, + opacity, + } => { + self.draw_svg( + handle.clone(), + *color, + *bounds, + transformation, + *rotation, + *opacity, + ); + } + } + } + + pub fn draw_raster( &mut self, handle: crate::core::image::Handle, filter_method: crate::core::image::FilterMethod, @@ -120,6 +163,7 @@ impl Layer { transformation: Transformation, rotation: Radians, opacity: f32, + snap: bool, ) { let image = Image::Raster { handle, @@ -127,6 +171,7 @@ impl Layer { bounds: bounds * transformation, rotation, opacity, + snap, }; self.images.push(image); diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 954340ec..24e60979 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -536,13 +536,14 @@ impl core::image::Renderer for Renderer { opacity: f32, ) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_image( + layer.draw_raster( handle, filter_method, bounds, transformation, rotation, opacity, + true, ); } } @@ -593,8 +594,17 @@ impl graphics::geometry::Renderer for Renderer { let (layer, transformation) = self.layers.current_mut(); match geometry { - Geometry::Live { meshes, text } => { + Geometry::Live { + meshes, + images, + text, + } => { layer.draw_mesh_group(meshes, transformation); + + for image in images { + layer.draw_image(&image, transformation); + } + layer.draw_text_group(text, transformation); } Geometry::Cached(cache) => { @@ -602,6 +612,12 @@ impl graphics::geometry::Renderer for Renderer { layer.draw_mesh_cache(meshes, transformation); } + if let Some(images) = cache.images { + for image in images.iter() { + layer.draw_image(image, transformation); + } + } + if let Some(text) = cache.text { layer.draw_text_cache(text, transformation); } diff --git a/wgpu/src/shader/image.wgsl b/wgpu/src/shader/image.wgsl index 0eeb100f..bc922838 100644 --- a/wgpu/src/shader/image.wgsl +++ b/wgpu/src/shader/image.wgsl @@ -1,5 +1,6 @@ struct Globals { transform: mat4x4, + scale_factor: f32, } @group(0) @binding(0) var globals: Globals; @@ -16,6 +17,7 @@ struct VertexInput { @location(5) atlas_pos: vec2, @location(6) atlas_scale: vec2, @location(7) layer: i32, + @location(8) snap: u32, } struct VertexOutput { @@ -38,7 +40,7 @@ fn vs_main(input: VertexInput) -> VertexOutput { out.opacity = input.opacity; // Calculate the vertex position and move the center to the origin - v_pos = round(input.pos) + v_pos * input.scale - input.center; + v_pos = input.pos + v_pos * input.scale - input.center; // Apply the rotation around the center of the image let cos_rot = cos(input.rotation); @@ -51,7 +53,13 @@ fn vs_main(input: VertexInput) -> VertexOutput { ); // Calculate the final position of the vertex - out.position = globals.transform * (vec4(input.center, 0.0, 0.0) + rotate * vec4(v_pos, 0.0, 1.0)); + out.position = vec4(vec2(globals.scale_factor), 1.0, 1.0) * (vec4(input.center, 0.0, 0.0) + rotate * vec4(v_pos, 0.0, 1.0)); + + if bool(input.snap) { + out.position = round(out.position); + } + + out.position = globals.transform * out.position; return out; } -- cgit From 4f5de3bbe910a36163daa52af85d21461eff2f96 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 4 Aug 2024 03:29:06 +0200 Subject: Showcase `canvas` image support in `solar_system` example --- examples/solar_system/Cargo.toml | 2 +- examples/solar_system/assets/earth.png | Bin 0 -> 91888 bytes examples/solar_system/assets/moon.png | Bin 0 -> 105100 bytes examples/solar_system/assets/sun.png | Bin 0 -> 114689 bytes examples/solar_system/src/main.rs | 62 +++++++++++++++++++++------------ 5 files changed, 41 insertions(+), 23 deletions(-) create mode 100644 examples/solar_system/assets/earth.png create mode 100644 examples/solar_system/assets/moon.png create mode 100644 examples/solar_system/assets/sun.png diff --git a/examples/solar_system/Cargo.toml b/examples/solar_system/Cargo.toml index ca64da14..e2c18c50 100644 --- a/examples/solar_system/Cargo.toml +++ b/examples/solar_system/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] iced.workspace = true -iced.features = ["debug", "canvas", "tokio"] +iced.features = ["debug", "canvas", "image", "tokio"] rand = "0.8.3" tracing-subscriber = "0.3" diff --git a/examples/solar_system/assets/earth.png b/examples/solar_system/assets/earth.png new file mode 100644 index 00000000..e81321d9 Binary files /dev/null and b/examples/solar_system/assets/earth.png differ diff --git a/examples/solar_system/assets/moon.png b/examples/solar_system/assets/moon.png new file mode 100644 index 00000000..03f10cb7 Binary files /dev/null and b/examples/solar_system/assets/moon.png differ diff --git a/examples/solar_system/assets/sun.png b/examples/solar_system/assets/sun.png new file mode 100644 index 00000000..29a928a7 Binary files /dev/null and b/examples/solar_system/assets/sun.png differ diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index a6f1ba6f..1d420959 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -7,10 +7,9 @@ //! //! [1]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations#An_animated_solar_system use iced::mouse; -use iced::widget::canvas; -use iced::widget::canvas::gradient; use iced::widget::canvas::stroke::{self, Stroke}; use iced::widget::canvas::{Geometry, Path}; +use iced::widget::{canvas, image}; use iced::window; use iced::{ Color, Element, Fill, Point, Rectangle, Renderer, Size, Subscription, @@ -66,6 +65,9 @@ impl SolarSystem { #[derive(Debug)] struct State { + sun: image::Handle, + earth: image::Handle, + moon: image::Handle, space_cache: canvas::Cache, system_cache: canvas::Cache, start: Instant, @@ -85,6 +87,15 @@ impl State { let size = window::Settings::default().size; State { + sun: image::Handle::from_bytes( + include_bytes!("../assets/sun.png").as_slice(), + ), + earth: image::Handle::from_bytes( + include_bytes!("../assets/earth.png").as_slice(), + ), + moon: image::Handle::from_bytes( + include_bytes!("../assets/moon.png").as_slice(), + ), space_cache: canvas::Cache::default(), system_cache: canvas::Cache::default(), start: now, @@ -132,6 +143,8 @@ impl canvas::Program for State { let background = self.space_cache.draw(renderer, bounds.size(), |frame| { + frame.fill_rectangle(Point::ORIGIN, frame.size(), Color::BLACK); + let stars = Path::new(|path| { for (p, size) in &self.stars { path.rectangle(*p, Size::new(*size, *size)); @@ -144,17 +157,21 @@ impl canvas::Program for State { let system = self.system_cache.draw(renderer, bounds.size(), |frame| { let center = frame.center(); + frame.translate(Vector::new(center.x, center.y)); + + frame.draw_image( + &self.sun, + Rectangle::with_radius(Self::SUN_RADIUS), + image::FilterMethod::Linear, + 0, + 1.0, + ); - let sun = Path::circle(center, Self::SUN_RADIUS); - let orbit = Path::circle(center, Self::ORBIT_RADIUS); - - frame.fill(&sun, Color::from_rgb8(0xF9, 0xD7, 0x1C)); + let orbit = Path::circle(Point::ORIGIN, Self::ORBIT_RADIUS); frame.stroke( &orbit, Stroke { - style: stroke::Style::Solid(Color::from_rgba8( - 0, 153, 255, 0.1, - )), + style: stroke::Style::Solid(Color::WHITE.scale_alpha(0.1)), width: 1.0, line_dash: canvas::LineDash { offset: 0, @@ -169,27 +186,28 @@ impl canvas::Program for State { + (2.0 * PI / 60_000.0) * elapsed.subsec_millis() as f32; frame.with_save(|frame| { - frame.translate(Vector::new(center.x, center.y)); frame.rotate(rotation); frame.translate(Vector::new(Self::ORBIT_RADIUS, 0.0)); - let earth = Path::circle(Point::ORIGIN, Self::EARTH_RADIUS); - - let earth_fill = gradient::Linear::new( - Point::new(-Self::EARTH_RADIUS, 0.0), - Point::new(Self::EARTH_RADIUS, 0.0), - ) - .add_stop(0.2, Color::from_rgb(0.15, 0.50, 1.0)) - .add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47)); - - frame.fill(&earth, earth_fill); + frame.draw_image( + &self.earth, + Rectangle::with_radius(Self::EARTH_RADIUS), + image::FilterMethod::Linear, + rotation * 10.0, + 1.0, + ); frame.with_save(|frame| { frame.rotate(rotation * 10.0); frame.translate(Vector::new(0.0, Self::MOON_DISTANCE)); - let moon = Path::circle(Point::ORIGIN, Self::MOON_RADIUS); - frame.fill(&moon, Color::WHITE); + frame.draw_image( + &self.moon, + Rectangle::with_radius(Self::MOON_RADIUS), + image::FilterMethod::Linear, + 0, + 1.0, + ); }); }); }); -- cgit From ed959023e96f9e5fca66b0bc6b3b3b2e13bdc359 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 4 Aug 2024 03:36:05 +0200 Subject: Remove unnecessary `with_save` calls in `solar_system` example --- examples/solar_system/src/main.rs | 46 ++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 1d420959..1f4e642b 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -185,31 +185,27 @@ impl canvas::Program for State { let rotation = (2.0 * PI / 60.0) * elapsed.as_secs() as f32 + (2.0 * PI / 60_000.0) * elapsed.subsec_millis() as f32; - frame.with_save(|frame| { - frame.rotate(rotation); - frame.translate(Vector::new(Self::ORBIT_RADIUS, 0.0)); - - frame.draw_image( - &self.earth, - Rectangle::with_radius(Self::EARTH_RADIUS), - image::FilterMethod::Linear, - rotation * 10.0, - 1.0, - ); - - frame.with_save(|frame| { - frame.rotate(rotation * 10.0); - frame.translate(Vector::new(0.0, Self::MOON_DISTANCE)); - - frame.draw_image( - &self.moon, - Rectangle::with_radius(Self::MOON_RADIUS), - image::FilterMethod::Linear, - 0, - 1.0, - ); - }); - }); + frame.rotate(rotation); + frame.translate(Vector::new(Self::ORBIT_RADIUS, 0.0)); + + frame.draw_image( + &self.earth, + Rectangle::with_radius(Self::EARTH_RADIUS), + image::FilterMethod::Linear, + rotation * 10.0, + 1.0, + ); + + frame.rotate(rotation * 10.0); + frame.translate(Vector::new(0.0, Self::MOON_DISTANCE)); + + frame.draw_image( + &self.moon, + Rectangle::with_radius(Self::MOON_RADIUS), + image::FilterMethod::Linear, + 0, + 1.0, + ); }); vec![background, system] -- cgit From 2ad3cff72223de7291d9c42149765c458944aacc Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 4 Aug 2024 03:37:29 +0200 Subject: Increase Earth's spin a bit in `solar_system` example --- examples/solar_system/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 1f4e642b..a4931465 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -192,7 +192,7 @@ impl canvas::Program for State { &self.earth, Rectangle::with_radius(Self::EARTH_RADIUS), image::FilterMethod::Linear, - rotation * 10.0, + rotation * 20.0, 1.0, ); -- cgit From 974ae6d1e7cd9df6967762a6d308106f4fe03edc Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 4 Aug 2024 03:39:53 +0200 Subject: Fix broken imports in `iced_renderer` --- renderer/src/fallback.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs index ddf7fd95..73e91dc6 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -441,7 +441,9 @@ where #[cfg(feature = "geometry")] mod geometry { use super::Renderer; - use crate::core::{Point, Radians, Rectangle, Size, Vector}; + use crate::core::image; + use crate::core::svg; + use crate::core::{Color, Point, Radians, Rectangle, Size, Vector}; use crate::graphics::cache::{self, Cached}; use crate::graphics::geometry::{self, Fill, Path, Stroke, Text}; @@ -574,9 +576,9 @@ mod geometry { fn draw_image( &mut self, - handle: &iced_wgpu::core::image::Handle, + handle: &image::Handle, bounds: Rectangle, - filter_method: iced_wgpu::core::image::FilterMethod, + filter_method: image::FilterMethod, rotation: Radians, opacity: f32, ) { @@ -595,9 +597,9 @@ mod geometry { fn draw_svg( &mut self, - handle: &iced_wgpu::core::svg::Handle, + handle: &svg::Handle, bounds: Rectangle, - color: Option, + color: Option, rotation: Radians, opacity: f32, ) { -- cgit From 92bd3ecd6b4a6618f0fc725dea3694c3b40e5314 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 4 Aug 2024 04:30:12 +0200 Subject: Introduce `Image` struct in `core::image` --- core/src/image.rs | 79 ++++++++++++++++++++++++++++++++++----- core/src/lib.rs | 1 + core/src/renderer/null.rs | 14 ++----- examples/solar_system/src/main.rs | 15 ++------ graphics/src/geometry.rs | 1 + graphics/src/geometry/frame.rs | 39 +++---------------- graphics/src/image.rs | 28 ++------------ renderer/src/fallback.rs | 48 ++++-------------------- tiny_skia/src/engine.rs | 17 +++------ tiny_skia/src/geometry.rs | 35 ++++++----------- tiny_skia/src/layer.rs | 57 +++++++++------------------- tiny_skia/src/lib.rs | 22 ++--------- wgpu/src/geometry.rs | 35 ++++++----------- wgpu/src/image/mod.rs | 19 +++------- wgpu/src/layer.rs | 51 ++++++------------------- wgpu/src/lib.rs | 23 ++---------- widget/src/canvas.rs | 4 +- widget/src/image.rs | 15 +++++--- widget/src/image/viewer.rs | 15 +++++--- 19 files changed, 184 insertions(+), 334 deletions(-) diff --git a/core/src/image.rs b/core/src/image.rs index 77ff7500..99d7f3ef 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -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 { + /// 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, from 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 a + /// [`FilterMethod::Nearest`]. + pub snap: bool, +} + +impl Image { + /// Creates a new [`Image`] with the given handle. + pub fn new(handle: impl Into) -> 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) -> Self { + self.rotation = rotation.into(); + self + } + + /// Sets the opacity of the [`Image`]. + pub fn opacity(mut self, opacity: impl Into) -> 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 { @@ -172,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; - /// 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, bounds: Rectangle); } diff --git a/core/src/lib.rs b/core/src/lib.rs index 40a288e5..0e17d430 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -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; diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 5c7513c6..e71117da 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -1,5 +1,5 @@ use crate::alignment; -use crate::image; +use crate::image::{self, Image}; use crate::renderer::{self, Renderer}; use crate::svg; use crate::text::{self, Text}; @@ -178,20 +178,14 @@ impl text::Editor for () { } impl image::Renderer for () { - type Handle = (); + type Handle = image::Handle; fn measure_image(&self, _handle: &Self::Handle) -> Size { 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) { + todo!() } } diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index a4931465..9da9fd34 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -160,11 +160,8 @@ impl canvas::Program for State { frame.translate(Vector::new(center.x, center.y)); frame.draw_image( - &self.sun, Rectangle::with_radius(Self::SUN_RADIUS), - image::FilterMethod::Linear, - 0, - 1.0, + &self.sun, ); let orbit = Path::circle(Point::ORIGIN, Self::ORBIT_RADIUS); @@ -189,22 +186,16 @@ impl canvas::Program for State { frame.translate(Vector::new(Self::ORBIT_RADIUS, 0.0)); frame.draw_image( - &self.earth, Rectangle::with_radius(Self::EARTH_RADIUS), - image::FilterMethod::Linear, - rotation * 20.0, - 1.0, + canvas::Image::new(&self.earth).rotation(rotation * 20.0), ); frame.rotate(rotation * 10.0); frame.translate(Vector::new(0.0, Self::MOON_DISTANCE)); frame.draw_image( - &self.moon, Rectangle::with_radius(Self::MOON_RADIUS), - image::FilterMethod::Linear, - 0, - 1.0, + &self.moon, ); }); diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index ab4a7a36..c7515e46 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -16,6 +16,7 @@ pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; pub use style::Style; pub use text::Text; +pub use crate::core::Image; pub use crate::gradient::{self, Gradient}; use crate::cache::Cached; diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs index d53d1331..1a7af8e6 100644 --- a/graphics/src/geometry/frame.rs +++ b/graphics/src/geometry/frame.rs @@ -1,8 +1,7 @@ //! Draw and generate geometry. -use crate::core::image; use crate::core::svg; use crate::core::{Color, Point, Radians, Rectangle, Size, Vector}; -use crate::geometry::{self, Fill, Path, Stroke, Text}; +use crate::geometry::{self, Fill, Image, Path, Stroke, Text}; /// The region of a surface that can be used to draw geometry. #[allow(missing_debug_implementations)] @@ -79,21 +78,8 @@ where /// Draws the given image on the [`Frame`] inside the given bounds. #[cfg(feature = "image")] - pub fn draw_image( - &mut self, - handle: &image::Handle, - bounds: Rectangle, - filter_method: image::FilterMethod, - rotation: impl Into, - opacity: f32, - ) { - self.raw.draw_image( - handle, - bounds, - filter_method, - rotation.into(), - opacity, - ); + pub fn draw_image(&mut self, bounds: Rectangle, image: impl Into) { + self.raw.draw_image(bounds, image); } /// Stores the current transform of the [`Frame`] and executes the given @@ -219,14 +205,7 @@ pub trait Backend: Sized { fill: impl Into, ); - fn draw_image( - &mut self, - handle: &image::Handle, - bounds: Rectangle, - filter_method: image::FilterMethod, - rotation: Radians, - opacity: f32, - ); + fn draw_image(&mut self, bounds: Rectangle, image: impl Into); fn draw_svg( &mut self, @@ -285,15 +264,7 @@ impl Backend for () { fn into_geometry(self) -> Self::Geometry {} - fn draw_image( - &mut self, - _handle: &image::Handle, - _bounds: Rectangle, - _filter_method: image::FilterMethod, - _rotation: Radians, - _opacity: f32, - ) { - } + fn draw_image(&mut self, _bounds: Rectangle, _image: impl Into) {} fn draw_svg( &mut self, diff --git a/graphics/src/image.rs b/graphics/src/image.rs index 0e8f2fe3..2e4f4b5a 100644 --- a/graphics/src/image.rs +++ b/graphics/src/image.rs @@ -8,28 +8,8 @@ use crate::core::{image, svg, Color, Radians, Rectangle}; #[derive(Debug, Clone, PartialEq)] pub enum Image { /// A raster image. - Raster { - /// The handle of a raster image. - handle: image::Handle, + Raster(image::Image, Rectangle), - /// The filter method of a raster image. - filter_method: image::FilterMethod, - - /// The bounds of the image. - bounds: Rectangle, - - /// The rotation of the image. - rotation: Radians, - - /// The opacity of the image. - opacity: f32, - - /// If set to `true`, the image will be snapped to the pixel grid. - /// - /// This can avoid graphical glitches, specially when using a - /// [`image::FilterMethod::Nearest`]. - snap: bool, - }, /// A vector image. Vector { /// The handle of a vector image. @@ -53,10 +33,8 @@ impl Image { /// Returns the bounds of the [`Image`]. pub fn bounds(&self) -> Rectangle { match self { - Image::Raster { - bounds, rotation, .. - } - | Image::Vector { + Image::Raster(image, bounds) => bounds.rotate(image.rotation), + Image::Vector { bounds, rotation, .. } => bounds.rotate(*rotation), } diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs index 73e91dc6..dc8a4107 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -3,7 +3,8 @@ use crate::core::image; use crate::core::renderer; use crate::core::svg; use crate::core::{ - self, Background, Color, Point, Radians, Rectangle, Size, Transformation, + self, Background, Color, Image, Point, Radians, Rectangle, Size, + Transformation, }; use crate::graphics; use crate::graphics::compositor; @@ -149,25 +150,8 @@ where delegate!(self, renderer, renderer.measure_image(handle)) } - fn draw_image( - &mut self, - handle: Self::Handle, - filter_method: image::FilterMethod, - bounds: Rectangle, - rotation: Radians, - opacity: f32, - ) { - delegate!( - self, - renderer, - renderer.draw_image( - handle, - filter_method, - bounds, - rotation, - opacity - ) - ); + fn draw_image(&mut self, image: Image, bounds: Rectangle) { + delegate!(self, renderer, renderer.draw_image(image, bounds)); } } @@ -441,11 +425,10 @@ where #[cfg(feature = "geometry")] mod geometry { use super::Renderer; - use crate::core::image; use crate::core::svg; use crate::core::{Color, Point, Radians, Rectangle, Size, Vector}; use crate::graphics::cache::{self, Cached}; - use crate::graphics::geometry::{self, Fill, Path, Stroke, Text}; + use crate::graphics::geometry::{self, Fill, Image, Path, Stroke, Text}; impl geometry::Renderer for Renderer where @@ -574,25 +557,8 @@ mod geometry { delegate!(self, frame, frame.fill_text(text)); } - fn draw_image( - &mut self, - handle: &image::Handle, - bounds: Rectangle, - filter_method: image::FilterMethod, - rotation: Radians, - opacity: f32, - ) { - delegate!( - self, - frame, - frame.draw_image( - handle, - bounds, - filter_method, - rotation, - opacity - ) - ); + fn draw_image(&mut self, bounds: Rectangle, image: impl Into) { + delegate!(self, frame, frame.draw_image(bounds, image)); } fn draw_svg( diff --git a/tiny_skia/src/engine.rs b/tiny_skia/src/engine.rs index c5c4d494..88e8a9b1 100644 --- a/tiny_skia/src/engine.rs +++ b/tiny_skia/src/engine.rs @@ -550,14 +550,7 @@ impl Engine { ) { match image { #[cfg(feature = "image")] - Image::Raster { - handle, - filter_method, - bounds, - rotation, - opacity, - snap: _, - } => { + Image::Raster(raster, bounds) => { let physical_bounds = *bounds * _transformation; if !_clip_bounds.intersects(&physical_bounds) { @@ -568,7 +561,7 @@ impl Engine { .then_some(_clip_mask as &_); let center = physical_bounds.center(); - let radians = f32::from(*rotation); + let radians = f32::from(raster.rotation); let transform = into_transform(_transformation).post_rotate_at( radians.to_degrees(), @@ -577,10 +570,10 @@ impl Engine { ); self.raster_pipeline.draw( - handle, - *filter_method, + &raster.handle, + raster.filter_method, *bounds, - *opacity, + raster.opacity, _pixels, transform, clip_mask, diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index 398b54f7..7b0e68f4 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -1,12 +1,11 @@ -use crate::core::image; use crate::core::svg; use crate::core::text::LineHeight; use crate::core::{Color, Pixels, Point, Radians, Rectangle, Size, Vector}; use crate::graphics::cache::{self, Cached}; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::stroke::{self, Stroke}; -use crate::graphics::geometry::{self, Path, Style}; -use crate::graphics::{Gradient, Image, Text}; +use crate::graphics::geometry::{self, Image, Path, Style}; +use crate::graphics::{self, Gradient, Text}; use crate::Primitive; use std::rc::Rc; @@ -15,7 +14,7 @@ use std::rc::Rc; pub enum Geometry { Live { text: Vec, - images: Vec, + images: Vec, primitives: Vec, clip_bounds: Rectangle, }, @@ -25,7 +24,7 @@ pub enum Geometry { #[derive(Debug, Clone)] pub struct Cache { pub text: Rc<[Text]>, - pub images: Rc<[Image]>, + pub images: Rc<[graphics::Image]>, pub primitives: Rc<[Primitive]>, pub clip_bounds: Rectangle, } @@ -61,7 +60,7 @@ pub struct Frame { transform: tiny_skia::Transform, stack: Vec, primitives: Vec, - images: Vec, + images: Vec, text: Vec, } @@ -283,25 +282,15 @@ impl geometry::frame::Backend for Frame { } } - fn draw_image( - &mut self, - handle: &image::Handle, - bounds: Rectangle, - filter_method: image::FilterMethod, - rotation: Radians, - opacity: f32, - ) { + fn draw_image(&mut self, bounds: Rectangle, image: impl Into) { + let mut image = image.into(); + let (bounds, external_rotation) = transform_rectangle(bounds, self.transform); - self.images.push(Image::Raster { - handle: handle.clone(), - filter_method, - bounds, - rotation: rotation + external_rotation, - opacity, - snap: false, - }); + image.rotation += external_rotation; + + self.images.push(graphics::Image::Raster(image, bounds)); } fn draw_svg( @@ -315,7 +304,7 @@ impl geometry::frame::Backend for Frame { let (bounds, external_rotation) = transform_rectangle(bounds, self.transform); - self.images.push(Image::Vector { + self.images.push(graphics::Image::Vector { handle: handle.clone(), bounds, color, diff --git a/tiny_skia/src/layer.rs b/tiny_skia/src/layer.rs index 9a169f46..33df0a86 100644 --- a/tiny_skia/src/layer.rs +++ b/tiny_skia/src/layer.rs @@ -1,11 +1,12 @@ +use crate::core::renderer::Quad; +use crate::core::svg; use crate::core::{ - image, renderer::Quad, svg, Background, Color, Point, Radians, Rectangle, - Transformation, + Background, Color, Image, Point, Radians, Rectangle, Transformation, }; +use crate::graphics; use crate::graphics::damage; use crate::graphics::layer; use crate::graphics::text::{Editor, Paragraph, Text}; -use crate::graphics::{self, Image}; use crate::Primitive; use std::rc::Rc; @@ -18,7 +19,7 @@ pub struct Layer { pub quads: Vec<(Quad, Background)>, pub primitives: Vec>, pub text: Vec>, - pub images: Vec, + pub images: Vec, } impl Layer { @@ -117,28 +118,14 @@ impl Layer { pub fn draw_image( &mut self, - image: &Image, + image: graphics::Image, transformation: Transformation, ) { match image { - Image::Raster { - handle, - filter_method, - bounds, - rotation, - opacity, - snap: _, - } => { - self.draw_raster( - handle.clone(), - *filter_method, - *bounds, - transformation, - *rotation, - *opacity, - ); + graphics::Image::Raster(raster, bounds) => { + self.draw_raster(raster.clone(), bounds, transformation); } - Image::Vector { + graphics::Image::Vector { handle, color, bounds, @@ -147,11 +134,11 @@ impl Layer { } => { self.draw_svg( handle.clone(), - *color, - *bounds, + color, + bounds, transformation, - *rotation, - *opacity, + rotation, + opacity, ); } } @@ -159,21 +146,11 @@ impl Layer { pub fn draw_raster( &mut self, - handle: image::Handle, - filter_method: image::FilterMethod, + image: Image, bounds: Rectangle, transformation: Transformation, - rotation: Radians, - opacity: f32, ) { - let image = Image::Raster { - handle, - filter_method, - bounds: bounds * transformation, - rotation, - opacity, - snap: false, - }; + let image = graphics::Image::Raster(image, bounds * transformation); self.images.push(image); } @@ -187,7 +164,7 @@ impl Layer { rotation: Radians, opacity: f32, ) { - let svg = Image::Vector { + let svg = graphics::Image::Vector { handle, color, bounds: bounds * transformation, @@ -304,7 +281,7 @@ impl Layer { &previous.images, ¤t.images, |image| vec![image.bounds().expand(1.0)], - Image::eq, + graphics::Image::eq, ); damage.extend(text); diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index f09e5aa3..00864c11 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -341,7 +341,7 @@ impl graphics::geometry::Renderer for Renderer { ); for image in images { - layer.draw_image(&image, transformation); + layer.draw_image(image, transformation); } layer.draw_text_group(text, clip_bounds, transformation); @@ -354,7 +354,7 @@ impl graphics::geometry::Renderer for Renderer { ); for image in cache.images.iter() { - layer.draw_image(image, transformation); + layer.draw_image(image.clone(), transformation); } layer.draw_text_cache( @@ -381,23 +381,9 @@ impl core::image::Renderer for Renderer { self.engine.raster_pipeline.dimensions(handle) } - fn draw_image( - &mut self, - handle: Self::Handle, - filter_method: core::image::FilterMethod, - bounds: Rectangle, - rotation: core::Radians, - opacity: f32, - ) { + fn draw_image(&mut self, image: core::Image, bounds: Rectangle) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_raster( - handle, - filter_method, - bounds, - transformation, - rotation, - opacity, - ); + layer.draw_raster(image, bounds, transformation); } } diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index cb629b3e..6b1bb074 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -1,5 +1,4 @@ //! Build and draw geometry. -use crate::core::image; use crate::core::svg; use crate::core::text::LineHeight; use crate::core::{ @@ -9,11 +8,11 @@ use crate::graphics::cache::{self, Cached}; use crate::graphics::color; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::{ - self, LineCap, LineDash, LineJoin, Path, Stroke, Style, + self, Image, LineCap, LineDash, LineJoin, Path, Stroke, Style, }; use crate::graphics::gradient::{self, Gradient}; use crate::graphics::mesh::{self, Mesh}; -use crate::graphics::{self, Image, Text}; +use crate::graphics::{self, Text}; use crate::text; use crate::triangle; @@ -27,7 +26,7 @@ use std::sync::Arc; pub enum Geometry { Live { meshes: Vec, - images: Vec, + images: Vec, text: Vec, }, Cached(Cache), @@ -36,7 +35,7 @@ pub enum Geometry { #[derive(Debug, Clone)] pub struct Cache { pub meshes: Option, - pub images: Option>, + pub images: Option>, pub text: Option, } @@ -99,7 +98,7 @@ pub struct Frame { clip_bounds: Rectangle, buffers: BufferStack, meshes: Vec, - images: Vec, + images: Vec, text: Vec, transforms: Transforms, fill_tessellator: tessellation::FillTessellator, @@ -377,25 +376,15 @@ impl geometry::frame::Backend for Frame { } } - fn draw_image( - &mut self, - handle: &image::Handle, - bounds: Rectangle, - filter_method: image::FilterMethod, - rotation: Radians, - opacity: f32, - ) { + fn draw_image(&mut self, bounds: Rectangle, image: impl Into) { + let mut image = image.into(); + let (bounds, external_rotation) = self.transforms.current.transform_rectangle(bounds); - self.images.push(Image::Raster { - handle: handle.clone(), - filter_method, - bounds, - rotation: rotation + external_rotation, - opacity, - snap: false, - }); + image.rotation += external_rotation; + + self.images.push(graphics::Image::Raster(image, bounds)); } fn draw_svg( @@ -409,7 +398,7 @@ impl geometry::frame::Backend for Frame { let (bounds, external_rotation) = self.transforms.current.transform_rectangle(bounds); - self.images.push(Image::Vector { + self.images.push(graphics::Image::Vector { handle: handle.clone(), color, bounds, diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index ea34e4ec..2b0d6251 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -220,25 +220,18 @@ impl Pipeline { for image in images { match &image { #[cfg(feature = "image")] - Image::Raster { - handle, - filter_method, - bounds, - rotation, - opacity, - snap, - } => { + Image::Raster(image, bounds) => { if let Some(atlas_entry) = - cache.upload_raster(device, encoder, handle) + cache.upload_raster(device, encoder, &image.handle) { add_instances( [bounds.x, bounds.y], [bounds.width, bounds.height], - f32::from(*rotation), - *opacity, - *snap, + f32::from(image.rotation), + image.opacity, + image.snap, atlas_entry, - match filter_method { + match image.filter_method { crate::core::image::FilterMethod::Nearest => { nearest_instances } diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index e714e281..71fa0250 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -1,5 +1,6 @@ use crate::core::{ - renderer, Background, Color, Point, Radians, Rectangle, Transformation, + self, renderer, Background, Color, Point, Radians, Rectangle, + Transformation, }; use crate::graphics; use crate::graphics::color; @@ -112,29 +113,10 @@ impl Layer { self.pending_text.push(text); } - pub fn draw_image( - &mut self, - image: &Image, - transformation: Transformation, - ) { + pub fn draw_image(&mut self, image: Image, transformation: Transformation) { match image { - Image::Raster { - handle, - filter_method, - bounds, - rotation, - opacity, - snap, - } => { - self.draw_raster( - handle.clone(), - *filter_method, - *bounds, - transformation, - *rotation, - *opacity, - *snap, - ); + Image::Raster(image, bounds) => { + self.draw_raster(image, bounds, transformation); } Image::Vector { handle, @@ -145,11 +127,11 @@ impl Layer { } => { self.draw_svg( handle.clone(), - *color, - *bounds, + color, + bounds, transformation, - *rotation, - *opacity, + rotation, + opacity, ); } } @@ -157,22 +139,11 @@ impl Layer { pub fn draw_raster( &mut self, - handle: crate::core::image::Handle, - filter_method: crate::core::image::FilterMethod, + image: core::Image, bounds: Rectangle, transformation: Transformation, - rotation: Radians, - opacity: f32, - snap: bool, ) { - let image = Image::Raster { - handle, - filter_method, - bounds: bounds * transformation, - rotation, - opacity, - snap, - }; + let image = Image::Raster(image, bounds * transformation); self.images.push(image); } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 24e60979..e5f45ad2 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -527,24 +527,9 @@ impl core::image::Renderer for Renderer { self.image_cache.borrow_mut().measure_image(handle) } - fn draw_image( - &mut self, - handle: Self::Handle, - filter_method: core::image::FilterMethod, - bounds: Rectangle, - rotation: core::Radians, - opacity: f32, - ) { + fn draw_image(&mut self, image: core::Image, bounds: Rectangle) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_raster( - handle, - filter_method, - bounds, - transformation, - rotation, - opacity, - true, - ); + layer.draw_raster(image, bounds, transformation); } } @@ -602,7 +587,7 @@ impl graphics::geometry::Renderer for Renderer { layer.draw_mesh_group(meshes, transformation); for image in images { - layer.draw_image(&image, transformation); + layer.draw_image(image, transformation); } layer.draw_text_group(text, transformation); @@ -613,7 +598,7 @@ impl graphics::geometry::Renderer for Renderer { } if let Some(images) = cache.images { - for image in images.iter() { + for image in images.iter().cloned() { layer.draw_image(image, transformation); } } diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 73cef087..185fa082 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -8,8 +8,8 @@ pub use program::Program; pub use crate::graphics::cache::Group; pub use crate::graphics::geometry::{ - fill, gradient, path, stroke, Fill, Gradient, LineCap, LineDash, LineJoin, - Path, Stroke, Style, Text, + fill, gradient, path, stroke, Fill, Gradient, Image, LineCap, LineDash, + LineJoin, Path, Stroke, Style, Text, }; use crate::core; diff --git a/widget/src/image.rs b/widget/src/image.rs index f1571400..55dd9816 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -8,8 +8,8 @@ use crate::core::mouse; use crate::core::renderer; use crate::core::widget::Tree; use crate::core::{ - ContentFit, Element, Layout, Length, Point, Rectangle, Rotation, Size, - Vector, Widget, + self, ContentFit, Element, Layout, Length, Point, Rectangle, Rotation, + Size, Vector, Widget, }; pub use image::{FilterMethod, Handle}; @@ -181,11 +181,14 @@ pub fn draw( let render = |renderer: &mut Renderer| { renderer.draw_image( - handle.clone(), - filter_method, + core::Image { + handle: handle.clone(), + filter_method, + rotation: rotation.radians(), + opacity, + snap: true, + }, drawing_bounds, - rotation.radians(), - opacity, ); }; diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index b8b69b60..b1aad22c 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -6,8 +6,8 @@ use crate::core::mouse; use crate::core::renderer; use crate::core::widget::tree::{self, Tree}; use crate::core::{ - Clipboard, ContentFit, Element, Layout, Length, Pixels, Point, Radians, - Rectangle, Shell, Size, Vector, Widget, + Clipboard, ContentFit, Element, Image, Layout, Length, Pixels, Point, + Radians, Rectangle, Shell, Size, Vector, Widget, }; /// A frame that displays an image with the ability to zoom in/out and pan. @@ -349,11 +349,14 @@ where let render = |renderer: &mut Renderer| { renderer.with_translation(translation, |renderer| { renderer.draw_image( - self.handle.clone(), - self.filter_method, + Image { + handle: self.handle.clone(), + filter_method: self.filter_method, + rotation: Radians(0.0), + opacity: 1.0, + snap: true, + }, drawing_bounds, - Radians(0.0), - 1.0, ); }); }; -- cgit From 3904f0b83af2fd3cd0d841d34d0d9c6193eeb845 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 4 Aug 2024 04:30:59 +0200 Subject: Remove `todo!` in `core::renderer::null` --- core/src/renderer/null.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index e71117da..3c6f8be0 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -184,9 +184,7 @@ impl image::Renderer for () { Size::default() } - fn draw_image(&mut self, _image: Image, _bounds: Rectangle) { - todo!() - } + fn draw_image(&mut self, _image: Image, _bounds: Rectangle) {} } impl svg::Renderer for () { -- cgit From 8708101c892540ffc966cf7ee9d66ca5cd2e8ca6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 4 Aug 2024 04:33:30 +0200 Subject: Simplify types in `tiny_skia::layer` --- tiny_skia/src/layer.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/tiny_skia/src/layer.rs b/tiny_skia/src/layer.rs index 33df0a86..5d3cb07b 100644 --- a/tiny_skia/src/layer.rs +++ b/tiny_skia/src/layer.rs @@ -1,12 +1,12 @@ use crate::core::renderer::Quad; use crate::core::svg; use crate::core::{ - Background, Color, Image, Point, Radians, Rectangle, Transformation, + self, Background, Color, Point, Radians, Rectangle, Transformation, }; -use crate::graphics; use crate::graphics::damage; use crate::graphics::layer; use crate::graphics::text::{Editor, Paragraph, Text}; +use crate::graphics::{self, Image}; use crate::Primitive; use std::rc::Rc; @@ -19,7 +19,7 @@ pub struct Layer { pub quads: Vec<(Quad, Background)>, pub primitives: Vec>, pub text: Vec>, - pub images: Vec, + pub images: Vec, } impl Layer { @@ -73,7 +73,7 @@ impl Layer { pub fn draw_text( &mut self, - text: crate::core::Text, + text: core::Text, position: Point, color: Color, clip_bounds: Rectangle, @@ -116,16 +116,12 @@ impl Layer { .push(Item::Cached(text, clip_bounds, transformation)); } - pub fn draw_image( - &mut self, - image: graphics::Image, - transformation: Transformation, - ) { + pub fn draw_image(&mut self, image: Image, transformation: Transformation) { match image { - graphics::Image::Raster(raster, bounds) => { + Image::Raster(raster, bounds) => { self.draw_raster(raster.clone(), bounds, transformation); } - graphics::Image::Vector { + Image::Vector { handle, color, bounds, @@ -146,11 +142,11 @@ impl Layer { pub fn draw_raster( &mut self, - image: Image, + image: core::Image, bounds: Rectangle, transformation: Transformation, ) { - let image = graphics::Image::Raster(image, bounds * transformation); + let image = Image::Raster(image, bounds * transformation); self.images.push(image); } @@ -164,7 +160,7 @@ impl Layer { rotation: Radians, opacity: f32, ) { - let svg = graphics::Image::Vector { + let svg = Image::Vector { handle, color, bounds: bounds * transformation, @@ -281,7 +277,7 @@ impl Layer { &previous.images, ¤t.images, |image| vec![image.bounds().expand(1.0)], - graphics::Image::eq, + Image::eq, ); damage.extend(text); @@ -313,7 +309,7 @@ impl graphics::Layer for Layer { fn flush(&mut self) {} - fn resize(&mut self, bounds: graphics::core::Rectangle) { + fn resize(&mut self, bounds: Rectangle) { self.bounds = bounds; } -- cgit From d4b08462e5a25929ec4df32f242898986902af56 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 4 Aug 2024 04:52:55 +0200 Subject: Introduce `Svg` struct in `core::svg` --- core/src/image.rs | 4 +-- core/src/lib.rs | 1 + core/src/renderer/null.rs | 13 ++------ core/src/svg.rs | 69 +++++++++++++++++++++++++++++++++++++----- graphics/src/geometry.rs | 2 +- graphics/src/geometry/frame.rs | 28 +++-------------- graphics/src/image.rs | 25 +++------------ renderer/src/fallback.rs | 36 ++++------------------ tiny_skia/src/engine.rs | 16 +++------- tiny_skia/src/geometry.rs | 30 ++++++------------ tiny_skia/src/layer.rs | 35 ++++----------------- tiny_skia/src/lib.rs | 18 ++--------- wgpu/src/geometry.rs | 40 +++++++++--------------- wgpu/src/image/mod.rs | 19 ++++++------ wgpu/src/layer.rs | 33 +++----------------- wgpu/src/lib.rs | 18 ++--------- widget/src/image.rs | 6 ++-- widget/src/svg.rs | 10 +++--- 18 files changed, 146 insertions(+), 257 deletions(-) diff --git a/core/src/image.rs b/core/src/image.rs index 99d7f3ef..f985636a 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -16,7 +16,7 @@ pub struct Image { /// The filter method of the image. pub filter_method: FilterMethod, - /// The rotation to be applied to the image, from its center. + /// The rotation to be applied to the image; on its center. pub rotation: Radians, /// The opacity of the image. @@ -26,7 +26,7 @@ pub struct Image { /// If set to `true`, the image will be snapped to the pixel grid. /// - /// This can avoid graphical glitches, specially when using a + /// This can avoid graphical glitches, specially when using /// [`FilterMethod::Nearest`]. pub snap: bool, } diff --git a/core/src/lib.rs b/core/src/lib.rs index 0e17d430..df599f45 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -70,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; diff --git a/core/src/renderer/null.rs b/core/src/renderer/null.rs index 3c6f8be0..e3a07280 100644 --- a/core/src/renderer/null.rs +++ b/core/src/renderer/null.rs @@ -4,8 +4,7 @@ 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 () { @@ -192,13 +191,5 @@ impl svg::Renderer for () { Size::default() } - fn draw_svg( - &mut self, - _handle: svg::Handle, - _color: Option, - _bounds: Rectangle, - _rotation: Radians, - _opacity: f32, - ) { - } + fn draw_svg(&mut self, _svg: svg::Svg, _bounds: Rectangle) {} } diff --git a/core/src/svg.rs b/core/src/svg.rs index 946b8156..ac19b223 100644 --- a/core/src/svg.rs +++ b/core/src/svg.rs @@ -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 { + /// 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, + + /// 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 { + /// Creates a new [`Svg`] with the given handle. + pub fn new(handle: impl Into) -> 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) -> Self { + self.color = Some(color.into()); + self + } + + /// Sets the rotation of the [`Svg`]. + pub fn rotation(mut self, rotation: impl Into) -> Self { + self.rotation = rotation.into(); + self + } + + /// Sets the opacity of the [`Svg`]. + pub fn opacity(mut self, opacity: impl Into) -> 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 { @@ -95,12 +155,5 @@ pub trait Renderer: crate::Renderer { fn measure_svg(&self, handle: &Handle) -> Size; /// 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, - bounds: Rectangle, - rotation: Radians, - opacity: f32, - ); + fn draw_svg(&mut self, svg: Svg, bounds: Rectangle); } diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index c7515e46..2b4b45a6 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -16,7 +16,7 @@ pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; pub use style::Style; pub use text::Text; -pub use crate::core::Image; +pub use crate::core::{Image, Svg}; pub use crate::gradient::{self, Gradient}; use crate::cache::Cached; diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs index 1a7af8e6..f3c0817c 100644 --- a/graphics/src/geometry/frame.rs +++ b/graphics/src/geometry/frame.rs @@ -1,7 +1,6 @@ //! Draw and generate geometry. -use crate::core::svg; -use crate::core::{Color, Point, Radians, Rectangle, Size, Vector}; -use crate::geometry::{self, Fill, Image, Path, Stroke, Text}; +use crate::core::{Point, Radians, Rectangle, Size, Vector}; +use crate::geometry::{self, Fill, Image, Path, Stroke, Svg, Text}; /// The region of a surface that can be used to draw geometry. #[allow(missing_debug_implementations)] @@ -206,15 +205,7 @@ pub trait Backend: Sized { ); fn draw_image(&mut self, bounds: Rectangle, image: impl Into); - - fn draw_svg( - &mut self, - handle: &svg::Handle, - bounds: Rectangle, - color: Option, - rotation: Radians, - opacity: f32, - ); + fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into); fn into_geometry(self) -> Self::Geometry; } @@ -262,17 +253,8 @@ impl Backend for () { ) { } - fn into_geometry(self) -> Self::Geometry {} - fn draw_image(&mut self, _bounds: Rectangle, _image: impl Into) {} + fn draw_svg(&mut self, _bounds: Rectangle, _svg: impl Into) {} - fn draw_svg( - &mut self, - _handle: &svg::Handle, - _bounds: Rectangle, - _color: Option, - _rotation: Radians, - _opacity: f32, - ) { - } + fn into_geometry(self) -> Self::Geometry {} } diff --git a/graphics/src/image.rs b/graphics/src/image.rs index 2e4f4b5a..67a5e0cf 100644 --- a/graphics/src/image.rs +++ b/graphics/src/image.rs @@ -2,7 +2,9 @@ #[cfg(feature = "image")] pub use ::image as image_rs; -use crate::core::{image, svg, Color, Radians, Rectangle}; +use crate::core::image; +use crate::core::svg; +use crate::core::Rectangle; /// A raster or vector image. #[derive(Debug, Clone, PartialEq)] @@ -11,22 +13,7 @@ pub enum Image { Raster(image::Image, Rectangle), /// A vector image. - Vector { - /// The handle of a vector image. - handle: svg::Handle, - - /// The [`Color`] filter - color: Option, - - /// The bounds of the image. - bounds: Rectangle, - - /// The rotation of the image. - rotation: Radians, - - /// The opacity of the image. - opacity: f32, - }, + Vector(svg::Svg, Rectangle), } impl Image { @@ -34,9 +21,7 @@ impl Image { pub fn bounds(&self) -> Rectangle { match self { Image::Raster(image, bounds) => bounds.rotate(image.rotation), - Image::Vector { - bounds, rotation, .. - } => bounds.rotate(*rotation), + Image::Vector(svg, bounds) => bounds.rotate(svg.rotation), } } } diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs index dc8a4107..fbd285db 100644 --- a/renderer/src/fallback.rs +++ b/renderer/src/fallback.rs @@ -3,8 +3,7 @@ use crate::core::image; use crate::core::renderer; use crate::core::svg; use crate::core::{ - self, Background, Color, Image, Point, Radians, Rectangle, Size, - Transformation, + self, Background, Color, Image, Point, Rectangle, Size, Svg, Transformation, }; use crate::graphics; use crate::graphics::compositor; @@ -164,19 +163,8 @@ where delegate!(self, renderer, renderer.measure_svg(handle)) } - fn draw_svg( - &mut self, - handle: svg::Handle, - color: Option, - bounds: Rectangle, - rotation: Radians, - opacity: f32, - ) { - delegate!( - self, - renderer, - renderer.draw_svg(handle, color, bounds, rotation, opacity) - ); + fn draw_svg(&mut self, svg: Svg, bounds: Rectangle) { + delegate!(self, renderer, renderer.draw_svg(svg, bounds)); } } @@ -425,8 +413,7 @@ where #[cfg(feature = "geometry")] mod geometry { use super::Renderer; - use crate::core::svg; - use crate::core::{Color, Point, Radians, Rectangle, Size, Vector}; + use crate::core::{Point, Radians, Rectangle, Size, Svg, Vector}; use crate::graphics::cache::{self, Cached}; use crate::graphics::geometry::{self, Fill, Image, Path, Stroke, Text}; @@ -561,19 +548,8 @@ mod geometry { delegate!(self, frame, frame.draw_image(bounds, image)); } - fn draw_svg( - &mut self, - handle: &svg::Handle, - bounds: Rectangle, - color: Option, - rotation: Radians, - opacity: f32, - ) { - delegate!( - self, - frame, - frame.draw_svg(handle, bounds, color, rotation, opacity) - ); + fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into) { + delegate!(self, frame, frame.draw_svg(bounds, svg)); } fn push_transform(&mut self) { diff --git a/tiny_skia/src/engine.rs b/tiny_skia/src/engine.rs index 88e8a9b1..196c36cf 100644 --- a/tiny_skia/src/engine.rs +++ b/tiny_skia/src/engine.rs @@ -580,13 +580,7 @@ impl Engine { ); } #[cfg(feature = "svg")] - Image::Vector { - handle, - color, - bounds, - rotation, - opacity, - } => { + Image::Vector(svg, bounds) => { let physical_bounds = *bounds * _transformation; if !_clip_bounds.intersects(&physical_bounds) { @@ -597,7 +591,7 @@ impl Engine { .then_some(_clip_mask as &_); let center = physical_bounds.center(); - let radians = f32::from(*rotation); + let radians = f32::from(svg.rotation); let transform = into_transform(_transformation).post_rotate_at( radians.to_degrees(), @@ -606,10 +600,10 @@ impl Engine { ); self.vector_pipeline.draw( - handle, - *color, + &svg.handle, + svg.color, physical_bounds, - *opacity, + svg.opacity, _pixels, transform, clip_mask, diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index 7b0e68f4..659612d1 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -1,11 +1,10 @@ -use crate::core::svg; use crate::core::text::LineHeight; -use crate::core::{Color, Pixels, Point, Radians, Rectangle, Size, Vector}; +use crate::core::{self, Pixels, Point, Radians, Rectangle, Size, Svg, Vector}; use crate::graphics::cache::{self, Cached}; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::stroke::{self, Stroke}; -use crate::graphics::geometry::{self, Image, Path, Style}; -use crate::graphics::{self, Gradient, Text}; +use crate::graphics::geometry::{self, Path, Style}; +use crate::graphics::{self, Gradient, Image, Text}; use crate::Primitive; use std::rc::Rc; @@ -282,7 +281,7 @@ impl geometry::frame::Backend for Frame { } } - fn draw_image(&mut self, bounds: Rectangle, image: impl Into) { + fn draw_image(&mut self, bounds: Rectangle, image: impl Into) { let mut image = image.into(); let (bounds, external_rotation) = @@ -293,24 +292,15 @@ impl geometry::frame::Backend for Frame { self.images.push(graphics::Image::Raster(image, bounds)); } - fn draw_svg( - &mut self, - handle: &svg::Handle, - bounds: Rectangle, - color: Option, - rotation: Radians, - opacity: f32, - ) { + fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into) { + let mut svg = svg.into(); + let (bounds, external_rotation) = transform_rectangle(bounds, self.transform); - self.images.push(graphics::Image::Vector { - handle: handle.clone(), - bounds, - color, - rotation: rotation + external_rotation, - opacity, - }); + svg.rotation += external_rotation; + + self.images.push(Image::Vector(svg, bounds)); } } diff --git a/tiny_skia/src/layer.rs b/tiny_skia/src/layer.rs index 5d3cb07b..bdfd4d38 100644 --- a/tiny_skia/src/layer.rs +++ b/tiny_skia/src/layer.rs @@ -1,7 +1,6 @@ use crate::core::renderer::Quad; -use crate::core::svg; use crate::core::{ - self, Background, Color, Point, Radians, Rectangle, Transformation, + self, Background, Color, Point, Rectangle, Svg, Transformation, }; use crate::graphics::damage; use crate::graphics::layer; @@ -119,23 +118,10 @@ impl Layer { pub fn draw_image(&mut self, image: Image, transformation: Transformation) { match image { Image::Raster(raster, bounds) => { - self.draw_raster(raster.clone(), bounds, transformation); + self.draw_raster(raster, bounds, transformation); } - Image::Vector { - handle, - color, - bounds, - rotation, - opacity, - } => { - self.draw_svg( - handle.clone(), - color, - bounds, - transformation, - rotation, - opacity, - ); + Image::Vector(svg, bounds) => { + self.draw_svg(svg, bounds, transformation); } } } @@ -153,20 +139,11 @@ impl Layer { pub fn draw_svg( &mut self, - handle: svg::Handle, - color: Option, + svg: Svg, bounds: Rectangle, transformation: Transformation, - rotation: Radians, - opacity: f32, ) { - let svg = Image::Vector { - handle, - color, - bounds: bounds * transformation, - rotation, - opacity, - }; + let svg = Image::Vector(svg, bounds * transformation); self.images.push(svg); } diff --git a/tiny_skia/src/lib.rs b/tiny_skia/src/lib.rs index 00864c11..758921d4 100644 --- a/tiny_skia/src/lib.rs +++ b/tiny_skia/src/lib.rs @@ -396,23 +396,9 @@ impl core::svg::Renderer for Renderer { self.engine.vector_pipeline.viewport_dimensions(handle) } - fn draw_svg( - &mut self, - handle: core::svg::Handle, - color: Option, - bounds: Rectangle, - rotation: core::Radians, - opacity: f32, - ) { + fn draw_svg(&mut self, svg: core::Svg, bounds: Rectangle) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_svg( - handle, - color, - bounds, - transformation, - rotation, - opacity, - ); + layer.draw_svg(svg, bounds, transformation); } } diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index 6b1bb074..be65ba36 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -1,18 +1,17 @@ //! Build and draw geometry. -use crate::core::svg; use crate::core::text::LineHeight; use crate::core::{ - Color, Pixels, Point, Radians, Rectangle, Size, Transformation, Vector, + self, Pixels, Point, Radians, Rectangle, Size, Svg, Transformation, Vector, }; use crate::graphics::cache::{self, Cached}; use crate::graphics::color; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::{ - self, Image, LineCap, LineDash, LineJoin, Path, Stroke, Style, + self, LineCap, LineDash, LineJoin, Path, Stroke, Style, }; use crate::graphics::gradient::{self, Gradient}; use crate::graphics::mesh::{self, Mesh}; -use crate::graphics::{self, Text}; +use crate::graphics::{Image, Text}; use crate::text; use crate::triangle; @@ -26,7 +25,7 @@ use std::sync::Arc; pub enum Geometry { Live { meshes: Vec, - images: Vec, + images: Vec, text: Vec, }, Cached(Cache), @@ -35,7 +34,7 @@ pub enum Geometry { #[derive(Debug, Clone)] pub struct Cache { pub meshes: Option, - pub images: Option>, + pub images: Option>, pub text: Option, } @@ -98,7 +97,7 @@ pub struct Frame { clip_bounds: Rectangle, buffers: BufferStack, meshes: Vec, - images: Vec, + images: Vec, text: Vec, transforms: Transforms, fill_tessellator: tessellation::FillTessellator, @@ -292,7 +291,7 @@ impl geometry::frame::Backend for Frame { height: f32::INFINITY, }; - self.text.push(graphics::Text::Cached { + self.text.push(Text::Cached { content: text.content, bounds, color: text.color, @@ -376,7 +375,7 @@ impl geometry::frame::Backend for Frame { } } - fn draw_image(&mut self, bounds: Rectangle, image: impl Into) { + fn draw_image(&mut self, bounds: Rectangle, image: impl Into) { let mut image = image.into(); let (bounds, external_rotation) = @@ -384,27 +383,18 @@ impl geometry::frame::Backend for Frame { image.rotation += external_rotation; - self.images.push(graphics::Image::Raster(image, bounds)); + self.images.push(Image::Raster(image, bounds)); } - fn draw_svg( - &mut self, - handle: &svg::Handle, - bounds: Rectangle, - color: Option, - rotation: Radians, - opacity: f32, - ) { + fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into) { + let mut svg = svg.into(); + let (bounds, external_rotation) = self.transforms.current.transform_rectangle(bounds); - self.images.push(graphics::Image::Vector { - handle: handle.clone(), - color, - bounds, - rotation: rotation + external_rotation, - opacity, - }); + svg.rotation += external_rotation; + + self.images.push(Image::Vector(svg, bounds)); } } diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 2b0d6251..1b16022a 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -246,23 +246,22 @@ impl Pipeline { Image::Raster { .. } => {} #[cfg(feature = "svg")] - Image::Vector { - handle, - color, - bounds, - rotation, - opacity, - } => { + Image::Vector(svg, bounds) => { let size = [bounds.width, bounds.height]; if let Some(atlas_entry) = cache.upload_vector( - device, encoder, handle, *color, size, scale, + device, + encoder, + &svg.handle, + svg.color, + size, + scale, ) { add_instances( [bounds.x, bounds.y], size, - f32::from(*rotation), - *opacity, + f32::from(svg.rotation), + svg.opacity, true, atlas_entry, nearest_instances, diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 71fa0250..68d5a015 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -1,6 +1,5 @@ use crate::core::{ - self, renderer, Background, Color, Point, Radians, Rectangle, - Transformation, + self, renderer, Background, Color, Point, Rectangle, Svg, Transformation, }; use crate::graphics; use crate::graphics::color; @@ -118,21 +117,8 @@ impl Layer { Image::Raster(image, bounds) => { self.draw_raster(image, bounds, transformation); } - Image::Vector { - handle, - color, - bounds, - rotation, - opacity, - } => { - self.draw_svg( - handle.clone(), - color, - bounds, - transformation, - rotation, - opacity, - ); + Image::Vector(svg, bounds) => { + self.draw_svg(svg, bounds, transformation); } } } @@ -150,20 +136,11 @@ impl Layer { pub fn draw_svg( &mut self, - handle: crate::core::svg::Handle, - color: Option, + svg: Svg, bounds: Rectangle, transformation: Transformation, - rotation: Radians, - opacity: f32, ) { - let svg = Image::Vector { - handle, - color, - bounds: bounds * transformation, - rotation, - opacity, - }; + let svg = Image::Vector(svg, bounds * transformation); self.images.push(svg); } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index e5f45ad2..39167514 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -539,23 +539,9 @@ impl core::svg::Renderer for Renderer { self.image_cache.borrow_mut().measure_svg(handle) } - fn draw_svg( - &mut self, - handle: core::svg::Handle, - color_filter: Option, - bounds: Rectangle, - rotation: core::Radians, - opacity: f32, - ) { + fn draw_svg(&mut self, svg: core::Svg, bounds: Rectangle) { let (layer, transformation) = self.layers.current_mut(); - layer.draw_svg( - handle, - color_filter, - bounds, - transformation, - rotation, - opacity, - ); + layer.draw_svg(svg, bounds, transformation); } } diff --git a/widget/src/image.rs b/widget/src/image.rs index 55dd9816..e04f2d6f 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -8,8 +8,8 @@ use crate::core::mouse; use crate::core::renderer; use crate::core::widget::Tree; use crate::core::{ - self, ContentFit, Element, Layout, Length, Point, Rectangle, Rotation, - Size, Vector, Widget, + ContentFit, Element, Layout, Length, Point, Rectangle, Rotation, Size, + Vector, Widget, }; pub use image::{FilterMethod, Handle}; @@ -181,7 +181,7 @@ pub fn draw( let render = |renderer: &mut Renderer| { renderer.draw_image( - core::Image { + image::Image { handle: handle.clone(), filter_method, rotation: rotation.radians(), diff --git a/widget/src/svg.rs b/widget/src/svg.rs index 4551bcad..bec0090f 100644 --- a/widget/src/svg.rs +++ b/widget/src/svg.rs @@ -211,11 +211,13 @@ where let render = |renderer: &mut Renderer| { renderer.draw_svg( - self.handle.clone(), - style.color, + svg::Svg { + handle: self.handle.clone(), + color: style.color, + rotation: self.rotation.radians(), + opacity: self.opacity, + }, drawing_bounds, - self.rotation.radians(), - self.opacity, ); }; -- cgit From 2b1b9c984ac1b290c351d0a9edc7bca69f8bd526 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 4 Aug 2024 05:03:48 +0200 Subject: Implement missing `draw_svg` in `Frame` wrapper --- graphics/src/geometry/frame.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs index f3c0817c..b5f2f139 100644 --- a/graphics/src/geometry/frame.rs +++ b/graphics/src/geometry/frame.rs @@ -75,12 +75,18 @@ where self.raw.fill_text(text); } - /// Draws the given image on the [`Frame`] inside the given bounds. + /// Draws the given [`Image`] on the [`Frame`] inside the given bounds. #[cfg(feature = "image")] pub fn draw_image(&mut self, bounds: Rectangle, image: impl Into) { self.raw.draw_image(bounds, image); } + /// Draws the given [`Svg`] on the [`Frame`] inside the given bounds. + #[cfg(feature = "svg")] + pub fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into) { + self.raw.draw_svg(bounds, svg); + } + /// Stores the current transform of the [`Frame`] and executes the given /// drawing operations, restoring the transform afterwards. /// -- cgit From cc076903dda18f79dbd82238f7a8216bab8c679d Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sun, 4 Aug 2024 14:38:42 +0200 Subject: Invert Earth's rotation in `solar_system` example --- examples/solar_system/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 9da9fd34..1e74f2bd 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -187,7 +187,7 @@ impl canvas::Program for State { frame.draw_image( Rectangle::with_radius(Self::EARTH_RADIUS), - canvas::Image::new(&self.earth).rotation(rotation * 20.0), + canvas::Image::new(&self.earth).rotation(-rotation * 20.0), ); frame.rotate(rotation * 10.0); -- cgit