diff --git a/packages/blitz/src/renderer/multicolor_rounded_rect.rs b/packages/blitz/src/renderer/multicolor_rounded_rect.rs index 32ac6263..a108416f 100644 --- a/packages/blitz/src/renderer/multicolor_rounded_rect.rs +++ b/packages/blitz/src/renderer/multicolor_rounded_rect.rs @@ -208,6 +208,38 @@ impl ElementFrame { } } + /// Construct a bezpath drawing the frame + pub fn shadow_clip(&self) -> BezPath { + let mut path = BezPath::new(); + self.shadow_clip_shape(&mut path); + path + } + + fn shadow_clip_shape(&self, path: &mut BezPath) { + use Corner::*; + + for corner in [TopLeft, TopRight, BottomRight, BottomLeft] { + path.insert_point(self.shadow_clip_corner(corner, 100.0)); + } + + if self.is_sharp(TopLeft) { + path.move_to(self.corner(TopLeft, ArcSide::Outer)); + } else { + const TOLERANCE: f64 = 0.1; + let arc = self.full_arc(TopLeft, ArcSide::Outer, Direction::Anticlockwise); + let elements = arc.path_elements(TOLERANCE); + path.extend(elements); + } + + for corner in [/*TopLeft, */ BottomLeft, BottomRight, TopRight] { + if self.is_sharp(corner) { + path.insert_point(self.corner(corner, ArcSide::Outer)); + } else { + path.insert_arc(self.full_arc(corner, ArcSide::Outer, Direction::Anticlockwise)); + } + } + } + fn corner(&self, corner: Corner, side: ArcSide) -> Point { let Rect { x0, y0, x1, y1 } = self.outer_rect; @@ -241,6 +273,19 @@ impl ElementFrame { Point { x, y } } + fn shadow_clip_corner(&self, corner: Corner, offset: f64) -> Point { + let Rect { x0, y0, x1, y1 } = self.outer_rect; + + let (x, y) = match corner { + Corner::TopLeft => (x0 - offset, y0 - offset), + Corner::TopRight => (x1 + offset, y0 - offset), + Corner::BottomRight => (x1 + offset, y1 + offset), + Corner::BottomLeft => (x0 - offset, y1 + offset), + }; + + Point { x, y } + } + /// Check if the corner width is smaller than the radius. /// If it is, we need to fill in the gap with an arc fn corner_needs_infill(&self, corner: Corner) -> bool { diff --git a/packages/blitz/src/renderer/render.rs b/packages/blitz/src/renderer/render.rs index b05ed6db..7306623d 100644 --- a/packages/blitz/src/renderer/render.rs +++ b/packages/blitz/src/renderer/render.rs @@ -935,6 +935,20 @@ impl ElementCx<'_> { fn draw_outset_box_shadow(&self, scene: &mut Scene) { let box_shadow = &self.style.get_effects().box_shadow.0; + + // TODO: Only apply clip if element has transparency + let has_outset_shadow = box_shadow.iter().any(|s| !s.inset); + if has_outset_shadow { + CLIPS_WANTED.fetch_add(1, atomic::Ordering::SeqCst); + let clips_available = CLIPS_USED.load(atomic::Ordering::SeqCst) <= CLIP_LIMIT; + if clips_available { + scene.push_layer(Mix::Clip, 1.0, self.transform, &self.frame.shadow_clip()); + CLIPS_USED.fetch_add(1, atomic::Ordering::SeqCst); + let depth = CLIP_DEPTH.fetch_add(1, atomic::Ordering::SeqCst) + 1; + CLIP_DEPTH_USED.fetch_max(depth, atomic::Ordering::SeqCst); + } + } + for shadow in box_shadow.iter().filter(|s| !s.inset) { let shadow_color = shadow.base.color.as_vello(); if shadow_color != Color::TRANSPARENT { @@ -964,6 +978,11 @@ impl ElementCx<'_> { ); } } + + if has_outset_shadow { + scene.pop_layer(); + CLIP_DEPTH.fetch_sub(1, atomic::Ordering::SeqCst); + } } fn draw_inset_box_shadow(&self, scene: &mut Scene) {