Skip to content

Commit 0fa80fd

Browse files
StefanoDclaude
andcommitted
Fix panic in feComposite arithmetic with oversized filter region
The source pixmap of a filtered group is clamped to `max_bbox` in `render_group`, but the filter `region` in `apply_inner` was derived directly from the unclamped filter rect. When the filter region was larger than the clamped buffer, `feComposite` with the `arithmetic` operator panicked on a size mismatch, since it requires its inputs and destination to have identical dimensions: assertion failed: src1.height == src2.height && src1.height == dest.height All intermediate filter images are expected to share the source's dimensions, so clamp the region to the source bounds. This keeps every buffer the same size and makes the affected SVG render correctly instead of crashing (or being silently cleared). This also fixes the `huge-region` test, whose reference image previously captured the buggy behaviour where the filtered element disappeared; it now renders the blurred shape correctly. Fixes #1021. Fixes #1007. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent fd5eab9 commit 0fa80fd

6 files changed

Lines changed: 27 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ This changelog also contains important changes in dependencies.
1010

1111
This release has an MSRV of 1.87.0 for `usvg` and `resvg` and the C API.
1212

13+
### Fixed
14+
- Panic in `feComposite` with the `arithmetic` operator when the filter region is larger than the clamped layer. (#1021, #1007)
15+
1316
## [0.47.0] 2026-02-05
1417

1518
This release has an MSRV of 1.87.0 for `usvg` and `resvg` and the C API.

crates/resvg/src/filter/mod.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,16 @@ fn apply_inner(
365365
.map(|r| r.to_int_rect())
366366
.ok_or(Error::InvalidRegion)?;
367367

368+
// The source pixmap is clamped to `max_bbox` in `render_group`, so the filter
369+
// region (derived directly from the unclamped filter rect) can be larger than
370+
// the buffer we actually render into. All intermediate filter images share the
371+
// source's dimensions, so clamp the region to the source bounds to keep them
372+
// consistent. Otherwise `feComposite` with the `arithmetic` operator, which
373+
// requires equally sized inputs, would panic on a size mismatch.
374+
let source_rect = IntRect::from_xywh(0, 0, source.width(), source.height())
375+
.ok_or(Error::InvalidRegion)?;
376+
let region = crate::geom::fit_to_rect(region, source_rect).ok_or(Error::InvalidRegion)?;
377+
368378
let mut results: Vec<FilterResult> = Vec::new();
369379

370380
for primitive in filter.primitives() {

crates/resvg/tests/integration/render.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ use crate::render;
8181
#[test] fn filters_feComposite_invalid_operator() { assert_eq!(render("tests/filters/feComposite/invalid-operator"), 0); }
8282
#[test] fn filters_feComposite_operator_eq_arithmetic_and_invalid_k1_4() { assert_eq!(render("tests/filters/feComposite/operator=arithmetic-and-invalid-k1-4"), 0); }
8383
#[test] fn filters_feComposite_operator_eq_arithmetic_on_sRGB() { assert_eq!(render("tests/filters/feComposite/operator=arithmetic-on-sRGB"), 0); }
84+
#[test] fn filters_feComposite_operator_eq_arithmetic_with_huge_region() { assert_eq!(render("tests/filters/feComposite/operator=arithmetic-with-huge-region"), 0); }
8485
#[test] fn filters_feComposite_operator_eq_arithmetic_with_large_k1_4() { assert_eq!(render("tests/filters/feComposite/operator=arithmetic-with-large-k1-4"), 0); }
8586
#[test] fn filters_feComposite_operator_eq_arithmetic_with_opacity_on_sRGB() { assert_eq!(render("tests/filters/feComposite/operator=arithmetic-with-opacity-on-sRGB"), 0); }
8687
#[test] fn filters_feComposite_operator_eq_arithmetic_with_opacity() { assert_eq!(render("tests/filters/feComposite/operator=arithmetic-with-opacity"), 0); }
403 Bytes
Loading
Lines changed: 13 additions & 0 deletions
Loading
1.23 KB
Loading

0 commit comments

Comments
 (0)