palette/encoding/
lut.rs

1mod codegen;
2
3use core::{iter::FromIterator, marker::PhantomData};
4
5use palette_math::{
6    gamma::lut::{GammaLut, GammaLutInput, GammaLutOutput},
7    lut::{Lookup, Lut, LutType},
8};
9
10pub(crate) use crate::encoding::lut::codegen::*;
11use crate::{
12    convert::{Convert, ConvertOnce},
13    luma::{Luma, LumaStandard},
14    num::Real,
15    rgb::{Rgb, RgbStandard},
16};
17
18use super::{GetLutBuilder, Linear};
19
20/// Lookup table from gamma encoded to linear values.
21///
22/// `E` and `L` are the encoded and linear types, `F` is the transfer function,
23/// and `T` determines the storage type for the table.
24///
25/// ```
26/// use palette::{Srgb, SrgbLuma, encoding, math::lut::VecTable};
27///
28/// // Accessing a pre-generated lookup table:
29/// let lut = encoding::Srgb::get_u8_to_f32_lut();
30/// let linear = lut.lookup_rgb(Srgb::new(23, 198, 76));
31///
32/// // Generating a new lookup table, allocated as a Vec:
33/// let lut = encoding::IntoLinearLut::<_, f32, _, VecTable>::new_u8();
34/// let linear = lut.lookup_rgb(Srgb::new(23, 198, 76));
35///
36/// // Looking up a Luma value:
37/// let lut = encoding::Srgb::get_u8_to_f32_lut();
38/// let linear = lut.lookup_luma(SrgbLuma::new(23));
39/// ```
40pub struct IntoLinearLut<E, L, F, T>
41where
42    T: LutType<L>,
43    T::Table: Sized,
44{
45    table: Lut<E, L, T>,
46    #[allow(clippy::type_complexity)]
47    transfer_fn: PhantomData<fn(Rgb<F, E>) -> Rgb<Linear<F>, L>>,
48}
49
50impl<L, F, T> IntoLinearLut<u8, L, F, T>
51where
52    T: LutType<L>,
53    T::Table: Sized,
54{
55    /// Generate a new lookup table that decodes from `u8` values.
56    #[inline]
57    pub fn new_u8() -> Self
58    where
59        L: Real,
60        F: GetLutBuilder,
61        T::Table: FromIterator<L>,
62    {
63        let builder = F::get_lut_builder();
64
65        Self::from_table(Lut::new(
66            builder.u8_to_linear_entries().map(L::from_f64).collect(),
67        ))
68    }
69}
70
71impl<L, F, T> IntoLinearLut<u16, L, F, T>
72where
73    T: LutType<L>,
74    T::Table: Sized,
75{
76    /// Generate a new lookup table that decodes from `u16` values.
77    #[inline]
78    pub fn new_u16() -> Self
79    where
80        L: Real,
81        F: GetLutBuilder,
82        T::Table: FromIterator<L>,
83    {
84        let builder = F::get_lut_builder();
85
86        Self::from_table(Lut::new(
87            builder.u16_to_linear_entries().map(L::from_f64).collect(),
88        ))
89    }
90}
91
92impl<E, L, F, T> IntoLinearLut<E, L, F, T>
93where
94    T: LutType<L>,
95    T::Table: Sized,
96{
97    /// Create a lookup table from a component lookup table.
98    ///
99    /// The input table will not be verified for correctness and may result in
100    /// unintended values if it's not for the intended transfer function. This
101    /// function is primarily useful for code generation.
102    #[inline]
103    pub const fn from_table(table: Lut<E, L, T>) -> Self {
104        IntoLinearLut {
105            table,
106            transfer_fn: PhantomData,
107        }
108    }
109
110    /// Get an RGB value from the table.
111    #[inline]
112    pub fn lookup_rgb<S>(&self, encoded: Rgb<S, E>) -> Rgb<Linear<S>, L>
113    where
114        L: Clone,
115        T: Lookup<E, L>,
116        S: RgbStandard<TransferFn = F>,
117    {
118        Rgb {
119            red: self.table.lookup(encoded.red).clone(),
120            green: self.table.lookup(encoded.green).clone(),
121            blue: self.table.lookup(encoded.blue).clone(),
122            standard: PhantomData,
123        }
124    }
125
126    /// Get a luma value from the table.
127    #[inline]
128    pub fn lookup_luma<S>(&self, encoded: Luma<S, E>) -> Luma<Linear<S>, L>
129    where
130        L: Clone,
131        T: Lookup<E, L>,
132        S: LumaStandard<TransferFn = F>,
133    {
134        Luma {
135            luma: self.table.lookup(encoded.luma).clone(),
136            standard: PhantomData,
137        }
138    }
139}
140
141impl<E, L, S, T> ConvertOnce<Rgb<S, E>, Rgb<Linear<S>, L>> for IntoLinearLut<E, L, S::TransferFn, T>
142where
143    L: Clone,
144    T: Lookup<E, L>,
145    T::Table: Sized,
146    S: RgbStandard,
147{
148    #[inline]
149    fn convert_once(self, input: Rgb<S, E>) -> Rgb<Linear<S>, L> {
150        self.lookup_rgb(input)
151    }
152}
153
154impl<E, L, S, T> Convert<Rgb<S, E>, Rgb<Linear<S>, L>> for IntoLinearLut<E, L, S::TransferFn, T>
155where
156    L: Clone,
157    T: Lookup<E, L>,
158    T::Table: Sized,
159    S: RgbStandard,
160{
161    #[inline]
162    fn convert(&self, input: Rgb<S, E>) -> Rgb<Linear<S>, L> {
163        self.lookup_rgb(input)
164    }
165}
166
167impl<E, L, S, T> ConvertOnce<Luma<S, E>, Luma<Linear<S>, L>>
168    for IntoLinearLut<E, L, S::TransferFn, T>
169where
170    L: Clone,
171    T: Lookup<E, L>,
172    T::Table: Sized,
173    S: LumaStandard,
174{
175    #[inline]
176    fn convert_once(self, input: Luma<S, E>) -> Luma<Linear<S>, L> {
177        self.lookup_luma(input)
178    }
179}
180
181impl<E, L, S, T> Convert<Luma<S, E>, Luma<Linear<S>, L>> for IntoLinearLut<E, L, S::TransferFn, T>
182where
183    L: Clone,
184    T: Lookup<E, L>,
185    T::Table: Sized,
186    S: LumaStandard,
187{
188    #[inline]
189    fn convert(&self, input: Luma<S, E>) -> Luma<Linear<S>, L> {
190        self.lookup_luma(input)
191    }
192}
193
194impl<E, L, F, T> From<Lut<E, L, T>> for IntoLinearLut<E, L, F, T>
195where
196    T: LutType<L>,
197    T::Table: Sized,
198{
199    #[inline]
200    fn from(table: Lut<E, L, T>) -> Self {
201        Self {
202            table,
203            transfer_fn: PhantomData,
204        }
205    }
206}
207
208/// Lookup table from linear to gamma encoded values.
209///
210/// `L` and `E` are the linear and encoded types, `F` is the transfer function,
211/// and `T` determines the storage type for the table.
212///
213/// ```
214/// use palette::{LinSrgb, SrgbLuma, LinLuma, encoding, math::lut::VecTable};
215///
216/// // Accessing a pre-generated lookup table:
217/// let lut = encoding::Srgb::get_f32_to_u8_lut();
218/// let encoded = lut.lookup_rgb(LinSrgb::new(0.3, 0.8, 0.1));
219///
220/// // Generating a new lookup table, allocated as a Vec:
221/// let lut = encoding::FromLinearLut::<f32, _, _, VecTable>::new_u8();
222/// let encoded = lut.lookup_rgb(LinSrgb::new(0.3, 0.8, 0.1));
223///
224/// // Looking up a Luma value:
225/// let lut = encoding::Srgb::get_f32_to_u8_lut();
226/// let encoded: SrgbLuma<_> = lut.lookup_luma(LinLuma::new(0.3));
227/// ```
228pub struct FromLinearLut<L, E, F, T>
229where
230    T: LutType<E::TableValue>,
231    T::Table: Sized,
232    E: GammaLutOutput,
233{
234    table: GammaLut<L, E, T>,
235    #[allow(clippy::type_complexity)]
236    transfer_fn: PhantomData<fn(Rgb<Linear<F>, L>) -> Rgb<F, E>>,
237}
238
239impl<L, F, T> FromLinearLut<L, u8, F, T>
240where
241    T: LutType<u32>,
242    T::Table: Sized,
243{
244    /// Generate a new lookup table that encodes into `u8` values.
245    #[inline]
246    pub fn new_u8() -> Self
247    where
248        F: GetLutBuilder,
249        T::Table: FromIterator<u32>,
250    {
251        let builder = F::get_lut_builder();
252
253        Self {
254            table: GammaLut::from_builder_u8(&builder),
255            transfer_fn: PhantomData,
256        }
257    }
258}
259
260impl<L, F, T> FromLinearLut<L, u16, F, T>
261where
262    T: LutType<u64>,
263    T::Table: Sized,
264{
265    /// Generate a new lookup table that encodes into `u16` values.
266    #[inline]
267    pub fn new_u16() -> Self
268    where
269        F: GetLutBuilder,
270        T::Table: FromIterator<u64>,
271    {
272        let builder = F::get_lut_builder();
273
274        Self {
275            table: GammaLut::from_builder_u16(&builder),
276            transfer_fn: PhantomData,
277        }
278    }
279}
280
281impl<L, E, F, T> FromLinearLut<L, E, F, T>
282where
283    T: LutType<E::TableValue>,
284    T::Table: Sized,
285    E: GammaLutOutput,
286{
287    /// Create a lookup table from a component lookup table.
288    ///
289    /// The input table will not be verified for correctness and may result in
290    /// unintended values if it's not for the intended transfer function. This
291    /// function is primarily useful for code generation.
292    #[inline]
293    pub const fn from_table(table: GammaLut<L, E, T>) -> Self {
294        Self {
295            table,
296            transfer_fn: PhantomData,
297        }
298    }
299
300    /// Get an RGB value from the table.
301    #[inline]
302    pub fn lookup_rgb<S>(&self, encoded: Rgb<Linear<S>, L>) -> Rgb<S, E>
303    where
304        L: GammaLutInput,
305        S: RgbStandard<TransferFn = F>,
306    {
307        Rgb {
308            red: self.table.lookup(encoded.red),
309            green: self.table.lookup(encoded.green),
310            blue: self.table.lookup(encoded.blue),
311            standard: PhantomData,
312        }
313    }
314
315    /// Get a luma from in the table.
316    #[inline]
317    pub fn lookup_luma<S>(&self, encoded: Luma<Linear<S>, L>) -> Luma<S, E>
318    where
319        L: GammaLutInput,
320        S: LumaStandard<TransferFn = F>,
321    {
322        Luma {
323            luma: self.table.lookup(encoded.luma),
324            standard: PhantomData,
325        }
326    }
327}
328
329impl<L, E, S, T> ConvertOnce<Rgb<Linear<S>, L>, Rgb<S, E>> for FromLinearLut<L, E, S::TransferFn, T>
330where
331    L: GammaLutInput,
332    E: GammaLutOutput,
333    T: LutType<E::TableValue>,
334    T::Table: Sized,
335    S: RgbStandard,
336{
337    #[inline]
338    fn convert_once(self, input: Rgb<Linear<S>, L>) -> Rgb<S, E> {
339        self.lookup_rgb(input)
340    }
341}
342
343impl<L, E, S, T> Convert<Rgb<Linear<S>, L>, Rgb<S, E>> for FromLinearLut<L, E, S::TransferFn, T>
344where
345    L: GammaLutInput,
346    E: GammaLutOutput,
347    T: LutType<E::TableValue>,
348    T::Table: Sized,
349    S: RgbStandard,
350{
351    #[inline]
352    fn convert(&self, input: Rgb<Linear<S>, L>) -> Rgb<S, E> {
353        self.lookup_rgb(input)
354    }
355}
356
357impl<L, E, S, T> ConvertOnce<Luma<Linear<S>, L>, Luma<S, E>>
358    for FromLinearLut<L, E, S::TransferFn, T>
359where
360    L: GammaLutInput,
361    E: GammaLutOutput,
362    T: LutType<E::TableValue>,
363    T::Table: Sized,
364    S: LumaStandard,
365{
366    #[inline]
367    fn convert_once(self, input: Luma<Linear<S>, L>) -> Luma<S, E> {
368        self.lookup_luma(input)
369    }
370}
371
372impl<L, E, S, T> Convert<Luma<Linear<S>, L>, Luma<S, E>> for FromLinearLut<L, E, S::TransferFn, T>
373where
374    L: GammaLutInput,
375    E: GammaLutOutput,
376    T: LutType<E::TableValue>,
377    T::Table: Sized,
378    S: LumaStandard,
379{
380    #[inline]
381    fn convert(&self, input: Luma<Linear<S>, L>) -> Luma<S, E> {
382        self.lookup_luma(input)
383    }
384}