From 15aa94c0f169123e025a792d0baa5c8d88cfd7eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Mon, 26 May 2025 23:59:15 +0200 Subject: [PATCH] Try to align `quad` and `image` primitives in `iced_wgpu` --- core/src/image.rs | 13 ------------- core/src/rectangle.rs | 11 +++++++---- wgpu/src/image/mod.rs | 27 ++++++++++++++------------- wgpu/src/shader/image.wgsl | 7 +------ wgpu/src/shader/quad/gradient.wgsl | 6 +++--- wgpu/src/shader/quad/solid.wgsl | 6 +++--- widget/src/image.rs | 1 - widget/src/image/viewer.rs | 1 - 8 files changed, 28 insertions(+), 44 deletions(-) diff --git a/core/src/image.rs b/core/src/image.rs index f985636a..225ee029 100644 --- a/core/src/image.rs +++ b/core/src/image.rs @@ -23,12 +23,6 @@ pub struct Image { /// /// 0 means transparent. 1 means opaque. pub opacity: f32, - - /// If set to `true`, the image will be snapped to the pixel grid. - /// - /// This can avoid graphical glitches, specially when using - /// [`FilterMethod::Nearest`]. - pub snap: bool, } impl Image { @@ -39,7 +33,6 @@ impl Image { filter_method: FilterMethod::default(), rotation: Radians(0.0), opacity: 1.0, - snap: false, } } @@ -60,12 +53,6 @@ impl Image { 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 { diff --git a/core/src/rectangle.rs b/core/src/rectangle.rs index 0c4add05..c2c88304 100644 --- a/core/src/rectangle.rs +++ b/core/src/rectangle.rs @@ -250,11 +250,14 @@ impl Rectangle { return None; } + let x = (self.x + 0.01).round(); + let y = (self.y + 0.01).round(); + Some(Rectangle { - x: self.x as u32, - y: self.y as u32, - width, - height, + x: x as u32, + y: y as u32, + width: ((self.x + self.width + 0.01).round() - x) as u32, + height: ((self.y + self.height + 0.01).round() - y) as u32, }) } diff --git a/wgpu/src/image/mod.rs b/wgpu/src/image/mod.rs index 51d2acef..7b82bf40 100644 --- a/wgpu/src/image/mod.rs +++ b/wgpu/src/image/mod.rs @@ -147,8 +147,6 @@ impl Pipeline { 6 => Float32x2, // Layer 7 => Sint32, - // Snap - 8 => Uint32, ), }], compilation_options: @@ -243,7 +241,7 @@ impl State { [bounds.width, bounds.height], f32::from(image.rotation), image.opacity, - image.snap, + scale, atlas_entry, match image.filter_method { crate::core::image::FilterMethod::Nearest => { @@ -276,7 +274,7 @@ impl State { size, f32::from(svg.rotation), svg.opacity, - true, + scale, atlas_entry, nearest_instances, ); @@ -506,7 +504,6 @@ struct Instance { _position_in_atlas: [f32; 2], _size_in_atlas: [f32; 2], _layer: u32, - _snap: u32, } impl Instance { @@ -524,14 +521,21 @@ struct Uniforms { } fn add_instances( - image_position: [f32; 2], - image_size: [f32; 2], + mut image_position: [f32; 2], + mut image_size: [f32; 2], rotation: f32, opacity: f32, - snap: bool, + scale: f32, entry: &atlas::Entry, instances: &mut Vec, ) { + let snap = |coordinate: f32| (coordinate * scale).round() / scale; + + image_position[0] = snap(image_position[0]); + image_position[1] = snap(image_position[1]); + image_size[0] = snap(image_size[0]); + image_size[1] = snap(image_size[1]); + let center = [ image_position[0] + image_size[0] / 2.0, image_position[1] + image_size[1] / 2.0, @@ -545,7 +549,6 @@ fn add_instances( image_size, rotation, opacity, - snap, allocation, instances, ); @@ -575,8 +578,8 @@ fn add_instances( ]; add_instance( - position, center, size, rotation, opacity, snap, - allocation, instances, + position, center, size, rotation, opacity, allocation, + instances, ); } } @@ -590,7 +593,6 @@ fn add_instance( size: [f32; 2], rotation: f32, opacity: f32, - snap: bool, allocation: &atlas::Allocation, instances: &mut Vec, ) { @@ -613,7 +615,6 @@ 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/shader/image.wgsl b/wgpu/src/shader/image.wgsl index bc922838..51c9183c 100644 --- a/wgpu/src/shader/image.wgsl +++ b/wgpu/src/shader/image.wgsl @@ -17,7 +17,6 @@ struct VertexInput { @location(5) atlas_pos: vec2, @location(6) atlas_scale: vec2, @location(7) layer: i32, - @location(8) snap: u32, } struct VertexOutput { @@ -54,11 +53,7 @@ fn vs_main(input: VertexInput) -> VertexOutput { // Calculate the final position of the vertex 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 = round(out.position); out.position = globals.transform * out.position; return out; diff --git a/wgpu/src/shader/quad/gradient.wgsl b/wgpu/src/shader/quad/gradient.wgsl index 7eab8ad1..e5e9c552 100644 --- a/wgpu/src/shader/quad/gradient.wgsl +++ b/wgpu/src/shader/quad/gradient.wgsl @@ -33,8 +33,8 @@ fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput { var pos: vec2 = input.position_and_scale.xy * globals.scale; var scale: vec2 = input.position_and_scale.zw * globals.scale; - var pos_snap: vec2 = vec2(round(pos.x + 0.01) - pos.x, round(pos.y + 0.01) - pos.y); - var scale_snap: vec2 = vec2(round(scale.x + 0.01) - scale.x, round(scale.y + 0.01) - scale.y); + var pos_snap: vec2 = round(pos + vec2(0.01, 0.01)) - pos; + var scale_snap: vec2 = round(pos + scale + vec2(0.01, 0.01)) - pos - pos_snap - scale; var min_border_radius = min(input.position_and_scale.z, input.position_and_scale.w) * 0.5; var border_radius: vec4 = vec4( @@ -48,7 +48,7 @@ fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput { vec4(scale.x + scale_snap.x + 1.0, 0.0, 0.0, 0.0), vec4(0.0, scale.y + scale_snap.y + 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), - vec4(pos + pos_snap - vec2(0.5, 0.5), 0.0, 1.0) + vec4(pos + pos_snap, 0.0, 1.0) ); out.position = globals.transform * transform * vec4(vertex_position(input.vertex_index), 0.0, 1.0); diff --git a/wgpu/src/shader/quad/solid.wgsl b/wgpu/src/shader/quad/solid.wgsl index 3a0de625..86f7214a 100644 --- a/wgpu/src/shader/quad/solid.wgsl +++ b/wgpu/src/shader/quad/solid.wgsl @@ -31,8 +31,8 @@ fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput { var pos: vec2 = (input.pos + min(input.shadow_offset, vec2(0.0, 0.0)) - input.shadow_blur_radius) * globals.scale; var scale: vec2 = (input.scale + vec2(abs(input.shadow_offset.x), abs(input.shadow_offset.y)) + input.shadow_blur_radius * 2.0) * globals.scale; - var pos_snap: vec2 = vec2(round(pos.x + 0.01) - pos.x, round(pos.y + 0.01) - pos.y); - var scale_snap: vec2 = vec2(round(scale.x + 0.01) - scale.x, round(scale.y + 0.01) - scale.y); + var pos_snap: vec2 = round(pos + vec2(0.01, 0.01)) - pos; + var scale_snap: vec2 = round(pos + scale + vec2(0.01, 0.01)) - pos - pos_snap - scale; var min_border_radius = min(input.scale.x, input.scale.y) * 0.5; var border_radius: vec4 = vec4( @@ -46,7 +46,7 @@ fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput { vec4(scale.x + scale_snap.x + 1.0, 0.0, 0.0, 0.0), vec4(0.0, scale.y + scale_snap.y + 1.0, 0.0, 0.0), vec4(0.0, 0.0, 1.0, 0.0), - vec4(pos + pos_snap - vec2(0.5, 0.5), 0.0, 1.0) + vec4(pos + pos_snap, 0.0, 1.0) ); out.position = globals.transform * transform * vec4(vertex_position(input.vertex_index), 0.0, 1.0); diff --git a/widget/src/image.rs b/widget/src/image.rs index 80ebd721..09f04f50 100644 --- a/widget/src/image.rs +++ b/widget/src/image.rs @@ -296,7 +296,6 @@ fn render( filter_method, rotation: rotation.radians(), opacity, - snap: true, }, drawing_bounds, ); diff --git a/widget/src/image/viewer.rs b/widget/src/image/viewer.rs index 811241a9..3f48b8cd 100644 --- a/widget/src/image/viewer.rs +++ b/widget/src/image/viewer.rs @@ -351,7 +351,6 @@ where filter_method: self.filter_method, rotation: Radians(0.0), opacity: 1.0, - snap: true, }, drawing_bounds, );