1use std::cmp::min;
2use std::f64;
3
4use markup5ever::{expanded_name, local_name, ns};
5use nalgebra::{DMatrix, Dyn, VecStorage};
6
7use crate::document::AcquiredNodes;
8use crate::element::{set_attribute, ElementTrait};
9use crate::node::{CascadedValues, Node};
10use crate::parsers::{NumberOptionalNumber, ParseValue};
11use crate::properties::ColorInterpolationFilters;
12use crate::rect::IRect;
13use crate::session::Session;
14use crate::surface_utils::{
15 shared_surface::{BlurDirection, Horizontal, SharedImageSurface, Vertical},
16 EdgeMode,
17};
18use crate::xml::Attributes;
19
20use super::bounds::BoundsBuilder;
21use super::context::{FilterContext, FilterOutput};
22use super::{
23 FilterEffect, FilterError, FilterResolveError, Input, InputRequirements, Primitive,
24 PrimitiveParams, ResolvedPrimitive,
25};
26
27const MAXIMUM_KERNEL_SIZE: usize = 500;
31
32#[derive(Default)]
34pub struct FeGaussianBlur {
35 base: Primitive,
36 params: GaussianBlur,
37}
38
39#[derive(Clone)]
41pub struct GaussianBlur {
42 pub in1: Input,
43 pub std_deviation: NumberOptionalNumber<f64>,
44 pub edge_mode: EdgeMode,
45 pub color_interpolation_filters: ColorInterpolationFilters,
46}
47
48impl Default for GaussianBlur {
50 fn default() -> GaussianBlur {
51 GaussianBlur {
52 in1: Default::default(),
53 std_deviation: NumberOptionalNumber(0.0, 0.0),
54 edge_mode: EdgeMode::None,
57 color_interpolation_filters: Default::default(),
58 }
59 }
60}
61
62impl ElementTrait for FeGaussianBlur {
63 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
64 self.params.in1 = self.base.parse_one_input(attrs, session);
65
66 for (attr, value) in attrs.iter() {
67 match attr.expanded() {
68 expanded_name!("", "stdDeviation") => {
69 set_attribute(&mut self.params.std_deviation, attr.parse(value), session)
70 }
71 expanded_name!("", "edgeMode") => {
72 set_attribute(&mut self.params.edge_mode, attr.parse(value), session)
73 }
74
75 _ => (),
76 }
77 }
78 }
79}
80
81fn gaussian_kernel(std_deviation: f64) -> Vec<f64> {
83 assert!(std_deviation > 0.0);
84
85 let maximal_deviation = (MAXIMUM_KERNEL_SIZE / 2) as f64 / 3.0;
87
88 let radius = ((std_deviation.min(maximal_deviation) * 3.0) + 0.5) as usize;
90 let radius = min(radius, (MAXIMUM_KERNEL_SIZE - 1) / 2);
93 let diameter = radius * 2 + 1;
94
95 let mut kernel = Vec::with_capacity(diameter);
96
97 let gauss_point = |x: f64| (-x.powi(2) / (2.0 * std_deviation.powi(2))).exp();
98
99 for i in 0..diameter / 2 {
104 let base_x = (diameter / 2 + 1 - i) as f64 - 0.5;
105
106 let mut sum = 0.0;
107 for j in 1..=50 {
108 let r = base_x + 0.02 * f64::from(j);
109 sum += gauss_point(r);
110 }
111
112 kernel.push(sum / 50.0);
113 }
114
115 kernel.push(0.0);
117
118 for i in 0..diameter / 2 {
120 let x = kernel[diameter / 2 - 1 - i];
121 kernel.push(x);
122 }
123
124 let mut sum = 0.0;
127 for j in 0..=50 {
128 let r = -0.5 + 0.02 * f64::from(j);
129 sum += gauss_point(r);
130 }
131 kernel[diameter / 2] = sum / 51.0;
132
133 let sum = kernel.iter().sum::<f64>();
135 kernel.iter_mut().for_each(|x| *x /= sum);
136
137 kernel
138}
139
140fn box_blur_kernel_size(std_deviation: f64) -> usize {
142 let d = (std_deviation * 3.0 * (2.0 * f64::consts::PI).sqrt() / 4.0 + 0.5).floor();
143 let d = d.min(MAXIMUM_KERNEL_SIZE as f64);
144 d as usize
145}
146
147fn three_box_blurs<B: BlurDirection>(
151 surface: &SharedImageSurface,
152 bounds: IRect,
153 std_deviation: f64,
154) -> Result<SharedImageSurface, FilterError> {
155 let d = box_blur_kernel_size(std_deviation);
156 if d == 0 {
157 return Ok(surface.clone());
158 }
159
160 let surface = if d % 2 == 1 {
161 let mut surface = surface.clone();
163
164 for _ in 0..3 {
165 surface = surface.box_blur::<B>(bounds, d, d / 2)?;
166 }
167
168 surface
169 } else {
170 let surface = surface.box_blur::<B>(bounds, d, d / 2)?;
172 let surface = surface.box_blur::<B>(bounds, d, d / 2 - 1)?;
173
174 let d = d + 1;
175 surface.box_blur::<B>(bounds, d, d / 2)?
176 };
177
178 Ok(surface)
179}
180
181fn gaussian_blur(
185 input_surface: &SharedImageSurface,
186 bounds: IRect,
187 std_deviation: f64,
188 edge_mode: EdgeMode,
189 vertical: bool,
190) -> Result<SharedImageSurface, FilterError> {
191 let kernel = gaussian_kernel(std_deviation);
192 let (rows, cols) = if vertical {
193 (kernel.len(), 1)
194 } else {
195 (1, kernel.len())
196 };
197 let kernel = DMatrix::from_data(VecStorage::new(Dyn(rows), Dyn(cols), kernel));
198
199 Ok(input_surface.convolve(
200 bounds,
201 ((cols / 2) as i32, (rows / 2) as i32),
202 &kernel,
203 edge_mode,
204 )?)
205}
206
207impl GaussianBlur {
208 pub fn render(
209 &self,
210 bounds_builder: BoundsBuilder,
211 ctx: &FilterContext,
212 ) -> Result<FilterOutput, FilterError> {
213 let input_1 = ctx.get_input(&self.in1, self.color_interpolation_filters)?;
214 let bounds: IRect = bounds_builder
215 .add_input(&input_1)
216 .compute(ctx)
217 .clipped
218 .into();
219
220 let NumberOptionalNumber(std_x, std_y) = self.std_deviation;
221
222 if std_x <= 0.0 && std_y <= 0.0 {
226 return Ok(FilterOutput {
227 surface: input_1.surface().clone(),
228 bounds,
229 });
230 }
231
232 let (std_x, std_y) = ctx.paffine().transform_distance(std_x, std_y);
233
234 let std_x = std_x.abs();
236 let std_y = std_y.abs();
237
238 let horiz_result_surface = if std_x >= 2.0 {
244 three_box_blurs::<Horizontal>(input_1.surface(), bounds, std_x)?
246 } else if std_x != 0.0 {
247 gaussian_blur(input_1.surface(), bounds, std_x, self.edge_mode, false)?
248 } else {
249 input_1.surface().clone()
250 };
251
252 let output_surface = if std_y >= 2.0 {
254 three_box_blurs::<Vertical>(&horiz_result_surface, bounds, std_y)?
256 } else if std_y != 0.0 {
257 gaussian_blur(&horiz_result_surface, bounds, std_y, self.edge_mode, true)?
258 } else {
259 horiz_result_surface
260 };
261
262 Ok(FilterOutput {
263 surface: output_surface,
264 bounds,
265 })
266 }
267
268 pub fn get_input_requirements(&self) -> InputRequirements {
269 self.in1.get_requirements()
270 }
271}
272
273impl FilterEffect for FeGaussianBlur {
274 fn resolve(
275 &self,
276 _acquired_nodes: &mut AcquiredNodes<'_>,
277 node: &Node,
278 ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
279 let cascaded = CascadedValues::new_from_node(node);
280 let values = cascaded.get();
281
282 let mut params = self.params.clone();
283 params.color_interpolation_filters = values.color_interpolation_filters();
284
285 Ok(vec![ResolvedPrimitive {
286 primitive: self.base.clone(),
287 params: PrimitiveParams::GaussianBlur(params),
288 }])
289 }
290}