1
use cssparser::Parser;
2
use markup5ever::{expanded_name, local_name, namespace_url, ns, QualName};
3
use nalgebra::{Matrix3, Matrix4x5, Matrix5, Vector5};
4

            
5
use crate::document::AcquiredNodes;
6
use crate::drawing_ctx::DrawingCtx;
7
use crate::element::{set_attribute, ElementTrait};
8
use crate::error::*;
9
use crate::node::{CascadedValues, Node};
10
use crate::parse_identifiers;
11
use crate::parsers::{NumberList, Parse, ParseValue};
12
use crate::properties::ColorInterpolationFilters;
13
use crate::rect::IRect;
14
use crate::rsvg_log;
15
use crate::session::Session;
16
use crate::surface_utils::{
17
    iterators::Pixels, shared_surface::ExclusiveImageSurface, ImageSurfaceDataExt, Pixel,
18
};
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
/// Color matrix operation types.
30
28
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
31
enum OperationType {
32
    #[default]
33
14
    Matrix,
34
    Saturate,
35
    HueRotate,
36
    LuminanceToAlpha,
37
}
38

            
39
/// The `feColorMatrix` filter primitive.
40
14
#[derive(Default)]
41
pub struct FeColorMatrix {
42
14
    base: Primitive,
43
14
    params: ColorMatrix,
44
}
45

            
46
/// Resolved `feColorMatrix` primitive for rendering.
47
28
#[derive(Clone)]
48
pub struct ColorMatrix {
49
14
    pub in1: Input,
50
14
    pub matrix: Matrix5<f64>,
51
14
    pub color_interpolation_filters: ColorInterpolationFilters,
52
}
53

            
54
impl Default for ColorMatrix {
55
18
    fn default() -> ColorMatrix {
56
18
        ColorMatrix {
57
18
            in1: Default::default(),
58
18
            color_interpolation_filters: Default::default(),
59

            
60
            // nalgebra's Default for Matrix5 is all zeroes, so we actually need this :(
61
18
            matrix: Matrix5::identity(),
62
        }
63
18
    }
64
}
65

            
66
impl ElementTrait for FeColorMatrix {
67
14
    fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
68
14
        self.params.in1 = self.base.parse_one_input(attrs, session);
69

            
70
        // First, determine the operation type.
71
14
        let mut operation_type = Default::default();
72
28
        for (attr, value) in attrs
73
            .iter()
74
35
            .filter(|(attr, _)| attr.expanded() == expanded_name!("", "type"))
75
        {
76
14
            set_attribute(&mut operation_type, attr.parse(value), session);
77
14
        }
78

            
79
        // Now read the matrix correspondingly.
80
        //
81
        // Here we cannot assume that ColorMatrix::default() has provided the correct
82
        // initial value for the matrix itself, since the initial value for the matrix
83
        // (i.e. the value to which it should fall back if the `values` attribute is in
84
        // error) depends on the operation_type.
85
        //
86
        // So, for each operation_type, first initialize the proper default matrix, then
87
        // try to parse the value.
88

            
89
        use OperationType::*;
90

            
91
28
        self.params.matrix = match operation_type {
92
5
            Matrix => ColorMatrix::default_matrix(),
93
3
            Saturate => ColorMatrix::saturate_matrix(1.0),
94
5
            HueRotate => ColorMatrix::hue_rotate_matrix(0.0),
95
1
            LuminanceToAlpha => ColorMatrix::luminance_to_alpha_matrix(),
96
        };
97

            
98
27
        for (attr, value) in attrs
99
            .iter()
100
35
            .filter(|(attr, _)| attr.expanded() == expanded_name!("", "values"))
101
        {
102
13
            match operation_type {
103
5
                Matrix => parse_matrix(&mut self.params.matrix, attr, value, session),
104
3
                Saturate => parse_saturate_matrix(&mut self.params.matrix, attr, value, session),
105
5
                HueRotate => parse_hue_rotate_matrix(&mut self.params.matrix, attr, value, session),
106
                LuminanceToAlpha => {
107
                    parse_luminance_to_alpha_matrix(&mut self.params.matrix, attr, value, session)
108
                }
109
            }
110
        }
111
14
    }
112
}
113

            
114
5
fn parse_matrix(dest: &mut Matrix5<f64>, attr: QualName, value: &str, session: &Session) {
115
5
    let parsed: Result<NumberList<20, 20>, _> = attr.parse(value);
116

            
117
5
    match parsed {
118
5
        Ok(NumberList(v)) => {
119
5
            let matrix = Matrix4x5::from_row_slice(&v);
120
5
            let mut matrix = matrix.fixed_resize(0.0);
121
5
            matrix[(4, 4)] = 1.0;
122
5
            *dest = matrix;
123
5
        }
124

            
125
        Err(e) => {
126
            rsvg_log!(session, "element feColorMatrix with type=\"matrix\", expected a values attribute with 20 numbers: {}", e);
127
        }
128
    }
129
5
}
130

            
131
3
fn parse_saturate_matrix(dest: &mut Matrix5<f64>, attr: QualName, value: &str, session: &Session) {
132
3
    let parsed: Result<f64, _> = attr.parse(value);
133

            
134
3
    match parsed {
135
3
        Ok(s) => {
136
3
            *dest = ColorMatrix::saturate_matrix(s);
137
3
        }
138

            
139
        Err(e) => {
140
            rsvg_log!(session, "element feColorMatrix with type=\"saturate\", expected a values attribute with 1 number: {}", e);
141
        }
142
    }
143
3
}
144

            
145
5
fn parse_hue_rotate_matrix(
146
    dest: &mut Matrix5<f64>,
147
    attr: QualName,
148
    value: &str,
149
    session: &Session,
150
) {
151
5
    let parsed: Result<f64, _> = attr.parse(value);
152

            
153
5
    match parsed {
154
5
        Ok(degrees) => {
155
5
            *dest = ColorMatrix::hue_rotate_matrix(degrees.to_radians());
156
5
        }
157

            
158
        Err(e) => {
159
            rsvg_log!(session, "element feColorMatrix with type=\"hueRotate\", expected a values attribute with 1 number: {}", e);
160
        }
161
    }
162
5
}
163

            
164
fn parse_luminance_to_alpha_matrix(
165
    _dest: &mut Matrix5<f64>,
166
    _attr: QualName,
167
    _value: &str,
168
    session: &Session,
169
) {
170
    // There's nothing to parse, since our caller already supplied the default value,
171
    // and type="luminanceToAlpha" does not takes a `values` attribute.  So, just warn
172
    // that the value is being ignored.
173

            
174
    rsvg_log!(
175
        session,
176
        "ignoring \"values\" attribute for feColorMatrix with type=\"luminanceToAlpha\""
177
    );
178
}
179

            
180
impl ColorMatrix {
181
16
    pub fn render(
182
        &self,
183
        bounds_builder: BoundsBuilder,
184
        ctx: &FilterContext,
185
        acquired_nodes: &mut AcquiredNodes<'_>,
186
        draw_ctx: &mut DrawingCtx,
187
    ) -> Result<FilterOutput, FilterError> {
188
32
        let input_1 = ctx.get_input(
189
            acquired_nodes,
190
            draw_ctx,
191
            &self.in1,
192
16
            self.color_interpolation_filters,
193
        )?;
194
16
        let bounds: IRect = bounds_builder
195
            .add_input(&input_1)
196
            .compute(ctx)
197
            .clipped
198
            .into();
199

            
200
16
        let mut surface = ExclusiveImageSurface::new(
201
16
            ctx.source_graphic().width(),
202
16
            ctx.source_graphic().height(),
203
16
            input_1.surface().surface_type(),
204
        )?;
205

            
206
32
        surface.modify(&mut |data, stride| {
207
953529
            for (x, y, pixel) in Pixels::within(input_1.surface(), bounds) {
208
953513
                let alpha = f64::from(pixel.a) / 255f64;
209

            
210
953513
                let pixel_vec = if alpha == 0.0 {
211
572087
                    Vector5::new(0.0, 0.0, 0.0, 0.0, 1.0)
212
                } else {
213
381426
                    Vector5::new(
214
381426
                        f64::from(pixel.r) / 255f64 / alpha,
215
381426
                        f64::from(pixel.g) / 255f64 / alpha,
216
381426
                        f64::from(pixel.b) / 255f64 / alpha,
217
                        alpha,
218
                        1.0,
219
                    )
220
                };
221
953513
                let mut new_pixel_vec = Vector5::zeros();
222
953513
                self.matrix.mul_to(&pixel_vec, &mut new_pixel_vec);
223

            
224
953513
                let new_alpha = clamp(new_pixel_vec[3], 0.0, 1.0);
225

            
226
3118504
                let premultiply = |x: f64| ((clamp(x, 0.0, 1.0) * new_alpha * 255f64) + 0.5) as u8;
227

            
228
953513
                let output_pixel = Pixel {
229
953513
                    r: premultiply(new_pixel_vec[0]),
230
953513
                    g: premultiply(new_pixel_vec[1]),
231
953513
                    b: premultiply(new_pixel_vec[2]),
232
953513
                    a: ((new_alpha * 255f64) + 0.5) as u8,
233
                };
234

            
235
953513
                data.set_pixel(stride, output_pixel, x, y);
236
            }
237
16
        });
238

            
239
16
        Ok(FilterOutput {
240
16
            surface: surface.share()?,
241
16
            bounds,
242
        })
243
16
    }
244

            
245
    /// Compute a `type="hueRotate"` matrix.
246
    ///
247
    /// <https://drafts.fxtf.org/filter-effects/#element-attrdef-fecolormatrix-values>
248
    #[rustfmt::skip]
249
11
    pub fn hue_rotate_matrix(radians: f64) -> Matrix5<f64> {
250
11
        let (sin, cos) = radians.sin_cos();
251

            
252
11
        let a = Matrix3::new(
253
            0.213, 0.715, 0.072,
254
            0.213, 0.715, 0.072,
255
            0.213, 0.715, 0.072,
256
        );
257

            
258
11
        let b = Matrix3::new(
259
             0.787, -0.715, -0.072,
260
            -0.213,  0.285, -0.072,
261
            -0.213, -0.715,  0.928,
262
        );
263

            
264
11
        let c = Matrix3::new(
265
            -0.213, -0.715,  0.928,
266
             0.143,  0.140, -0.283,
267
            -0.787,  0.715,  0.072,
268
        );
269

            
270
11
        let top_left = a + b * cos + c * sin;
271

            
272
11
        let mut matrix = top_left.fixed_resize(0.0);
273
11
        matrix[(3, 3)] = 1.0;
274
11
        matrix[(4, 4)] = 1.0;
275
11
        matrix
276
11
    }
277

            
278
    /// Compute a `type="luminanceToAlpha"` matrix.
279
    ///
280
    /// <https://drafts.fxtf.org/filter-effects/#element-attrdef-fecolormatrix-values>
281
    #[rustfmt::skip]
282
1
    fn luminance_to_alpha_matrix() -> Matrix5<f64> {
283
1
        Matrix5::new(
284
            0.0,    0.0,    0.0,    0.0, 0.0,
285
            0.0,    0.0,    0.0,    0.0, 0.0,
286
            0.0,    0.0,    0.0,    0.0, 0.0,
287
            0.2126, 0.7152, 0.0722, 0.0, 0.0,
288
            0.0,    0.0,    0.0,    0.0, 1.0,
289
        )
290
1
    }
291

            
292
    /// Compute a `type="saturate"` matrix.
293
    ///
294
    /// <https://drafts.fxtf.org/filter-effects/#element-attrdef-fecolormatrix-values>
295
    #[rustfmt::skip]
296
6
    fn saturate_matrix(s: f64) -> Matrix5<f64> {
297
6
        Matrix5::new(
298
6
            0.213 + 0.787 * s, 0.715 - 0.715 * s, 0.072 - 0.072 * s, 0.0, 0.0,
299
6
            0.213 - 0.213 * s, 0.715 + 0.285 * s, 0.072 - 0.072 * s, 0.0, 0.0,
300
6
            0.213 - 0.213 * s, 0.715 - 0.715 * s, 0.072 + 0.928 * s, 0.0, 0.0,
301
            0.0,               0.0,               0.0,               1.0, 0.0,
302
            0.0,               0.0,               0.0,               0.0, 1.0,
303
        )
304
6
    }
305

            
306
    /// Default for `type="matrix"`.
307
    ///
308
    /// <https://drafts.fxtf.org/filter-effects/#element-attrdef-fecolormatrix-values>
309
5
    fn default_matrix() -> Matrix5<f64> {
310
5
        Matrix5::identity()
311
5
    }
312
}
313

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

            
323
14
        let mut params = self.params.clone();
324
14
        params.color_interpolation_filters = values.color_interpolation_filters();
325

            
326
14
        Ok(vec![ResolvedPrimitive {
327
14
            primitive: self.base.clone(),
328
14
            params: PrimitiveParams::ColorMatrix(params),
329
        }])
330
14
    }
331
}
332

            
333
impl Parse for OperationType {
334
14
    fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
335
28
        Ok(parse_identifiers!(
336
            parser,
337
            "matrix" => OperationType::Matrix,
338
            "saturate" => OperationType::Saturate,
339
            "hueRotate" => OperationType::HueRotate,
340
            "luminanceToAlpha" => OperationType::LuminanceToAlpha,
341
        )?)
342
14
    }
343
}