Skip to content

Make draw_box_shadow take a CSS-style box/offset/spread; add NonUniformRoundedRect - #117

Open
nicoburns wants to merge 2 commits into
mainfrom
devin/1791068178-box-shadow-api
Open

nicoburns wants to merge 2 commits into
mainfrom
devin/1791068178-box-shadow-api

Conversation

@nicoburns

Copy link
Copy Markdown
Member

Summary

Breaking change to PaintScene::draw_box_shadow. AnyRender now takes the box casting the shadow plus the CSS parameters, and computes the shadow geometry itself. This works with the currently released vello 0.11 / vello_cpu 0.3 / vello_gpu 0.3. Switching the Vello backends to linebender/vello#1718's *_blurred_rounded_rect_in + invert is left to a follow-up (#113), which also removes the inset-shadow layers.

// before
fn draw_box_shadow(&mut self, transform, rect, brush, radius, std_dev);
// after
fn draw_box_shadow(
    &mut self,
    transform: Affine,
    box_shape: &NonUniformRoundedRect, // border box (outset) / padding box (inset)
    offset: Vec2,
    spread: f64,
    std_dev: f64,                      // CSS blur radius b => std_dev = b / 2
    brush: Color,
    kind: BoxShadowKind,               // Outset { clip_to_box: bool } | Inset
);

New anyrender::NonUniformRoundedRect (rect + NonUniformRoundedRectRadii { top_left, top_right, bottom_right, bottom_left: Vec2 }) is a kurbo::Shape for CSS-style boxes with elliptical per-corner radii.

  • Fast paths: as_rect() returns Some when every corner is sharp, and as_rounded_rect() when every corner is circular and within kurbo's clamping, so backends can use their rect / rounded-rect fast paths.
  • Paths: path_elements is a non-allocating iterator. perimeter is computed analytically.
  • spread(s) follows the CSS spread rules:
    • Sharp corners stay sharp.
    • When shrinking, r' = max(r - s, 0).
    • When growing, r' = r + s, except that a radius r < s grows by s * (1 + (r/s - 1)^3) instead.
    • The result then goes through scale_overlapping_radii().
  • Conversions: From<Rect> and From<RoundedRect>.

BoxShadowGeometry::new(box_shape, offset, spread, std_dev, kind) is shared by all backends. It computes:

  • shadow: the box spread by ±spread and translated by offset. For inset shadows this is the unshadowed hole.
  • area: the non-zero region to paint.
    • Outset: the shadow's 3σ extent, with the reversed box cut out when clip_to_box.
    • Inset: the box.

Backends:

vello_cpu / vello_hybrid / WebGL vello (classic) Skia
std_dev == 0 push_clip_path(area) + fill_path(unblurred_path()). It's a single fill, so the non-isolated clip is safe draw_unblurred (clip layer + fill) native
blurred outset push_clip_path(area) (if clipped) + fill_blurred_rounded_rect draw_blurred_rounded_rect_in(area, ..) native
blurred inset draw_inset_with_layers: clip layer filled with brush, then a DestOut layer containing the blurred hole same native
  • Vello radii: the Vello backends average the radii (average_radius()), because the blurred rounded rect only supports one radius.
  • Skia: draws exact per-corner and elliptical radii. It uses clip_rrect(box, Difference / Intersect) and then a blurred RRect / DRRect, with no layer.

Recording / serialization:

  • Shapes: stored as enum RecordedShape { Rect(Rect), RoundedRect(RoundedRect), Path(BezPath) }, chosen via as_rect() / as_rounded_rect(). Replayed scenes keep the fast paths.
  • BoxShadowCommand: stores the new parameters.
  • Archive version: ResourceManifest::CURRENT_VERSION is now 2, so version 1 archives won't deserialize.

Testing

  • cargo clippy --workspace --all-targets -D warnings, cargo test --workspace (plus --all-features for anyrender / anyrender_serialize, and vello_cpu with and without multithreading), and a wasm32 webgl clippy of anyrender_vello_hybrid.
  • New unit tests:
    • Spread radii: growing, shrinking, collapsing, and overlap scaling.
    • BoxShadowGeometry areas.
    • vello_cpu render tests for unblurred and blurred, outset and inset shadows.

Link to Devin session: https://dioxus.staging.devinenterprise.com/sessions/45dbc6c083354777aaa43442ce8ef8ab
Open in Devin Desktop: https://dioxus.staging.devinenterprise.com/desktop/session/45dbc6c083354777aaa43442ce8ef8ab?variant=devin-insiders
Requested by: @nicoburns

…rmRoundedRect and shape-preserving recordings
@staging-devin-ai-integration

Copy link
Copy Markdown

I'll fix CI failures and address comments from users with write access that start with 'Devin'.

  • Disable automatic comment, CI, and merge conflict monitoring

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant