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, Hsva, Hsl, Rgb};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Hsv<T> {
pub hue: T,
pub saturation: T,
pub value: T,
}
impl<T: IsColorChannel> IsColor for Hsv<T> {
type Channel = T;
}
impl<T: IsColorChannel> HasntAlpha for Hsv<T> {
type Alphaful = Hsva<T>;
fn with_alpha(self, alpha: Self::Channel) -> Self::Alphaful {
let Hsv { hue, saturation, value } = self;
Hsva { hue, saturation, value, alpha }
}
}
impl<T> Hsv<T> {
pub fn new(hue: T, saturation: T, value: T) -> Self {
Self { hue, saturation, value }
}
}
impl<T: IsColorChannel> Default for Hsv<T> {
fn default() -> Self {
Self::new(T::MIN, T::MIN, T::MIN)
}
}
impl<T: IsColorChannel> From<Hsva<T>> for Hsv<T> {
fn from(hsva: Hsva<T>) -> Self {
hsva.without_alpha()
}
}
impl From<Hsv<u8>> for Hsv<f32> {
fn from(Hsv { hue, saturation, value }: Hsv<u8>) -> Self {
Self::new(
hue as f32 / 255.0,
saturation as f32 / 255.0,
value as f32 / 255.0,
)
}
}
impl From<Hsv<f32>> for Hsv<u8> {
fn from(Hsv { hue, saturation, value }: Hsv<f32>) -> Self {
Self::new(
(hue.clamp_channel() * 255.0).round() as u8,
(saturation.clamp_channel() * 255.0).round() as u8,
(value.clamp_channel() * 255.0).round() as u8,
)
}
}
impl From<Hsl<f32>> for Hsv<f32> {
fn from(Hsl { hue, saturation, lightness }: Hsl<f32>) -> Self {
let lightness = lightness * 2.0;
let saturation = saturation * if lightness > 1.0 { 2.0 - lightness } else { lightness };
let lightness_plus_saturation = lightness + saturation;
let value = lightness_plus_saturation * 0.5;
let saturation = 2.0 * saturation / lightness_plus_saturation;
Self { hue, saturation, value }
}
}
impl From<Rgb<f32>> for Hsv<f32> {
fn from(Rgb { red, green, blue }: Rgb<f32>) -> Self {
let max = 0.0f32.max(red).max(green).max(blue);
let min = 1.0f32.min(red).min(green).min(blue);
if max > 0.0 {
let value = max;
let delta = max - min;
let saturation = delta / max;
let hue = if delta != 0.0 {
(
if red == max {
(green - blue) / delta
} else if green == max {
2.0 + (blue - red) / delta
} else {
4.0 + (red - green) / delta
}
* 1.0 / 6.0
).unwind_channel()
} else {
0.0
};
Self { hue, saturation, value }
} else {
Self { hue: 0.0, saturation: 0.0, value: 0.0 }
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn from_rgb() {
assert_eq!(Hsv::<u8>::from(Hsv::from(Rgb::<f32>::from(Rgb::new(84u8, 37, 181)))), Hsv::new(184u8, 203, 181));
}
#[test]
fn from_hsl() {
assert_eq!(Hsv::<u8>::from(Hsv::from(Hsl::<f32>::from(Hsl::new(184u8, 169, 109)))), Hsv::new(184u8, 203, 181));
}
}