summaryrefslogtreecommitdiffstats
path: root/tiny_skia
diff options
context:
space:
mode:
authorLibravatar Nick Senger <dev@nsenger.com>2023-11-08 19:12:53 -0800
committerLibravatar Héctor Ramón Jiménez <hector@hecrj.dev>2024-01-20 11:59:37 +0100
commitcc906c83cdf896d94b7ccf91258466714be631f6 (patch)
treead873e88cb711d4f186cb0c83497329903c200c3 /tiny_skia
parentb3e3f6e3c9fc6879e6681810f54d7eaa7c0f3d30 (diff)
downloadiced-cc906c83cdf896d94b7ccf91258466714be631f6.tar.gz
iced-cc906c83cdf896d94b7ccf91258466714be631f6.tar.bz2
iced-cc906c83cdf896d94b7ccf91258466714be631f6.zip
feat: quad shadows
Diffstat (limited to 'tiny_skia')
-rw-r--r--tiny_skia/src/backend.rs106
1 files changed, 106 insertions, 0 deletions
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::<Vec<_>>();
+ 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 {