Skip to content

Commit

Permalink
feat: quad shadows on software renderer
Browse files Browse the repository at this point in the history
  • Loading branch information
nicksenger committed Jul 17, 2023
1 parent e1a9185 commit 9234841
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 4 deletions.
23 changes: 20 additions & 3 deletions graphics/src/damage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,26 @@ impl<T: Damage> Damage for Primitive<T> {

bounds.expand(1.5)
}
Self::Quad { bounds, .. }
| Self::Image { bounds, .. }
| Self::Svg { bounds, .. } => bounds.expand(1.0),
Self::Quad {
bounds,
shadow_offset,
shadow_softness,
..
} => {
let shadow_bounds = Rectangle {
x: bounds.x + shadow_offset.x.min(0.0) - shadow_softness,
y: bounds.y + shadow_offset.y.min(0.0) - shadow_softness,
width: bounds.width
+ (shadow_offset.x.max(0.0) + shadow_softness) * 2.0,
height: bounds.height
+ (shadow_offset.y.max(0.0) + shadow_softness) * 2.0,
};

shadow_bounds.expand(1.0)
}
Self::Image { bounds, .. } | Self::Svg { bounds, .. } => {
bounds.expand(1.0)
}
Self::Clip { bounds, .. } => bounds.expand(1.0),
Self::Group { primitives } => primitives
.iter()
Expand Down
126 changes: 125 additions & 1 deletion tiny_skia/src/backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,9 @@ impl Backend {
border_radius,
border_width,
border_color,
..
shadow_color,
shadow_offset,
shadow_softness,
} => {
let physical_bounds = (*bounds + translation) * scale_factor;

Expand Down Expand Up @@ -189,6 +191,128 @@ 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.clamp(a, b);
let x = x.clamp(0.0, (x - a) / (b - a));

x * x * (3.0 - 2.0 * x)
}

fn rounded_box_sdf(
center_distance: Vector,
size: Size,
radius: [f32; 4],
) -> f32 {
let radius = match (
center_distance.x > 0.0,
center_distance.y > 0.0,
) {
(false, false) => radius[0],
(true, false) => radius[1],
(true, true) => radius[2],
(false, true) => radius[3],
};

let x = (center_distance.x.abs() - size.width + radius)
.max(0.0);
let y = (center_distance.y.abs() - size.height
+ radius)
.max(0.0);

(x * x + y * y).sqrt() - radius
}

let shadow_bounds = Rectangle {
x: bounds.x + shadow_offset.x.min(0.0)
- shadow_softness,
y: bounds.y + shadow_offset.y.min(0.0)
- shadow_softness,
width: bounds.width
+ (shadow_offset.x.max(0.0) + shadow_softness)
* 2.0,
height: bounds.height
+ (shadow_offset.y.max(0.0) + shadow_softness)
* 2.0,
} * scale_factor;

if let Some(shadow_color) = tiny_skia::Color::from_rgba(
shadow_color.r,
shadow_color.g,
shadow_color.b,
shadow_color.a,
) {
let mut bytes = vec![];
let radius = [
fill_border_radius[0] * scale_factor,
fill_border_radius[1] * scale_factor,
fill_border_radius[2] * scale_factor,
fill_border_radius[3] * scale_factor,
];
for y in shadow_bounds.y as u32
..(shadow_bounds.y as u32
+ shadow_bounds.height as u32)
{
for x in shadow_bounds.x as u32
..(shadow_bounds.x as u32
+ shadow_bounds.width as u32)
{
let shadow_distance = rounded_box_sdf(
Vector {
x: x as f32
- physical_bounds.position().x
- shadow_offset.x * scale_factor
- (physical_bounds.width / 2.0),
y: y as f32
- physical_bounds.position().y
- shadow_offset.y * scale_factor
- (physical_bounds.height / 2.0),
},
Size::new(
physical_bounds.width / 2.0,
physical_bounds.height / 2.0,
),
radius,
);
let shadow_alpha = 1.0
- smoothstep(
-shadow_softness * scale_factor,
*shadow_softness * scale_factor,
shadow_distance,
);

let mut color = shadow_color;
color.apply_opacity(shadow_alpha);

bytes.extend(bytemuck::bytes_of(
&color.to_color_u8().premultiply(),
));
}
}

if let Some(p) = tiny_skia::Pixmap::from_vec(
bytes,
tiny_skia::IntSize::from_wh(
shadow_bounds.width as u32,
shadow_bounds.height as u32,
)
.unwrap(),
) {
pixels.draw_pixmap(
shadow_bounds.x as i32,
shadow_bounds.y as i32,

Check failure on line 303 in tiny_skia/src/backend.rs

View workflow job for this annotation

GitHub Actions / all

Diff in /home/runner/work/iced/iced/tiny_skia/src/backend.rs
p.as_ref(),
&tiny_skia::PixmapPaint {
blend_mode: tiny_skia::BlendMode::SourceOver,
..Default::default()
},
Default::default(),
None,
)
}
}
}

pixels.fill_path(
&path,
&tiny_skia::Paint {
Expand Down

0 comments on commit 9234841

Please sign in to comment.