diff --git a/crates/resvg/src/filter/morphology.rs b/crates/resvg/src/filter/morphology.rs index 09585ebd8..980593fa2 100644 --- a/crates/resvg/src/filter/morphology.rs +++ b/crates/resvg/src/filter/morphology.rs @@ -13,19 +13,15 @@ use usvg::filter::MorphologyOperator; /// /// This method will allocate a copy of the `src` image as a back buffer. pub fn apply(operator: MorphologyOperator, rx: f32, ry: f32, src: ImageRefMut) { - // No point in making matrix larger than image. - let columns = std::cmp::min(rx.ceil() as u32 * 2, src.width); - let rows = std::cmp::min(ry.ceil() as u32 * 2, src.height); - let target_x = (columns as f32 / 2.0).floor() as u32; - let target_y = (rows as f32 / 2.0).floor() as u32; - - let width_max = src.width as i32 - 1; - let height_max = src.height as i32 - 1; + let width_max = src.width - 1; + let height_max = src.height - 1; + let rx = (rx.round() as u32).min(width_max); + let ry = (ry.round() as u32).min(height_max); let mut buf = vec![RGBA8::default(); src.data.len()]; let mut buf = ImageRefMut::new(src.width, src.height, &mut buf); - let mut x = 0; - let mut y = 0; + let mut x: u32 = 0; + let mut y: u32 = 0; for _ in src.data.iter() { let mut new_p = RGBA8::default(); if operator == MorphologyOperator::Erode { @@ -35,26 +31,22 @@ pub fn apply(operator: MorphologyOperator, rx: f32, ry: f32, src: ImageRefMut) { new_p.a = 255; } - for oy in 0..rows { - for ox in 0..columns { - let tx = x as i32 - target_x as i32 + ox as i32; - let ty = y as i32 - target_y as i32 + oy as i32; - - if tx < 0 || tx > width_max || ty < 0 || ty > height_max { - continue; - } + let x_range = x.saturating_sub(rx)..=(x + rx).min(width_max); + let y_range = y.saturating_sub(ry)..=(y + ry).min(height_max); - let p = src.pixel_at(tx as u32, ty as u32); + for ty in y_range { + for tx in x_range.clone() { + let p = src.pixel_at(tx, ty); if operator == MorphologyOperator::Erode { - new_p.r = std::cmp::min(p.r, new_p.r); - new_p.g = std::cmp::min(p.g, new_p.g); - new_p.b = std::cmp::min(p.b, new_p.b); - new_p.a = std::cmp::min(p.a, new_p.a); + new_p.r = p.r.min(new_p.r); + new_p.g = p.g.min(new_p.g); + new_p.b = p.b.min(new_p.b); + new_p.a = p.a.min(new_p.a); } else { - new_p.r = std::cmp::max(p.r, new_p.r); - new_p.g = std::cmp::max(p.g, new_p.g); - new_p.b = std::cmp::max(p.b, new_p.b); - new_p.a = std::cmp::max(p.a, new_p.a); + new_p.r = p.r.max(new_p.r); + new_p.g = p.g.max(new_p.g); + new_p.b = p.b.max(new_p.b); + new_p.a = p.a.max(new_p.a); } } } diff --git a/crates/resvg/tests/integration/render.rs b/crates/resvg/tests/integration/render.rs index cb1e91fa5..79c9e3c79 100644 --- a/crates/resvg/tests/integration/render.rs +++ b/crates/resvg/tests/integration/render.rs @@ -209,10 +209,13 @@ use crate::render; #[test] fn filters_feMerge_color_interpolation_filters_eq_linearRGB() { assert_eq!(render("tests/filters/feMerge/color-interpolation-filters=linearRGB"), 0); } #[test] fn filters_feMerge_color_interpolation_filters_eq_sRGB() { assert_eq!(render("tests/filters/feMerge/color-interpolation-filters=sRGB"), 0); } #[test] fn filters_feMerge_complex_transform() { assert_eq!(render("tests/filters/feMerge/complex-transform"), 0); } +#[test] fn filters_feMorphology_centered_kernel() { assert_eq!(render("tests/filters/feMorphology/centered-kernel"), 0); } #[test] fn filters_feMorphology_empty_radius() { assert_eq!(render("tests/filters/feMorphology/empty-radius"), 0); } +#[test] fn filters_feMorphology_fractional_radius() { assert_eq!(render("tests/filters/feMorphology/fractional-radius"), 0); } #[test] fn filters_feMorphology_negative_radius() { assert_eq!(render("tests/filters/feMorphology/negative-radius"), 0); } #[test] fn filters_feMorphology_no_radius() { assert_eq!(render("tests/filters/feMorphology/no-radius"), 0); } #[test] fn filters_feMorphology_operator_eq_dilate() { assert_eq!(render("tests/filters/feMorphology/operator=dilate"), 0); } +#[test] fn filters_feMorphology_radius_larger_than_region() { assert_eq!(render("tests/filters/feMorphology/radius-larger-than-region"), 0); } #[test] fn filters_feMorphology_radius_with_too_many_values() { assert_eq!(render("tests/filters/feMorphology/radius-with-too-many-values"), 0); } #[test] fn filters_feMorphology_radius_eq_0_5_with_objectBoundingBox() { assert_eq!(render("tests/filters/feMorphology/radius=0.5-with-objectBoundingBox"), 0); } #[test] fn filters_feMorphology_radius_eq_0_5() { assert_eq!(render("tests/filters/feMorphology/radius=0.5"), 0); } diff --git a/crates/resvg/tests/tests/filters/feMorphology/centered-kernel.png b/crates/resvg/tests/tests/filters/feMorphology/centered-kernel.png new file mode 100644 index 000000000..52210b18e Binary files /dev/null and b/crates/resvg/tests/tests/filters/feMorphology/centered-kernel.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/centered-kernel.svg b/crates/resvg/tests/tests/filters/feMorphology/centered-kernel.svg new file mode 100644 index 000000000..d65f109b0 --- /dev/null +++ b/crates/resvg/tests/tests/filters/feMorphology/centered-kernel.svg @@ -0,0 +1,12 @@ + + Dilation with a centered kernel. No red should be visible. + + + + + + + + + + diff --git a/crates/resvg/tests/tests/filters/feMorphology/empty-radius.png b/crates/resvg/tests/tests/filters/feMorphology/empty-radius.png index 17dbf5945..f6638e93d 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/empty-radius.png and b/crates/resvg/tests/tests/filters/feMorphology/empty-radius.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/fractional-radius.png b/crates/resvg/tests/tests/filters/feMorphology/fractional-radius.png new file mode 100644 index 000000000..52210b18e Binary files /dev/null and b/crates/resvg/tests/tests/filters/feMorphology/fractional-radius.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/fractional-radius.svg b/crates/resvg/tests/tests/filters/feMorphology/fractional-radius.svg new file mode 100644 index 000000000..612f34410 --- /dev/null +++ b/crates/resvg/tests/tests/filters/feMorphology/fractional-radius.svg @@ -0,0 +1,12 @@ + + Erosion with a fractional radius. No red should be visible. + + + + + + + + + + diff --git a/crates/resvg/tests/tests/filters/feMorphology/negative-radius.png b/crates/resvg/tests/tests/filters/feMorphology/negative-radius.png index 17dbf5945..f6638e93d 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/negative-radius.png and b/crates/resvg/tests/tests/filters/feMorphology/negative-radius.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/no-radius.png b/crates/resvg/tests/tests/filters/feMorphology/no-radius.png index 17dbf5945..f6638e93d 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/no-radius.png and b/crates/resvg/tests/tests/filters/feMorphology/no-radius.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/operator=dilate.png b/crates/resvg/tests/tests/filters/feMorphology/operator=dilate.png index c67a4bb03..4c5a6f9a3 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/operator=dilate.png and b/crates/resvg/tests/tests/filters/feMorphology/operator=dilate.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/radius-larger-than-region.png b/crates/resvg/tests/tests/filters/feMorphology/radius-larger-than-region.png new file mode 100644 index 000000000..ca89852e2 Binary files /dev/null and b/crates/resvg/tests/tests/filters/feMorphology/radius-larger-than-region.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/radius-larger-than-region.svg b/crates/resvg/tests/tests/filters/feMorphology/radius-larger-than-region.svg new file mode 100644 index 000000000..619b695a6 --- /dev/null +++ b/crates/resvg/tests/tests/filters/feMorphology/radius-larger-than-region.svg @@ -0,0 +1,12 @@ + + Dilation with a radius larger than the filter region. No red should be visible. + + + + + + + + + + diff --git a/crates/resvg/tests/tests/filters/feMorphology/radius-with-too-many-values.png b/crates/resvg/tests/tests/filters/feMorphology/radius-with-too-many-values.png index 17dbf5945..f6638e93d 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/radius-with-too-many-values.png and b/crates/resvg/tests/tests/filters/feMorphology/radius-with-too-many-values.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/radius=0.5-with-objectBoundingBox.png b/crates/resvg/tests/tests/filters/feMorphology/radius=0.5-with-objectBoundingBox.png index ec9b1a4d4..b629f4fbf 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/radius=0.5-with-objectBoundingBox.png and b/crates/resvg/tests/tests/filters/feMorphology/radius=0.5-with-objectBoundingBox.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/radius=0.5.png b/crates/resvg/tests/tests/filters/feMorphology/radius=0.5.png index 02ca2ab14..20877c5d1 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/radius=0.5.png and b/crates/resvg/tests/tests/filters/feMorphology/radius=0.5.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/radius=1-10.png b/crates/resvg/tests/tests/filters/feMorphology/radius=1-10.png index 4721f1252..c49c9b868 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/radius=1-10.png and b/crates/resvg/tests/tests/filters/feMorphology/radius=1-10.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/radius=10-0.png b/crates/resvg/tests/tests/filters/feMorphology/radius=10-0.png index 8e75958c9..3034ce86c 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/radius=10-0.png and b/crates/resvg/tests/tests/filters/feMorphology/radius=10-0.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/radius=10-1.png b/crates/resvg/tests/tests/filters/feMorphology/radius=10-1.png index 8e75958c9..3034ce86c 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/radius=10-1.png and b/crates/resvg/tests/tests/filters/feMorphology/radius=10-1.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/simple-case.png b/crates/resvg/tests/tests/filters/feMorphology/simple-case.png index 090f6d70b..0de4be989 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/simple-case.png and b/crates/resvg/tests/tests/filters/feMorphology/simple-case.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/source-with-opacity.png b/crates/resvg/tests/tests/filters/feMorphology/source-with-opacity.png index f9d807e53..1b0e335d4 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/source-with-opacity.png and b/crates/resvg/tests/tests/filters/feMorphology/source-with-opacity.png differ diff --git a/crates/resvg/tests/tests/filters/feMorphology/zero-radius.png b/crates/resvg/tests/tests/filters/feMorphology/zero-radius.png index 17dbf5945..f6638e93d 100644 Binary files a/crates/resvg/tests/tests/filters/feMorphology/zero-radius.png and b/crates/resvg/tests/tests/filters/feMorphology/zero-radius.png differ