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