color/
hsv.rs

1// Copyright 2013 The color-rs developers. For a full listing of the authors,
2// refer to the AUTHORS file at the top-level directory of this distribution.
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use 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    /// Clamps the components of the color to the range `(lo,hi)`.
59    #[inline]
60    fn clamp_s(self, lo: T, hi: T) -> Hsv<T, S> {
61        Hsv::new(self.h, // Should the hue component be clamped?
62                 self.s.clamp(lo, hi),
63                 self.v.clamp(lo, hi))
64    }
65
66    /// Clamps the components of the color component-wise between `lo` and `hi`.
67    #[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    /// Inverts the color.
75    #[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() // TODO: can we mix the hsv directly?
85    }
86}
87
88impl<T: FloatChannel> FloatColor<T> for Hsv<T> {
89    /// Normalizes the components of the color. Modulo `360` is applied to the
90    /// `h` component, and `s` and `v` are clamped to the range `(0,1)`.
91    #[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}