1use super::{IsColorChannel, IsColor, HasAlpha, HasntAlpha, Hsva, Hsl, Rgb};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct Hsv<T> {
6 pub hue: T,
7 pub saturation: T,
8 pub value: T,
9}
10
11impl<T: IsColorChannel> IsColor for Hsv<T> {
12 type Channel = T;
13}
14
15impl<T: IsColorChannel> HasntAlpha for Hsv<T> {
16 type Alphaful = Hsva<T>;
17
18 fn with_alpha(self, alpha: Self::Channel) -> Self::Alphaful {
19 let Hsv { hue, saturation, value } = self;
20 Hsva { hue, saturation, value, alpha }
21 }
22}
23
24impl<T> Hsv<T> {
25 pub fn new(hue: T, saturation: T, value: T) -> Self {
26 Self { hue, saturation, value }
27 }
28}
29
30impl<T: IsColorChannel> Default for Hsv<T> {
31 fn default() -> Self {
32 Self::new(T::MIN, T::MIN, T::MIN)
33 }
34}
35
36impl<T: IsColorChannel> From<Hsva<T>> for Hsv<T> {
37 fn from(hsva: Hsva<T>) -> Self {
38 hsva.without_alpha()
39 }
40}
41
42impl From<Hsv<u8>> for Hsv<f32> {
43 fn from(Hsv { hue, saturation, value }: Hsv<u8>) -> Self {
44 Self::new(
45 hue as f32 / 255.0,
46 saturation as f32 / 255.0,
47 value as f32 / 255.0,
48 )
49 }
50}
51
52impl From<Hsv<f32>> for Hsv<u8> {
53 fn from(Hsv { hue, saturation, value }: Hsv<f32>) -> Self {
54 Self::new(
55 (hue.clamp_channel() * 255.0).round() as u8,
56 (saturation.clamp_channel() * 255.0).round() as u8,
57 (value.clamp_channel() * 255.0).round() as u8,
58 )
59 }
60}
61
62impl From<Hsl<f32>> for Hsv<f32> {
63 fn from(Hsl { hue, saturation, lightness }: Hsl<f32>) -> Self {
64 let lightness = lightness * 2.0;
65 let saturation = saturation * if lightness > 1.0 { 2.0 - lightness } else { lightness };
66
67 let lightness_plus_saturation = lightness + saturation;
68
69 let value = lightness_plus_saturation * 0.5;
70 let saturation = 2.0 * saturation / lightness_plus_saturation;
71
72 Self { hue, saturation, value }
73 }
74}
75
76impl From<Rgb<f32>> for Hsv<f32> {
77 fn from(Rgb { red, green, blue }: Rgb<f32>) -> Self {
78 let max = 0.0f32.max(red).max(green).max(blue);
79 let min = 1.0f32.min(red).min(green).min(blue);
80
81 if max > 0.0 {
82 let value = max;
83 let delta = max - min;
84 let saturation = delta / max;
85
86 let hue = if delta != 0.0 {
87 (
88 if red == max {
89 (green - blue) / delta
90 } else if green == max {
91 2.0 + (blue - red) / delta
92 } else {
93 4.0 + (red - green) / delta
94 }
95 * 1.0 / 6.0
96 ).unwind_channel()
97 } else {
98 0.0
99 };
100
101 Self { hue, saturation, value }
102 } else {
103 Self { hue: 0.0, saturation: 0.0, value: 0.0 }
104 }
105 }
106}
107
108#[cfg(test)]
109mod test {
110 use super::*;
111
112 #[test]
113 fn from_rgb() {
114 assert_eq!(Hsv::<u8>::from(Hsv::from(Rgb::<f32>::from(Rgb::new(84u8, 37, 181)))), Hsv::new(184u8, 203, 181));
115 }
116
117 #[test]
118 fn from_hsl() {
119 assert_eq!(Hsv::<u8>::from(Hsv::from(Hsl::<f32>::from(Hsl::new(184u8, 169, 109)))), Hsv::new(184u8, 203, 181));
120 }
121}