1 use std::cmp::{max, min};
2 
3 use cssparser::Parser;
4 use markup5ever::{expanded_name, local_name, namespace_url, ns};
5 
6 use crate::document::AcquiredNodes;
7 use crate::drawing_ctx::DrawingCtx;
8 use crate::element::{ElementResult, SetAttributes};
9 use crate::error::*;
10 use crate::node::Node;
11 use crate::parsers::{NonNegative, NumberOptionalNumber, Parse, ParseValue};
12 use crate::properties::ColorInterpolationFilters;
13 use crate::rect::IRect;
14 use crate::surface_utils::{
15     iterators::{PixelRectangle, Pixels},
16     shared_surface::ExclusiveImageSurface,
17     EdgeMode, ImageSurfaceDataExt, Pixel,
18 };
19 use crate::xml::Attributes;
20 
21 use super::bounds::BoundsBuilder;
22 use super::context::{FilterContext, FilterOutput};
23 use super::{
24     FilterEffect, FilterError, FilterResolveError, Input, Primitive, PrimitiveParams,
25     ResolvedPrimitive,
26 };
27 
28 /// Enumeration of the possible morphology operations.
29 #[derive(Clone)]
30 enum Operator {
31     Erode,
32     Dilate,
33 }
34 
35 enum_default!(Operator, Operator::Erode);
36 
37 /// The `feMorphology` filter primitive.
38 #[derive(Default)]
39 pub struct FeMorphology {
40     base: Primitive,
41     params: Morphology,
42 }
43 
44 /// Resolved `feMorphology` primitive for rendering.
45 #[derive(Clone, Default)]
46 pub struct Morphology {
47     in1: Input,
48     operator: Operator,
49     radius: (f64, f64),
50 }
51 
52 impl SetAttributes for FeMorphology {
set_attributes(&mut self, attrs: &Attributes) -> ElementResult53     fn set_attributes(&mut self, attrs: &Attributes) -> ElementResult {
54         self.params.in1 = self.base.parse_one_input(attrs)?;
55 
56         for (attr, value) in attrs.iter() {
57             match attr.expanded() {
58                 expanded_name!("", "operator") => self.params.operator = attr.parse(value)?,
59                 expanded_name!("", "radius") => {
60                     let NumberOptionalNumber(NonNegative(x), NonNegative(y)) = attr.parse(value)?;
61                     self.params.radius = (x, y);
62                 }
63                 _ => (),
64             }
65         }
66 
67         Ok(())
68     }
69 }
70 
71 impl Morphology {
render( &self, bounds_builder: BoundsBuilder, ctx: &FilterContext, acquired_nodes: &mut AcquiredNodes<'_>, draw_ctx: &mut DrawingCtx, ) -> Result<FilterOutput, FilterError>72     pub fn render(
73         &self,
74         bounds_builder: BoundsBuilder,
75         ctx: &FilterContext,
76         acquired_nodes: &mut AcquiredNodes<'_>,
77         draw_ctx: &mut DrawingCtx,
78     ) -> Result<FilterOutput, FilterError> {
79         // Although https://www.w3.org/TR/filter-effects/#propdef-color-interpolation-filters does not mention
80         // feMorphology as being one of the primitives that does *not* use that property,
81         // the SVG1.1 test for filters-morph-01-f.svg fails if we pass the value from the ComputedValues here (that
82         // document does not specify the color-interpolation-filters property, so it defaults to linearRGB).
83         // So, we pass Auto, which will get resolved to SRGB, and that makes that test pass.
84         //
85         // I suppose erosion/dilation doesn't care about the color space of the source image?
86 
87         let input_1 = ctx.get_input(
88             acquired_nodes,
89             draw_ctx,
90             &self.in1,
91             ColorInterpolationFilters::Auto,
92         )?;
93         let bounds: IRect = bounds_builder
94             .add_input(&input_1)
95             .compute(ctx)
96             .clipped
97             .into();
98 
99         let (rx, ry) = self.radius;
100         let (rx, ry) = ctx.paffine().transform_distance(rx, ry);
101 
102         // The radii can become negative here due to the transform.
103         // Additionally The radii being excessively large causes cpu hangups
104         let (rx, ry) = (rx.abs().min(10.0), ry.abs().min(10.0));
105 
106         let mut surface = ExclusiveImageSurface::new(
107             ctx.source_graphic().width(),
108             ctx.source_graphic().height(),
109             input_1.surface().surface_type(),
110         )?;
111 
112         surface.modify(&mut |data, stride| {
113             for (x, y, _pixel) in Pixels::within(input_1.surface(), bounds) {
114                 // Compute the kernel rectangle bounds.
115                 let kernel_bounds = IRect::new(
116                     (f64::from(x) - rx).floor() as i32,
117                     (f64::from(y) - ry).floor() as i32,
118                     (f64::from(x) + rx).ceil() as i32 + 1,
119                     (f64::from(y) + ry).ceil() as i32 + 1,
120                 );
121 
122                 // Compute the new pixel values.
123                 let initial = match self.operator {
124                     Operator::Erode => u8::max_value(),
125                     Operator::Dilate => u8::min_value(),
126                 };
127 
128                 let mut output_pixel = Pixel {
129                     r: initial,
130                     g: initial,
131                     b: initial,
132                     a: initial,
133                 };
134 
135                 for (_x, _y, pixel) in
136                     PixelRectangle::within(input_1.surface(), bounds, kernel_bounds, EdgeMode::None)
137                 {
138                     let op = match self.operator {
139                         Operator::Erode => min,
140                         Operator::Dilate => max,
141                     };
142 
143                     output_pixel.r = op(output_pixel.r, pixel.r);
144                     output_pixel.g = op(output_pixel.g, pixel.g);
145                     output_pixel.b = op(output_pixel.b, pixel.b);
146                     output_pixel.a = op(output_pixel.a, pixel.a);
147                 }
148 
149                 data.set_pixel(stride, output_pixel, x, y);
150             }
151         });
152 
153         Ok(FilterOutput {
154             surface: surface.share()?,
155             bounds,
156         })
157     }
158 }
159 
160 impl FilterEffect for FeMorphology {
resolve( &self, _acquired_nodes: &mut AcquiredNodes<'_>, _node: &Node, ) -> Result<ResolvedPrimitive, FilterResolveError>161     fn resolve(
162         &self,
163         _acquired_nodes: &mut AcquiredNodes<'_>,
164         _node: &Node,
165     ) -> Result<ResolvedPrimitive, FilterResolveError> {
166         Ok(ResolvedPrimitive {
167             primitive: self.base.clone(),
168             params: PrimitiveParams::Morphology(self.params.clone()),
169         })
170     }
171 }
172 
173 impl Parse for Operator {
parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>>174     fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
175         Ok(parse_identifiers!(
176             parser,
177             "erode" => Operator::Erode,
178             "dilate" => Operator::Dilate,
179         )?)
180     }
181 }
182