Skip to main content

colr_types/
encode.rs

1//! Signal encoding from linear light to a stored signal value.
2
3use crate::math::Float;
4use crate::transfer::*;
5
6/// Encode a value from linear light to a stored signal (OETF).
7///
8/// Implement for scalar `F: Float` to get a transfer function at any precision.
9/// A blanket impl provides `Encode<[F; N]>` automatically for any `Encode<F>`.
10pub trait Encode<T>: TransferFunction {
11    /// Convert a linear-light value to an encoded signal value.
12    fn encode(v: T) -> T;
13}
14
15impl<F: Float, TF: Encode<F>, const N: usize> Encode<[F; N]> for TF {
16    #[inline(always)]
17    fn encode(v: [F; N]) -> [F; N] {
18        v.map(|x| TF::encode(x))
19    }
20}
21
22impl<F: Float> Encode<F> for Linear {
23    #[inline(always)]
24    fn encode(v: F) -> F {
25        v
26    }
27}
28
29impl<F: Float> Encode<F> for Srgb {
30    #[inline(always)]
31    fn encode(x: F) -> F {
32        let zero = F::ZERO;
33        let one = F::ONE;
34        let neg_one = zero - one;
35        let sign = if x < zero { neg_one } else { one };
36        let abs = x.abs();
37        let threshold = F::from_f64(Self::LINEAR_THRESHOLD as f64);
38        let slope = F::from_f64(Self::LINEAR_SLOPE as f64);
39        let alpha = F::from_f64(Self::ALPHA as f64);
40        let inv_gamma = F::from_f64(1.0 / Self::GAMMA as f64);
41        sign * if abs <= threshold {
42            abs * slope
43        } else {
44            alpha * abs.powf(inv_gamma) - (alpha - one)
45        }
46    }
47}
48
49impl<F: Float> Encode<F> for Rec709 {
50    #[inline(always)]
51    fn encode(x: F) -> F {
52        let zero = F::ZERO;
53        let one = F::ONE;
54        let neg_one = zero - one;
55        let sign = if x < zero { neg_one } else { one };
56        let abs = x.abs();
57        let threshold = F::from_f64(Self::LINEAR_THRESHOLD as f64);
58        let slope = F::from_f64(Self::LINEAR_SLOPE as f64);
59        let alpha = F::from_f64(Self::ALPHA as f64);
60        let power = F::from_f64(Self::POWER as f64);
61        sign * if abs <= threshold {
62            abs * slope
63        } else {
64            alpha * abs.powf(power) - (alpha - one)
65        }
66    }
67}
68
69impl<F: Float> Encode<F> for Pq {
70    #[inline(always)]
71    fn encode(x: F) -> F {
72        let zero = F::ZERO;
73        let m1 = F::from_f64(Self::M1 as f64);
74        let m2 = F::from_f64(Self::M2 as f64);
75        let c1 = F::from_f64(Self::C1 as f64);
76        let c2 = F::from_f64(Self::C2 as f64);
77        let c3 = F::from_f64(Self::C3 as f64);
78        let one = F::ONE;
79        let y_m1 = x.max(zero).powf(m1);
80        ((c1 + c2 * y_m1) / (one + c3 * y_m1)).powf(m2)
81    }
82}
83
84impl<F: Float> Encode<F> for Hlg {
85    #[inline(always)]
86    fn encode(x: F) -> F {
87        let zero = F::ZERO;
88        let one = F::ONE;
89        let neg_one = zero - one;
90        let abs = x.abs();
91        let sign = if x < zero { neg_one } else { one };
92        let threshold = F::from_f64(Self::LINEAR_THRESHOLD as f64);
93        let a = F::from_f64(Self::A as f64);
94        let b = F::from_f64(Self::B as f64);
95        let c = F::from_f64(Self::C as f64);
96        let three = F::from_f64(3.0);
97        let twelve = F::from_f64(12.0);
98        if abs <= threshold {
99            sign * (three * abs).sqrt()
100        } else {
101            sign * (a * (twelve * abs - b).max(F::MIN_POSITIVE).ln() + c)
102        }
103    }
104}
105
106impl<F: Float> Encode<F> for ProPhoto {
107    #[inline(always)]
108    fn encode(x: F) -> F {
109        let zero = F::ZERO;
110        let one = F::ONE;
111        let neg_one = zero - one;
112        let sign = if x < zero { neg_one } else { one };
113        let abs = x.abs();
114        let threshold = F::from_f64(Self::LINEAR_THRESHOLD as f64);
115        let slope = F::from_f64(Self::LINEAR_SLOPE as f64);
116        let inv_gamma = F::from_f64(1.0 / Self::GAMMA as f64);
117        sign * if abs <= threshold {
118            abs * slope
119        } else {
120            abs.powf(inv_gamma)
121        }
122    }
123}
124
125impl<F: Float> Encode<F> for AcesCc {
126    #[inline(always)]
127    fn encode(x: F) -> F {
128        let zero = F::ZERO;
129        let scale = F::from_f64(Self::LOG_SCALE as f64);
130        let offset = F::from_f64(Self::LOG_OFFSET as f64);
131        let log2_recip = F::from_f64(Self::LOG2_RECIP as f64);
132        let neg16: F = F::from_f64(2.0f64.powi(-16));
133        let neg15: F = F::from_f64(2.0f64.powi(-15));
134        let half = F::from_f64(0.5);
135        if x < zero {
136            (neg16.ln() * log2_recip + offset) / scale
137        } else if x < neg15 {
138            (neg16 + x * half).ln() * log2_recip * (F::ONE / scale) + offset / scale
139        } else {
140            x.ln() * log2_recip * (F::ONE / scale) + offset / scale
141        }
142    }
143}
144
145impl<F: Float> Encode<F> for AcesCct {
146    #[inline(always)]
147    fn encode(x: F) -> F {
148        let x_brk = F::from_f64(Self::X_BRK as f64);
149        let a = F::from_f64(Self::A as f64);
150        let b = F::from_f64(Self::B as f64);
151        let scale = F::from_f64(Self::LOG_SCALE as f64);
152        let offset = F::from_f64(Self::LOG_OFFSET as f64);
153        let log2_recip = F::from_f64(Self::LOG2_RECIP as f64);
154        if x <= x_brk {
155            a * x + b
156        } else {
157            (x.ln() * log2_recip + offset) / scale
158        }
159    }
160}
161
162impl<F: Float> Encode<F> for DciP3 {
163    #[inline(always)]
164    fn encode(x: F) -> F {
165        let inv_gamma = F::from_f64(1.0 / Self::GAMMA as f64);
166        x.max(F::ZERO).powf(inv_gamma)
167    }
168}