colorutils_rs/
lab.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::Rgb;
8use crate::taxicab::TaxicabDistance;
9use crate::xyz::Xyz;
10use crate::EuclideanDistance;
11use num_traits::Pow;
12use std::ops::{
13    Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Sub, SubAssign,
14};
15
16/// Represents CIELAB color space.
17#[repr(C)]
18#[derive(Copy, Clone, Debug, Default, PartialOrd, PartialEq)]
19pub struct Lab {
20    /// `l`: lightness component (0 to 100)
21    pub l: f32,
22    /// `a`: green (negative) and red (positive) component.
23    pub a: f32,
24    /// `b`: blue (negative) and yellow (positive) component
25    pub b: f32,
26}
27
28impl Lab {
29    /// Create a new CIELAB color.
30    ///
31    /// `l`: lightness component (0 to 100)
32    ///
33    /// `a`: green (negative) and red (positive) component.
34    ///
35    /// `b`: blue (negative) and yellow (positive) component.
36    #[inline]
37    pub fn new(l: f32, a: f32, b: f32) -> Self {
38        Self { l, a, b }
39    }
40}
41
42impl Lab {
43    /// Converts to CIE Lab from CIE XYZ
44    #[inline]
45    pub fn from_xyz(xyz: Xyz) -> Self {
46        let x = xyz.x * 100f32 / 95.047f32;
47        let y = xyz.y;
48        let z = xyz.z * 100f32 / 108.883f32;
49        let x = if x > 0.008856f32 {
50            x.cbrt()
51        } else {
52            7.787f32 * x + 16f32 / 116f32
53        };
54        let y = if y > 0.008856f32 {
55            y.cbrt()
56        } else {
57            7.787f32 * y + 16f32 / 116f32
58        };
59        let z = if z > 0.008856f32 {
60            z.cbrt()
61        } else {
62            7.787f32 * z + 16f32 / 116f32
63        };
64        Self::new((116f32 * y) - 16f32, 500f32 * (x - y), 200f32 * (y - z))
65    }
66
67    /// Converts to CIE Lab from Rgb
68    #[inline]
69    pub fn from_rgb(rgb: Rgb<u8>) -> Self {
70        let xyz = Xyz::from_srgb(rgb);
71        Self::from_xyz(xyz)
72    }
73
74    pub const fn l_range() -> (f32, f32) {
75        (0., 100.)
76    }
77
78    pub const fn a_range() -> (f32, f32) {
79        (-86.185f32, 98.254f32)
80    }
81
82    pub const fn b_range() -> (f32, f32) {
83        (-107.863f32, 94.482f32)
84    }
85}
86
87impl Lab {
88    /// Converts CIE [Lab] into CIE [Xyz]
89    #[inline]
90    pub fn to_xyz(&self) -> Xyz {
91        let y = (self.l + 16.0) / 116.0;
92        let x = self.a * (1f32 / 500f32) + y;
93        let z = y - self.b * (1f32 / 200f32);
94        let dx = x * x;
95        let x3 = dx * x;
96        let dy = y * y;
97        let y3 = dy * y;
98        let dz = z * z;
99        let z3 = dz * z;
100        let x = 95.047
101            * if x3 > 0.008856 {
102                x3
103            } else {
104                (x - 16.0 / 116.0) / 7.787
105            };
106        let y = 100.0
107            * if y3 > 0.008856 {
108                y3
109            } else {
110                (y - 16.0 / 116.0) / 7.787
111            };
112        let z = 108.883
113            * if z3 > 0.008856 {
114                z3
115            } else {
116                (z - 16.0 / 116.0) / 7.787
117            };
118        Xyz::new(x * (1. / 100f32), y * (1. / 100f32), z * (1. / 100f32))
119    }
120
121    /// Converts CIE Lab into Rgb
122    #[inline]
123    pub fn to_rgb8(&self) -> Rgb<u8> {
124        let xyz = self.to_xyz();
125        Xyz::new(xyz.x, xyz.y, xyz.z).to_srgb()
126    }
127
128    /// Converts CIE [Lab] into linear [Rgb]
129    #[inline]
130    pub fn to_linear_rgb(&self, matrix: &[[f32; 3]; 3]) -> Rgb<f32> {
131        let xyz = self.to_xyz();
132        xyz.to_linear_rgb(matrix)
133    }
134
135    /// Converts CIE Lab into Rgb
136    #[inline]
137    pub fn to_rgb(&self) -> Rgb<u8> {
138        self.to_rgb8()
139    }
140
141    #[inline]
142    pub fn hybrid_distance(&self, other: Self) -> f32 {
143        let lax = self.l - other.l;
144        let dax = self.a - other.a;
145        let bax = self.b - other.b;
146        (dax * dax + bax * bax).sqrt() + lax.abs()
147    }
148}
149
150impl EuclideanDistance for Lab {
151    #[inline]
152    fn euclidean_distance(&self, other: Lab) -> f32 {
153        let lax = self.l - other.l;
154        let dax = self.a - other.a;
155        let bax = self.b - other.b;
156        (lax * lax + dax * dax + bax * bax).sqrt()
157    }
158}
159
160impl TaxicabDistance for Lab {
161    #[inline]
162    fn taxicab_distance(&self, other: Self) -> f32 {
163        let lax = self.l - other.l;
164        let dax = self.a - other.a;
165        let bax = self.b - other.b;
166        lax.abs() + dax.abs() + bax.abs()
167    }
168}
169
170impl Index<usize> for Lab {
171    type Output = f32;
172
173    #[inline]
174    fn index(&self, index: usize) -> &f32 {
175        match index {
176            0 => &self.l,
177            1 => &self.a,
178            2 => &self.b,
179            _ => panic!("Index out of bounds for Lab"),
180        }
181    }
182}
183
184impl IndexMut<usize> for Lab {
185    #[inline]
186    fn index_mut(&mut self, index: usize) -> &mut f32 {
187        match index {
188            0 => &mut self.l,
189            1 => &mut self.a,
190            2 => &mut self.b,
191            _ => panic!("Index out of bounds for Lab"),
192        }
193    }
194}
195
196impl Add<Lab> for Lab {
197    type Output = Lab;
198
199    #[inline]
200    fn add(self, rhs: Self) -> Lab {
201        Lab::new(self.l + rhs.l, self.a + rhs.a, self.b + rhs.b)
202    }
203}
204
205impl Add<f32> for Lab {
206    type Output = Lab;
207
208    #[inline]
209    fn add(self, rhs: f32) -> Lab {
210        Lab::new(self.l + rhs, self.a + rhs, self.b + rhs)
211    }
212}
213
214impl AddAssign<Lab> for Lab {
215    #[inline]
216    fn add_assign(&mut self, rhs: Lab) {
217        self.l += rhs.l;
218        self.a += rhs.a;
219        self.b += rhs.b;
220    }
221}
222
223impl AddAssign<f32> for Lab {
224    #[inline]
225    fn add_assign(&mut self, rhs: f32) {
226        self.l += rhs;
227        self.a += rhs;
228        self.b += rhs;
229    }
230}
231
232impl Mul<f32> for Lab {
233    type Output = Lab;
234
235    #[inline]
236    fn mul(self, rhs: f32) -> Self::Output {
237        Lab::new(self.l * rhs, self.a * rhs, self.b * rhs)
238    }
239}
240
241impl Mul<Lab> for Lab {
242    type Output = Lab;
243
244    #[inline]
245    fn mul(self, rhs: Lab) -> Self::Output {
246        Lab::new(self.l * rhs.l, self.a * rhs.a, self.b * rhs.b)
247    }
248}
249
250impl MulAssign<f32> for Lab {
251    #[inline]
252    fn mul_assign(&mut self, rhs: f32) {
253        self.l *= rhs;
254        self.a *= rhs;
255        self.b *= rhs;
256    }
257}
258
259impl MulAssign<Lab> for Lab {
260    #[inline]
261    fn mul_assign(&mut self, rhs: Lab) {
262        self.l *= rhs.l;
263        self.a *= rhs.a;
264        self.b *= rhs.b;
265    }
266}
267
268impl Sub<f32> for Lab {
269    type Output = Lab;
270
271    #[inline]
272    fn sub(self, rhs: f32) -> Self::Output {
273        Lab::new(self.l - rhs, self.a - rhs, self.b - rhs)
274    }
275}
276
277impl Sub<Lab> for Lab {
278    type Output = Lab;
279
280    #[inline]
281    fn sub(self, rhs: Lab) -> Self::Output {
282        Lab::new(self.l - rhs.l, self.a - rhs.a, self.b - rhs.b)
283    }
284}
285
286impl SubAssign<f32> for Lab {
287    #[inline]
288    fn sub_assign(&mut self, rhs: f32) {
289        self.l -= rhs;
290        self.a -= rhs;
291        self.b -= rhs;
292    }
293}
294
295impl SubAssign<Lab> for Lab {
296    #[inline]
297    fn sub_assign(&mut self, rhs: Lab) {
298        self.l -= rhs.l;
299        self.a -= rhs.a;
300        self.b -= rhs.b;
301    }
302}
303
304impl Div<f32> for Lab {
305    type Output = Lab;
306
307    #[inline]
308    fn div(self, rhs: f32) -> Self::Output {
309        Lab::new(self.l / rhs, self.a / rhs, self.b / rhs)
310    }
311}
312
313impl Div<Lab> for Lab {
314    type Output = Lab;
315
316    #[inline]
317    fn div(self, rhs: Lab) -> Self::Output {
318        Lab::new(self.l / rhs.l, self.a / rhs.a, self.b / rhs.b)
319    }
320}
321
322impl DivAssign<f32> for Lab {
323    #[inline]
324    fn div_assign(&mut self, rhs: f32) {
325        self.l /= rhs;
326        self.a /= rhs;
327        self.b /= rhs;
328    }
329}
330
331impl DivAssign<Lab> for Lab {
332    #[inline]
333    fn div_assign(&mut self, rhs: Lab) {
334        self.l /= rhs.l;
335        self.a /= rhs.a;
336        self.b /= rhs.b;
337    }
338}
339
340impl Neg for Lab {
341    type Output = Lab;
342
343    #[inline]
344    fn neg(self) -> Self::Output {
345        Lab::new(-self.l, -self.a, -self.b)
346    }
347}
348
349impl Pow<f32> for Lab {
350    type Output = Lab;
351
352    #[inline]
353    fn pow(self, rhs: f32) -> Self::Output {
354        Lab::new(self.l * rhs, self.a * rhs, self.b * rhs)
355    }
356}
357
358impl Pow<Lab> for Lab {
359    type Output = Lab;
360
361    #[inline]
362    fn pow(self, rhs: Lab) -> Self::Output {
363        Lab::new(self.l.powf(rhs.l), self.a.powf(rhs.a), self.b.powf(self.b))
364    }
365}
366
367impl Lab {
368    #[inline]
369    pub fn sqrt(&self) -> Lab {
370        Lab::new(
371            if self.l < 0. { 0. } else { self.l.sqrt() },
372            if self.a < 0. { 0. } else { self.a.sqrt() },
373            if self.b < 0. { 0. } else { self.b.sqrt() },
374        )
375    }
376
377    #[inline]
378    pub fn cbrt(&self) -> Lab {
379        Lab::new(self.l.cbrt(), self.a.cbrt(), self.b.cbrt())
380    }
381}