From c0596179bd8582e4f4b5289cdeee8de4fa3de464 Mon Sep 17 00:00:00 2001 From: Robert Krahn Date: Thu, 3 Nov 2022 00:35:01 +0100 Subject: [PATCH 1/7] non uniform border radius for quads --- examples/custom_widget/src/main.rs | 2 +- glow/src/quad/compatibility.rs | 10 +++---- glow/src/quad/core.rs | 4 +-- glow/src/shader/core/quad.frag | 24 ++++++++++++--- glow/src/shader/core/quad.vert | 12 ++++---- graphics/src/layer/quad.rs | 2 +- graphics/src/primitive.rs | 2 +- graphics/src/renderer.rs | 2 +- native/src/element.rs | 2 +- native/src/overlay/menu.rs | 4 +-- native/src/renderer.rs | 26 ++++++++++++++++- native/src/widget/button.rs | 4 +-- native/src/widget/checkbox.rs | 2 +- native/src/widget/container.rs | 2 +- native/src/widget/pane_grid.rs | 2 +- native/src/widget/pick_list.rs | 2 +- native/src/widget/progress_bar.rs | 4 +-- native/src/widget/radio.rs | 4 +-- native/src/widget/rule.rs | 2 +- native/src/widget/scrollable.rs | 4 +-- native/src/widget/slider.rs | 6 ++-- native/src/widget/text_input.rs | 6 ++-- native/src/widget/toggler.rs | 4 +-- wgpu/src/quad.rs | 2 +- wgpu/src/shader/quad.wgsl | 47 +++++++++++++++++++++--------- 25 files changed, 121 insertions(+), 60 deletions(-) diff --git a/examples/custom_widget/src/main.rs b/examples/custom_widget/src/main.rs index c37a1a1212..f6bb3b1e6f 100644 --- a/examples/custom_widget/src/main.rs +++ b/examples/custom_widget/src/main.rs @@ -61,7 +61,7 @@ mod circle { renderer.fill_quad( renderer::Quad { bounds: layout.bounds(), - border_radius: self.radius, + border_radius: self.radius.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/glow/src/quad/compatibility.rs b/glow/src/quad/compatibility.rs index 28a5ea7ce4..5734c15926 100644 --- a/glow/src/quad/compatibility.rs +++ b/glow/src/quad/compatibility.rs @@ -254,7 +254,7 @@ unsafe fn create_buffers( gl.enable_vertex_attrib_array(4); gl.vertex_attrib_pointer_f32( 4, - 1, + 4, glow::FLOAT, false, stride, @@ -268,7 +268,7 @@ unsafe fn create_buffers( glow::FLOAT, false, stride, - 4 * (2 + 2 + 4 + 4 + 1), + 4 * (2 + 2 + 4 + 4 + 4), ); gl.enable_vertex_attrib_array(6); @@ -278,7 +278,7 @@ unsafe fn create_buffers( glow::FLOAT, false, stride, - 4 * (2 + 2 + 4 + 4 + 1 + 1), + 4 * (2 + 2 + 4 + 4 + 4 + 1), ); gl.bind_vertex_array(None); @@ -307,7 +307,7 @@ pub struct Vertex { pub border_color: [f32; 4], /// The border radius of the [`Vertex`]. - pub border_radius: f32, + pub border_radius: [f32; 4], /// The border width of the [`Vertex`]. pub border_width: f32, @@ -325,7 +325,7 @@ impl Vertex { size: quad.size, color: quad.color, border_color: quad.color, - border_radius: quad.border_radius, + border_radius: [quad.border_radius[0]; 4], border_width: quad.border_width, q_position: [0.0, 0.0], }; diff --git a/glow/src/quad/core.rs b/glow/src/quad/core.rs index 16bec38539..890365301e 100644 --- a/glow/src/quad/core.rs +++ b/glow/src/quad/core.rs @@ -218,7 +218,7 @@ unsafe fn create_instance_buffer( gl.enable_vertex_attrib_array(4); gl.vertex_attrib_pointer_f32( 4, - 1, + 4, glow::FLOAT, false, stride, @@ -233,7 +233,7 @@ unsafe fn create_instance_buffer( glow::FLOAT, false, stride, - 4 * (2 + 2 + 4 + 4 + 1), + 4 * (2 + 2 + 4 + 4 + 4), ); gl.vertex_attrib_divisor(5, 1); diff --git a/glow/src/shader/core/quad.frag b/glow/src/shader/core/quad.frag index 57e2e8e737..d036ea1a45 100644 --- a/glow/src/shader/core/quad.frag +++ b/glow/src/shader/core/quad.frag @@ -17,7 +17,7 @@ in vec4 v_Color; in vec4 v_BorderColor; in vec2 v_Pos; in vec2 v_Scale; -in float v_BorderRadius; +in vec4 v_BorderRadius; in float v_BorderWidth; float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) @@ -38,14 +38,30 @@ float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) return sqrt(distance.x * distance.x + distance.y * distance.y); } +float selectBorderRadius(vec4 radi, vec2 position, vec2 center) +{ + float rx = radi.x; + float ry = radi.y; + rx = position.x > center.x ? radi.y : radi.x; + ry = position.x > center.x ? radi.z : radi.w; + rx = position.y > center.y ? ry : rx; + return rx; +} + void main() { vec4 mixed_color; vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y); + float borderRadius = selectBorderRadius( + v_BorderRadius, + fragCoord, + (v_Pos + v_Scale * 0.5).xy + ); + // TODO: Remove branching (?) if(v_BorderWidth > 0.0) { - float internal_border = max(v_BorderRadius - v_BorderWidth, 0.0); + float internal_border = max(borderRadius - v_BorderWidth, 0.0); float internal_distance = fDistance( fragCoord, @@ -69,11 +85,11 @@ void main() { fragCoord, v_Pos, v_Scale, - v_BorderRadius + borderRadius ); float radius_alpha = - 1.0 - smoothstep(max(v_BorderRadius - 0.5, 0.0), v_BorderRadius + 0.5, d); + 1.0 - smoothstep(max(borderRadius - 0.5, 0.0), borderRadius + 0.5, d); gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); } diff --git a/glow/src/shader/core/quad.vert b/glow/src/shader/core/quad.vert index b1fb2365d2..17c3e64191 100644 --- a/glow/src/shader/core/quad.vert +++ b/glow/src/shader/core/quad.vert @@ -5,14 +5,14 @@ in vec2 i_Pos; in vec2 i_Scale; in vec4 i_Color; in vec4 i_BorderColor; -in float i_BorderRadius; +in vec4 i_BorderRadius; in float i_BorderWidth; out vec4 v_Color; out vec4 v_BorderColor; out vec2 v_Pos; out vec2 v_Scale; -out float v_BorderRadius; +out vec4 v_BorderRadius; out float v_BorderWidth; vec2 positions[4] = vec2[]( @@ -27,9 +27,11 @@ void main() { vec2 p_Pos = i_Pos * u_Scale; vec2 p_Scale = i_Scale * u_Scale; - float i_BorderRadius = min( - i_BorderRadius, - min(i_Scale.x, i_Scale.y) / 2.0 + vec4 i_BorderRadius = vec4( + min(i_BorderRadius.x, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.y, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.z, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.w, min(i_Scale.x, i_Scale.y) / 2.0) ); mat4 i_Transform = mat4( diff --git a/graphics/src/layer/quad.rs b/graphics/src/layer/quad.rs index 4def8427ea..0d8bde9dde 100644 --- a/graphics/src/layer/quad.rs +++ b/graphics/src/layer/quad.rs @@ -17,7 +17,7 @@ pub struct Quad { pub border_color: [f32; 4], /// The border radius of the [`Quad`]. - pub border_radius: f32, + pub border_radius: [f32; 4], /// The border width of the [`Quad`]. pub border_width: f32, diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index b481ac0baa..84f0462422 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -41,7 +41,7 @@ pub enum Primitive { /// The background of the quad background: Background, /// The border radius of the quad - border_radius: f32, + border_radius: [f32; 4], /// The border width of the quad border_width: f32, /// The border color of the quad diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index cdbc4f4062..46d37cd680 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -109,7 +109,7 @@ where self.primitives.push(Primitive::Quad { bounds: quad.bounds, background: background.into(), - border_radius: quad.border_radius, + border_radius: quad.border_radius.to_array(), border_width: quad.border_width, border_color: quad.border_color, }); diff --git a/native/src/element.rs b/native/src/element.rs index 074e422ef4..955e1beeed 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -503,7 +503,7 @@ where bounds: layout.bounds(), border_color: color, border_width: 1.0, - border_radius: 0.0, + border_radius: 0.0.into(), }, Color::TRANSPARENT, ); diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index 0813587288..dbcf23d453 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -299,7 +299,7 @@ where }, border_color: appearance.border_color, border_width: appearance.border_width, - border_radius: appearance.border_radius, + border_radius: appearance.border_radius.into(), }, appearance.background, ); @@ -491,7 +491,7 @@ where bounds, border_color: Color::TRANSPARENT, border_width: 0.0, - border_radius: appearance.border_radius, + border_radius: appearance.border_radius.into(), }, appearance.selected_background, ); diff --git a/native/src/renderer.rs b/native/src/renderer.rs index ef64ac36f4..39c1b4c7cb 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -50,7 +50,7 @@ pub struct Quad { pub bounds: Rectangle, /// The border radius of the [`Quad`]. - pub border_radius: f32, + pub border_radius: QuadBorderRadius, /// The border width of the [`Quad`]. pub border_width: f32, @@ -59,6 +59,30 @@ pub struct Quad { pub border_color: Color, } +/// The border radi for the corners of a [`Quad`] in the order: +/// top-left, top-right, bottom-right, bottom-left. +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct QuadBorderRadius([f32; 4]); + +impl QuadBorderRadius { + /// Convert the corners of the Quad into an array [top_left, top_right, bottom_left, bottom_right]. + pub fn to_array(self) -> [f32; 4] { + self.0 + } +} + +impl From for QuadBorderRadius { + fn from(w: f32) -> Self { + Self([w; 4]) + } +} + +impl From<[f32; 4]> for QuadBorderRadius { + fn from(radi: [f32; 4]) -> Self { + Self(radi) + } +} + /// The styling attributes of a [`Renderer`]. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Style { diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 6c0b8f6e79..14759cfd64 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -393,7 +393,7 @@ where y: bounds.y + styling.shadow_offset.y, ..bounds }, - border_radius: styling.border_radius, + border_radius: styling.border_radius.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -404,7 +404,7 @@ where renderer.fill_quad( renderer::Quad { bounds, - border_radius: styling.border_radius, + border_radius: styling.border_radius.into(), border_width: styling.border_width, border_color: styling.border_color, }, diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index dc3c0bd061..8f30037ae2 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -236,7 +236,7 @@ where renderer.fill_quad( renderer::Quad { bounds, - border_radius: custom_style.border_radius, + border_radius: custom_style.border_radius.into(), border_width: custom_style.border_width, border_color: custom_style.border_color, }, diff --git a/native/src/widget/container.rs b/native/src/widget/container.rs index 2afad3f2d3..b0c4938adc 100644 --- a/native/src/widget/container.rs +++ b/native/src/widget/container.rs @@ -321,7 +321,7 @@ pub fn draw_background( renderer.fill_quad( renderer::Quad { bounds, - border_radius: appearance.border_radius, + border_radius: appearance.border_radius.into(), border_width: appearance.border_width, border_color: appearance.border_color, }, diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs index 96cf78efa2..45090ecf6c 100644 --- a/native/src/widget/pane_grid.rs +++ b/native/src/widget/pane_grid.rs @@ -828,7 +828,7 @@ pub fn draw( height: split_region.height, }, }, - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index 896f5b3582..a92ea6552c 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -514,7 +514,7 @@ pub fn draw( bounds, border_color: style.border_color, border_width: style.border_width, - border_radius: style.border_radius, + border_radius: style.border_radius.into(), }, style.background, ); diff --git a/native/src/widget/progress_bar.rs b/native/src/widget/progress_bar.rs index 8a9454332b..5f8892fe45 100644 --- a/native/src/widget/progress_bar.rs +++ b/native/src/widget/progress_bar.rs @@ -129,7 +129,7 @@ where renderer.fill_quad( renderer::Quad { bounds: Rectangle { ..bounds }, - border_radius: style.border_radius, + border_radius: style.border_radius.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -143,7 +143,7 @@ where width: active_progress_width, ..bounds }, - border_radius: style.border_radius, + border_radius: style.border_radius.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index cb83f745df..10e8987093 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -245,7 +245,7 @@ where renderer.fill_quad( renderer::Quad { bounds, - border_radius: size / 2.0, + border_radius: (size / 2.0).into(), border_width: custom_style.border_width, border_color: custom_style.border_color, }, @@ -261,7 +261,7 @@ where width: bounds.width - dot_size, height: bounds.height - dot_size, }, - border_radius: dot_size / 2.0, + border_radius: (dot_size / 2.0).into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/native/src/widget/rule.rs b/native/src/widget/rule.rs index 56f8c80d36..1d1c04cfcd 100644 --- a/native/src/widget/rule.rs +++ b/native/src/widget/rule.rs @@ -123,7 +123,7 @@ where renderer.fill_quad( renderer::Quad { bounds, - border_radius: style.radius, + border_radius: style.radius.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 4ebb07a065..b445e505a8 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -698,7 +698,7 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds: scrollbar.bounds, - border_radius: style.border_radius, + border_radius: style.border_radius.into(), border_width: style.border_width, border_color: style.border_color, }, @@ -715,7 +715,7 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds: scrollbar.scroller.bounds, - border_radius: style.scroller.border_radius, + border_radius: style.scroller.border_radius.into(), border_width: style.scroller.border_width, border_color: style.scroller.border_color, }, diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index 585d9c35c9..011454de90 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -380,7 +380,7 @@ pub fn draw( width: bounds.width, height: 2.0, }, - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -395,7 +395,7 @@ pub fn draw( width: bounds.width, height: 2.0, }, - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -435,7 +435,7 @@ pub fn draw( width: handle_width, height: handle_height, }, - border_radius: handle_border_radius, + border_radius: handle_border_radius.into(), border_width: style.handle.border_width, border_color: style.handle.border_color, }, diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs index 54a6aaf805..ae0305dc57 100644 --- a/native/src/widget/text_input.rs +++ b/native/src/widget/text_input.rs @@ -766,7 +766,7 @@ pub fn draw( renderer.fill_quad( renderer::Quad { bounds, - border_radius: appearance.border_radius, + border_radius: appearance.border_radius.into(), border_width: appearance.border_width, border_color: appearance.border_color, }, @@ -798,7 +798,7 @@ pub fn draw( width: 1.0, height: text_bounds.height, }, - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, @@ -842,7 +842,7 @@ pub fn draw( width, height: text_bounds.height, }, - border_radius: 0.0, + border_radius: 0.0.into(), border_width: 0.0, border_color: Color::TRANSPARENT, }, diff --git a/native/src/widget/toggler.rs b/native/src/widget/toggler.rs index 7893f78c79..c5c2d82afb 100644 --- a/native/src/widget/toggler.rs +++ b/native/src/widget/toggler.rs @@ -278,7 +278,7 @@ where renderer.fill_quad( renderer::Quad { bounds: toggler_background_bounds, - border_radius, + border_radius: border_radius.into(), border_width: 1.0, border_color: style .background_border @@ -302,7 +302,7 @@ where renderer.fill_quad( renderer::Quad { bounds: toggler_foreground_bounds, - border_radius, + border_radius: border_radius.into(), border_width: 1.0, border_color: style .foreground_border diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index a117df64c2..027a34bebe 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -91,7 +91,7 @@ impl Pipeline { 2 => Float32x2, 3 => Float32x4, 4 => Float32x4, - 5 => Float32, + 5 => Float32x4, 6 => Float32, ), }, diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl index 73edd97ce0..cf4f7e4d5b 100644 --- a/wgpu/src/shader/quad.wgsl +++ b/wgpu/src/shader/quad.wgsl @@ -11,7 +11,7 @@ struct VertexInput { @location(2) scale: vec2, @location(3) color: vec4, @location(4) border_color: vec4, - @location(5) border_radius: f32, + @location(5) border_radius: vec4, @location(6) border_width: f32, } @@ -21,7 +21,7 @@ struct VertexOutput { @location(1) border_color: vec4, @location(2) pos: vec2, @location(3) scale: vec2, - @location(4) border_radius: f32, + @location(4) border_radius: vec4, @location(5) border_width: f32, } @@ -32,9 +32,12 @@ fn vs_main(input: VertexInput) -> VertexOutput { var pos: vec2 = input.pos * globals.scale; var scale: vec2 = input.scale * globals.scale; - var border_radius: f32 = min( - input.border_radius, - min(input.scale.x, input.scale.y) / 2.0 + var min_border_radius = min(input.scale.x, input.scale.y) * 0.5; + var border_radius: vec4 = vec4( + min(input.border_radius.x, min_border_radius), + min(input.border_radius.y, min_border_radius), + min(input.border_radius.z, min_border_radius), + min(input.border_radius.w, min_border_radius) ); var transform: mat4x4 = mat4x4( @@ -76,6 +79,18 @@ fn distance_alg( return sqrt(dist.x * dist.x + dist.y * dist.y); } +// Based on the fragement position and the center of the quad, select one of the 4 radi. +// Order matches CSS border radius attribute: +// radi.x = top-left, radi.y = top-right, radi.z = bottom-right, radi.w = bottom-left +fn select_border_radius(radi: vec4, position: vec2, center: vec2) -> f32 { + var rx = radi.x; + var ry = radi.y; + rx = select(radi.x, radi.y, position.x > center.x); + ry = select(radi.w, radi.z, position.x > center.x); + rx = select(rx, ry, position.y > center.y); + return rx; +} + @fragment fn fs_main( @@ -83,14 +98,17 @@ fn fs_main( ) -> @location(0) vec4 { var mixed_color: vec4 = input.color; + var border_radius = select_border_radius( + input.border_radius, + input.position.xy, + (input.pos + input.scale * 0.5).xy + ); + if (input.border_width > 0.0) { - var internal_border: f32 = max( - input.border_radius - input.border_width, - 0.0 - ); + var internal_border: f32 = max(border_radius - input.border_width, 0.0); var internal_distance: f32 = distance_alg( - vec2(input.position.x, input.position.y), + input.position.xy, input.pos + vec2(input.border_width, input.border_width), input.scale - vec2(input.border_width * 2.0, input.border_width * 2.0), internal_border @@ -109,13 +127,14 @@ fn fs_main( vec2(input.position.x, input.position.y), input.pos, input.scale, - input.border_radius + border_radius ); var radius_alpha: f32 = 1.0 - smoothstep( - max(input.border_radius - 0.5, 0.0), - input.border_radius + 0.5, - dist); + max(border_radius - 0.5, 0.0), + border_radius + 0.5, + dist + ); return vec4(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha); } From dcec3fd792cf422aef7a7217ec7f0311fbd12390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 8 Nov 2022 04:50:26 +0100 Subject: [PATCH 2/7] Fix `compatibility::quad` pipeline in `iced_glow` --- glow/src/quad/compatibility.rs | 2 +- glow/src/shader/compatibility/quad.frag | 24 ++++++++++++++++++++---- glow/src/shader/compatibility/quad.vert | 12 +++++++----- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/glow/src/quad/compatibility.rs b/glow/src/quad/compatibility.rs index 5734c15926..e909162c3b 100644 --- a/glow/src/quad/compatibility.rs +++ b/glow/src/quad/compatibility.rs @@ -325,7 +325,7 @@ impl Vertex { size: quad.size, color: quad.color, border_color: quad.color, - border_radius: [quad.border_radius[0]; 4], + border_radius: quad.border_radius, border_width: quad.border_width, q_position: [0.0, 0.0], }; diff --git a/glow/src/shader/compatibility/quad.frag b/glow/src/shader/compatibility/quad.frag index 8ea5693d1d..bb9d8122d1 100644 --- a/glow/src/shader/compatibility/quad.frag +++ b/glow/src/shader/compatibility/quad.frag @@ -12,7 +12,7 @@ varying vec4 v_Color; varying vec4 v_BorderColor; varying vec2 v_Pos; varying vec2 v_Scale; -varying float v_BorderRadius; +varying vec4 v_BorderRadius; varying float v_BorderWidth; float _distance(vec2 frag_coord, vec2 position, vec2 size, float radius) @@ -33,10 +33,26 @@ float _distance(vec2 frag_coord, vec2 position, vec2 size, float radius) return sqrt(distance.x * distance.x + distance.y * distance.y); } +float selectBorderRadius(vec4 radi, vec2 position, vec2 center) +{ + float rx = radi.x; + float ry = radi.y; + rx = position.x > center.x ? radi.y : radi.x; + ry = position.x > center.x ? radi.z : radi.w; + rx = position.y > center.y ? ry : rx; + return rx; +} + void main() { vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y); - float internal_border = max(v_BorderRadius - v_BorderWidth, 0.0); + float border_radius = selectBorderRadius( + v_BorderRadius, + fragCoord, + (v_Pos + v_Scale * 0.5).xy + ); + + float internal_border = max(border_radius - v_BorderWidth, 0.0); float internal_distance = _distance( fragCoord, @@ -57,11 +73,11 @@ void main() { fragCoord, v_Pos, v_Scale, - v_BorderRadius + border_radius ); float radius_alpha = - 1.0 - smoothstep(max(v_BorderRadius - 0.5, 0.0), v_BorderRadius + 0.5, d); + 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); } diff --git a/glow/src/shader/compatibility/quad.vert b/glow/src/shader/compatibility/quad.vert index abe70c0eed..89931f0671 100644 --- a/glow/src/shader/compatibility/quad.vert +++ b/glow/src/shader/compatibility/quad.vert @@ -5,7 +5,7 @@ attribute vec2 i_Pos; attribute vec2 i_Scale; attribute vec4 i_Color; attribute vec4 i_BorderColor; -attribute float i_BorderRadius; +attribute vec4 i_BorderRadius; attribute float i_BorderWidth; attribute vec2 q_Pos; @@ -13,7 +13,7 @@ varying vec4 v_Color; varying vec4 v_BorderColor; varying vec2 v_Pos; varying vec2 v_Scale; -varying float v_BorderRadius; +varying vec4 v_BorderRadius; varying float v_BorderWidth; @@ -21,9 +21,11 @@ void main() { vec2 p_Pos = i_Pos * u_Scale; vec2 p_Scale = i_Scale * u_Scale; - float i_BorderRadius = min( - i_BorderRadius, - min(i_Scale.x, i_Scale.y) / 2.0 + vec4 i_BorderRadius = vec4( + min(i_BorderRadius.x, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.y, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.z, min(i_Scale.x, i_Scale.y) / 2.0), + min(i_BorderRadius.w, min(i_Scale.x, i_Scale.y) / 2.0) ); mat4 i_Transform = mat4( From 15f21641b7a27bbfb99f9f9f5d21b84445173900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 8 Nov 2022 04:51:45 +0100 Subject: [PATCH 3/7] Fix casing of `border_radius` in `quad` shader --- glow/src/shader/core/quad.frag | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/glow/src/shader/core/quad.frag b/glow/src/shader/core/quad.frag index d036ea1a45..71147aa51d 100644 --- a/glow/src/shader/core/quad.frag +++ b/glow/src/shader/core/quad.frag @@ -53,7 +53,7 @@ void main() { vec2 fragCoord = vec2(gl_FragCoord.x, u_ScreenHeight - gl_FragCoord.y); - float borderRadius = selectBorderRadius( + float border_radius = selectBorderRadius( v_BorderRadius, fragCoord, (v_Pos + v_Scale * 0.5).xy @@ -61,7 +61,7 @@ void main() { // TODO: Remove branching (?) if(v_BorderWidth > 0.0) { - float internal_border = max(borderRadius - v_BorderWidth, 0.0); + float internal_border = max(border_radius - v_BorderWidth, 0.0); float internal_distance = fDistance( fragCoord, @@ -85,11 +85,11 @@ void main() { fragCoord, v_Pos, v_Scale, - borderRadius + border_radius ); float radius_alpha = - 1.0 - smoothstep(max(borderRadius - 0.5, 0.0), borderRadius + 0.5, d); + 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); } From 676d8efe03ebdbeeb95aef96b8097395b788b1ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Tue, 8 Nov 2022 04:59:34 +0100 Subject: [PATCH 4/7] Rename `QuadBorderRadius` to `BorderRadius` --- graphics/src/renderer.rs | 2 +- native/src/renderer.rs | 25 ++++++++++++------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/graphics/src/renderer.rs b/graphics/src/renderer.rs index 46d37cd680..ff0dad2b6a 100644 --- a/graphics/src/renderer.rs +++ b/graphics/src/renderer.rs @@ -109,7 +109,7 @@ where self.primitives.push(Primitive::Quad { bounds: quad.bounds, background: background.into(), - border_radius: quad.border_radius.to_array(), + border_radius: quad.border_radius.into(), border_width: quad.border_width, border_color: quad.border_color, }); diff --git a/native/src/renderer.rs b/native/src/renderer.rs index 39c1b4c7cb..5e776be6cd 100644 --- a/native/src/renderer.rs +++ b/native/src/renderer.rs @@ -50,7 +50,7 @@ pub struct Quad { pub bounds: Rectangle, /// The border radius of the [`Quad`]. - pub border_radius: QuadBorderRadius, + pub border_radius: BorderRadius, /// The border width of the [`Quad`]. pub border_width: f32, @@ -59,30 +59,29 @@ pub struct Quad { pub border_color: Color, } -/// The border radi for the corners of a [`Quad`] in the order: +/// The border radi for the corners of a graphics primitive in the order: /// top-left, top-right, bottom-right, bottom-left. -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct QuadBorderRadius([f32; 4]); - -impl QuadBorderRadius { - /// Convert the corners of the Quad into an array [top_left, top_right, bottom_left, bottom_right]. - pub fn to_array(self) -> [f32; 4] { - self.0 - } -} +#[derive(Debug, Clone, Copy, PartialEq, Default)] +pub struct BorderRadius([f32; 4]); -impl From for QuadBorderRadius { +impl From for BorderRadius { fn from(w: f32) -> Self { Self([w; 4]) } } -impl From<[f32; 4]> for QuadBorderRadius { +impl From<[f32; 4]> for BorderRadius { fn from(radi: [f32; 4]) -> Self { Self(radi) } } +impl From for [f32; 4] { + fn from(radi: BorderRadius) -> Self { + radi.0 + } +} + /// The styling attributes of a [`Renderer`]. #[derive(Debug, Clone, Copy, PartialEq)] pub struct Style { From 6d39aebec61fa50ab7a5f58056adce1b87ac25e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 2 Dec 2022 18:55:10 +0100 Subject: [PATCH 5/7] Fix `border_radius` in `modal` example --- examples/modal/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/modal/src/main.rs b/examples/modal/src/main.rs index 25483cf3bc..2f20795c0a 100644 --- a/examples/modal/src/main.rs +++ b/examples/modal/src/main.rs @@ -412,7 +412,7 @@ mod modal { renderer.fill_quad( renderer::Quad { bounds: layout.bounds(), - border_radius: 0.0, + border_radius: renderer::BorderRadius::from(0.0), border_width: 0.0, border_color: Color::TRANSPARENT, }, From 18fd5300854522a091a2bcccb47b65f1d1d8f063 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 2 Dec 2022 19:06:42 +0100 Subject: [PATCH 6/7] Add `custom_quad` example Thanks to @rksm :bow: --- examples/custom_quad/Cargo.toml | 10 ++ examples/custom_quad/src/main.rs | 159 +++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) create mode 100644 examples/custom_quad/Cargo.toml create mode 100644 examples/custom_quad/src/main.rs diff --git a/examples/custom_quad/Cargo.toml b/examples/custom_quad/Cargo.toml new file mode 100644 index 0000000000..39154786e1 --- /dev/null +++ b/examples/custom_quad/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "custom_quad" +version = "0.1.0" +authors = ["Robert Krahn"] +edition = "2021" +publish = false + +[dependencies] +iced = { path = "../.." } +iced_native = { path = "../../native" } diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs new file mode 100644 index 0000000000..2a5fe798d5 --- /dev/null +++ b/examples/custom_quad/src/main.rs @@ -0,0 +1,159 @@ +//! This example showcases a drawing a quad. +mod quad { + use iced_native::layout::{self, Layout}; + use iced_native::renderer; + use iced_native::widget::{self, Widget}; + use iced_native::{Color, Element, Length, Point, Rectangle, Size}; + + pub struct CustomQuad { + size: f32, + radius: [f32; 4], + border_width: f32, + } + + impl CustomQuad { + pub fn new(size: f32, radius: [f32; 4], border_width: f32) -> Self { + Self { + size, + radius, + border_width, + } + } + } + + impl Widget for CustomQuad + where + Renderer: renderer::Renderer, + { + fn width(&self) -> Length { + Length::Shrink + } + + fn height(&self) -> Length { + Length::Shrink + } + + fn layout( + &self, + _renderer: &Renderer, + _limits: &layout::Limits, + ) -> layout::Node { + layout::Node::new(Size::new(self.size, self.size)) + } + + fn draw( + &self, + _state: &widget::Tree, + renderer: &mut Renderer, + _theme: &Renderer::Theme, + _style: &renderer::Style, + layout: Layout<'_>, + _cursor_position: Point, + _viewport: &Rectangle, + ) { + renderer.fill_quad( + renderer::Quad { + bounds: layout.bounds(), + border_radius: self.radius.into(), + border_width: self.border_width, + border_color: Color::from_rgb(1.0, 0.0, 0.0), + }, + Color::BLACK, + ); + } + } + + impl<'a, Message, Renderer> From for Element<'a, Message, Renderer> + where + Renderer: renderer::Renderer, + { + fn from(circle: CustomQuad) -> Self { + Self::new(circle) + } + } +} + +use iced::widget::{column, container, slider, text}; +use iced::{Alignment, Element, Length, Sandbox, Settings}; + +pub fn main() -> iced::Result { + Example::run(Settings::default()) +} + +struct Example { + radius: [f32; 4], + border_width: f32, +} + +#[derive(Debug, Clone, Copy)] +enum Message { + RadiusTopLeftChanged(f32), + RadiusTopRightChanged(f32), + RadiusBottomRightChanged(f32), + RadiusBottomLeftChanged(f32), + BorderWidthChanged(f32), +} + +impl Sandbox for Example { + type Message = Message; + + fn new() -> Self { + Self { + radius: [50.0; 4], + border_width: 0.0, + } + } + + fn title(&self) -> String { + String::from("Custom widget - Iced") + } + + fn update(&mut self, message: Message) { + let [tl, tr, br, bl] = self.radius; + match message { + Message::RadiusTopLeftChanged(radius) => { + self.radius = [radius, tr, br, bl]; + } + Message::RadiusTopRightChanged(radius) => { + self.radius = [tl, radius, br, bl]; + } + Message::RadiusBottomRightChanged(radius) => { + self.radius = [tl, tr, radius, bl]; + } + Message::RadiusBottomLeftChanged(radius) => { + self.radius = [tl, tr, br, radius]; + } + Message::BorderWidthChanged(width) => { + self.border_width = width; + } + } + } + + fn view(&self) -> Element { + let [tl, tr, br, bl] = self.radius; + + let content = column![ + quad::CustomQuad::new(200.0, self.radius, self.border_width), + text(format!("Radius: {tl:.2}/{tr:.2}/{br:.2}/{bl:.2}")), + slider(1.0..=100.0, tl, Message::RadiusTopLeftChanged).step(0.01), + slider(1.0..=100.0, tr, Message::RadiusTopRightChanged).step(0.01), + slider(1.0..=100.0, br, Message::RadiusBottomRightChanged) + .step(0.01), + slider(1.0..=100.0, bl, Message::RadiusBottomLeftChanged) + .step(0.01), + slider(1.0..=10.0, self.border_width, Message::BorderWidthChanged) + .step(0.01), + ] + .padding(20) + .spacing(20) + .max_width(500) + .align_items(Alignment::Center); + + container(content) + .width(Length::Fill) + .height(Length::Fill) + .center_x() + .center_y() + .into() + } +} From 60e41666d0e203d9777de981121c39f569bc3a7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20Ram=C3=B3n=20Jim=C3=A9nez?= Date: Fri, 2 Dec 2022 19:13:04 +0100 Subject: [PATCH 7/7] Ask `clippy` to behave --- examples/custom_quad/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/custom_quad/src/main.rs b/examples/custom_quad/src/main.rs index 2a5fe798d5..6509887c52 100644 --- a/examples/custom_quad/src/main.rs +++ b/examples/custom_quad/src/main.rs @@ -86,6 +86,7 @@ struct Example { } #[derive(Debug, Clone, Copy)] +#[allow(clippy::enum_variant_names)] enum Message { RadiusTopLeftChanged(f32), RadiusTopRightChanged(f32),