summaryrefslogtreecommitdiffstats
path: root/wgpu
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector@hecrj.dev>2024-08-04 14:52:29 +0200
committerLibravatar GitHub <noreply@github.com>2024-08-04 14:52:29 +0200
commit145c3dc8fc4f92c400fbc3f8202ed22e1d498663 (patch)
treefe48b8e7f0021100aa2a0c697437527212af3475 /wgpu
parent9cccaebb04944f2295cadff716d9708f4caa5642 (diff)
parentcc076903dda18f79dbd82238f7a8216bab8c679d (diff)
downloadiced-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.toml2
-rw-r--r--wgpu/src/geometry.rs72
-rw-r--r--wgpu/src/image/mod.rs58
-rw-r--r--wgpu/src/layer.rs43
-rw-r--r--wgpu/src/lib.rs101
-rw-r--r--wgpu/src/shader/image.wgsl12
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;
}