ini_material_color_utilities_rs/util/
color.rs1use crate::util::math;
2
3pub const SRGB_TO_XYZ: [[f64; 3]; 3] = [
4 [0.41233895, 0.35762064, 0.18051042],
5 [0.2126, 0.7152, 0.0722],
6 [0.01932141, 0.11916382, 0.95034478],
7];
8
9pub const XYZ_TO_SRGB: [[f64; 3]; 3] = [
10 [
11 3.2413774792388685,
12 -1.5376652402851851,
13 -0.49885366846268053,
14 ],
15 [-0.9691452513005321, 1.8758853451067872, 0.04156585616912061],
16 [
17 0.05562093689691305,
18 -0.20395524564742123,
19 1.0571799111220335,
20 ],
21];
22
23pub const WHITE_POINT_D65: [f64; 3] = [95.047, 100.0, 108.883];
24
25pub type RGB = [u8; 3];
26pub type ARGB = [u8; 4];
27pub type LinearRGB = [f64; 3];
28pub type XYZ = [f64; 3];
29pub type LAB = [f64; 3];
30
31pub fn argb_from_rgb([r, g, b]: RGB) -> ARGB {
33 [255, r, g, b]
34}
35
36pub fn format_argb_as_rgb(argb: [u8; 4]) -> String {
38 format!("#{:02x}{:02x}{:02x}", argb[1], argb[2], argb[3])
39}
40
41pub fn argb_from_linrgb([r, g, b]: LinearRGB) -> ARGB {
43 let r = delinearized(r);
44 let g = delinearized(g);
45 let b = delinearized(b);
46
47 argb_from_rgb([r, g, b])
48}
49
50pub fn argb_from_xyz(xyz: XYZ) -> ARGB {
52 let [r, g, b] = math::matrix_multiply(xyz, XYZ_TO_SRGB);
53 let r = delinearized(r);
54 let g = delinearized(g);
55 let b = delinearized(b);
56
57 argb_from_rgb([r, g, b])
58}
59
60pub fn xyz_from_argb([_, r, g, b]: ARGB) -> XYZ {
62 let r = linearized(r);
63 let g = linearized(g);
64 let b = linearized(b);
65
66 math::matrix_multiply([r, g, b], SRGB_TO_XYZ)
67}
68
69pub fn argb_from_lab(l: f64, a: f64, b: f64) -> ARGB {
71 let fy = (l + 16.0) / 116.0;
72 let fx = a / 500.0 + fy;
73 let fz = fy - b / 200.0;
74 let x = lab_invf(fx) * WHITE_POINT_D65[0];
75 let y = lab_invf(fy) * WHITE_POINT_D65[1];
76 let z = lab_invf(fz) * WHITE_POINT_D65[2];
77
78 argb_from_xyz([x, y, z])
79}
80
81pub fn lab_from_argb(argb: ARGB) -> LAB {
89 let [x, y, z] = xyz_from_argb(argb);
90 let fx = lab_f(x / WHITE_POINT_D65[0]);
91 let fy = lab_f(y / WHITE_POINT_D65[1]);
92 let fz = lab_f(z / WHITE_POINT_D65[2]);
93 let l = 116.0 * fy - 16.0;
94 let a = 500.0 * (fx - fy);
95 let b = 200.0 * (fy - fz);
96
97 [l, a, b]
98}
99
100pub fn argb_from_lstar(lstar: f64) -> ARGB {
108 let y = y_from_lstar(lstar);
109 let w = delinearized(y);
110
111 argb_from_rgb([w, w, w])
112}
113
114pub fn lstar_from_argb(argb: ARGB) -> f64 {
122 let y = xyz_from_argb(argb)[1];
123
124 116.0 * lab_f(y / 100.0) - 16.0
125}
126
127pub fn y_from_lstar(lstar: f64) -> f64 {
138 100.0 * lab_invf((lstar + 16.0) / 116.0)
139}
140
141pub fn linearized(rgb_comp: u8) -> f64 {
149 let normalized = rgb_comp as f64 / 255.0;
150 if normalized <= 0.040449936 {
151 normalized / 12.92 * 100.0
152 } else {
153 ((normalized + 0.055) / 1.055).powf(2.4) * 100.0
154 }
155}
156
157pub fn delinearized(rgb_comp: f64) -> u8 {
165 let normalized = rgb_comp / 100.0;
166 let delinearized = if normalized <= 0.0031308 {
167 normalized * 12.92
168 } else {
169 1.055 * normalized.powf(1.0 / 2.4) - 0.055
170 };
171 (delinearized * 255.0).round().clamp(0.0, 255.0) as u8
172}
173
174fn lab_f(t: f64) -> f64 {
175 let e = 216.0 / 24389.0;
176 let kappa = 24389.0 / 27.0;
177 if t > e {
178 t.powf(1.0 / 3.0)
179 } else {
180 (kappa * t + 16.0) / 116.0
181 }
182}
183
184fn lab_invf(ft: f64) -> f64 {
185 let e = 216.0 / 24389.0;
186 let kappa = 24389.0 / 27.0;
187 let ft3 = ft * ft * ft;
188 if ft3 > e {
189 ft3
190 } else {
191 (116.0 * ft - 16.0) / kappa
192 }
193}