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
113
114
115
116
117
118
119
120
121
use super::{IsColorChannel, IsColor, HasAlpha, HasntAlpha, Rgb, Hsv, Hsla};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Hsl<T> {
pub hue: T,
pub saturation: T,
pub lightness: T,
}
impl<T: IsColorChannel> IsColor for Hsl<T> {
type Channel = T;
}
impl<T: IsColorChannel> HasntAlpha for Hsl<T> {
type Alphaful = Hsla<T>;
fn with_alpha(self, alpha: Self::Channel) -> Self::Alphaful {
let Hsl { hue, saturation, lightness } = self;
Hsla { hue, saturation, lightness, alpha }
}
}
impl<T> Hsl<T> {
pub fn new(hue: T, saturation: T, lightness: T) -> Self {
Self { hue, saturation, lightness }
}
}
impl<T: IsColorChannel> Default for Hsl<T> {
fn default() -> Self {
Self::new(T::MIN, T::MIN, T::MIN)
}
}
impl<T: IsColorChannel> From<Hsla<T>> for Hsl<T> {
fn from(hsla: Hsla<T>) -> Self {
hsla.without_alpha()
}
}
impl From<Hsl<u8>> for Hsl<f32> {
fn from(Hsl { hue, saturation, lightness }: Hsl<u8>) -> Self {
Self::new(
hue as f32 / 255.0,
saturation as f32 / 255.0,
lightness as f32 / 255.0,
)
}
}
impl From<Hsl<f32>> for Hsl<u8> {
fn from(Hsl { hue, saturation, lightness }: Hsl<f32>) -> Self {
Self::new(
(hue.clamp_channel() * 255.0).round() as u8,
(saturation.clamp_channel() * 255.0).round() as u8,
(lightness.clamp_channel() * 255.0).round() as u8,
)
}
}
impl From<Hsv<f32>> for Hsl<f32> {
fn from(Hsv { hue, saturation, value }: Hsv<f32>) -> Self {
let lightness = (2.0 - saturation) * value;
let denominator = if lightness > 1.0 {
2.0 - lightness
} else {
lightness
};
let saturation = if denominator > 0.0 {
saturation * value / denominator
} else {
0.0
};
let lightness = lightness * 0.5;
Self { hue, saturation, lightness }
}
}
impl From<Rgb<f32>> for Hsl<f32> {
fn from(Rgb { red, green, blue }: Rgb<f32>) -> Self {
let c_max = red.max(green).max(blue);
let c_min = red.min(green).min(blue);
let c_delta = c_max - c_min;
let mut hue = 0.0;
let mut saturation = 0.0;
let lightness = (c_max + c_min) / 2.0;
if c_delta != 0.0 {
if c_max == red {
hue = 1.0/6.0 * (((green - blue) / c_delta) % 6.0);
} else if c_max == green {
hue = 1.0/6.0 * ((blue - red) / c_delta + 2.0);
} else if c_max == blue {
hue = 1.0/6.0 * ((red - green) / c_delta + 4.0);
}
saturation = c_delta / (1.0 - (2.0 * lightness - 1.0).abs());
}
let hue = hue.unwind_channel();
Self { hue, saturation, lightness }
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn from_rgb() {
assert_eq!(Hsl::<u8>::from(Hsl::from(Rgb::<f32>::from(Rgb::new(84u8, 37, 181)))), Hsl::new(184u8, 168, 109));
}
#[test]
fn from_hsv() {
assert_eq!(Hsl::<u8>::from(Hsl::from(Hsv::<f32>::from(Hsv::new(184u8, 203, 181)))), Hsl::new(184u8, 169, 109));
}
}