resvg/filter/
component_transfer.rs

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 in src.data {
13        if !is_dummy(fe.func_r()) {
14            pixel.r = transfer(fe.func_r(), pixel.r);
15        }
16
17        if !is_dummy(fe.func_b()) {
18            pixel.b = transfer(fe.func_b(), pixel.b);
19        }
20
21        if !is_dummy(fe.func_g()) {
22            pixel.g = transfer(fe.func_g(), pixel.g);
23        }
24
25        if !is_dummy(fe.func_a()) {
26            pixel.a = transfer(fe.func_a(), pixel.a);
27        }
28    }
29}
30
31fn is_dummy(func: &TransferFunction) -> bool {
32    match func {
33        TransferFunction::Identity => true,
34        TransferFunction::Table(values) => values.is_empty(),
35        TransferFunction::Discrete(values) => 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}