resvg/filter/
component_transfer.rs

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