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
105
106
107
108
109
110
111
112
use std::convert::From;
use min_max::*;
use crate::rgb::*;
#[derive(Debug, PartialEq)]
pub struct Hsl {
pub h: f32,
pub s: f32,
pub l: f32,
}
#[derive(Debug, PartialEq)]
pub struct HslInt {
pub h: u16,
pub s: u8,
pub l: u8,
}
impl From<Hsl> for HslInt {
fn from(hsl: Hsl) -> Self {
Self {
h: hsl.h.round() as u16,
s: (hsl.s * 100.0).round() as u8,
l: (hsl.l * 100.0).round() as u8,
}
}
}
impl From<HslInt> for Hsl {
fn from(hsl: HslInt) -> Self {
Self {
h: hsl.h as f32,
s: hsl.s as f32 / 100.,
l: hsl.l as f32 / 100.,
}
}
}
impl From<RgbScaled> for Hsl {
fn from(rgb: RgbScaled) -> Self {
let RgbScaled { r, g, b } = rgb;
let (min, max) = (min_partial!(r, g, b), max_partial!(r, g, b));
let chroma = max - min;
let hue = if chroma < 1e-3 {
0f32
} else {
60f32
* if (max - r).abs() < 1e-3 {
let segment = (g - b) / chroma;
let mut shift = 0 / 60;
if segment < 0.0 {
shift = 360 / 60
}
segment + shift as f32
} else if (max - g).abs() < 1e-3 {
let segment = (b - r) / chroma;
let shift = 120 / 60;
segment + shift as f32
} else if (max - b).abs() < 1e-3 {
let segment = (r - g) / chroma;
let shift = 240 / 60;
segment + shift as f32
} else {
unreachable!()
}
};
let lightness = (min + max) / 2.0;
let saturation = if chroma < 1e-3 {
chroma
} else {
chroma / (1.0 - (2.0 * lightness - 1.0))
};
Self {
h: hue,
s: saturation,
l: lightness,
}
}
}
impl From<Rgb> for Hsl {
fn from(rgb: Rgb) -> Self {
Self::from(Hsl::from(RgbScaled::from(rgb)))
}
}