1
use cssparser::Parser;
2
use markup5ever::{expanded_name, local_name, namespace_url, ns};
3
use nalgebra::{DMatrix, Dyn, VecStorage};
4

            
5
use crate::bench_only::{
6
    EdgeMode, ExclusiveImageSurface, ImageSurfaceDataExt, Pixel, PixelRectangle, Pixels,
7
};
8
use crate::document::AcquiredNodes;
9
use crate::drawing_ctx::DrawingCtx;
10
use crate::element::{set_attribute, ElementTrait};
11
use crate::error::*;
12
use crate::node::{CascadedValues, Node};
13
use crate::parse_identifiers;
14
use crate::parsers::{NumberList, NumberOptionalNumber, Parse, ParseValue};
15
use crate::properties::ColorInterpolationFilters;
16
use crate::rect::IRect;
17
use crate::rsvg_log;
18
use crate::session::Session;
19
use crate::util::clamp;
20
use crate::xml::Attributes;
21

            
22
use super::bounds::BoundsBuilder;
23
use super::context::{FilterContext, FilterOutput};
24
use super::{
25
    FilterEffect, FilterError, FilterResolveError, Input, Primitive, PrimitiveParams,
26
    ResolvedPrimitive,
27
};
28

            
29
/// The `feConvolveMatrix` filter primitive.
30
31
#[derive(Default)]
31
pub struct FeConvolveMatrix {
32
31
    base: Primitive,
33
31
    params: ConvolveMatrix,
34
}
35

            
36
/// Resolved `feConvolveMatrix` primitive for rendering.
37
64
#[derive(Clone)]
38
pub struct ConvolveMatrix {
39
32
    in1: Input,
40
32
    order: NumberOptionalNumber<u32>,
41
32
    kernel_matrix: NumberList<0, 400>, // #691: Limit list to 400 (20x20) to mitigate malicious SVGs
42
32
    divisor: f64,
43
32
    bias: f64,
44
32
    target_x: Option<u32>,
45
32
    target_y: Option<u32>,
46
32
    edge_mode: EdgeMode,
47
32
    kernel_unit_length: Option<(f64, f64)>,
48
32
    preserve_alpha: bool,
49
32
    color_interpolation_filters: ColorInterpolationFilters,
50
}
51

            
52
impl Default for ConvolveMatrix {
53
    /// Constructs a new `ConvolveMatrix` with empty properties.
54
    #[inline]
55
31
    fn default() -> ConvolveMatrix {
56
31
        ConvolveMatrix {
57
31
            in1: Default::default(),
58
31
            order: NumberOptionalNumber(3, 3),
59
31
            kernel_matrix: NumberList(Vec::new()),
60
            divisor: 0.0,
61
            bias: 0.0,
62
31
            target_x: None,
63
31
            target_y: None,
64
            // Note that per the spec, `edgeMode` has a different initial value
65
            // in feConvolveMatrix than feGaussianBlur.
66
31
            edge_mode: EdgeMode::Duplicate,
67
31
            kernel_unit_length: None,
68
            preserve_alpha: false,
69
31
            color_interpolation_filters: Default::default(),
70
        }
71
31
    }
72
}
73

            
74
impl ElementTrait for FeConvolveMatrix {
75
31
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
76
31
        self.params.in1 = self.base.parse_one_input(attrs, session);
77

            
78
133
        for (attr, value) in attrs.iter() {
79
102
            match attr.expanded() {
80
                expanded_name!("", "order") => {
81
14
                    set_attribute(&mut self.params.order, attr.parse(value), session)
82
                }
83
                expanded_name!("", "kernelMatrix") => {
84
30
                    set_attribute(&mut self.params.kernel_matrix, attr.parse(value), session)
85
                }
86
                expanded_name!("", "divisor") => {
87
3
                    set_attribute(&mut self.params.divisor, attr.parse(value), session)
88
                }
89
                expanded_name!("", "bias") => {
90
2
                    set_attribute(&mut self.params.bias, attr.parse(value), session)
91
                }
92
                expanded_name!("", "targetX") => {
93
3
                    set_attribute(&mut self.params.target_x, attr.parse(value), session)
94
                }
95
                expanded_name!("", "targetY") => {
96
3
                    set_attribute(&mut self.params.target_y, attr.parse(value), session)
97
                }
98
                expanded_name!("", "edgeMode") => {
99
15
                    set_attribute(&mut self.params.edge_mode, attr.parse(value), session)
100
                }
101
                expanded_name!("", "kernelUnitLength") => {
102
3
                    let v: Result<NumberOptionalNumber<f64>, _> = attr.parse(value);
103
3
                    match v {
104
3
                        Ok(NumberOptionalNumber(x, y)) => {
105
3
                            self.params.kernel_unit_length = Some((x, y));
106
3
                        }
107

            
108
                        Err(e) => {
109
                            rsvg_log!(session, "ignoring attribute with invalid value: {}", e);
110
                        }
111
                    }
112
                }
113
                expanded_name!("", "preserveAlpha") => {
114
14
                    set_attribute(&mut self.params.preserve_alpha, attr.parse(value), session);
115
                }
116

            
117
                _ => (),
118
            }
119
102
        }
120
31
    }
121
}
122

            
123
impl ConvolveMatrix {
124
32
    pub fn render(
125
        &self,
126
        bounds_builder: BoundsBuilder,
127
        ctx: &FilterContext,
128
        acquired_nodes: &mut AcquiredNodes<'_>,
129
        draw_ctx: &mut DrawingCtx,
130
    ) -> Result<FilterOutput, FilterError> {
131
        #![allow(clippy::many_single_char_names)]
132

            
133
64
        let input_1 = ctx.get_input(
134
            acquired_nodes,
135
            draw_ctx,
136
32
            &self.in1,
137
32
            self.color_interpolation_filters,
138
        )?;
139
32
        let mut bounds: IRect = bounds_builder
140
            .add_input(&input_1)
141
            .compute(ctx)
142
            .clipped
143
            .into();
144
32
        let original_bounds = bounds;
145

            
146
32
        let target_x = match self.target_x {
147
3
            Some(x) if x >= self.order.0 => {
148
                return Err(FilterError::InvalidParameter(
149
                    "targetX must be less than orderX".to_string(),
150
                ))
151
            }
152
3
            Some(x) => x,
153
29
            None => self.order.0 / 2,
154
        };
155

            
156
32
        let target_y = match self.target_y {
157
3
            Some(y) if y >= self.order.1 => {
158
                return Err(FilterError::InvalidParameter(
159
                    "targetY must be less than orderY".to_string(),
160
                ))
161
            }
162
3
            Some(y) => y,
163
29
            None => self.order.1 / 2,
164
        };
165

            
166
37
        let mut input_surface = if self.preserve_alpha {
167
            // preserve_alpha means we need to premultiply and unpremultiply the values.
168
5
            input_1.surface().unpremultiply(bounds)?
169
        } else {
170
27
            input_1.surface().clone()
171
        };
172

            
173
32
        let scale = self
174
            .kernel_unit_length
175
3
            .and_then(|(x, y)| {
176
3
                if x <= 0.0 || y <= 0.0 {
177
                    None
178
                } else {
179
3
                    Some((x, y))
180
                }
181
3
            })
182
35
            .map(|(dx, dy)| ctx.paffine().transform_distance(dx, dy));
183

            
184
35
        if let Some((ox, oy)) = scale {
185
            // Scale the input surface to match kernel_unit_length.
186
3
            let (new_surface, new_bounds) = input_surface.scale(bounds, 1.0 / ox, 1.0 / oy)?;
187

            
188
3
            input_surface = new_surface;
189
3
            bounds = new_bounds;
190
3
        }
191

            
192
32
        let cols = self.order.0 as usize;
193
32
        let rows = self.order.1 as usize;
194
32
        let number_of_elements = cols * rows;
195
32
        let numbers = self.kernel_matrix.0.clone();
196

            
197
32
        if numbers.len() != number_of_elements && numbers.len() != 400 {
198
            // "If the result of orderX * orderY is not equal to the the number of entries
199
            // in the value list, the filter primitive acts as a pass through filter."
200
            //
201
            // https://drafts.fxtf.org/filter-effects/#element-attrdef-feconvolvematrix-kernelmatrix
202
            rsvg_log!(
203
                draw_ctx.session(),
204
                "feConvolveMatrix got {} elements when it expected {}; ignoring it",
205
                numbers.len(),
206
                number_of_elements
207
            );
208
            return Ok(FilterOutput {
209
                surface: input_1.surface().clone(),
210
                bounds: original_bounds,
211
            });
212
        }
213

            
214
32
        let matrix = DMatrix::from_data(VecStorage::new(Dyn(rows), Dyn(cols), numbers));
215

            
216
32
        let divisor = if self.divisor != 0.0 {
217
1
            self.divisor
218
        } else {
219
31
            let d = matrix.iter().sum();
220

            
221
31
            if d != 0.0 {
222
24
                d
223
            } else {
224
7
                1.0
225
            }
226
        };
227

            
228
32
        let mut surface = ExclusiveImageSurface::new(
229
32
            input_surface.width(),
230
32
            input_surface.height(),
231
32
            input_1.surface().surface_type(),
232
        )?;
233

            
234
64
        surface.modify(&mut |data, stride| {
235
187979
            for (x, y, pixel) in Pixels::within(&input_surface, bounds) {
236
                // Compute the convolution rectangle bounds.
237
187947
                let kernel_bounds = IRect::new(
238
187947
                    x as i32 - target_x as i32,
239
187947
                    y as i32 - target_y as i32,
240
187947
                    x as i32 - target_x as i32 + self.order.0 as i32,
241
187947
                    y as i32 - target_y as i32 + self.order.1 as i32,
242
                );
243

            
244
                // Do the convolution.
245
187947
                let mut r = 0.0;
246
187947
                let mut g = 0.0;
247
187947
                let mut b = 0.0;
248
187947
                let mut a = 0.0;
249

            
250
1888759
                for (x, y, pixel) in
251
1888759
                    PixelRectangle::within(&input_surface, bounds, kernel_bounds, self.edge_mode)
252
                {
253
1700812
                    let kernel_x = (kernel_bounds.x1 - x - 1) as usize;
254
1700812
                    let kernel_y = (kernel_bounds.y1 - y - 1) as usize;
255

            
256
1700812
                    r += f64::from(pixel.r) / 255.0 * matrix[(kernel_y, kernel_x)];
257
1700812
                    g += f64::from(pixel.g) / 255.0 * matrix[(kernel_y, kernel_x)];
258
1700812
                    b += f64::from(pixel.b) / 255.0 * matrix[(kernel_y, kernel_x)];
259

            
260
1700812
                    if !self.preserve_alpha {
261
1208474
                        a += f64::from(pixel.a) / 255.0 * matrix[(kernel_y, kernel_x)];
262
                    }
263
                }
264

            
265
                // If preserve_alpha is true, set a to the source alpha value.
266
187947
                if self.preserve_alpha {
267
49750
                    a = f64::from(pixel.a) / 255.0;
268
                } else {
269
138197
                    a = a / divisor + self.bias;
270
                }
271

            
272
187947
                let clamped_a = clamp(a, 0.0, 1.0);
273

            
274
751342
                let compute = |x| {
275
563395
                    let x = x / divisor + self.bias * a;
276

            
277
563395
                    let x = if self.preserve_alpha {
278
                        // Premultiply the output value.
279
149046
                        clamp(x, 0.0, 1.0) * clamped_a
280
                    } else {
281
414349
                        clamp(x, 0.0, clamped_a)
282
                    };
283

            
284
563395
                    ((x * 255.0) + 0.5) as u8
285
563395
                };
286

            
287
187947
                let output_pixel = Pixel {
288
187947
                    r: compute(r),
289
187947
                    g: compute(g),
290
187947
                    b: compute(b),
291
187947
                    a: ((clamped_a * 255.0) + 0.5) as u8,
292
                };
293

            
294
187947
                data.set_pixel(stride, output_pixel, x, y);
295
            }
296
32
        });
297

            
298
32
        let mut surface = surface.share()?;
299

            
300
35
        if let Some((ox, oy)) = scale {
301
            // Scale the output surface back.
302
3
            surface = surface.scale_to(
303
3
                ctx.source_graphic().width(),
304
3
                ctx.source_graphic().height(),
305
                original_bounds,
306
                ox,
307
                oy,
308
            )?;
309

            
310
3
            bounds = original_bounds;
311
        }
312

            
313
32
        Ok(FilterOutput { surface, bounds })
314
32
    }
315
}
316

            
317
impl FilterEffect for FeConvolveMatrix {
318
32
    fn resolve(
319
        &self,
320
        _acquired_nodes: &mut AcquiredNodes<'_>,
321
        node: &Node,
322
    ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
323
32
        let cascaded = CascadedValues::new_from_node(node);
324
32
        let values = cascaded.get();
325

            
326
32
        let mut params = self.params.clone();
327
32
        params.color_interpolation_filters = values.color_interpolation_filters();
328

            
329
32
        Ok(vec![ResolvedPrimitive {
330
32
            primitive: self.base.clone(),
331
32
            params: PrimitiveParams::ConvolveMatrix(params),
332
        }])
333
32
    }
334
}
335

            
336
// Used for the preserveAlpha attribute
337
impl Parse for bool {
338
14
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
339
28
        Ok(parse_identifiers!(
340
            parser,
341
            "false" => false,
342
            "true" => true,
343
        )?)
344
14
    }
345
}