1use cssparser::Parser;
2use markup5ever::{QualName, expanded_name, local_name, ns};
3use nalgebra::{Matrix3, Matrix4x5, Matrix5, Vector5};
4
5use crate::document::AcquiredNodes;
6use crate::element::{ElementTrait, set_attribute};
7use crate::error::*;
8use crate::node::{CascadedValues, Node};
9use crate::parse_identifiers;
10use crate::parsers::{CommaSeparatedList, Parse, ParseValue};
11use crate::properties::ColorInterpolationFilters;
12use crate::rect::IRect;
13use crate::rsvg_log;
14use crate::session::Session;
15use crate::surface_utils::{
16 ImageSurfaceDataExt, Pixel, iterators::Pixels, shared_surface::ExclusiveImageSurface,
17};
18use crate::util::clamp;
19use crate::xml::Attributes;
20
21use super::bounds::BoundsBuilder;
22use super::context::{FilterContext, FilterOutput};
23use super::{
24 FilterEffect, FilterError, FilterResolveError, Input, InputRequirements, Primitive,
25 PrimitiveParams, ResolvedPrimitive,
26};
27
28#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)]
30enum OperationType {
31 #[default]
32 Matrix,
33 Saturate,
34 HueRotate,
35 LuminanceToAlpha,
36}
37
38#[derive(Default)]
40pub struct FeColorMatrix {
41 base: Primitive,
42 params: ColorMatrix,
43}
44
45#[derive(Clone)]
47pub struct ColorMatrix {
48 pub in1: Input,
49 pub matrix: Matrix5<f64>,
50 pub color_interpolation_filters: ColorInterpolationFilters,
51}
52
53impl Default for ColorMatrix {
54 fn default() -> ColorMatrix {
55 ColorMatrix {
56 in1: Default::default(),
57 color_interpolation_filters: Default::default(),
58
59 matrix: Matrix5::identity(),
61 }
62 }
63}
64
65impl ElementTrait for FeColorMatrix {
66 fn set_attributes(&mut self, attrs: &Attributes, session: &Session) {
67 self.params.in1 = self.base.parse_one_input(attrs, session);
68
69 let mut operation_type = Default::default();
71 for (attr, value) in attrs
72 .iter()
73 .filter(|(attr, _)| attr.expanded() == expanded_name!("", "type"))
74 {
75 set_attribute(&mut operation_type, attr.parse(value), session);
76 }
77
78 use OperationType::*;
89
90 self.params.matrix = match operation_type {
91 Matrix => ColorMatrix::default_matrix(),
92 Saturate => ColorMatrix::saturate_matrix(1.0),
93 HueRotate => ColorMatrix::hue_rotate_matrix(0.0),
94 LuminanceToAlpha => ColorMatrix::luminance_to_alpha_matrix(),
95 };
96
97 for (attr, value) in attrs
98 .iter()
99 .filter(|(attr, _)| attr.expanded() == expanded_name!("", "values"))
100 {
101 match operation_type {
102 Matrix => parse_matrix(&mut self.params.matrix, attr, value, session),
103 Saturate => parse_saturate_matrix(&mut self.params.matrix, attr, value, session),
104 HueRotate => parse_hue_rotate_matrix(&mut self.params.matrix, attr, value, session),
105 LuminanceToAlpha => {
106 parse_luminance_to_alpha_matrix(&mut self.params.matrix, attr, value, session)
107 }
108 }
109 }
110 }
111}
112
113fn parse_matrix(dest: &mut Matrix5<f64>, attr: QualName, value: &str, session: &Session) {
114 let parsed: Result<CommaSeparatedList<f64, 20, 20>, _> = attr.parse(value);
115
116 match parsed {
117 Ok(CommaSeparatedList(v)) => {
118 let matrix = Matrix4x5::from_row_slice(&v);
119 let mut matrix = matrix.fixed_resize(0.0);
120 matrix[(4, 4)] = 1.0;
121 *dest = matrix;
122 }
123
124 Err(e) => {
125 rsvg_log!(
126 session,
127 "element feColorMatrix with type=\"matrix\", expected a values attribute with 20 numbers: {}",
128 e
129 );
130 }
131 }
132}
133
134fn parse_saturate_matrix(dest: &mut Matrix5<f64>, attr: QualName, value: &str, session: &Session) {
135 let parsed: Result<f64, _> = attr.parse(value);
136
137 match parsed {
138 Ok(s) => {
139 *dest = ColorMatrix::saturate_matrix(s);
140 }
141
142 Err(e) => {
143 rsvg_log!(
144 session,
145 "element feColorMatrix with type=\"saturate\", expected a values attribute with 1 number: {}",
146 e
147 );
148 }
149 }
150}
151
152fn parse_hue_rotate_matrix(
153 dest: &mut Matrix5<f64>,
154 attr: QualName,
155 value: &str,
156 session: &Session,
157) {
158 let parsed: Result<f64, _> = attr.parse(value);
159
160 match parsed {
161 Ok(degrees) => {
162 *dest = ColorMatrix::hue_rotate_matrix(degrees.to_radians());
163 }
164
165 Err(e) => {
166 rsvg_log!(
167 session,
168 "element feColorMatrix with type=\"hueRotate\", expected a values attribute with 1 number: {}",
169 e
170 );
171 }
172 }
173}
174
175fn parse_luminance_to_alpha_matrix(
176 _dest: &mut Matrix5<f64>,
177 _attr: QualName,
178 _value: &str,
179 session: &Session,
180) {
181 rsvg_log!(
186 session,
187 "ignoring \"values\" attribute for feColorMatrix with type=\"luminanceToAlpha\""
188 );
189}
190
191impl ColorMatrix {
192 pub fn render(
193 &self,
194 bounds_builder: BoundsBuilder,
195 ctx: &FilterContext,
196 ) -> Result<FilterOutput, FilterError> {
197 let input_1 = ctx.get_input(&self.in1, self.color_interpolation_filters)?;
198 let bounds: IRect = bounds_builder
199 .add_input(&input_1)
200 .compute(ctx)
201 .clipped
202 .into();
203
204 let mut surface = ExclusiveImageSurface::new(
205 ctx.source_graphic().width(),
206 ctx.source_graphic().height(),
207 input_1.surface().surface_type(),
208 )?;
209
210 surface.modify(&mut |data, stride| {
211 for (x, y, pixel) in Pixels::within(input_1.surface(), bounds) {
212 let alpha = f64::from(pixel.a) / 255f64;
213
214 let pixel_vec = if alpha == 0.0 {
215 Vector5::new(0.0, 0.0, 0.0, 0.0, 1.0)
216 } else {
217 Vector5::new(
218 f64::from(pixel.r) / 255f64 / alpha,
219 f64::from(pixel.g) / 255f64 / alpha,
220 f64::from(pixel.b) / 255f64 / alpha,
221 alpha,
222 1.0,
223 )
224 };
225 let mut new_pixel_vec = Vector5::zeros();
226 self.matrix.mul_to(&pixel_vec, &mut new_pixel_vec);
227
228 let new_alpha = clamp(new_pixel_vec[3], 0.0, 1.0);
229
230 let premultiply = |x: f64| ((clamp(x, 0.0, 1.0) * new_alpha * 255f64) + 0.5) as u8;
231
232 let output_pixel = Pixel {
233 r: premultiply(new_pixel_vec[0]),
234 g: premultiply(new_pixel_vec[1]),
235 b: premultiply(new_pixel_vec[2]),
236 a: ((new_alpha * 255f64) + 0.5) as u8,
237 };
238
239 data.set_pixel(stride, output_pixel, x, y);
240 }
241 });
242
243 Ok(FilterOutput {
244 surface: surface.share()?,
245 bounds,
246 })
247 }
248
249 #[rustfmt::skip]
253 pub fn hue_rotate_matrix(radians: f64) -> Matrix5<f64> {
254 let (sin, cos) = radians.sin_cos();
255
256 let a = Matrix3::new(
257 0.213, 0.715, 0.072,
258 0.213, 0.715, 0.072,
259 0.213, 0.715, 0.072,
260 );
261
262 let b = Matrix3::new(
263 0.787, -0.715, -0.072,
264 -0.213, 0.285, -0.072,
265 -0.213, -0.715, 0.928,
266 );
267
268 let c = Matrix3::new(
269 -0.213, -0.715, 0.928,
270 0.143, 0.140, -0.283,
271 -0.787, 0.715, 0.072,
272 );
273
274 let top_left = a + b * cos + c * sin;
275
276 let mut matrix = top_left.fixed_resize(0.0);
277 matrix[(3, 3)] = 1.0;
278 matrix[(4, 4)] = 1.0;
279 matrix
280 }
281
282 #[rustfmt::skip]
286 fn luminance_to_alpha_matrix() -> Matrix5<f64> {
287 Matrix5::new(
288 0.0, 0.0, 0.0, 0.0, 0.0,
289 0.0, 0.0, 0.0, 0.0, 0.0,
290 0.0, 0.0, 0.0, 0.0, 0.0,
291 0.2126, 0.7152, 0.0722, 0.0, 0.0,
292 0.0, 0.0, 0.0, 0.0, 1.0,
293 )
294 }
295
296 #[rustfmt::skip]
300 fn saturate_matrix(s: f64) -> Matrix5<f64> {
301 Matrix5::new(
302 0.213 + 0.787 * s, 0.715 - 0.715 * s, 0.072 - 0.072 * s, 0.0, 0.0,
303 0.213 - 0.213 * s, 0.715 + 0.285 * s, 0.072 - 0.072 * s, 0.0, 0.0,
304 0.213 - 0.213 * s, 0.715 - 0.715 * s, 0.072 + 0.928 * s, 0.0, 0.0,
305 0.0, 0.0, 0.0, 1.0, 0.0,
306 0.0, 0.0, 0.0, 0.0, 1.0,
307 )
308 }
309
310 fn default_matrix() -> Matrix5<f64> {
314 Matrix5::identity()
315 }
316
317 pub fn get_input_requirements(&self) -> InputRequirements {
318 self.in1.get_requirements()
319 }
320}
321
322impl FilterEffect for FeColorMatrix {
323 fn resolve(
324 &self,
325 _acquired_nodes: &mut AcquiredNodes<'_>,
326 node: &Node,
327 ) -> Result<Vec<ResolvedPrimitive>, FilterResolveError> {
328 let cascaded = CascadedValues::new_from_node(node);
329 let values = cascaded.get();
330
331 let mut params = self.params.clone();
332 params.color_interpolation_filters = values.color_interpolation_filters();
333
334 Ok(vec![ResolvedPrimitive {
335 primitive: self.base.clone(),
336 params: PrimitiveParams::ColorMatrix(params),
337 }])
338 }
339}
340
341impl Parse for OperationType {
342 fn parse<'i>(parser: &mut Parser<'i, '_>) -> Result<Self, ParseError<'i>> {
343 Ok(parse_identifiers!(
344 parser,
345 "matrix" => OperationType::Matrix,
346 "saturate" => OperationType::Saturate,
347 "hueRotate" => OperationType::HueRotate,
348 "luminanceToAlpha" => OperationType::LuminanceToAlpha,
349 )?)
350 }
351}