freya_core/render/utils/
shadows.rs

1use freya_engine::prelude::*;
2use torin::prelude::Area;
3
4use crate::{
5    states::StyleState,
6    values::{
7        CornerRadius,
8        Shadow,
9        ShadowPosition,
10    },
11};
12
13pub fn render_shadow(
14    canvas: &Canvas,
15    node_style: &StyleState,
16    path: &mut Path,
17    rounded_rect: RRect,
18    area: Area,
19    shadow: &Shadow,
20    corner_radius: &CornerRadius,
21) {
22    let mut shadow_path = Path::new();
23    let mut shadow_paint = Paint::default();
24    shadow_paint.set_anti_alias(true);
25
26    shadow.fill.apply_to_paint(&mut shadow_paint, area);
27
28    // Shadows can be either outset or inset
29    // If they are outset, we fill a copy of the path outset by spread_radius, and blur it.
30    // Otherwise, we draw a stroke with the inner portion being spread_radius width, and the outer portion being blur_radius width.
31    let outset: Point = match shadow.position {
32        ShadowPosition::Normal => {
33            shadow_paint.set_style(PaintStyle::Fill);
34            (shadow.spread, shadow.spread).into()
35        }
36        ShadowPosition::Inset => {
37            shadow_paint.set_style(PaintStyle::Stroke);
38            shadow_paint.set_stroke_width(shadow.blur / 2.0 + shadow.spread);
39            (-shadow.spread / 2.0, -shadow.spread / 2.0).into()
40        }
41    };
42
43    // Apply gassuan blur to the copied path.
44    if shadow.blur > 0.0 {
45        shadow_paint.set_mask_filter(MaskFilter::blur(
46            BlurStyle::Normal,
47            shadow.blur / 2.0,
48            false,
49        ));
50    }
51
52    // Add either the RRect or smoothed path based on whether smoothing is used.
53    if corner_radius.smoothing > 0.0 {
54        shadow_path.add_path(
55            &node_style
56                .corner_radius
57                .smoothed_path(rounded_rect.with_outset(outset)),
58            Point::new(area.min_x(), area.min_y()) - outset,
59            None,
60        );
61    } else {
62        shadow_path.add_rrect(rounded_rect.with_outset(outset), None);
63    }
64
65    // Offset our path by the shadow's x and y coordinates.
66    shadow_path.offset((shadow.x, shadow.y));
67
68    // Exclude the original path bounds from the shadow using a clip, then draw the shadow.
69    canvas.save();
70    canvas.clip_path(
71        path,
72        match shadow.position {
73            ShadowPosition::Normal => ClipOp::Difference,
74            ShadowPosition::Inset => ClipOp::Intersect,
75        },
76        true,
77    );
78    canvas.draw_path(&shadow_path, &shadow_paint);
79    canvas.restore();
80}