1fn fnv1a_utf16(name: &str) -> u32 {
11 let mut hash: u32 = 2166136261;
12 for unit in name.encode_utf16() {
13 hash ^= unit as u32;
14 hash = hash.wrapping_mul(16777619);
15 }
16 hash
17}
18
19fn hsl_to_rgb(h: f64, s: f64, l: f64) -> [f64; 3] {
21 let q = if l < 0.5 { l * (1.0 + s) } else { l + s - l * s };
22 let p = 2.0 * l - q;
23 let channel = |t: f64| -> f64 {
24 let t = t.rem_euclid(1.0);
25 if t < 1.0 / 6.0 {
26 p + (q - p) * 6.0 * t
27 } else if t < 0.5 {
28 q
29 } else if t < 2.0 / 3.0 {
30 p + (q - p) * (2.0 / 3.0 - t) * 6.0
31 } else {
32 p
33 }
34 };
35 [channel(h + 1.0 / 3.0), channel(h), channel(h - 1.0 / 3.0)]
36}
37
38pub fn solid_color_srgb(name: &str) -> [f64; 3] {
40 let hue = ((fnv1a_utf16(name) >> 8) % 360) as f64 / 360.0;
41 hsl_to_rgb(hue, 0.45, 0.6)
42}
43
44pub fn srgb_to_linear(c: f64) -> f64 {
46 if c <= 0.04045 {
47 c / 12.92
48 } else {
49 ((c + 0.055) / 1.055).powf(2.4)
50 }
51}
52
53pub fn hex_to_linear(hex: u32) -> [f32; 3] {
55 let r = ((hex >> 16) & 0xff) as f64 / 255.0;
56 let g = ((hex >> 8) & 0xff) as f64 / 255.0;
57 let b = (hex & 0xff) as f64 / 255.0;
58 [
59 srgb_to_linear(r) as f32,
60 srgb_to_linear(g) as f32,
61 srgb_to_linear(b) as f32,
62 ]
63}
64
65pub fn hex_to_srgb(hex: u32) -> [f64; 3] {
68 [
69 ((hex >> 16) & 0xff) as f64 / 255.0,
70 ((hex >> 8) & 0xff) as f64 / 255.0,
71 (hex & 0xff) as f64 / 255.0,
72 ]
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78
79 #[test]
80 fn solid_color_matches_js_reference() {
81 let hash = fnv1a_utf16("E17");
84 assert_eq!(hash, {
85 let mut h: u32 = 2166136261;
86 for c in "E17".chars() {
87 h ^= c as u32;
88 h = h.wrapping_mul(16777619);
89 }
90 h
91 });
92 let rgb = solid_color_srgb("E17");
93 for c in rgb {
94 assert!((0.0..=1.0).contains(&c));
95 }
96 assert_eq!(solid_color_srgb("E17"), solid_color_srgb("E17"));
98 assert_ne!(solid_color_srgb("E17"), solid_color_srgb("P.CU1"));
99 }
100
101 #[test]
102 fn hsl_formula_reference_points() {
103 let grey = hsl_to_rgb(0.37, 0.0, 0.6);
106 for c in grey {
107 assert!((c - 0.6).abs() < 1e-12);
108 }
109 }
110}