From cc906c83cdf896d94b7ccf91258466714be631f6 Mon Sep 17 00:00:00 2001 From: Nick Senger Date: Wed, 8 Nov 2023 19:12:53 -0800 Subject: feat: quad shadows --- tiny_skia/src/backend.rs | 106 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) (limited to 'tiny_skia/src') diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index d1393b4d..b62f20d5 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -1,3 +1,5 @@ +use tiny_skia::Size; + use crate::core::{Background, Color, Gradient, Rectangle, Vector}; use crate::graphics::backend; use crate::graphics::text; @@ -153,6 +155,9 @@ impl Backend { border_radius, border_width, border_color, + shadow_color, + shadow_offset, + shadow_blur_radius, } => { let physical_bounds = (*bounds + translation) * scale_factor; @@ -182,6 +187,107 @@ impl Backend { } let path = rounded_rectangle(*bounds, fill_border_radius); + if shadow_color.a > 0.0 { + fn smoothstep(a: f32, b: f32, x: f32) -> f32 { + let x = ((x - a) / (b - a)).clamp(0.0, 1.0); + + x * x * (3.0 - 2.0 * x) + } + + fn rounded_box_sdf( + to_center: Vector, + size: Size, + radii: &[f32], + ) -> f32 { + let radius = + match (to_center.x > 0.0, to_center.y > 0.0) { + (true, true) => radii[2], + (true, false) => radii[1], + (false, true) => radii[3], + (false, false) => radii[0], + }; + + let x = (to_center.x.abs() - size.width() + radius) + .max(0.0); + let y = (to_center.y.abs() - size.height() + radius) + .max(0.0); + + (x.powf(2.0) + y.powf(2.0)).sqrt() - radius + } + + let shadow_bounds = (Rectangle { + x: bounds.x + shadow_offset.x - shadow_blur_radius, + y: bounds.y + shadow_offset.y - shadow_blur_radius, + width: bounds.width + shadow_blur_radius * 2.0, + height: bounds.height + shadow_blur_radius * 2.0, + } + translation) + * scale_factor; + + let radii = fill_border_radius + .into_iter() + .map(|radius| radius * scale_factor) + .collect::>(); + let (x, y, width, height) = ( + shadow_bounds.x as u32, + shadow_bounds.y as u32, + shadow_bounds.width as u32, + shadow_bounds.height as u32, + ); + let half_width = physical_bounds.width / 2.0; + let half_height = physical_bounds.height / 2.0; + + let colors = (y..y + height) + .flat_map(|y| { + (x..x + width).map(move |x| (x as f32, y as f32)) + }) + .filter_map(|(x, y)| { + Size::from_wh(half_width, half_height).map(|size| { + let shadow_distance = rounded_box_sdf( + Vector::new( + x - physical_bounds.position().x + - (shadow_offset.x * scale_factor) + - half_width, + y - physical_bounds.position().y + - (shadow_offset.y * scale_factor) + - half_height, + ), + size, + &radii, + ); + let shadow_alpha = 1.0 + - smoothstep( + -shadow_blur_radius * scale_factor, + *shadow_blur_radius * scale_factor, + shadow_distance, + ); + + let mut color = into_color(*shadow_color); + color.apply_opacity(shadow_alpha); + + color.to_color_u8().premultiply() + }) + }) + .collect(); + + if let Some(p) = tiny_skia::IntSize::from_wh(width, height) + .and_then(|size| { + tiny_skia::Pixmap::from_vec( + bytemuck::cast_vec(colors), + size, + ) + }) + { + pixels.draw_pixmap( + x as i32, + y as i32, + p.as_ref(), + &Default::default(), + Default::default(), + None, + ) + } + } + pixels.fill_path( &path, &tiny_skia::Paint { -- cgit From b7b457a575cdd103915994f640c50262ce30a7c5 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 20 Jan 2024 12:11:18 +0100 Subject: Make `shadow` optional in `renderer::Quad` --- tiny_skia/src/backend.rs | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'tiny_skia/src') diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index b62f20d5..ae1abc84 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -155,9 +155,7 @@ impl Backend { border_radius, border_width, border_color, - shadow_color, - shadow_offset, - shadow_blur_radius, + shadow, } => { let physical_bounds = (*bounds + translation) * scale_factor; @@ -187,7 +185,7 @@ impl Backend { } let path = rounded_rectangle(*bounds, fill_border_radius); - if shadow_color.a > 0.0 { + if let Some(shadow) = shadow { fn smoothstep(a: f32, b: f32, x: f32) -> f32 { let x = ((x - a) / (b - a)).clamp(0.0, 1.0); @@ -216,10 +214,10 @@ impl Backend { } let shadow_bounds = (Rectangle { - x: bounds.x + shadow_offset.x - shadow_blur_radius, - y: bounds.y + shadow_offset.y - shadow_blur_radius, - width: bounds.width + shadow_blur_radius * 2.0, - height: bounds.height + shadow_blur_radius * 2.0, + x: bounds.x + shadow.offset.x - shadow.blur_radius, + y: bounds.y + shadow.offset.y - shadow.blur_radius, + width: bounds.width + shadow.blur_radius * 2.0, + height: bounds.height + shadow.blur_radius * 2.0, } + translation) * scale_factor; @@ -245,10 +243,10 @@ impl Backend { let shadow_distance = rounded_box_sdf( Vector::new( x - physical_bounds.position().x - - (shadow_offset.x * scale_factor) + - (shadow.offset.x * scale_factor) - half_width, y - physical_bounds.position().y - - (shadow_offset.y * scale_factor) + - (shadow.offset.y * scale_factor) - half_height, ), size, @@ -256,12 +254,12 @@ impl Backend { ); let shadow_alpha = 1.0 - smoothstep( - -shadow_blur_radius * scale_factor, - *shadow_blur_radius * scale_factor, + -shadow.blur_radius * scale_factor, + shadow.blur_radius * scale_factor, shadow_distance, ); - let mut color = into_color(*shadow_color); + let mut color = into_color(shadow.color); color.apply_opacity(shadow_alpha); color.to_color_u8().premultiply() -- cgit From 370b2f6df799c948188d3949e34112258b2a8498 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 20 Jan 2024 12:25:07 +0100 Subject: Use `Default` implementation of `renderer::Quad` --- tiny_skia/src/backend.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'tiny_skia/src') diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index ae1abc84..5321d8c6 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -267,22 +267,23 @@ impl Backend { }) .collect(); - if let Some(p) = tiny_skia::IntSize::from_wh(width, height) - .and_then(|size| { - tiny_skia::Pixmap::from_vec( - bytemuck::cast_vec(colors), - size, - ) - }) - { + if let Some(pixmap) = tiny_skia::IntSize::from_wh( + width, height, + ) + .and_then(|size| { + tiny_skia::Pixmap::from_vec( + bytemuck::cast_vec(colors), + size, + ) + }) { pixels.draw_pixmap( x as i32, y as i32, - p.as_ref(), - &Default::default(), - Default::default(), + pixmap.as_ref(), + &tiny_skia::PixmapPaint::default(), + tiny_skia::Transform::default(), None, - ) + ); } } -- cgit From 4d502012b3e3ed9d9ef80f21078d53d182cdaa1b Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 20 Jan 2024 12:39:41 +0100 Subject: Skip transparent shadows in `iced_tiny_skia` --- tiny_skia/src/backend.rs | 195 ++++++++++++++++++++++++----------------------- 1 file changed, 101 insertions(+), 94 deletions(-) (limited to 'tiny_skia/src') diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 5321d8c6..bad6221f 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -186,104 +186,91 @@ impl Backend { let path = rounded_rectangle(*bounds, fill_border_radius); if let Some(shadow) = shadow { - fn smoothstep(a: f32, b: f32, x: f32) -> f32 { - let x = ((x - a) / (b - a)).clamp(0.0, 1.0); - - x * x * (3.0 - 2.0 * x) - } - - fn rounded_box_sdf( - to_center: Vector, - size: Size, - radii: &[f32], - ) -> f32 { - let radius = - match (to_center.x > 0.0, to_center.y > 0.0) { - (true, true) => radii[2], - (true, false) => radii[1], - (false, true) => radii[3], - (false, false) => radii[0], - }; - - let x = (to_center.x.abs() - size.width() + radius) - .max(0.0); - let y = (to_center.y.abs() - size.height() + radius) - .max(0.0); - - (x.powf(2.0) + y.powf(2.0)).sqrt() - radius - } + if shadow.color.a > 0.0 { + let shadow_bounds = (Rectangle { + x: bounds.x + shadow.offset.x - shadow.blur_radius, + y: bounds.y + shadow.offset.y - shadow.blur_radius, + width: bounds.width + shadow.blur_radius * 2.0, + height: bounds.height + shadow.blur_radius * 2.0, + } + translation) + * scale_factor; + + let radii = fill_border_radius + .into_iter() + .map(|radius| radius * scale_factor) + .collect::>(); + let (x, y, width, height) = ( + shadow_bounds.x as u32, + shadow_bounds.y as u32, + shadow_bounds.width as u32, + shadow_bounds.height as u32, + ); + let half_width = physical_bounds.width / 2.0; + let half_height = physical_bounds.height / 2.0; - let shadow_bounds = (Rectangle { - x: bounds.x + shadow.offset.x - shadow.blur_radius, - y: bounds.y + shadow.offset.y - shadow.blur_radius, - width: bounds.width + shadow.blur_radius * 2.0, - height: bounds.height + shadow.blur_radius * 2.0, - } + translation) - * scale_factor; - - let radii = fill_border_radius - .into_iter() - .map(|radius| radius * scale_factor) - .collect::>(); - let (x, y, width, height) = ( - shadow_bounds.x as u32, - shadow_bounds.y as u32, - shadow_bounds.width as u32, - shadow_bounds.height as u32, - ); - let half_width = physical_bounds.width / 2.0; - let half_height = physical_bounds.height / 2.0; - - let colors = (y..y + height) - .flat_map(|y| { - (x..x + width).map(move |x| (x as f32, y as f32)) - }) - .filter_map(|(x, y)| { - Size::from_wh(half_width, half_height).map(|size| { - let shadow_distance = rounded_box_sdf( - Vector::new( - x - physical_bounds.position().x - - (shadow.offset.x * scale_factor) - - half_width, - y - physical_bounds.position().y - - (shadow.offset.y * scale_factor) - - half_height, - ), - size, - &radii, - ); - let shadow_alpha = 1.0 - - smoothstep( - -shadow.blur_radius * scale_factor, - shadow.blur_radius * scale_factor, - shadow_distance, - ); - - let mut color = into_color(shadow.color); - color.apply_opacity(shadow_alpha); - - color.to_color_u8().premultiply() + let colors = (y..y + height) + .flat_map(|y| { + (x..x + width) + .map(move |x| (x as f32, y as f32)) }) - }) - .collect(); + .filter_map(|(x, y)| { + Size::from_wh(half_width, half_height).map( + |size| { + let shadow_distance = rounded_box_sdf( + Vector::new( + x - physical_bounds + .position() + .x + - (shadow.offset.x + * scale_factor) + - half_width, + y - physical_bounds + .position() + .y + - (shadow.offset.y + * scale_factor) + - half_height, + ), + size, + &radii, + ); + let shadow_alpha = 1.0 + - smoothstep( + -shadow.blur_radius + * scale_factor, + shadow.blur_radius + * scale_factor, + shadow_distance, + ); + + let mut color = + into_color(shadow.color); + color.apply_opacity(shadow_alpha); + + color.to_color_u8().premultiply() + }, + ) + }) + .collect(); - if let Some(pixmap) = tiny_skia::IntSize::from_wh( - width, height, - ) - .and_then(|size| { - tiny_skia::Pixmap::from_vec( - bytemuck::cast_vec(colors), - size, + if let Some(pixmap) = tiny_skia::IntSize::from_wh( + width, height, ) - }) { - pixels.draw_pixmap( - x as i32, - y as i32, - pixmap.as_ref(), - &tiny_skia::PixmapPaint::default(), - tiny_skia::Transform::default(), - None, - ); + .and_then(|size| { + tiny_skia::Pixmap::from_vec( + bytemuck::cast_vec(colors), + size, + ) + }) { + pixels.draw_pixmap( + x as i32, + y as i32, + pixmap.as_ref(), + &tiny_skia::PixmapPaint::default(), + tiny_skia::Transform::default(), + None, + ); + } } } @@ -969,6 +956,26 @@ fn adjust_clip_mask(clip_mask: &mut tiny_skia::Mask, bounds: Rectangle) { ); } +fn smoothstep(a: f32, b: f32, x: f32) -> f32 { + let x = ((x - a) / (b - a)).clamp(0.0, 1.0); + + x * x * (3.0 - 2.0 * x) +} + +fn rounded_box_sdf(to_center: Vector, size: Size, radii: &[f32]) -> f32 { + let radius = match (to_center.x > 0.0, to_center.y > 0.0) { + (true, true) => radii[2], + (true, false) => radii[1], + (false, true) => radii[3], + (false, false) => radii[0], + }; + + let x = (to_center.x.abs() - size.width() + radius).max(0.0); + let y = (to_center.y.abs() - size.height() + radius).max(0.0); + + (x.powf(2.0) + y.powf(2.0)).sqrt() - radius +} + impl iced_graphics::Backend for Backend { type Primitive = primitive::Custom; } -- cgit From 25f182f933ea6b7c112c8f9a450a98dc9b9eebdd Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 20 Jan 2024 13:29:25 +0100 Subject: Introduce `Border` struct analogous to `Shadow` --- tiny_skia/src/backend.rs | 167 +++++++++++++++++++++-------------------------- 1 file changed, 76 insertions(+), 91 deletions(-) (limited to 'tiny_skia/src') diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index bad6221f..ea4a3ec6 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -152,9 +152,7 @@ impl Backend { Primitive::Quad { bounds, background, - border_radius, - border_width, - border_color, + border, shadow, } => { let physical_bounds = (*bounds + translation) * scale_factor; @@ -173,11 +171,12 @@ impl Backend { .post_scale(scale_factor, scale_factor); // Make sure the border radius is not larger than the bounds - let border_width = border_width + let border_width = border + .width .min(bounds.width / 2.0) .min(bounds.height / 2.0); - let mut fill_border_radius = *border_radius; + let mut fill_border_radius = <[f32; 4]>::from(border.radius); for radius in &mut fill_border_radius { *radius = (*radius) .min(bounds.width / 2.0) @@ -185,92 +184,78 @@ impl Backend { } let path = rounded_rectangle(*bounds, fill_border_radius); - if let Some(shadow) = shadow { - if shadow.color.a > 0.0 { - let shadow_bounds = (Rectangle { - x: bounds.x + shadow.offset.x - shadow.blur_radius, - y: bounds.y + shadow.offset.y - shadow.blur_radius, - width: bounds.width + shadow.blur_radius * 2.0, - height: bounds.height + shadow.blur_radius * 2.0, - } + translation) - * scale_factor; - - let radii = fill_border_radius - .into_iter() - .map(|radius| radius * scale_factor) - .collect::>(); - let (x, y, width, height) = ( - shadow_bounds.x as u32, - shadow_bounds.y as u32, - shadow_bounds.width as u32, - shadow_bounds.height as u32, - ); - let half_width = physical_bounds.width / 2.0; - let half_height = physical_bounds.height / 2.0; - - let colors = (y..y + height) - .flat_map(|y| { - (x..x + width) - .map(move |x| (x as f32, y as f32)) - }) - .filter_map(|(x, y)| { - Size::from_wh(half_width, half_height).map( - |size| { - let shadow_distance = rounded_box_sdf( - Vector::new( - x - physical_bounds - .position() - .x - - (shadow.offset.x - * scale_factor) - - half_width, - y - physical_bounds - .position() - .y - - (shadow.offset.y - * scale_factor) - - half_height, - ), - size, - &radii, - ); - let shadow_alpha = 1.0 - - smoothstep( - -shadow.blur_radius - * scale_factor, - shadow.blur_radius - * scale_factor, - shadow_distance, - ); - - let mut color = - into_color(shadow.color); - color.apply_opacity(shadow_alpha); - - color.to_color_u8().premultiply() - }, - ) + if shadow.color.a > 0.0 { + let shadow_bounds = (Rectangle { + x: bounds.x + shadow.offset.x - shadow.blur_radius, + y: bounds.y + shadow.offset.y - shadow.blur_radius, + width: bounds.width + shadow.blur_radius * 2.0, + height: bounds.height + shadow.blur_radius * 2.0, + } + translation) + * scale_factor; + + let radii = fill_border_radius + .into_iter() + .map(|radius| radius * scale_factor) + .collect::>(); + let (x, y, width, height) = ( + shadow_bounds.x as u32, + shadow_bounds.y as u32, + shadow_bounds.width as u32, + shadow_bounds.height as u32, + ); + let half_width = physical_bounds.width / 2.0; + let half_height = physical_bounds.height / 2.0; + + let colors = (y..y + height) + .flat_map(|y| { + (x..x + width).map(move |x| (x as f32, y as f32)) + }) + .filter_map(|(x, y)| { + Size::from_wh(half_width, half_height).map(|size| { + let shadow_distance = rounded_box_sdf( + Vector::new( + x - physical_bounds.position().x + - (shadow.offset.x * scale_factor) + - half_width, + y - physical_bounds.position().y + - (shadow.offset.y * scale_factor) + - half_height, + ), + size, + &radii, + ); + let shadow_alpha = 1.0 + - smoothstep( + -shadow.blur_radius * scale_factor, + shadow.blur_radius * scale_factor, + shadow_distance, + ); + + let mut color = into_color(shadow.color); + color.apply_opacity(shadow_alpha); + + color.to_color_u8().premultiply() }) - .collect(); + }) + .collect(); - if let Some(pixmap) = tiny_skia::IntSize::from_wh( - width, height, + if let Some(pixmap) = tiny_skia::IntSize::from_wh( + width, height, + ) + .and_then(|size| { + tiny_skia::Pixmap::from_vec( + bytemuck::cast_vec(colors), + size, ) - .and_then(|size| { - tiny_skia::Pixmap::from_vec( - bytemuck::cast_vec(colors), - size, - ) - }) { - pixels.draw_pixmap( - x as i32, - y as i32, - pixmap.as_ref(), - &tiny_skia::PixmapPaint::default(), - tiny_skia::Transform::default(), - None, - ); - } + }) { + pixels.draw_pixmap( + x as i32, + y as i32, + pixmap.as_ref(), + &tiny_skia::PixmapPaint::default(), + tiny_skia::Transform::default(), + None, + ); } } @@ -344,7 +329,7 @@ impl Backend { }; // Make sure the border radius is correct - let mut border_radius = *border_radius; + let mut border_radius = <[f32; 4]>::from(border.radius); let mut is_simple_border = true; for radius in &mut border_radius { @@ -370,7 +355,7 @@ impl Backend { &border_path, &tiny_skia::Paint { shader: tiny_skia::Shader::SolidColor( - into_color(*border_color), + into_color(border.color), ), anti_alias: true, ..tiny_skia::Paint::default() @@ -426,7 +411,7 @@ impl Backend { &border_radius_path, &tiny_skia::Paint { shader: tiny_skia::Shader::SolidColor( - into_color(*border_color), + into_color(border.color), ), anti_alias: true, ..tiny_skia::Paint::default() -- cgit