svgfilters/
color_matrix.rs1use crate::{ImageRefMut, RGBA8, f64_bound};
6
7#[inline]
8fn to_normalized_components(pixel: RGBA8) -> (f64, f64, f64, f64) {
9 (pixel.r as f64 / 255.0,
10 pixel.g as f64 / 255.0,
11 pixel.b as f64 / 255.0,
12 pixel.a as f64 / 255.0)
13}
14
15#[inline]
16fn from_normalized(c: f64) -> u8 {
17 (f64_bound(0.0, c, 1.0) * 255.0) as u8
18}
19
20#[derive(Clone, Copy, Debug)]
22#[allow(missing_docs)]
23pub enum ColorMatrix<'a> {
24 Matrix(&'a [f64; 20]),
25 Saturate(f64),
26 HueRotate(f64),
27 LuminanceToAlpha,
28}
29
30pub fn color_matrix(
34 matrix: ColorMatrix,
35 src: ImageRefMut,
36) {
37 match matrix {
38 ColorMatrix::Matrix(m) => {
39 for pixel in src.data {
40 let (r, g, b, a) = to_normalized_components(*pixel);
41
42 let new_r = r * m[0] + g * m[1] + b * m[2] + a * m[3] + m[4];
43 let new_g = r * m[5] + g * m[6] + b * m[7] + a * m[8] + m[9];
44 let new_b = r * m[10] + g * m[11] + b * m[12] + a * m[13] + m[14];
45 let new_a = r * m[15] + g * m[16] + b * m[17] + a * m[18] + m[19];
46
47 pixel.r = from_normalized(new_r);
48 pixel.g = from_normalized(new_g);
49 pixel.b = from_normalized(new_b);
50 pixel.a = from_normalized(new_a);
51 }
52 }
53 ColorMatrix::Saturate(v) => {
54 let v = v.max(0.0);
55 let m = [
56 0.213 + 0.787 * v, 0.715 - 0.715 * v, 0.072 - 0.072 * v,
57 0.213 - 0.213 * v, 0.715 + 0.285 * v, 0.072 - 0.072 * v,
58 0.213 - 0.213 * v, 0.715 - 0.715 * v, 0.072 + 0.928 * v,
59 ];
60
61 for pixel in src.data {
62 let (r, g, b, _) = to_normalized_components(*pixel);
63
64 let new_r = r * m[0] + g * m[1] + b * m[2];
65 let new_g = r * m[3] + g * m[4] + b * m[5];
66 let new_b = r * m[6] + g * m[7] + b * m[8];
67
68 pixel.r = from_normalized(new_r);
69 pixel.g = from_normalized(new_g);
70 pixel.b = from_normalized(new_b);
71 }
72 }
73 ColorMatrix::HueRotate(angle) => {
74 let angle = angle.to_radians();
75 let a1 = angle.cos();
76 let a2 = angle.sin();
77 let m = [
78 0.213 + 0.787 * a1 - 0.213 * a2,
79 0.715 - 0.715 * a1 - 0.715 * a2,
80 0.072 - 0.072 * a1 + 0.928 * a2,
81 0.213 - 0.213 * a1 + 0.143 * a2,
82 0.715 + 0.285 * a1 + 0.140 * a2,
83 0.072 - 0.072 * a1 - 0.283 * a2,
84 0.213 - 0.213 * a1 - 0.787 * a2,
85 0.715 - 0.715 * a1 + 0.715 * a2,
86 0.072 + 0.928 * a1 + 0.072 * a2,
87 ];
88
89 for pixel in src.data {
90 let (r, g, b, _) = to_normalized_components(*pixel);
91
92 let new_r = r * m[0] + g * m[1] + b * m[2];
93 let new_g = r * m[3] + g * m[4] + b * m[5];
94 let new_b = r * m[6] + g * m[7] + b * m[8];
95
96 pixel.r = from_normalized(new_r);
97 pixel.g = from_normalized(new_g);
98 pixel.b = from_normalized(new_b);
99 }
100 }
101 ColorMatrix::LuminanceToAlpha => {
102 for pixel in src.data {
103 let (r, g, b, _) = to_normalized_components(*pixel);
104
105 let new_a = r * 0.2125 + g * 0.7154 + b * 0.0721;
106
107 pixel.r = 0;
108 pixel.g = 0;
109 pixel.b = 0;
110 pixel.a = from_normalized(new_a);
111 }
112 }
113 }
114}