1use std::cmp::{max, min};
2
3use cssparser::Parser;
4use markup5ever::{expanded_name, local_name, ns};
5
6use crate::document::AcquiredNodes;
7use crate::element::{set_attribute, ElementTrait};
8use crate::error::*;
9use crate::node::Node;
10use crate::parse_identifiers;
11use crate::parsers::{NumberOptionalNumber, Parse, ParseValue};
12use crate::properties::ColorInterpolationFilters;
13use crate::rect::IRect;
14use crate::session::Session;
15use crate::surface_utils::{
16 iterators::{PixelRectangle, Pixels},
17 shared_surface::ExclusiveImageSurface,
18 EdgeMode, ImageSurfaceDataExt, Pixel,
19};
20use crate::xml::Attributes;
21
22use super::bounds::BoundsBuilder;
23use super::context::{FilterContext, FilterOutput};
24use super::{
25 FilterEffect, FilterError, FilterResolveError, Input, InputRequirements, Primitive,
26 PrimitiveParams, ResolvedPrimitive,
27};
28
29#[derive(Default, Clone)]
31enum Operator {
32 #[default]
33 Erode,
34 Dilate,
35}
36
37#[derive(Default)]
39pub struct FeMorphology {
40 base: Primitive,
41 params: Morphology,
42}
43
44#[derive(Clone)]
46pub struct Morphology {
47 in1: Input,
48 operator: Operator,
49 radius: NumberOptionalNumber<f64>,
50}
51
52impl Default for Morphology {
54 fn default() -> Morphology {
55 Morphology {
56 in1: Default::default(),
57 operator: Default::default(),
58 radius: NumberOptionalNumber(0.0, 0.0),
59 }
60 }
61}
62
63impl ElementTrait for FeMorphology {
64 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
65 self.params.in1 = self.base.parse_one_input(attrs, session);
66
67 for (attr, value) in attrs.iter() {
68 match attr.expanded() {
69 expanded_name!("", "operator") => {
70 set_attribute(&mut self.params.operator, attr.parse(value), session);
71 }
72 expanded_name!("", "radius") => {
73 set_attribute(&mut self.params.radius, attr.parse(value), session);
74 }
75 _ => (),
76 }
77 }
78 }
79}
80
81impl Morphology {
82 pub fn render(
83 &self,
84 bounds_builder: BoundsBuilder,
85 ctx: &FilterContext,
86 ) -> Result<FilterOutput, FilterError> {
87 let input_1 = ctx.get_input(&self.in1, ColorInterpolationFilters::Auto)?;
96 let bounds: IRect = bounds_builder
97 .add_input(&input_1)
98 .compute(ctx)
99 .clipped
100 .into();
101
102 let NumberOptionalNumber(rx, ry) = self.radius;
103
104 if rx <= 0.0 && ry <= 0.0 {
105 return Ok(FilterOutput {
106 surface: input_1.surface().clone(),
107 bounds,
108 });
109 }
110
111 let (rx, ry) = ctx.paffine().transform_distance(rx, ry);
112
113 let (rx, ry) = (rx.abs().min(10.0), ry.abs().min(10.0));
116
117 let mut surface = ExclusiveImageSurface::new(
118 ctx.source_graphic().width(),
119 ctx.source_graphic().height(),
120 input_1.surface().surface_type(),
121 )?;
122
123 surface.modify(&mut |data, stride| {
124 for (x, y, _pixel) in Pixels::within(input_1.surface(), bounds) {
125 let kernel_bounds = IRect::new(
127 (f64::from(x) - rx).floor() as i32,
128 (f64::from(y) - ry).floor() as i32,
129 (f64::from(x) + rx).ceil() as i32 + 1,
130 (f64::from(y) + ry).ceil() as i32 + 1,
131 );
132
133 let initial = match self.operator {
135 Operator::Erode => u8::MAX,
136 Operator::Dilate => u8::MIN,
137 };
138
139 let mut output_pixel = Pixel {
140 r: initial,
141 g: initial,
142 b: initial,
143 a: initial,
144 };
145
146 for (_x, _y, pixel) in
147 PixelRectangle::within(input_1.surface(), bounds, kernel_bounds, EdgeMode::None)
148 {
149 let op = match self.operator {
150 Operator::Erode => min,
151 Operator::Dilate => max,
152 };
153
154 output_pixel.r = op(output_pixel.r, pixel.r);
155 output_pixel.g = op(output_pixel.g, pixel.g);
156 output_pixel.b = op(output_pixel.b, pixel.b);
157 output_pixel.a = op(output_pixel.a, pixel.a);
158 }
159
160 data.set_pixel(stride, output_pixel, x, y);
161 }
162 });
163
164 Ok(FilterOutput {
165 surface: surface.share()?,
166 bounds,
167 })
168 }
169
170 pub fn get_input_requirements(&self) -> InputRequirements {
171 self.in1.get_requirements()
172 }
173}
174
175impl FilterEffect for FeMorphology {
176 fn resolve(
177 &self,
178 _acquired_nodes: &mut AcquiredNodes<'_>,
179 _node: &Node,
180 ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
181 Ok(vec![ResolvedPrimitive {
182 primitive: self.base.clone(),
183 params: PrimitiveParams::Morphology(self.params.clone()),
184 }])
185 }
186}
187
188impl Parse for Operator {
189 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
190 Ok(parse_identifiers!(
191 parser,
192 "erode" => Operator::Erode,
193 "dilate" => Operator::Dilate,
194 )?)
195 }
196}