diff options
author | 2024-08-04 14:52:29 +0200 | |
---|---|---|
committer | 2024-08-04 14:52:29 +0200 | |
commit | 145c3dc8fc4f92c400fbc3f8202ed22e1d498663 (patch) | |
tree | fe48b8e7f0021100aa2a0c697437527212af3475 /wgpu | |
parent | 9cccaebb04944f2295cadff716d9708f4caa5642 (diff) | |
parent | cc076903dda18f79dbd82238f7a8216bab8c679d (diff) | |
download | iced-145c3dc8fc4f92c400fbc3f8202ed22e1d498663.tar.gz iced-145c3dc8fc4f92c400fbc3f8202ed22e1d498663.tar.bz2 iced-145c3dc8fc4f92c400fbc3f8202ed22e1d498663.zip |
Merge pull request #2537 from iced-rs/feature/canvas-image-support
`image` and `svg` support for `canvas`
Diffstat (limited to '')
-rw-r--r-- | wgpu/Cargo.toml | 2 | ||||
-rw-r--r-- | wgpu/src/geometry.rs | 72 | ||||
-rw-r--r-- | wgpu/src/image/mod.rs | 58 | ||||
-rw-r--r-- | wgpu/src/layer.rs | 43 | ||||
-rw-r--r-- | wgpu/src/lib.rs | 101 | ||||
-rw-r--r-- | wgpu/src/shader/image.wgsl | 12 |
6 files changed, 172 insertions, 116 deletions
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..be65ba36 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -1,7 +1,7 @@ //! Build and draw geometry. use crate::core::text::LineHeight; use crate::core::{ - 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; @@ -11,7 +11,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::{Image, Text}; use crate::text; use crate::triangle; @@ -19,16 +19,22 @@ use lyon::geom::euclid; use lyon::tessellation; use std::borrow::Cow; +use std::sync::Arc; #[derive(Debug)] pub enum Geometry { - Live { meshes: Vec<Mesh>, text: Vec<Text> }, + Live { + meshes: Vec<Mesh>, + images: Vec<Image>, + text: Vec<Text>, + }, Cached(Cache), } #[derive(Debug, Clone)] pub struct Cache { pub meshes: Option<triangle::Cache>, + pub images: Option<Arc<[Image]>>, pub text: Option<text::Cache>, } @@ -45,7 +51,17 @@ impl Cached for Geometry { previous: Option<Self::Cache>, ) -> 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 +75,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 +97,7 @@ pub struct Frame { clip_bounds: Rectangle, buffers: BufferStack, meshes: Vec<Mesh>, + images: Vec<Image>, text: Vec<Text>, transforms: Transforms, fill_tessellator: tessellation::FillTessellator, @@ -96,6 +116,7 @@ impl Frame { clip_bounds: bounds, buffers: BufferStack::new(), meshes: Vec::new(), + images: Vec::new(), text: Vec::new(), transforms: Transforms { previous: Vec::new(), @@ -270,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, @@ -335,10 +356,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 +370,32 @@ impl geometry::frame::Backend for Frame { Geometry::Live { meshes: self.meshes, + images: self.images, text: self.text, } } + + fn draw_image(&mut self, bounds: Rectangle, image: impl Into<core::Image>) { + let mut image = image.into(); + + let (bounds, external_rotation) = + self.transforms.current.transform_rectangle(bounds); + + image.rotation += external_rotation; + + self.images.push(Image::Raster(image, bounds)); + } + + fn draw_svg(&mut self, bounds: Rectangle, svg: impl Into<Svg>) { + let mut svg = svg.into(); + + let (bounds, external_rotation) = + self.transforms.current.transform_rectangle(bounds); + + svg.rotation += external_rotation; + + self.images.push(Image::Vector(svg, bounds)); + } } enum Buffer { @@ -518,6 +563,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..1b16022a 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,31 +214,24 @@ impl Pipeline { transformation: Transformation, scale: f32, ) { - let transformation = transformation * Transformation::scale(scale); - let nearest_instances: &mut Vec<Instance> = &mut Vec::new(); let linear_instances: &mut Vec<Instance> = &mut Vec::new(); for image in images { match &image { #[cfg(feature = "image")] - Image::Raster { - handle, - filter_method, - bounds, - rotation, - opacity, - } => { + 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, + f32::from(image.rotation), + image.opacity, + image.snap, atlas_entry, - match filter_method { + match image.filter_method { crate::core::image::FilterMethod::Nearest => { nearest_instances } @@ -251,23 +246,23 @@ 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, ); @@ -300,6 +295,7 @@ impl Pipeline { nearest_instances, linear_instances, transformation, + scale, ); self.prepare_layer += 1; @@ -375,9 +371,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 +491,7 @@ struct Instance { _position_in_atlas: [f32; 2], _size_in_atlas: [f32; 2], _layer: u32, + _snap: u32, } impl Instance { @@ -502,6 +502,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<f32> which aligns to 16, so align to that + _padding: [f32; 3], } fn add_instances( @@ -509,6 +513,7 @@ fn add_instances( image_size: [f32; 2], rotation: f32, opacity: f32, + snap: bool, entry: &atlas::Entry, instances: &mut Vec<Instance>, ) { @@ -525,6 +530,7 @@ fn add_instances( image_size, rotation, opacity, + snap, allocation, instances, ); @@ -554,8 +560,8 @@ fn add_instances( ]; add_instance( - position, center, size, rotation, opacity, allocation, - instances, + position, center, size, rotation, opacity, snap, + allocation, instances, ); } } @@ -569,6 +575,7 @@ fn add_instance( size: [f32; 2], rotation: f32, opacity: f32, + snap: bool, allocation: &atlas::Allocation, instances: &mut Vec<Instance>, ) { @@ -591,6 +598,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 9551311d..68d5a015 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -1,5 +1,5 @@ use crate::core::{ - renderer, Background, Color, Point, Radians, Rectangle, Transformation, + self, renderer, Background, Color, Point, Rectangle, Svg, Transformation, }; use crate::graphics; use crate::graphics::color; @@ -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<Mesh>, pending_text: Vec<Text>, } @@ -112,42 +112,35 @@ impl Layer { self.pending_text.push(text); } - pub fn draw_image( + pub fn draw_image(&mut self, image: Image, transformation: Transformation) { + match image { + Image::Raster(image, bounds) => { + self.draw_raster(image, bounds, transformation); + } + Image::Vector(svg, bounds) => { + self.draw_svg(svg, bounds, transformation); + } + } + } + + 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, ) { - let image = Image::Raster { - handle, - filter_method, - bounds: bounds * transformation, - rotation, - opacity, - }; + let image = Image::Raster(image, bounds * transformation); self.images.push(image); } pub fn draw_svg( &mut self, - handle: crate::core::svg::Handle, - color: Option<Color>, + 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 ad88ce3e..39167514 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); @@ -527,23 +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_image( - handle, - filter_method, - bounds, - transformation, - rotation, - opacity, - ); + layer.draw_raster(image, bounds, transformation); } } @@ -553,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<Color>, - 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); } } @@ -593,8 +565,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 +583,12 @@ impl graphics::geometry::Renderer for Renderer { layer.draw_mesh_cache(meshes, transformation); } + if let Some(images) = cache.images { + for image in images.iter().cloned() { + 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<f32>, + scale_factor: f32, } @group(0) @binding(0) var<uniform> globals: Globals; @@ -16,6 +17,7 @@ struct VertexInput { @location(5) atlas_pos: vec2<f32>, @location(6) atlas_scale: vec2<f32>, @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<f32>(input.center, 0.0, 0.0) + rotate * vec4<f32>(v_pos, 0.0, 1.0)); + out.position = vec4(vec2(globals.scale_factor), 1.0, 1.0) * (vec4<f32>(input.center, 0.0, 0.0) + rotate * vec4<f32>(v_pos, 0.0, 1.0)); + + if bool(input.snap) { + out.position = round(out.position); + } + + out.position = globals.transform * out.position; return out; } |