1use crate::rgb::Rgb;
8
9#[repr(C)]
10#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
11pub struct Hsv {
13 pub h: f32,
15 pub s: f32,
17 pub v: f32,
19}
20
21static HSV_U8_SCALE: f32 = 1f32 / 255f32;
22static HSV_PERCENTAGE_SCALE: f32 = 1f32 / 100f32;
23
24impl Hsv {
25 #[inline]
26 pub fn new(h: u16, s: u16, l: u16) -> Hsv {
27 Hsv {
28 h: h as f32,
29 s: s as f32 * HSV_PERCENTAGE_SCALE,
30 v: l as f32 * HSV_PERCENTAGE_SCALE,
31 }
32 }
33 #[inline]
34 pub fn from_components(h: f32, s: f32, v: f32) -> Hsv {
35 Hsv { h, s, v }
36 }
37 #[inline]
38 pub fn from(rgb: Rgb<u8>) -> Hsv {
39 let (h, s, v) = rgb_to_hsv(
40 rgb.r as f32 * HSV_U8_SCALE,
41 rgb.g as f32 * HSV_U8_SCALE,
42 rgb.b as f32 * HSV_U8_SCALE,
43 );
44 Hsv { h, s, v }
45 }
46 #[inline]
47 pub fn to_rgb8(&self) -> Rgb<u8> {
48 let (rf, gf, bf) = hsv_to_rgb(self.h, self.s, self.v);
49 Rgb {
50 r: (rf * 255f32) as u8,
51 g: (gf * 255f32) as u8,
52 b: (bf * 255f32) as u8,
53 }
54 }
55 #[inline]
56 pub fn get_hue(&self) -> f32 {
57 self.h
58 }
59 #[inline]
60 pub fn get_saturation(&self) -> f32 {
61 self.s
62 }
63 #[inline]
64 pub fn get_value(&self) -> f32 {
65 self.v
66 }
67 #[inline]
68 pub fn get_hue_p(&self) -> u16 {
69 self.h.max(0f32).min(360f32) as u16
70 }
71 #[inline]
72 pub fn get_saturation_p(&self) -> u16 {
73 (self.s * 100f32).max(0f32).min(100f32) as u16
74 }
75 #[inline]
76 pub fn get_value_p(&self) -> u16 {
77 (self.v * 100f32).max(0f32).min(100f32) as u16
78 }
79}
80
81#[inline]
82fn rgb_to_hsv(r: f32, g: f32, b: f32) -> (f32, f32, f32) {
83 let c_max = r.max(g).max(b);
84 let c_min = r.min(g).min(b);
85 let delta = c_max - c_min;
86
87 let mut h = 0f32;
88 let mut s = 0f32;
89 let v = c_max;
90
91 if delta > 0f32 {
92 if c_max == r {
93 h = 60f32 * (((g - b) / delta) % 6f32);
94 } else if c_max == g {
95 h = 60f32 * (((b - r) / delta) + 2f32);
96 } else if c_max == b {
97 h = 60f32 * (((r - g) / delta) + 4f32);
98 }
99
100 if c_max > 0f32 {
101 s = delta / c_max;
102 }
103 }
104
105 if h < 0f32 {
106 h += 360f32;
107 }
108
109 (h, s, v)
110}
111
112#[inline]
113#[allow(clippy::manual_range_contains)]
114fn hsv_to_rgb(h: f32, s: f32, v: f32) -> (f32, f32, f32) {
115 let c = v * s;
116 let h_prime = (h / 60f32) % 6f32;
117 let x = c * (1f32 - ((h_prime % 2f32) - 1f32).abs());
118 let m = v - c;
119
120 let (r, g, b) = if h_prime >= 0f32 && h_prime < 1f32 {
121 (c, x, 0f32)
122 } else if h_prime >= 1f32 && h_prime < 2f32 {
123 (x, c, 0f32)
124 } else if h_prime >= 2f32 && h_prime < 3f32 {
125 (0f32, c, x)
126 } else if h_prime >= 3f32 && h_prime < 4f32 {
127 (0f32, x, c)
128 } else if h_prime >= 4f32 && h_prime < 5f32 {
129 (x, 0f32, c)
130 } else if h_prime >= 5f32 && h_prime < 6f32 {
131 (c, 0f32, x)
132 } else {
133 (0f32, 0f32, 0f32)
134 };
135
136 (r + m, g + m, b + m)
137}