| 1 | // Copyright 2020 the Resvg Authors |
| 2 | // SPDX-License-Identifier: Apache-2.0 OR MIT |
| 3 | |
| 4 | use super::{f32_bound, ImageRefMut}; |
| 5 | use rgb::RGBA8; |
| 6 | use usvg::filter::ColorMatrixKind as ColorMatrix; |
| 7 | |
| 8 | /// Applies a color matrix filter. |
| 9 | /// |
| 10 | /// Input image pixels should have an **unpremultiplied alpha**. |
| 11 | pub fn apply(matrix: &ColorMatrix, src: ImageRefMut) { |
| 12 | match matrix { |
| 13 | ColorMatrix::Matrix(m) => { |
| 14 | for pixel in src.data { |
| 15 | let (r, g, b, a) = to_normalized_components(*pixel); |
| 16 | |
| 17 | let new_r = r * m[0] + g * m[1] + b * m[2] + a * m[3] + m[4]; |
| 18 | let new_g = r * m[5] + g * m[6] + b * m[7] + a * m[8] + m[9]; |
| 19 | let new_b = r * m[10] + g * m[11] + b * m[12] + a * m[13] + m[14]; |
| 20 | let new_a = r * m[15] + g * m[16] + b * m[17] + a * m[18] + m[19]; |
| 21 | |
| 22 | pixel.r = from_normalized(new_r); |
| 23 | pixel.g = from_normalized(new_g); |
| 24 | pixel.b = from_normalized(new_b); |
| 25 | pixel.a = from_normalized(new_a); |
| 26 | } |
| 27 | } |
| 28 | ColorMatrix::Saturate(v) => { |
| 29 | let v = v.get().max(0.0); |
| 30 | let m = [ |
| 31 | 0.213 + 0.787 * v, |
| 32 | 0.715 - 0.715 * v, |
| 33 | 0.072 - 0.072 * v, |
| 34 | 0.213 - 0.213 * v, |
| 35 | 0.715 + 0.285 * v, |
| 36 | 0.072 - 0.072 * v, |
| 37 | 0.213 - 0.213 * v, |
| 38 | 0.715 - 0.715 * v, |
| 39 | 0.072 + 0.928 * v, |
| 40 | ]; |
| 41 | |
| 42 | for pixel in src.data { |
| 43 | let (r, g, b, _) = to_normalized_components(*pixel); |
| 44 | |
| 45 | let new_r = r * m[0] + g * m[1] + b * m[2]; |
| 46 | let new_g = r * m[3] + g * m[4] + b * m[5]; |
| 47 | let new_b = r * m[6] + g * m[7] + b * m[8]; |
| 48 | |
| 49 | pixel.r = from_normalized(new_r); |
| 50 | pixel.g = from_normalized(new_g); |
| 51 | pixel.b = from_normalized(new_b); |
| 52 | } |
| 53 | } |
| 54 | ColorMatrix::HueRotate(angle) => { |
| 55 | let angle = angle.to_radians(); |
| 56 | let a1 = angle.cos(); |
| 57 | let a2 = angle.sin(); |
| 58 | let m = [ |
| 59 | 0.213 + 0.787 * a1 - 0.213 * a2, |
| 60 | 0.715 - 0.715 * a1 - 0.715 * a2, |
| 61 | 0.072 - 0.072 * a1 + 0.928 * a2, |
| 62 | 0.213 - 0.213 * a1 + 0.143 * a2, |
| 63 | 0.715 + 0.285 * a1 + 0.140 * a2, |
| 64 | 0.072 - 0.072 * a1 - 0.283 * a2, |
| 65 | 0.213 - 0.213 * a1 - 0.787 * a2, |
| 66 | 0.715 - 0.715 * a1 + 0.715 * a2, |
| 67 | 0.072 + 0.928 * a1 + 0.072 * a2, |
| 68 | ]; |
| 69 | |
| 70 | for pixel in src.data { |
| 71 | let (r, g, b, _) = to_normalized_components(*pixel); |
| 72 | |
| 73 | let new_r = r * m[0] + g * m[1] + b * m[2]; |
| 74 | let new_g = r * m[3] + g * m[4] + b * m[5]; |
| 75 | let new_b = r * m[6] + g * m[7] + b * m[8]; |
| 76 | |
| 77 | pixel.r = from_normalized(new_r); |
| 78 | pixel.g = from_normalized(new_g); |
| 79 | pixel.b = from_normalized(new_b); |
| 80 | } |
| 81 | } |
| 82 | ColorMatrix::LuminanceToAlpha => { |
| 83 | for pixel in src.data { |
| 84 | let (r, g, b, _) = to_normalized_components(*pixel); |
| 85 | |
| 86 | let new_a = r * 0.2125 + g * 0.7154 + b * 0.0721; |
| 87 | |
| 88 | pixel.r = 0; |
| 89 | pixel.g = 0; |
| 90 | pixel.b = 0; |
| 91 | pixel.a = from_normalized(new_a); |
| 92 | } |
| 93 | } |
| 94 | } |
| 95 | } |
| 96 | |
| 97 | #[inline ] |
| 98 | fn to_normalized_components(pixel: RGBA8) -> (f32, f32, f32, f32) { |
| 99 | ( |
| 100 | pixel.r as f32 / 255.0, |
| 101 | pixel.g as f32 / 255.0, |
| 102 | pixel.b as f32 / 255.0, |
| 103 | pixel.a as f32 / 255.0, |
| 104 | ) |
| 105 | } |
| 106 | |
| 107 | #[inline ] |
| 108 | fn from_normalized(c: f32) -> u8 { |
| 109 | (f32_bound(min:0.0, val:c, max:1.0) * 255.0) as u8 |
| 110 | } |
| 111 | |