1use 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#[repr(C)]
18#[derive(Copy, Clone, Debug, Default, PartialOrd, PartialEq)]
19pub struct Lab {
20 pub l: f32,
22 pub a: f32,
24 pub b: f32,
26}
27
28impl Lab {
29 #[inline]
37 pub fn new(l: f32, a: f32, b: f32) -> Self {
38 Self { l, a, b }
39 }
40}
41
42impl Lab {
43 #[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 #[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 #[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 #[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 #[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 #[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}