[librsvg: 7/10] Translate the blur() filter function into filter primitives




commit 06951ed56f1d4b3e287e4ee4aae1f818290fa41c
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Apr 28 17:48:44 2021 -0500

    Translate the blur() filter function into filter primitives
    
    This basically involves constructing a FilterSpec by hand, mostly out
    of default values, and a PrimitiveParams::GaussianBlur.
    
    The test for this renders two SVG documents.  On one, an element is
    modified using a blur() filter function.  On another, the reference,
    the same element is modified with a <filter> element with a
    <feGaussianBlur> - the same expansion for the filter function as the
    spec suggests.

 src/drawing_ctx.rs           |  2 +-
 src/filter.rs                |  3 ++-
 src/filter_func.rs           | 52 +++++++++++++++++++++++++++++++++++++---
 src/filters/gaussian_blur.rs |  6 ++---
 src/filters/mod.rs           | 10 +++++---
 tests/src/filters.rs         | 57 ++++++++++++++++++++++++++++++++++++++++++++
 6 files changed, 119 insertions(+), 11 deletions(-)
---
diff --git a/src/drawing_ctx.rs b/src/drawing_ctx.rs
index eff1857d..a1a620a4 100644
--- a/src/drawing_ctx.rs
+++ b/src/drawing_ctx.rs
@@ -913,7 +913,7 @@ impl DrawingCtx {
                 if let Ok(specs) = filter_list
                     .iter()
                     .map(|filter_value| {
-                        filter_value.to_filter_spec(acquired_nodes, self, node_name)
+                        filter_value.to_filter_spec(acquired_nodes, values, self, node_name)
                     })
                     .collect::<Result<Vec<FilterSpec>, _>>()
                 {
diff --git a/src/filter.rs b/src/filter.rs
index f626b4f2..9bb0b1ec 100644
--- a/src/filter.rs
+++ b/src/filter.rs
@@ -113,6 +113,7 @@ impl FilterValue {
     pub fn to_filter_spec(
         &self,
         acquired_nodes: &mut AcquiredNodes<'_>,
+        values: &ComputedValues,
         draw_ctx: &DrawingCtx,
         node_being_filtered_name: &str,
     ) -> Result<FilterSpec, FilterResolveError> {
@@ -124,7 +125,7 @@ impl FilterValue {
                 node_being_filtered_name,
             ),
 
-            FilterValue::Function(ref func) => func.to_filter_spec(),
+            FilterValue::Function(ref func) => func.to_filter_spec(values, draw_ctx),
         }
     }
 }
diff --git a/src/filter_func.rs b/src/filter_func.rs
index eb84fff8..e7e51a67 100644
--- a/src/filter_func.rs
+++ b/src/filter_func.rs
@@ -1,9 +1,16 @@
 use cssparser::Parser;
 
+use crate::coord_units::CoordUnits;
+use crate::drawing_ctx::{DrawingCtx, ViewParams};
 use crate::error::*;
-use crate::filters::{FilterResolveError, FilterSpec};
+use crate::filter::Filter;
+use crate::filters::{
+    gaussian_blur::GaussianBlur, FilterResolveError, FilterSpec, Primitive, PrimitiveParams,
+    ResolvedPrimitive,
+};
 use crate::length::*;
 use crate::parsers::Parse;
+use crate::properties::ComputedValues;
 
 /// CSS Filter functions from the Filter Effects Module Level 1
 ///
@@ -45,6 +52,36 @@ fn parse_blur<'i>(parser: &mut Parser<'i, '_>) -> Result<FilterFunction, ParseEr
     }))
 }
 
+impl Blur {
+    fn to_filter_spec(
+        &self,
+        values: &ComputedValues,
+        params: &ViewParams,
+    ) -> Result<FilterSpec, FilterResolveError> {
+        // The 0.0 default is from the spec
+        let std_dev = self
+            .std_deviation
+            .map(|l| l.normalize(values, params))
+            .unwrap_or(0.0);
+
+        let user_space_filter = Filter::default().to_user_space(values, params);
+
+        let gaussian_blur = ResolvedPrimitive {
+            primitive: Primitive::default(),
+            params: PrimitiveParams::GaussianBlur(GaussianBlur {
+                std_deviation: (std_dev, std_dev),
+                ..GaussianBlur::default()
+            }),
+        }
+        .into_user_space(values, params);
+
+        Ok(FilterSpec {
+            user_space_filter,
+            primitives: vec![gaussian_blur],
+        })
+    }
+}
+
 impl Parse for FilterFunction {
     fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, crate::error::ParseError<'i>> {
         let loc = parser.current_source_location();
@@ -58,8 +95,17 @@ impl Parse for FilterFunction {
 }
 
 impl FilterFunction {
-    pub fn to_filter_spec(&self) -> Result<FilterSpec, FilterResolveError> {
-        unimplemented!()
+    pub fn to_filter_spec(
+        &self,
+        values: &ComputedValues,
+        draw_ctx: &DrawingCtx,
+    ) -> Result<FilterSpec, FilterResolveError> {
+        // This is the default for primitive_units
+        let params = draw_ctx.push_coord_units(CoordUnits::UserSpaceOnUse);
+
+        match self {
+            FilterFunction::Blur(v) => v.to_filter_spec(values, &params),
+        }
     }
 }
 
diff --git a/src/filters/gaussian_blur.rs b/src/filters/gaussian_blur.rs
index 692920ed..2cfe6150 100644
--- a/src/filters/gaussian_blur.rs
+++ b/src/filters/gaussian_blur.rs
@@ -39,9 +39,9 @@ pub struct FeGaussianBlur {
 /// Resolved `feGaussianBlur` primitive for rendering.
 #[derive(Default, Clone)]
 pub struct GaussianBlur {
-    in1: Input,
-    std_deviation: (f64, f64),
-    color_interpolation_filters: ColorInterpolationFilters,
+    pub in1: Input,
+    pub std_deviation: (f64, f64),
+    pub color_interpolation_filters: ColorInterpolationFilters,
 }
 
 impl SetAttributes for FeGaussianBlur {
diff --git a/src/filters/mod.rs b/src/filters/mod.rs
index f2944a3e..393d2a8e 100644
--- a/src/filters/mod.rs
+++ b/src/filters/mod.rs
@@ -60,8 +60,8 @@ pub mod tile;
 pub mod turbulence;
 
 pub struct FilterSpec {
-    user_space_filter: UserSpaceFilter,
-    primitives: Vec<UserSpacePrimitive>,
+    pub user_space_filter: UserSpaceFilter,
+    pub primitives: Vec<UserSpacePrimitive>,
 }
 
 /// Resolved parameters for each filter primitive.
@@ -178,7 +178,11 @@ impl Parse for Input {
 }
 
 impl ResolvedPrimitive {
-    fn into_user_space(self, values: &ComputedValues, params: &ViewParams) -> UserSpacePrimitive {
+    pub fn into_user_space(
+        self,
+        values: &ComputedValues,
+        params: &ViewParams,
+    ) -> UserSpacePrimitive {
         let x = self.primitive.x.map(|l| l.normalize(values, &params));
         let y = self.primitive.y.map(|l| l.normalize(values, &params));
         let width = self.primitive.width.map(|l| l.normalize(values, &params));
diff --git a/tests/src/filters.rs b/tests/src/filters.rs
index 5b7381bb..fce32516 100644
--- a/tests/src/filters.rs
+++ b/tests/src/filters.rs
@@ -98,3 +98,60 @@ fn non_filter_reference_cancels_filter_chain() {
         .compare(&output_surf)
         .evaluate(&output_surf, "non_filter_reference_cancels_filter_chain");
 }
+
+#[test]
+fn blur_filter_func() {
+    // Create an element with a filter function, and compare it to the
+    // supposed equivalent using the <filter> element.
+    let svg = load_svg(
+        br##"<?xml version="1.0" encoding="UTF-8"?>
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; width="400" 
height="400">
+  <rect x="100" y="100" width="200" height="200" fill="lime" filter="blur(5)"/>
+</svg>
+"##,
+    ).unwrap();
+
+    let output_surf = render_document(
+        &svg,
+        SurfaceSize(400, 400),
+        |_| (),
+        cairo::Rectangle {
+            x: 0.0,
+            y: 0.0,
+            width: 400.0,
+            height: 400.0,
+        },
+    )
+    .unwrap();
+
+    let reference = load_svg(
+        br##"<?xml version="1.0" encoding="UTF-8"?>
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"; xmlns:xlink="http://www.w3.org/1999/xlink"; width="400" 
height="400">
+  <defs>
+    <filter id="filter">
+      <feGaussianBlur stdDeviation="5 5" edgeMode="none"/>
+    </filter>
+  </defs>
+
+  <rect x="100" y="100" width="200" height="200" fill="lime" filter="url(#filter)"/>
+</svg>
+"##,
+    ).unwrap();
+
+    let reference_surf = render_document(
+        &reference,
+        SurfaceSize(400, 400),
+        |_| (),
+        cairo::Rectangle {
+            x: 0.0,
+            y: 0.0,
+            width: 400.0,
+            height: 400.0,
+        },
+    )
+    .unwrap();
+
+    Reference::from_surface(reference_surf.into_image_surface().unwrap())
+        .compare(&output_surf)
+        .evaluate(&output_surf, "blur_filter_func");
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]