colorutils_rs/
lalphabeta.rs

1/*
2 * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved.
3 * //
4 * // Use of this source code is governed by a BSD-style
5 * // license that can be found in the LICENSE file.
6 */
7use crate::{Rgb, TransferFunction, Xyz, SRGB_TO_XYZ_D65, XYZ_TO_SRGB_D65};
8use std::ops::{Index, IndexMut, Neg};
9
10/// Represents l-alpha-beta (lαβ) colorspace
11#[repr(C)]
12#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
13pub struct LAlphaBeta {
14    pub l: f32,
15    pub alpha: f32,
16    pub beta: f32,
17}
18
19impl LAlphaBeta {
20    #[inline]
21    /// Creates new instance
22    pub fn new(l: f32, alpha: f32, beta: f32) -> LAlphaBeta {
23        LAlphaBeta { l, alpha, beta }
24    }
25
26    /// Converts RGB to l-alpha-beta
27    #[inline]
28    pub fn from_rgb(rgb: Rgb<u8>, transfer_function: TransferFunction) -> LAlphaBeta {
29        let xyz = rgb.to_xyz(&SRGB_TO_XYZ_D65, transfer_function);
30        LAlphaBeta::from_xyz(xyz)
31    }
32
33    #[inline]
34    /// Converts linear [Rgb] to [LAlphaBeta] using [Xyz] matrix
35    pub fn from_linear_rgb(rgb: Rgb<f32>, matrix: &[[f32; 3]; 3]) -> LAlphaBeta {
36        let xyz = Xyz::from_linear_rgb(rgb, matrix);
37        LAlphaBeta::from_xyz(xyz)
38    }
39
40    /// Converts XYZ to l-alpha-beta
41    #[inline]
42    pub fn from_xyz(xyz: Xyz) -> LAlphaBeta {
43        let l_lms = 0.3897 * xyz.x + 0.6890 * xyz.y - 0.0787 * xyz.z;
44        let m = -0.2298 * xyz.x + 1.1834 * xyz.y + 0.0464 * xyz.z;
45        let s = xyz.z;
46        let lp = if l_lms > 0. { l_lms.log10() } else { 0. };
47        let mp = if m > 0. { m.log10() } else { 0. };
48        let sp = if s > 0. { s.log10() } else { 0. };
49        const ONE_F_SQRT3: f32 = 0.57735026918962576f32;
50        const ONE_F_SQRT6: f32 = 0.40824829046386301f32;
51        const TWO_F_SQRT6: f32 = 0.816496580927726032f32;
52        const ONE_F_SQRT2: f32 = std::f32::consts::FRAC_1_SQRT_2;
53        let l = ONE_F_SQRT3 * lp + ONE_F_SQRT3 * mp + ONE_F_SQRT3 * sp;
54        let alpha = ONE_F_SQRT6 * lp + ONE_F_SQRT6 * mp - TWO_F_SQRT6 * sp;
55        let beta = ONE_F_SQRT2 * lp - ONE_F_SQRT2 * mp;
56        LAlphaBeta::new(l, alpha, beta)
57    }
58
59    /// Converts l-alpha-beta to XYZ
60    #[inline]
61    pub fn to_xyz(&self) -> Xyz {
62        const ONE_F_SQRT3: f32 = 0.57735026918962576f32;
63        const ONE_F_SQRT6: f32 = 0.40824829046386301f32;
64        const ONE_F_SQRT2: f32 = std::f32::consts::FRAC_1_SQRT_2;
65        const TWO_F_SQRT6: f32 = 0.816496580927726032f32;
66        let l_a = self.l * ONE_F_SQRT3;
67        let s_a = ONE_F_SQRT6 * self.alpha;
68        let p_b = ONE_F_SQRT2 * self.beta;
69        let lp = l_a + s_a + p_b;
70        let mp = l_a + s_a - p_b;
71        let sp = l_a - TWO_F_SQRT6 * self.alpha;
72        let l = if lp == 0. { 0. } else { 10f32.powf(lp) };
73        let m = if mp == 0. { 0. } else { 10f32.powf(mp) };
74        let s = if sp == 0. { 0. } else { 10f32.powf(sp) };
75        let x = 1.91024 * l - 1.11218 * m + 0.201941 * s;
76        let y = 0.370942 * l + 0.62905 * m + 5.13315e-6 * s;
77        let z = s;
78        Xyz::new(x, y, z)
79    }
80
81    /// Converts l-alpha-beta to [Rgb]
82    #[inline]
83    pub fn to_rgb(&self, transfer_function: TransferFunction) -> Rgb<u8> {
84        let xyz = self.to_xyz();
85        xyz.to_rgb(&XYZ_TO_SRGB_D65, transfer_function)
86    }
87
88    /// Converts l-alpha-beta to Linear [Rgb]
89    #[inline]
90    pub fn to_linear_rgb(&self, matrix: &[[f32; 3]; 3]) -> Rgb<f32> {
91        let xyz = self.to_xyz();
92        xyz.to_linear_rgb(matrix)
93    }
94}
95
96impl Index<usize> for LAlphaBeta {
97    type Output = f32;
98
99    #[inline]
100    fn index(&self, index: usize) -> &f32 {
101        match index {
102            0 => &self.l,
103            1 => &self.alpha,
104            2 => &self.beta,
105            _ => panic!("Index out of bounds for LAlphaBeta"),
106        }
107    }
108}
109
110impl IndexMut<usize> for LAlphaBeta {
111    #[inline]
112    fn index_mut(&mut self, index: usize) -> &mut f32 {
113        match index {
114            0 => &mut self.l,
115            1 => &mut self.alpha,
116            2 => &mut self.beta,
117            _ => panic!("Index out of bounds for LAlphaBeta"),
118        }
119    }
120}
121
122impl Neg for LAlphaBeta {
123    type Output = LAlphaBeta;
124
125    fn neg(self) -> Self::Output {
126        LAlphaBeta::new(-self.l, -self.alpha, -self.beta)
127    }
128}