[librsvg: 7/10] Translate the blur() filter function into filter primitives
- From: Federico Mena Quintero <federico src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [librsvg: 7/10] Translate the blur() filter function into filter primitives
- Date: Thu, 29 Apr 2021 00:06:46 +0000 (UTC)
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, ¶ms),
+ }
}
}
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, ¶ms));
let y = self.primitive.y.map(|l| l.normalize(values, ¶ms));
let width = self.primitive.width.map(|l| l.normalize(values, ¶ms));
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]