diff options
author | Federico Mena Quintero <federico@gnome.org> | 2023-02-09 14:31:43 -0600 |
---|---|---|
committer | Federico Mena Quintero <federico@gnome.org> | 2023-02-09 19:34:28 -0600 |
commit | 0847de0a610e147ae5ff4aecad6270d42380bb0f (patch) | |
tree | 3209c4a59b30502d9564d223c54d31d68a709e38 | |
parent | 3a309538c35701be765a814dd4f9dbd27103cac0 (diff) | |
download | librsvg-0847de0a610e147ae5ff4aecad6270d42380bb0f.tar.gz |
(#743): implement the feDropShadow element
Fixes https://gitlab.gnome.org/GNOME/librsvg/-/issues/743
Part-of: <https://gitlab.gnome.org/GNOME/librsvg/-/merge_requests/793>
-rw-r--r-- | Makefile.am | 1 | ||||
-rw-r--r-- | src/element.rs | 9 | ||||
-rw-r--r-- | src/filters/drop_shadow.rs | 88 | ||||
-rw-r--r-- | src/filters/mod.rs | 5 | ||||
-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 |
7 files changed, 136 insertions, 2 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/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/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/mod.rs b/src/filters/mod.rs index 8b0f37d5..e6f4724e 100644 --- a/src/filters/mod.rs +++ b/src/filters/mod.rs @@ -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/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" +); |