1#[cfg(any(feature = "std", feature = "no_std"))]
16use crate::srgb::{LinearSrgb32, LinearSrgba32, Srgb32, Srgb8, Srgba32, Srgba8};
17use devela::cmp::{pclamp, pmax};
18
19#[cfg(all(feature = "no_std", not(feature = "std")))]
20use libm::{atan2f, cbrtf, cosf, hypotf, powf, sinf};
21
22#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd)]
33pub struct Oklab32 {
34 pub l: f32,
36 pub a: f32,
38 pub b: f32,
40}
41
42impl Oklab32 {
44 pub fn new(lightness: f32, a: f32, b: f32) -> Oklab32 {
51 let l = pmax(0.0, lightness);
52 let a = pclamp(a, -0.5, 0.5);
53 let b = pclamp(b, -0.5, 0.5);
54
55 Self { l, a, b }
56 }
57}
58
59impl Oklab32 {
61 pub const L_MIN: f32 = 0.;
63 pub const L_MAX: f32 = 100.;
65
66 pub const A_MIN: f32 = -0.5;
68 pub const A_MAX: f32 = 0.5;
70
71 pub const B_MIN: f32 = -0.5;
73 pub const B_MAX: f32 = 0.5;
75}
76
77impl Oklab32 {
79 #[inline]
82 #[cfg(any(feature = "std", feature = "no_std"))]
83 #[cfg_attr(
84 feature = "nightly",
85 doc(cfg(any(feature = "std", feature = "no_std")))
86 )]
87 pub fn squared_distance(&self, other: &Oklab32) -> f32 {
88 #[cfg(feature = "std")]
89 return (self.l - other.l).powi(2)
90 + (self.a - other.a).powi(2)
91 + (self.b - other.b).powi(2);
92
93 #[cfg(not(feature = "std"))]
94 return powf(self.l - other.l, 2.)
95 + powf(self.a - other.a, 2.)
96 + powf(self.b - other.b, 2.);
97 }
98
99 }
107
108#[derive(Debug, Clone, Copy, Default, PartialEq, PartialOrd)]
117pub struct Oklch32 {
118 pub l: f32,
120 pub c: f32,
122 pub h: f32,
128}
129
130impl Oklch32 {
132 pub fn new(luminance: f32, chroma: f32, hue: f32) -> Oklch32 {
134 let l = pclamp(luminance, 0.0, 100.0);
135 let c = pclamp(chroma, 0.0, 0.5);
136 let h = pclamp(hue, 0.0, 360.);
137
138 Self { l, c, h }
139 }
140}
141
142impl Oklch32 {
144 pub const L_MIN: f32 = 0.;
146 pub const L_MAX: f32 = 100.;
148
149 pub const C_MIN: f32 = 0.;
151 pub const C_MAX: f32 = 0.5;
153
154 pub const H_MIN: f32 = 0.;
156 pub const H_MAX: f32 = 360.;
158}
159
160#[inline]
169#[cfg(any(feature = "std", feature = "no_std"))]
170fn oklab32_to_oklch32(c: Oklab32) -> Oklch32 {
171 #[cfg(feature = "std")]
172 {
173 use core::f32::consts::PI as PI_32;
174 let hue = c.b.atan2(c.a) * 180. / PI_32;
175 #[rustfmt::skip]
176 let h = if hue >= 0. { hue } else { hue + 360. };
177
178 Oklch32 {
179 l: c.l,
180 c: c.a.hypot(c.b),
181 h,
182 }
183 }
184
185 #[cfg(not(feature = "std"))]
186 {
187 use core::f32::consts::PI as PI_32;
188 let hue = atan2f(c.b, c.a) * 180. / PI_32;
189 #[rustfmt::skip]
190 let h = if hue >= 0. { hue } else { hue + 360. };
191
192 Oklch32 {
193 l: c.l,
194 c: hypotf(c.a, c.b),
195 h,
196 }
197 }
198}
199
200#[inline]
202#[cfg(any(feature = "std", feature = "no_std"))]
203fn oklch32_to_oklab32(c: Oklch32) -> Oklab32 {
204 #[cfg(feature = "std")]
205 {
206 use core::f32::consts::PI as PI_32;
207 Oklab32 {
208 l: c.l,
209 a: c.c * (c.h * PI_32 / 180.).cos(),
210 b: c.c * (c.h * PI_32 / 180.).sin(),
211 }
212 }
213
214 #[cfg(not(feature = "std"))]
215 {
216 use core::f32::consts::PI as PI_32;
217 Oklab32 {
218 l: c.l,
219 a: c.c * cosf(c.h * PI_32 / 180.),
220 b: c.c * sinf(c.h * PI_32 / 180.),
221 }
222 }
223}
224
225#[cfg(any(feature = "std", feature = "no_std"))]
227fn linear_srgb32_to_oklab32(c: LinearSrgb32) -> Oklab32 {
228 #[cfg(feature = "std")]
229 let l = (0.4122214708 * c.r + 0.5363325363 * c.g + 0.0514459929 * c.b).cbrt();
230 #[cfg(not(feature = "std"))]
231 let l = cbrtf(0.4122214708 * c.r + 0.5363325363 * c.g + 0.0514459929 * c.b);
232 #[cfg(feature = "std")]
233 let m = (0.2119034982 * c.r + 0.6806995451 * c.g + 0.1073969566 * c.b).cbrt();
234 #[cfg(not(feature = "std"))]
235 let m = cbrtf(0.2119034982 * c.r + 0.6806995451 * c.g + 0.1073969566 * c.b);
236 #[cfg(feature = "std")]
237 let s = (0.0883024619 * c.r + 0.2817188376 * c.g + 0.6299787005 * c.b).cbrt();
238 #[cfg(not(feature = "std"))]
239 let s = cbrtf(0.0883024619 * c.r + 0.2817188376 * c.g + 0.6299787005 * c.b);
240
241 Oklab32 {
242 l: 0.2104542553 * l + 0.7936177850 * m - 0.0040720468 * s,
243 a: 1.9779984951 * l - 2.4285922050 * m + 0.4505937099 * s,
244 b: 0.0259040371 * l + 0.7827717662 * m - 0.8086757660 * s,
245 }
246}
247
248#[cfg(any(feature = "std", feature = "no_std"))]
250fn oklab32_to_linear_srgb32(c: Oklab32) -> LinearSrgb32 {
251 let _l = c.l + 0.3963377774 * c.a + 0.2158037573 * c.b;
252 let _m = c.l - 0.1055613458 * c.a - 0.0638541728 * c.b;
253 let _s = c.l - 0.0894841775 * c.a - 1.2914855480 * c.b;
254
255 let l = _l * _l * _l;
256 let m = _m * _m * _m;
257 let s = _s * _s * _s;
258
259 LinearSrgb32 {
260 r: 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s,
261 g: -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s,
262 b: -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s,
263 }
264}
265
266impl Oklab32 {
268 #[inline]
272 pub fn from_array(c: [f32; 3]) -> Oklab32 {
273 Oklab32 {
274 l: c[0],
275 a: c[1],
276 b: c[2],
277 }
278 }
279 #[inline]
281 pub fn to_array(c: Oklab32) -> [f32; 3] {
282 [c.l, c.a, c.b]
283 }
284
285 #[inline]
287 pub fn from_tuple(c: (f32, f32, f32)) -> Oklab32 {
288 Oklab32 {
289 l: c.0,
290 a: c.1,
291 b: c.2,
292 }
293 }
294 #[inline]
296 pub fn to_tuple(c: Oklab32) -> (f32, f32, f32) {
297 (c.l, c.a, c.b)
298 }
299}
300
301#[cfg(any(feature = "std", feature = "no_std"))]
302#[cfg_attr(
303 feature = "nightly",
304 doc(cfg(any(feature = "std", feature = "no_std")))
305)]
306impl Oklab32 {
307 #[inline]
311 pub fn from_linear_srgb32(c: LinearSrgb32) -> Oklab32 {
312 linear_srgb32_to_oklab32(c)
313 }
314
315 #[inline]
317 pub fn to_linear_srgb32(&self) -> LinearSrgb32 {
318 oklab32_to_linear_srgb32(*self)
319 }
320
321 #[inline]
327 pub fn from_linear_srgba32(c: LinearSrgba32) -> Oklab32 {
328 c.to_linear_srgb32().to_oklab32()
329 }
330
331 #[inline]
335 pub fn to_linear_srgba32(&self, alpha: f32) -> LinearSrgba32 {
336 oklab32_to_linear_srgb32(*self).to_linear_srgba32(alpha)
337 }
338
339 #[inline]
343 pub fn to_oklch32(&self) -> Oklch32 {
344 oklab32_to_oklch32(*self)
345 }
346
347 #[inline]
349 pub fn from_oklch32(c: Oklch32) -> Oklab32 {
350 oklch32_to_oklab32(c)
351 }
352}
353
354#[cfg(any(feature = "std", feature = "no_std"))]
356#[cfg_attr(
357 feature = "nightly",
358 doc(cfg(any(feature = "std", feature = "no_std")))
359)]
360impl Oklab32 {
361 #[inline]
365 pub fn from_srgb8(c: Srgb8) -> Oklab32 {
366 c.to_oklab32()
367 }
368
369 #[inline]
371 pub fn to_srgb8(&self) -> Srgb8 {
372 self.to_linear_srgb32().to_srgb32().to_srgb8()
373 }
374
375 #[inline]
381 pub fn from_srgba8(c: Srgba8) -> Oklab32 {
382 c.to_oklab32()
383 }
384
385 #[inline]
389 pub fn to_srgba8(&self, alpha: u8) -> Srgba8 {
390 self.to_linear_srgb32().to_srgb32().to_srgba8(alpha)
391 }
392
393 #[inline]
397 pub fn from_srgb32(c: Srgb32) -> Oklab32 {
398 c.to_oklab32()
399 }
400
401 #[inline]
403 pub fn to_srgb32(&self) -> Srgb32 {
404 self.to_linear_srgb32().to_srgb32()
405 }
406
407 #[inline]
413 pub fn from_srgba32(c: Srgba32) -> Oklab32 {
414 c.to_oklab32()
415 }
416
417 #[inline]
421 pub fn to_srgba32(&self, alpha: f32) -> Srgba32 {
422 self.to_linear_srgb32().to_srgba32(alpha)
423 }
424}
425
426impl Oklch32 {
428 #[inline]
432 pub fn from_array(c: [f32; 3]) -> Oklch32 {
433 Oklch32 {
434 l: c[0],
435 c: c[1],
436 h: c[2],
437 }
438 }
439 #[inline]
441 pub fn to_array(c: Oklch32) -> [f32; 3] {
442 [c.l, c.c, c.h]
443 }
444
445 #[inline]
447 pub fn from_tuple(c: (f32, f32, f32)) -> Oklch32 {
448 Oklch32 {
449 l: c.0,
450 c: c.1,
451 h: c.2,
452 }
453 }
454 #[inline]
456 pub fn to_tuple(c: Oklch32) -> (f32, f32, f32) {
457 (c.l, c.c, c.h)
458 }
459}
460
461#[cfg(any(feature = "std", feature = "no_std"))]
462#[cfg_attr(
463 feature = "nightly",
464 doc(cfg(any(feature = "std", feature = "no_std")))
465)]
466impl Oklch32 {
467 #[inline]
471 pub fn from_oklab32(c: Oklab32) -> Oklch32 {
472 oklab32_to_oklch32(c)
473 }
474
475 #[inline]
477 pub fn to_oklab32(&self) -> Oklab32 {
478 oklch32_to_oklab32(*self)
479 }
480
481 #[inline]
485 pub fn from_srgb8(c: Srgb8) -> Oklch32 {
486 c.to_oklch32()
487 }
488
489 #[inline]
491 pub fn to_srgb8(&self) -> Srgb8 {
492 self.to_oklab32().to_linear_srgb32().to_srgb32().to_srgb8()
493 }
494
495 #[inline]
501 pub fn from_srgba8(c: Srgba8) -> Oklch32 {
502 c.to_oklch32()
503 }
504
505 #[inline]
509 pub fn to_srgba8(&self, alpha: u8) -> Srgba8 {
510 self.to_oklab32()
511 .to_linear_srgb32()
512 .to_srgb32()
513 .to_srgba8(alpha)
514 }
515
516 #[inline]
520 pub fn from_srgb32(c: Srgb32) -> Oklch32 {
521 c.to_oklch32()
522 }
523 #[inline]
525 pub fn to_srgb32(&self) -> Srgb32 {
526 self.to_oklab32().to_linear_srgb32().to_srgb32()
527 }
528
529 #[inline]
535 pub fn from_srgba32(c: Srgba32) -> Oklch32 {
536 c.to_oklch32()
537 }
538
539 #[inline]
543 pub fn to_srgba32(&self, alpha: f32) -> Srgba32 {
544 self.to_oklab32().to_linear_srgb32().to_srgba32(alpha)
545 }
546
547 #[inline]
551 pub fn from_linear_srgb32(c: LinearSrgb32) -> Oklch32 {
552 c.to_oklch32()
553 }
554
555 #[inline]
557 pub fn to_linear_srgb32(&self) -> LinearSrgb32 {
558 self.to_oklab32().to_linear_srgb32()
559 }
560
561 #[inline]
567 pub fn from_linear_srgba32(c: LinearSrgba32) -> Oklch32 {
568 c.to_oklch32()
569 }
570
571 #[inline]
575 pub fn to_linear_srgba32(&self, alpha: f32) -> LinearSrgba32 {
576 self.to_oklab32().to_linear_srgba32(alpha)
577 }
578}
579
580mod impl_from {
581 use super::*;
582
583 impl From<[f32; 3]> for Oklab32 {
586 #[inline]
587 fn from(c: [f32; 3]) -> Oklab32 {
588 Oklab32::new(c[0], c[1], c[2])
589 }
590 }
591
592 impl From<(f32, f32, f32)> for Oklab32 {
593 #[inline]
594 fn from(c: (f32, f32, f32)) -> Oklab32 {
595 Oklab32::new(c.0, c.1, c.2)
596 }
597 }
598
599 impl From<[f32; 3]> for Oklch32 {
600 #[inline]
601 fn from(c: [f32; 3]) -> Oklch32 {
602 Oklch32::new(c[0], c[1], c[2])
603 }
604 }
605
606 impl From<(f32, f32, f32)> for Oklch32 {
607 #[inline]
608 fn from(c: (f32, f32, f32)) -> Oklch32 {
609 Oklch32::new(c.0, c.1, c.2)
610 }
611 }
612
613 impl From<Oklab32> for [f32; 3] {
616 #[inline]
617 fn from(c: Oklab32) -> [f32; 3] {
618 [c.l, c.a, c.b]
619 }
620 }
621
622 impl From<Oklab32> for (f32, f32, f32) {
623 #[inline]
624 fn from(c: Oklab32) -> (f32, f32, f32) {
625 (c.l, c.a, c.b)
626 }
627 }
628}
629
630#[cfg(any(feature = "std", feature = "no_std"))]
631#[cfg_attr(
632 feature = "nightly",
633 doc(cfg(any(feature = "std", feature = "no_std")))
634)]
635mod impl_from_std {
636 use super::*;
637
638 impl From<Oklab32> for Oklch32 {
639 #[inline]
640 fn from(c: Oklab32) -> Oklch32 {
641 c.to_oklch32()
642 }
643 }
644
645 impl From<Oklab32> for Srgb8 {
646 #[inline]
647 fn from(c: Oklab32) -> Srgb8 {
648 c.to_linear_srgb32().to_srgb32().to_srgb8()
649 }
650 }
651
652 impl From<Oklab32> for Srgba8 {
653 #[inline]
655 fn from(c: Oklab32) -> Srgba8 {
656 c.to_srgba8(u8::MAX)
657 }
658 }
659
660 impl From<Oklab32> for Srgb32 {
661 #[inline]
662 fn from(c: Oklab32) -> Srgb32 {
663 c.to_srgb32()
664 }
665 }
666
667 impl From<Oklab32> for Srgba32 {
668 #[inline]
670 fn from(c: Oklab32) -> Srgba32 {
671 c.to_srgba32(1.)
672 }
673 }
674
675 impl From<Oklab32> for LinearSrgb32 {
676 #[inline]
677 fn from(c: Oklab32) -> LinearSrgb32 {
678 c.to_linear_srgb32()
679 }
680 }
681
682 impl From<Oklab32> for LinearSrgba32 {
683 #[inline]
685 fn from(c: Oklab32) -> LinearSrgba32 {
686 c.to_linear_srgba32(1.)
687 }
688 }
689
690 impl From<Oklch32> for [f32; 3] {
693 #[inline]
694 fn from(c: Oklch32) -> [f32; 3] {
695 [c.l, c.c, c.h]
696 }
697 }
698
699 impl From<Oklch32> for (f32, f32, f32) {
700 #[inline]
701 fn from(c: Oklch32) -> (f32, f32, f32) {
702 (c.l, c.c, c.h)
703 }
704 }
705
706 impl From<Oklch32> for Oklab32 {
707 #[inline]
708 fn from(c: Oklch32) -> Oklab32 {
709 c.to_oklab32()
710 }
711 }
712}