1use num_traits::{self, NumCast, Num};
17use angle::*;
18
19use {Color, FloatColor};
20use {Channel, FloatChannel};
21use {Rgb, ToRgb};
22use alpha::{ToRgba, Rgba};
23use color_space::{Srgb, TransferFunction};
24use std::marker::PhantomData;
25
26#[inline]
27fn cast<T: num_traits::NumCast, U: num_traits::NumCast>(n: T) -> U {
28 num_traits::cast(n).unwrap()
29}
30
31#[derive(Serialize, Deserialize, Debug)]
32pub struct Hsv<T = f32, S = Srgb> { pub h: Deg<T>, pub s: T, pub v: T, pub standard: PhantomData<S> }
33
34impl<T: Clone,S> Clone for Hsv<T, S>{
35 fn clone(&self) -> Hsv<T, S>{
36 Hsv{ h: self.h.clone(), s: self.s.clone(), v: self.v.clone(), standard: PhantomData }
37 }
38}
39
40impl<T: Copy, S> Copy for Hsv<T, S>{}
41
42impl<N: Clone + PartialEq + Num + NumCast, S> PartialEq for Hsv<N, S>{
43 #[inline]
44 fn eq(&self, other: &Hsv<N, S>) -> bool{
45 self.h.clone().wrap().eq(&other.h.clone().wrap()) && self.s.eq(&other.s) && self.v.eq(&other.v)
46 }
47}
48
49impl<N: Clone + PartialEq + Eq + Num + NumCast, S> Eq for Hsv<N, S>{}
50
51impl<T, S> Hsv<T, S> {
52 pub const fn new(h: Deg<T>, s: T, v: T) -> Hsv<T, S> {
53 Hsv { h: h, s: s, v: v, standard: PhantomData }
54 }
55}
56
57impl<T: Channel + NumCast + Num, S: TransferFunction> Color<T> for Hsv<T, S> {
58 #[inline]
60 fn clamp_s(self, lo: T, hi: T) -> Hsv<T, S> {
61 Hsv::new(self.h, self.s.clamp(lo, hi),
63 self.v.clamp(lo, hi))
64 }
65
66 #[inline]
68 fn clamp_c(self, lo: Hsv<T, S>, hi: Hsv<T, S>) -> Hsv<T, S> {
69 Hsv::new(self.h,
70 self.s.clamp(lo.s, hi.s),
71 self.v.clamp(lo.v, hi.v))
72 }
73
74 #[inline]
76 fn inverse(self) -> Hsv<T, S> {
77 Hsv::new((self.h + Deg(cast(180))).wrap(),
78 self.s.invert_channel(),
79 self.v.invert_channel())
80 }
81
82 #[inline]
83 fn mix(self, other: Self, value: T) -> Self {
84 self.to_rgb().mix(other.to_rgb(),value).to_hsv() }
86}
87
88impl<T: FloatChannel> FloatColor<T> for Hsv<T> {
89 #[inline]
92 fn saturate(self) -> Hsv<T> {
93 Hsv::new(self.h.wrap(),
94 self.s.saturate(),
95 self.v.saturate())
96 }
97}
98
99pub trait ToHsv {
100 type Standard: TransferFunction;
101 fn to_hsv<U:Channel + NumCast + Num>(&self) -> Hsv<U, Self::Standard>;
102}
103
104impl ToHsv for u32 {
105 type Standard = Srgb;
106 #[inline]
107 fn to_hsv<U:Channel>(&self) -> Hsv<U, Srgb> {
108 panic!("Not yet implemented")
109 }
110}
111
112impl ToHsv for u64 {
113 type Standard = Srgb;
114 #[inline]
115 fn to_hsv<U:Channel + NumCast + Num>(&self) -> Hsv<U, Srgb> {
116 panic!("Not yet implemented")
117 }
118}
119
120impl<T:Channel + NumCast + Num, S: TransferFunction> ToHsv for Hsv<T, S> {
121 type Standard = S;
122 #[inline]
123 fn to_hsv<U:Channel + NumCast + Num>(&self) -> Hsv<U,S> {
124 Hsv::new(Deg(cast(self.h.value())),
125 self.s.to_channel(),
126 self.v.to_channel())
127 }
128}
129
130impl<T: Clone + FloatChannel, S: TransferFunction> ToRgba for Hsv<T, S> {
131 type Standard = S;
132 #[inline]
133 fn to_rgba<U: Channel>(&self) -> Rgba<U, S>{
134 Rgba{c: self.to_rgb(), a: 1.0f32.to_channel()}
135 }
136}
137
138impl<T:Clone + Channel + NumCast + Num, S: TransferFunction> ToRgb for Hsv<T, S> {
139 type Standard = S;
140 fn to_rgb<U:Channel>(&self) -> Rgb<U, S> {
141 if self.v.is_zero() {
142 Rgb::new(<U as Channel>::zero(), <U as Channel>::zero(), <U as Channel>::zero())
143 } else if self.s.is_zero() {
144 let gray = Channel::from(self.v);
145 Rgb::new(gray, gray, gray)
146 } else {
147 let max_f: f64 = cast(T::CHANNEL_MAX);
148 let hue: f64 = cast(self.h.wrap().value());
149 let hue_six: f64 = hue / 360f64 * 6f64;
150 let hue_six_cat: usize = cast(hue_six);
151 let hue_six_rem: T = cast(hue_six.fract() * max_f);
152 let pv = Channel::from((T::CHANNEL_MAX - self.s).normalized_mul(self.v));
153 let qv = Channel::from((T::CHANNEL_MAX - self.s.normalized_mul(hue_six_rem)).normalized_mul(self.v));
154 let tv = Channel::from((T::CHANNEL_MAX - self.s.normalized_mul(T::CHANNEL_MAX - hue_six_rem)).normalized_mul(self.v));
155 let b: U = Channel::from(self.v);
156 match hue_six_cat {
157 0 | 6 => Rgb::new(b,tv,pv),
158 1 => Rgb::new(qv, b, pv),
159 2 => Rgb::new(pv, b, tv),
160 3 => Rgb::new(pv, qv, b),
161 4 => Rgb::new(tv, pv, b),
162 5 => Rgb::new(b, pv, qv),
163 _ => panic!("Unreachable code")
164 }
165 }
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use {Hsv, ToHsv};
172 use {Rgb, ToRgb};
173 use angle::*;
174
175 #[test]
176 fn test_hsv_to_hsv() {
177 assert_eq!(Hsv::<f64>::new(Deg(0.0), 0.0, 1.0).to_hsv::<f32>(), Hsv::<f32>::new(Deg(0.0), 0.0, 1.0));
178 assert_eq!(Hsv::<f64>::new(Deg(0.0), 1.0, 0.6).to_hsv::<f32>(), Hsv::<f32>::new(Deg(0.0), 1.0, 0.6));
179 assert_eq!(Hsv::<f64>::new(Deg(120.0), 1.0, 0.6).to_hsv::<f32>(), Hsv::<f32>::new(Deg(120.0), 1.0, 0.6));
180 assert_eq!(Hsv::<f64>::new(Deg(240.0), 1.0, 0.6).to_hsv::<f32>(), Hsv::<f32>::new(Deg(240.0), 1.0, 0.6));
181 }
182
183 #[test]
184 fn test_hsv_to_rgb() {
185 assert_eq!(Hsv::<f32>::new(Deg(0.0), 0.0, 1.0).to_rgb::<u8>(), Rgb::<u8>::new(0xFF, 0xFF, 0xFF));
186 assert_eq!(Hsv::<f32>::new(Deg(0.0), 1.0, 0.6).to_rgb::<u8>(), Rgb::<u8>::new(0x99, 0x00, 0x00));
187 assert_eq!(Hsv::<f32>::new(Deg(120.0), 1.0, 0.6).to_rgb::<u8>(), Rgb::<u8>::new(0x00, 0x99, 0x00));
188 assert_eq!(Hsv::<f32>::new(Deg(240.0), 1.0, 0.6).to_rgb::<u8>(), Rgb::<u8>::new(0x00, 0x00, 0x99));
189 assert_eq!(Hsv::<u16>::new(Deg(0), 0, 65535).to_rgb::<u8>(), Rgb::<u8>::new(0xFF, 0xFF, 0xFF));
190 assert_eq!(Hsv::<u16>::new(Deg(0), 65535, 39321).to_rgb::<u8>(), Rgb::<u8>::new(0x99, 0x00, 0x00));
191 assert_eq!(Hsv::<u16>::new(Deg(120), 65535, 39321).to_rgb::<u8>(), Rgb::<u8>::new(0x00, 0x99, 0x00));
192 assert_eq!(Hsv::<u16>::new(Deg(240), 65535, 39321).to_rgb::<u8>(), Rgb::<u8>::new(0x00, 0x00, 0x99));
193 }
194}