colorutils_rs/
lalphabeta.rs1use crate::{Rgb, TransferFunction, Xyz, SRGB_TO_XYZ_D65, XYZ_TO_SRGB_D65};
8use std::ops::{Index, IndexMut, Neg};
9
10#[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 pub fn new(l: f32, alpha: f32, beta: f32) -> LAlphaBeta {
23 LAlphaBeta { l, alpha, beta }
24 }
25
26 #[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 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 #[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 #[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 #[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 #[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}