summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarge Bot <marge-bot@gnome.org>2023-02-10 02:16:17 +0000
committerMarge Bot <marge-bot@gnome.org>2023-02-10 02:16:17 +0000
commit8742c720018f80ad808a2133a6a8c41ca91526cf (patch)
tree2b54a3c5344e086fba8b9eaaad86514492b106a4
parent91c059331352775e879f3dea13c68b52965ae901 (diff)
parent0c40d1131b672b5f6eab93ae7be26c494ebc884f (diff)
downloadlibrsvg-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.am1
-rw-r--r--devel-docs/features.rst15
-rw-r--r--src/element.rs9
-rw-r--r--src/filter.rs52
-rw-r--r--src/filter_func.rs125
-rw-r--r--src/filters/blend.rs6
-rw-r--r--src/filters/color_matrix.rs6
-rw-r--r--src/filters/component_transfer.rs6
-rw-r--r--src/filters/composite.rs6
-rw-r--r--src/filters/convolve_matrix.rs6
-rw-r--r--src/filters/displacement_map.rs6
-rw-r--r--src/filters/drop_shadow.rs88
-rw-r--r--src/filters/flood.rs6
-rw-r--r--src/filters/gaussian_blur.rs6
-rw-r--r--src/filters/image.rs6
-rw-r--r--src/filters/lighting.rs18
-rw-r--r--src/filters/merge.rs9
-rw-r--r--src/filters/mod.rs7
-rw-r--r--src/filters/morphology.rs6
-rw-r--r--src/filters/offset.rs6
-rw-r--r--src/filters/tile.rs6
-rw-r--r--src/filters/turbulence.rs6
-rw-r--r--tests/fixtures/reftests/svg2/bug743-fe-drop-shadow-ref.svg17
-rw-r--r--tests/fixtures/reftests/svg2/bug743-fe-drop-shadow.svg10
-rw-r--r--tests/src/filters.rs8
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(&params))
- })
- .collect::<Result<Vec<UserSpacePrimitive>, FilterResolveError>>()?;
+ );
+ return Err(e);
+ }
+ };
+
+ for p in primitives {
+ user_space_primitives.push(p.into_user_space(&params));
+ }
+ }
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"
+);