diff options
author | Marge Bot <marge-bot@gnome.org> | 2023-02-10 02:16:17 +0000 |
---|---|---|
committer | Marge Bot <marge-bot@gnome.org> | 2023-02-10 02:16:17 +0000 |
commit | 8742c720018f80ad808a2133a6a8c41ca91526cf (patch) | |
tree | 2b54a3c5344e086fba8b9eaaad86514492b106a4 | |
parent | 91c059331352775e879f3dea13c68b52965ae901 (diff) | |
parent | 0c40d1131b672b5f6eab93ae7be26c494ebc884f (diff) | |
download | librsvg-8742c720018f80ad808a2133a6a8c41ca91526cf.tar.gz |
Merge branch 'fe-drop-shadow' into 'main'
Implement the feDropShadow element
Closes #743
See merge request GNOME/librsvg!793
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | devel-docs/features.rst | 15 | ||||
-rw-r--r-- | src/element.rs | 9 | ||||
-rw-r--r-- | src/filter.rs | 52 | ||||
-rw-r--r-- | src/filter_func.rs | 125 | ||||
-rw-r--r-- | src/filters/blend.rs | 6 | ||||
-rw-r--r-- | src/filters/color_matrix.rs | 6 | ||||
-rw-r--r-- | src/filters/component_transfer.rs | 6 | ||||
-rw-r--r-- | src/filters/composite.rs | 6 | ||||
-rw-r--r-- | src/filters/convolve_matrix.rs | 6 | ||||
-rw-r--r-- | src/filters/displacement_map.rs | 6 | ||||
-rw-r--r-- | src/filters/drop_shadow.rs | 88 | ||||
-rw-r--r-- | src/filters/flood.rs | 6 | ||||
-rw-r--r-- | src/filters/gaussian_blur.rs | 6 | ||||
-rw-r--r-- | src/filters/image.rs | 6 | ||||
-rw-r--r-- | src/filters/lighting.rs | 18 | ||||
-rw-r--r-- | src/filters/merge.rs | 9 | ||||
-rw-r--r-- | src/filters/mod.rs | 7 | ||||
-rw-r--r-- | src/filters/morphology.rs | 6 | ||||
-rw-r--r-- | src/filters/offset.rs | 6 | ||||
-rw-r--r-- | src/filters/tile.rs | 6 | ||||
-rw-r--r-- | src/filters/turbulence.rs | 6 | ||||
-rw-r--r-- | tests/fixtures/reftests/svg2/bug743-fe-drop-shadow-ref.svg | 17 | ||||
-rw-r--r-- | tests/fixtures/reftests/svg2/bug743-fe-drop-shadow.svg | 10 | ||||
-rw-r--r-- | tests/src/filters.rs | 8 |
25 files changed, 305 insertions, 132 deletions
diff --git a/Makefile.am b/Makefile.am index ac169ee9..24e47b34 100644 --- a/Makefile.am +++ b/Makefile.am @@ -63,6 +63,7 @@ LIBRSVG_SRC = \ src/filters/context.rs \ src/filters/convolve_matrix.rs \ src/filters/displacement_map.rs \ + src/filters/drop_shadow.rs \ src/filters/error.rs \ src/filters/flood.rs \ src/filters/gaussian_blur.rs \ diff --git a/devel-docs/features.rst b/devel-docs/features.rst index d975d01f..4f166f72 100644 --- a/devel-docs/features.rst +++ b/devel-docs/features.rst @@ -185,6 +185,21 @@ Elements and their specific attributes +-----------------------+-----------------------+-----------------------+ | | elevation | | +-----------------------+-----------------------+-----------------------+ +| feDropShadow | | See “Filter effects”. | +| | | | +| | | Also takes the | +| | | flood-color and | +| | | flood-opacity | +| | | properties. | ++-----------------------+-----------------------+-----------------------+ +| | in | | ++-----------------------+-----------------------+-----------------------+ +| | dx | | ++-----------------------+-----------------------+-----------------------+ +| | dy | | ++-----------------------+-----------------------+-----------------------+ +| | stdDeviation | | ++-----------------------+-----------------------+-----------------------+ | feFuncA | | See “Filter effect | | | | feComponentTransfer” | +-----------------------+-----------------------+-----------------------+ diff --git a/src/element.rs b/src/element.rs index bf5c1a30..2ee4e693 100644 --- a/src/element.rs +++ b/src/element.rs @@ -21,6 +21,7 @@ use crate::filters::{ composite::FeComposite, convolve_matrix::FeConvolveMatrix, displacement_map::FeDisplacementMap, + drop_shadow::FeDropShadow, flood::FeFlood, gaussian_blur::FeGaussianBlur, image::FeImage, @@ -315,6 +316,7 @@ pub enum Element { FeDiffuseLighting(Box<ElementInner<FeDiffuseLighting>>), FeDisplacementMap(Box<ElementInner<FeDisplacementMap>>), FeDistantLight(Box<ElementInner<FeDistantLight>>), + FeDropShadow(Box<ElementInner<FeDropShadow>>), FeFlood(Box<ElementInner<FeFlood>>), FeFuncA(Box<ElementInner<FeFuncA>>), FeFuncB(Box<ElementInner<FeFuncB>>), @@ -372,6 +374,7 @@ macro_rules! call_inner { Element::FeDiffuseLighting(i) => i.$method($($args),*), Element::FeDisplacementMap(i) => i.$method($($args),*), Element::FeDistantLight(i) => i.$method($($args),*), + Element::FeDropShadow(i) => i.$method($($args),*), Element::FeFlood(i) => i.$method($($args),*), Element::FeFuncA(i) => i.$method($($args),*), Element::FeFuncB(i) => i.$method($($args),*), @@ -494,6 +497,7 @@ impl Element { Element::FeConvolveMatrix(ref fe) => Some(&fe.element_impl), Element::FeDiffuseLighting(ref fe) => Some(&fe.element_impl), Element::FeDisplacementMap(ref fe) => Some(&fe.element_impl), + Element::FeDropShadow(ref fe) => Some(&fe.element_impl), Element::FeFlood(ref fe) => Some(&fe.element_impl), Element::FeGaussianBlur(ref fe) => Some(&fe.element_impl), Element::FeImage(ref fe) => Some(&fe.element_impl), @@ -591,8 +595,9 @@ mod creators { e!(create_fe_composite, FeComposite); e!(create_fe_convolve_matrix, FeConvolveMatrix); e!(create_fe_diffuse_lighting, FeDiffuseLighting); - e!(create_fe_distant_light, FeDistantLight); e!(create_fe_displacement_map, FeDisplacementMap); + e!(create_fe_distant_light, FeDistantLight); + e!(create_fe_drop_shadow, FeDropShadow); e!(create_fe_flood, FeFlood); e!(create_fe_gaussian_blur, FeGaussianBlur); e!(create_fe_image, FeImage); @@ -685,6 +690,7 @@ static ELEMENT_CREATORS: Lazy<HashMap<&'static str, (ElementCreateFn, ElementCre ("feDiffuseLighting", create_fe_diffuse_lighting, Default), ("feDisplacementMap", create_fe_displacement_map, Default), ("feDistantLight", create_fe_distant_light, IgnoreClass), + ("feDropShadow", create_fe_drop_shadow, Default), ("feFuncA", create_fe_func_a, IgnoreClass), ("feFuncB", create_fe_func_b, IgnoreClass), ("feFuncG", create_fe_func_g, IgnoreClass), @@ -788,6 +794,7 @@ mod sizes { print_size!(FeDiffuseLighting); print_size!(FeDistantLight); print_size!(FeDisplacementMap); + print_size!(FeDropShadow); print_size!(FeFlood); print_size!(FeGaussianBlur); print_size!(FeImage); diff --git a/src/filter.rs b/src/filter.rs index ebab3099..9f7d5312 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -10,7 +10,7 @@ use crate::drawing_ctx::{DrawingCtx, ViewParams}; use crate::element::{set_attribute, Draw, Element, SetAttributes}; use crate::error::ValueErrorKind; use crate::filter_func::FilterFunction; -use crate::filters::{FilterResolveError, FilterSpec, UserSpacePrimitive}; +use crate::filters::{FilterResolveError, FilterSpec}; use crate::length::*; use crate::node::{Node, NodeBorrow}; use crate::parsers::{Parse, ParseValue}; @@ -179,38 +179,44 @@ fn extract_filter_from_filter_node( let primitive_view_params = filter_view_params.get(user_space_filter.primitive_units); - let primitives = filter_node + let primitive_nodes = filter_node .children() .filter(|c| c.is_element()) // Keep only filter primitives (those that implement the Filter trait) - .filter(|c| c.borrow_element().as_filter_effect().is_some()) - .map(|primitive_node| { - let elt = primitive_node.borrow_element(); - let effect = elt.as_filter_effect().unwrap(); + .filter(|c| c.borrow_element().as_filter_effect().is_some()); - let primitive_name = format!("{primitive_node}"); + let mut user_space_primitives = Vec::new(); - let primitive_values = elt.get_computed_values(); - let params = NormalizeParams::new(primitive_values, primitive_view_params); + for primitive_node in primitive_nodes { + let elt = primitive_node.borrow_element(); + let effect = elt.as_filter_effect().unwrap(); - effect - .resolve(acquired_nodes, &primitive_node) - .map_err(|e| { - rsvg_log!( - session, - "(filter primitive {} returned an error: {})", - primitive_name, - e - ); + let primitive_name = format!("{primitive_node}"); + + let primitive_values = elt.get_computed_values(); + let params = NormalizeParams::new(primitive_values, primitive_view_params); + + let primitives = match effect.resolve(acquired_nodes, &primitive_node) { + Ok(primitives) => primitives, + Err(e) => { + rsvg_log!( + session, + "(filter primitive {} returned an error: {})", + primitive_name, e - }) - .map(|primitive| primitive.into_user_space(¶ms)) - }) - .collect::<Result<Vec<UserSpacePrimitive>, FilterResolveError>>()?; + ); + return Err(e); + } + }; + + for p in primitives { + user_space_primitives.push(p.into_user_space(¶ms)); + } + } Ok(FilterSpec { user_space_filter, - primitives, + primitives: user_space_primitives, }) } diff --git a/src/filter_func.rs b/src/filter_func.rs index 3639503e..a3f8cbf6 100644 --- a/src/filter_func.rs +++ b/src/filter_func.rs @@ -364,6 +364,70 @@ impl Contrast { } } +/// Creates the filter primitives required for a `feDropShadow` effect. +/// +/// Both the `drop-shadow()` filter function and the `feDropShadow` element need to create +/// a sequence of filter primitives (blur, offset, etc.) to build the drop shadow. This +/// function builds that sequence. +pub fn drop_shadow_primitives( + dx: f64, + dy: f64, + std_deviation: NumberOptionalNumber<f64>, + color: RGBA, +) -> Vec<ResolvedPrimitive> { + let offsetblur = CustomIdent("offsetblur".to_string()); + + let gaussian_blur = ResolvedPrimitive { + primitive: Primitive::default(), + params: PrimitiveParams::GaussianBlur(GaussianBlur { + in1: Input::SourceAlpha, + std_deviation, + ..GaussianBlur::default() + }), + }; + + let offset = ResolvedPrimitive { + primitive: Primitive { + result: Some(offsetblur.clone()), + ..Primitive::default() + }, + params: PrimitiveParams::Offset(Offset { + in1: Input::default(), + dx, + dy, + }), + }; + + let flood = ResolvedPrimitive { + primitive: Primitive::default(), + params: PrimitiveParams::Flood(Flood { color }), + }; + + let composite = ResolvedPrimitive { + primitive: Primitive::default(), + params: PrimitiveParams::Composite(Composite { + in2: Input::FilterOutput(offsetblur), + operator: Operator::In, + ..Composite::default() + }), + }; + + let merge = ResolvedPrimitive { + primitive: Primitive::default(), + params: PrimitiveParams::Merge(Merge { + merge_nodes: vec![ + MergeNode::default(), + MergeNode { + in1: Input::SourceGraphic, + ..MergeNode::default() + }, + ], + }), + }; + + vec![gaussian_blur, offset, flood, composite, merge] +} + impl DropShadow { /// Converts a DropShadow into the set of filter element primitives. /// @@ -373,70 +437,23 @@ impl DropShadow { let dx = self.dx.map(|l| l.to_user(params)).unwrap_or(0.0); let dy = self.dy.map(|l| l.to_user(params)).unwrap_or(0.0); let std_dev = self.std_deviation.map(|l| l.to_user(params)).unwrap_or(0.0); + let std_deviation = NumberOptionalNumber(std_dev, std_dev); let color = self .color .as_ref() .map(|c| resolve_color(c, UnitInterval::clamp(1.0), default_color)) .unwrap_or(default_color); - let offsetblur = CustomIdent("offsetblur".to_string()); + let resolved_primitives = drop_shadow_primitives(dx, dy, std_deviation, color); - let gaussian_blur = ResolvedPrimitive { - primitive: Primitive::default(), - params: PrimitiveParams::GaussianBlur(GaussianBlur { - in1: Input::SourceAlpha, - std_deviation: NumberOptionalNumber(std_dev, std_dev), - ..GaussianBlur::default() - }), - } - .into_user_space(params); - - let offset = ResolvedPrimitive { - primitive: Primitive { - result: Some(offsetblur.clone()), - ..Primitive::default() - }, - params: PrimitiveParams::Offset(Offset { - in1: Input::default(), - dx, - dy, - }), - } - .into_user_space(params); - - let flood = ResolvedPrimitive { - primitive: Primitive::default(), - params: PrimitiveParams::Flood(Flood { color }), - } - .into_user_space(params); - - let composite = ResolvedPrimitive { - primitive: Primitive::default(), - params: PrimitiveParams::Composite(Composite { - in2: Input::FilterOutput(offsetblur), - operator: Operator::In, - ..Composite::default() - }), - } - .into_user_space(params); - - let merge = ResolvedPrimitive { - primitive: Primitive::default(), - params: PrimitiveParams::Merge(Merge { - merge_nodes: vec![ - MergeNode::default(), - MergeNode { - in1: Input::SourceGraphic, - ..MergeNode::default() - }, - ], - }), - } - .into_user_space(params); + let primitives = resolved_primitives + .into_iter() + .map(|p| p.into_user_space(params)) + .collect(); FilterSpec { user_space_filter, - primitives: vec![gaussian_blur, offset, flood, composite, merge], + primitives, } } } diff --git a/src/filters/blend.rs b/src/filters/blend.rs index 4baf4476..28a8be25 100644 --- a/src/filters/blend.rs +++ b/src/filters/blend.rs @@ -113,17 +113,17 @@ impl FilterEffect for FeBlend { &self, _acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { let cascaded = CascadedValues::new_from_node(node); let values = cascaded.get(); let mut params = self.params.clone(); params.color_interpolation_filters = values.color_interpolation_filters(); - Ok(ResolvedPrimitive { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::Blend(params), - }) + }]) } } diff --git a/src/filters/color_matrix.rs b/src/filters/color_matrix.rs index 7bb3689f..b06cac1f 100644 --- a/src/filters/color_matrix.rs +++ b/src/filters/color_matrix.rs @@ -315,17 +315,17 @@ impl FilterEffect for FeColorMatrix { &self, _acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { let cascaded = CascadedValues::new_from_node(node); let values = cascaded.get(); let mut params = self.params.clone(); params.color_interpolation_filters = values.color_interpolation_filters(); - Ok(ResolvedPrimitive { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::ColorMatrix(params), - }) + }]) } } diff --git a/src/filters/component_transfer.rs b/src/filters/component_transfer.rs index 02efa6ca..ae358d72 100644 --- a/src/filters/component_transfer.rs +++ b/src/filters/component_transfer.rs @@ -385,7 +385,7 @@ impl FilterEffect for FeComponentTransfer { &self, _acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { let cascaded = CascadedValues::new_from_node(node); let values = cascaded.get(); @@ -393,10 +393,10 @@ impl FilterEffect for FeComponentTransfer { params.functions = get_functions(node)?; params.color_interpolation_filters = values.color_interpolation_filters(); - Ok(ResolvedPrimitive { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::ComponentTransfer(params), - }) + }]) } } diff --git a/src/filters/composite.rs b/src/filters/composite.rs index c05468e8..2be0e261 100644 --- a/src/filters/composite.rs +++ b/src/filters/composite.rs @@ -133,17 +133,17 @@ impl FilterEffect for FeComposite { &self, _acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { let cascaded = CascadedValues::new_from_node(node); let values = cascaded.get(); let mut params = self.params.clone(); params.color_interpolation_filters = values.color_interpolation_filters(); - Ok(ResolvedPrimitive { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::Composite(params), - }) + }]) } } diff --git a/src/filters/convolve_matrix.rs b/src/filters/convolve_matrix.rs index db9dc047..a445e733 100644 --- a/src/filters/convolve_matrix.rs +++ b/src/filters/convolve_matrix.rs @@ -321,17 +321,17 @@ impl FilterEffect for FeConvolveMatrix { &self, _acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { let cascaded = CascadedValues::new_from_node(node); let values = cascaded.get(); let mut params = self.params.clone(); params.color_interpolation_filters = values.color_interpolation_filters(); - Ok(ResolvedPrimitive { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::ConvolveMatrix(params), - }) + }]) } } diff --git a/src/filters/displacement_map.rs b/src/filters/displacement_map.rs index d8781d80..e3396e5a 100644 --- a/src/filters/displacement_map.rs +++ b/src/filters/displacement_map.rs @@ -168,17 +168,17 @@ impl FilterEffect for FeDisplacementMap { &self, _acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { let cascaded = CascadedValues::new_from_node(node); let values = cascaded.get(); let mut params = self.params.clone(); params.color_interpolation_filters = values.color_interpolation_filters(); - Ok(ResolvedPrimitive { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::DisplacementMap(params), - }) + }]) } } diff --git a/src/filters/drop_shadow.rs b/src/filters/drop_shadow.rs new file mode 100644 index 00000000..3969d5e5 --- /dev/null +++ b/src/filters/drop_shadow.rs @@ -0,0 +1,88 @@ +use markup5ever::{expanded_name, local_name, namespace_url, ns}; + +use crate::document::AcquiredNodes; +use crate::element::{set_attribute, SetAttributes}; +use crate::filter_func::drop_shadow_primitives; +use crate::node::{CascadedValues, Node}; +use crate::paint_server::resolve_color; +use crate::parsers::{NumberOptionalNumber, ParseValue}; +use crate::session::Session; +use crate::xml::Attributes; + +use super::{FilterEffect, FilterResolveError, Input, Primitive, ResolvedPrimitive}; + +/// The `feDropShadow` element. +#[derive(Default)] +pub struct FeDropShadow { + base: Primitive, + params: DropShadow, +} + +/// Resolved `feDropShadow` parameters for rendering. +#[derive(Clone)] +pub struct DropShadow { + pub in1: Input, + pub dx: f64, + pub dy: f64, + pub std_deviation: NumberOptionalNumber<f64>, +} + +impl Default for DropShadow { + /// Defaults come from https://www.w3.org/TR/filter-effects/#feDropShadowElement + fn default() -> Self { + Self { + in1: Default::default(), + dx: 2.0, + dy: 2.0, + std_deviation: NumberOptionalNumber(2.0, 2.0), + } + } +} + +impl SetAttributes for FeDropShadow { + fn set_attributes(&mut self, attrs: &Attributes, session: &Session) { + self.params.in1 = self.base.parse_one_input(attrs, session); + + for (attr, value) in attrs.iter() { + match attr.expanded() { + expanded_name!("", "dx") => { + set_attribute(&mut self.params.dx, attr.parse(value), session); + } + + expanded_name!("", "dy") => { + set_attribute(&mut self.params.dy, attr.parse(value), session); + } + + expanded_name!("", "stdDeviation") => { + set_attribute(&mut self.params.std_deviation, attr.parse(value), session); + } + + _ => (), + } + } + } +} + +impl FilterEffect for FeDropShadow { + fn resolve( + &self, + _acquired_nodes: &mut AcquiredNodes<'_>, + node: &Node, + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { + let cascaded = CascadedValues::new_from_node(node); + let values = cascaded.get(); + + let color = resolve_color( + &values.flood_color().0, + values.flood_opacity().0, + values.color().0, + ); + + Ok(drop_shadow_primitives( + self.params.dx, + self.params.dy, + self.params.std_deviation, + color, + )) + } +} diff --git a/src/filters/flood.rs b/src/filters/flood.rs index 50838207..cd158f62 100644 --- a/src/filters/flood.rs +++ b/src/filters/flood.rs @@ -52,11 +52,11 @@ impl FilterEffect for FeFlood { &self, _acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { let cascaded = CascadedValues::new_from_node(node); let values = cascaded.get(); - Ok(ResolvedPrimitive { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::Flood(Flood { color: resolve_color( @@ -65,6 +65,6 @@ impl FilterEffect for FeFlood { values.color().0, ), }), - }) + }]) } } diff --git a/src/filters/gaussian_blur.rs b/src/filters/gaussian_blur.rs index d44e5ae0..e6ae3b68 100644 --- a/src/filters/gaussian_blur.rs +++ b/src/filters/gaussian_blur.rs @@ -271,16 +271,16 @@ impl FilterEffect for FeGaussianBlur { &self, _acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { let cascaded = CascadedValues::new_from_node(node); let values = cascaded.get(); let mut params = self.params.clone(); params.color_interpolation_filters = values.color_interpolation_filters(); - Ok(ResolvedPrimitive { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::GaussianBlur(params), - }) + }]) } } diff --git a/src/filters/image.rs b/src/filters/image.rs index a926c952..c3f19c8f 100644 --- a/src/filters/image.rs +++ b/src/filters/image.rs @@ -180,7 +180,7 @@ impl FilterEffect for FeImage { &self, acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { let cascaded = CascadedValues::new_from_node(node); let feimage_values = cascaded.get().clone(); @@ -199,13 +199,13 @@ impl FilterEffect for FeImage { } }; - Ok(ResolvedPrimitive { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::Image(Image { aspect: self.params.aspect, source, feimage_values: Box::new(feimage_values), }), - }) + }]) } } diff --git a/src/filters/lighting.rs b/src/filters/lighting.rs index e222de22..6a0b52ee 100644 --- a/src/filters/lighting.rs +++ b/src/filters/lighting.rs @@ -705,7 +705,7 @@ macro_rules! impl_lighting_filter { &self, _acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { let mut sources = node.children().rev().filter(|c| { c.is_element() && matches!( @@ -740,7 +740,7 @@ macro_rules! impl_lighting_filter { let cascaded = CascadedValues::new_from_node(node); let values = cascaded.get(); - Ok(ResolvedPrimitive { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::$params_name($params_name { params: self.params.clone(), @@ -754,7 +754,7 @@ macro_rules! impl_lighting_filter { color_interpolation_filters: values.color_interpolation_filters(), }, }), - }) + }]) } } }; @@ -1045,8 +1045,8 @@ mod tests { let node = document.lookup_internal_node("diffuse_distant").unwrap(); let lighting = borrow_element_as!(node, FeDiffuseLighting); - let ResolvedPrimitive { params, .. } = - lighting.resolve(&mut acquired_nodes, &node).unwrap(); + let resolved = lighting.resolve(&mut acquired_nodes, &node).unwrap(); + let ResolvedPrimitive { params, .. } = resolved.first().unwrap(); let diffuse_lighting = match params { PrimitiveParams::DiffuseLighting(l) => l, _ => unreachable!(), @@ -1061,8 +1061,8 @@ mod tests { let node = document.lookup_internal_node("specular_point").unwrap(); let lighting = borrow_element_as!(node, FeSpecularLighting); - let ResolvedPrimitive { params, .. } = - lighting.resolve(&mut acquired_nodes, &node).unwrap(); + let resolved = lighting.resolve(&mut acquired_nodes, &node).unwrap(); + let ResolvedPrimitive { params, .. } = resolved.first().unwrap(); let specular_lighting = match params { PrimitiveParams::SpecularLighting(l) => l, _ => unreachable!(), @@ -1078,8 +1078,8 @@ mod tests { let node = document.lookup_internal_node("diffuse_spot").unwrap(); let lighting = borrow_element_as!(node, FeDiffuseLighting); - let ResolvedPrimitive { params, .. } = - lighting.resolve(&mut acquired_nodes, &node).unwrap(); + let resolved = lighting.resolve(&mut acquired_nodes, &node).unwrap(); + let ResolvedPrimitive { params, .. } = resolved.first().unwrap(); let diffuse_lighting = match params { PrimitiveParams::DiffuseLighting(l) => l, _ => unreachable!(), diff --git a/src/filters/merge.rs b/src/filters/merge.rs index 56292406..f76f9dd0 100644 --- a/src/filters/merge.rs +++ b/src/filters/merge.rs @@ -145,13 +145,13 @@ impl FilterEffect for FeMerge { &self, _acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { - Ok(ResolvedPrimitive { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::Merge(Merge { merge_nodes: resolve_merge_nodes(node)?, }), - }) + }]) } } @@ -199,7 +199,8 @@ mod tests { let node = document.lookup_internal_node("merge").unwrap(); let merge = borrow_element_as!(node, FeMerge); - let ResolvedPrimitive { params, .. } = merge.resolve(&mut acquired_nodes, &node).unwrap(); + let resolved = merge.resolve(&mut acquired_nodes, &node).unwrap(); + let ResolvedPrimitive { params, .. } = resolved.first().unwrap(); let params = match params { PrimitiveParams::Merge(m) => m, _ => unreachable!(), diff --git a/src/filters/mod.rs b/src/filters/mod.rs index 0bd1cefa..e6f4724e 100644 --- a/src/filters/mod.rs +++ b/src/filters/mod.rs @@ -37,7 +37,7 @@ pub trait FilterEffect: SetAttributes + Draw { &self, acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError>; + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError>; } // Filter Effects do not need to draw themselves @@ -49,6 +49,7 @@ pub mod component_transfer; pub mod composite; pub mod convolve_matrix; pub mod displacement_map; +pub mod drop_shadow; pub mod flood; pub mod gaussian_blur; pub mod image; @@ -349,6 +350,10 @@ fn render_primitive( let bounds_builder = primitive.get_bounds(ctx); + // Note that feDropShadow is not handled here. When its FilterElement::resolve() is called, + // it returns a series of lower-level primitives (flood, blur, offset, etc.) that make up + // the drop-shadow effect. + match primitive.params { Blend(ref p) => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx), ColorMatrix(ref p) => p.render(bounds_builder, ctx, acquired_nodes, draw_ctx), diff --git a/src/filters/morphology.rs b/src/filters/morphology.rs index 519efca7..b69b01d4 100644 --- a/src/filters/morphology.rs +++ b/src/filters/morphology.rs @@ -181,11 +181,11 @@ impl FilterEffect for FeMorphology { &self, _acquired_nodes: &mut AcquiredNodes<'_>, _node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { - Ok(ResolvedPrimitive { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::Morphology(self.params.clone()), - }) + }]) } } diff --git a/src/filters/offset.rs b/src/filters/offset.rs index 27698b05..49a2be19 100644 --- a/src/filters/offset.rs +++ b/src/filters/offset.rs @@ -91,10 +91,10 @@ impl FilterEffect for FeOffset { &self, _acquired_nodes: &mut AcquiredNodes<'_>, _node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { - Ok(ResolvedPrimitive { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::Offset(self.params.clone()), - }) + }]) } } diff --git a/src/filters/tile.rs b/src/filters/tile.rs index e3bf940a..358b996b 100644 --- a/src/filters/tile.rs +++ b/src/filters/tile.rs @@ -100,10 +100,10 @@ impl FilterEffect for FeTile { &self, _acquired_nodes: &mut AcquiredNodes<'_>, _node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { - Ok(ResolvedPrimitive { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::Tile(self.params.clone()), - }) + }]) } } diff --git a/src/filters/turbulence.rs b/src/filters/turbulence.rs index 32035f5d..191c51ae 100644 --- a/src/filters/turbulence.rs +++ b/src/filters/turbulence.rs @@ -432,17 +432,17 @@ impl FilterEffect for FeTurbulence { &self, _acquired_nodes: &mut AcquiredNodes<'_>, node: &Node, - ) -> Result<ResolvedPrimitive, FilterResolveError> { + ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> { let cascaded = CascadedValues::new_from_node(node); let values = cascaded.get(); let mut params = self.params.clone(); params.color_interpolation_filters = values.color_interpolation_filters(); - Ok(ResolvedPrimitive { + Ok(vec![ResolvedPrimitive { primitive: self.base.clone(), params: PrimitiveParams::Turbulence(params), - }) + }]) } } diff --git a/tests/fixtures/reftests/svg2/bug743-fe-drop-shadow-ref.svg b/tests/fixtures/reftests/svg2/bug743-fe-drop-shadow-ref.svg new file mode 100644 index 00000000..034f8b37 --- /dev/null +++ b/tests/fixtures/reftests/svg2/bug743-fe-drop-shadow-ref.svg @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"> + <filter id="drop-shadow" filterUnits="userSpaceOnUse"> + <feGaussianBlur in="SourceAlpha" stdDeviation="5 10"/> + <feOffset dx="5" dy="10" result="offsetblur"/> + <feFlood flood-color="black" flood-opacity="0.5"/> + <feComposite in2="offsetblur" operator="in"/> + <feMerge> + <feMergeNode/> + <feMergeNode in="SourceGraphic"/> + </feMerge> + </filter> + + <rect x="0" y="0" width="100%" height="100%" fill="white"/> + + <rect x="50" y="50" width="50" height="50" fill="blue" stroke="magenta" stroke-width="6" filter="url(#drop-shadow)"/> +</svg> diff --git a/tests/fixtures/reftests/svg2/bug743-fe-drop-shadow.svg b/tests/fixtures/reftests/svg2/bug743-fe-drop-shadow.svg new file mode 100644 index 00000000..7054201f --- /dev/null +++ b/tests/fixtures/reftests/svg2/bug743-fe-drop-shadow.svg @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200"> + <filter id="drop-shadow" filterUnits="userSpaceOnUse"> + <feDropShadow dx="5" dy="10" stdDeviation="5 10" flood-color="black" flood-opacity="0.5"/> + </filter> + + <rect x="0" y="0" width="100%" height="100%" fill="white"/> + + <rect x="50" y="50" width="50" height="50" fill="blue" stroke="magenta" stroke-width="6" filter="url(#drop-shadow)"/> +</svg> diff --git a/tests/src/filters.rs b/tests/src/filters.rs index f756a781..9517e651 100644 --- a/tests/src/filters.rs +++ b/tests/src/filters.rs @@ -1,8 +1,8 @@ use cairo; use crate::reference_utils::{Compare, Evaluate, Reference}; -use crate::test_compare_render_output; use crate::utils::{load_svg, render_document, SurfaceSize}; +use crate::{test_compare_render_output, test_svg_reference}; #[test] fn invalid_filter_reference_cancels_filter_chain() { @@ -363,3 +363,9 @@ test_compare_render_output!( </svg> "##, ); + +test_svg_reference!( + bug_743_fe_drop_shadow, + "tests/fixtures/reftests/svg2/bug743-fe-drop-shadow.svg", + "tests/fixtures/reftests/svg2/bug743-fe-drop-shadow-ref.svg" +); |