1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use super::{f32_bound, ImageRefMut};
6use usvg::filter::{ComponentTransfer, TransferFunction};
7
8/// Applies component transfer functions for each `src` image channel.
9///
10/// Input image pixels should have an **unpremultiplied alpha**.
11pub fn apply(fe: &ComponentTransfer, src: ImageRefMut) {
12 for pixel: &mut RGBA in src.data {
13 if !is_dummy(fe.func_r()) {
14 pixel.r = transfer(fe.func_r(), c:pixel.r);
15 }
16
17 if !is_dummy(fe.func_b()) {
18 pixel.b = transfer(fe.func_b(), c:pixel.b);
19 }
20
21 if !is_dummy(fe.func_g()) {
22 pixel.g = transfer(fe.func_g(), c:pixel.g);
23 }
24
25 if !is_dummy(fe.func_a()) {
26 pixel.a = transfer(fe.func_a(), c:pixel.a);
27 }
28 }
29}
30
31fn is_dummy(func: &TransferFunction) -> bool {
32 match func {
33 TransferFunction::Identity => true,
34 TransferFunction::Table(values: &Vec) => values.is_empty(),
35 TransferFunction::Discrete(values: &Vec) => values.is_empty(),
36 TransferFunction::Linear { .. } => false,
37 TransferFunction::Gamma { .. } => false,
38 }
39}
40
41fn transfer(func: &TransferFunction, c: u8) -> u8 {
42 let c = c as f32 / 255.0;
43 let c = match func {
44 TransferFunction::Identity => c,
45 TransferFunction::Table(values) => {
46 let n = values.len() - 1;
47 let k = (c * (n as f32)).floor() as usize;
48 let k = std::cmp::min(k, n);
49 if k == n {
50 values[k]
51 } else {
52 let vk = values[k];
53 let vk1 = values[k + 1];
54 let k = k as f32;
55 let n = n as f32;
56 vk + (c - k / n) * n * (vk1 - vk)
57 }
58 }
59 TransferFunction::Discrete(values) => {
60 let n = values.len();
61 let k = (c * (n as f32)).floor() as usize;
62 values[std::cmp::min(k, n - 1)]
63 }
64 TransferFunction::Linear { slope, intercept } => slope * c + intercept,
65 TransferFunction::Gamma {
66 amplitude,
67 exponent,
68 offset,
69 } => amplitude * c.powf(*exponent) + offset,
70 };
71
72 (f32_bound(0.0, c, 1.0) * 255.0) as u8
73}
74