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 'wgpu')
| -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;  }  | 
