1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
pub struct Oklab {
pub l: f32,
pub a: f32,
pub b: f32,
}
const M1: [[f32; 3]; 3] = [
[0.8189330101, 0.3618667424, -0.1288597137],
[0.0329845436, 0.9293118715, 0.0361456387],
[0.0482003018, 0.2643662691, 0.6338517070],
];
const M1_INV: [[f32; 3]; 3] = [
[1.2270138511, -0.5577999807, 0.2812561490],
[-0.0405801784, 1.1122568696, -0.0716766787],
[-0.0763812845, -0.4214819784, 1.5861632204],
];
const M2: [[f32; 3]; 3] = [
[0.2104542553, 0.7936177850, -0.0040720468],
[1.9779984951, -2.4285922050, 0.4505937099],
[0.0259040371, 0.7827717662, -0.8086757660],
];
const M2_INV: [[f32; 3]; 3] = [
[0.9999999985, 0.3963377922, 0.2158037581],
[1.0000000089, -0.1055613423, -0.0638541748],
[1.0000000547, -0.0894841821, -1.2914855379],
];
impl From<crate::Oklch> for Oklab {
fn from(oklch: crate::Oklch) -> Self {
Self {
l: oklch.l,
a: oklch.c * oklch.h.unnormalized_radians.cos(),
b: oklch.c * oklch.h.unnormalized_radians.sin(),
}
}
}
impl crate::ColorSpace for Oklab {
const BLACK: Self = Self {
l: 0.0,
a: 0.0,
b: 0.0,
};
const WHITE: Self = Self {
l: 1.0,
a: 0.0,
b: 0.0,
};
fn in_bounds(self) -> bool {
crate::approx_in_range(self.l, 0.0..1.0)
&& crate::approx_in_range(self.a, -1.0..1.0)
&& crate::approx_in_range(self.b, -1.0..1.0)
}
}
#[allow(clippy::many_single_char_names)]
impl crate::CoreColorSpace for Oklab {
fn from_xyz(xyz: crate::Xyz) -> Self {
let l = M1[0][0] * xyz.x + M1[0][1] * xyz.y + M1[0][2] * xyz.z;
let m = M1[1][0] * xyz.x + M1[1][1] * xyz.y + M1[1][2] * xyz.z;
let s = M1[2][0] * xyz.x + M1[2][1] * xyz.y + M1[2][2] * xyz.z;
let l_ = l.cbrt();
let m_ = m.cbrt();
let s_ = s.cbrt();
let l = M2[0][0] * l_ + M2[0][1] * m_ + M2[0][2] * s_;
let a = M2[1][0] * l_ + M2[1][1] * m_ + M2[1][2] * s_;
let b = M2[2][0] * l_ + M2[2][1] * m_ + M2[2][2] * s_;
Self { l, a, b }
}
fn to_xyz(self) -> crate::Xyz {
let l_ = M2_INV[0][0] * self.l + M2_INV[0][1] * self.a + M2_INV[0][2] * self.b;
let m_ = M2_INV[1][0] * self.l + M2_INV[1][1] * self.a + M2_INV[1][2] * self.b;
let s_ = M2_INV[2][0] * self.l + M2_INV[2][1] * self.a + M2_INV[2][2] * self.b;
let l = l_.powi(3);
let m = m_.powi(3);
let s = s_.powi(3);
let x = M1_INV[0][0] * l + M1_INV[0][1] * m + M1_INV[0][2] * s;
let y = M1_INV[1][0] * l + M1_INV[1][1] * m + M1_INV[1][2] * s;
let z = M1_INV[2][0] * l + M1_INV[2][1] * m + M1_INV[2][2] * s;
crate::Xyz { x, y, z }
}
}