1use num_traits::{self, NumCast, Num, Float};
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 Hsl<T = f32, S = Srgb> { pub h: Deg<T>, pub s: T, pub l: T, pub standard: PhantomData<S> }
33
34impl<T: Clone,S> Clone for Hsl<T, S>{
35 fn clone(&self) -> Hsl<T, S>{
36 Hsl{ h: self.h.clone(), s: self.s.clone(), l: self.l.clone(), standard: PhantomData }
37 }
38}
39
40impl<T: Copy, S> Copy for Hsl<T, S>{}
41
42impl<N: Clone + PartialEq + Num + NumCast, S> PartialEq for Hsl<N, S>{
43 #[inline]
44 fn eq(&self, other: &Hsl<N, S>) -> bool{
45 self.h.clone().wrap().eq(&other.h.clone().wrap()) && self.s.eq(&other.s) && self.l.eq(&other.l)
46 }
47}
48
49impl<N: Clone + PartialEq + Eq + Num + NumCast, S> Eq for Hsl<N, S>{}
50
51impl<T, S> Hsl<T, S> {
52 pub const fn new(h: Deg<T>, s: T, l: T) -> Hsl<T, S> {
53 Hsl { h: h, s: s, l: l, standard: PhantomData }
54 }
55}
56
57impl<T: Channel + NumCast + Num, S: TransferFunction> Color<T> for Hsl<T, S> {
58 #[inline]
60 fn clamp_s(self, lo: T, hi: T) -> Hsl<T, S> {
61 Hsl::new(self.h, self.s.clamp(lo, hi),
63 self.l.clamp(lo, hi))
64 }
65
66 #[inline]
68 fn clamp_c(self, lo: Hsl<T, S>, hi: Hsl<T, S>) -> Hsl<T, S> {
69 Hsl::new(self.h,
70 self.s.clamp(lo.s, hi.s),
71 self.l.clamp(lo.l, hi.l))
72 }
73
74 #[inline]
76 fn inverse(self) -> Hsl<T, S> {
77 Hsl::new((self.h + Deg(cast(180))).wrap(),
78 self.s.invert_channel(),
79 self.l.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_hsl() }
86}
87
88impl<T: FloatChannel> FloatColor<T> for Hsl<T> {
89 #[inline]
92 fn saturate(self) -> Hsl<T> {
93 Hsl::new(self.h.wrap(),
94 self.s.saturate(),
95 self.l.saturate())
96 }
97}
98
99pub trait ToHsl {
100 type Standard: TransferFunction;
101 fn to_hsl<U:Channel + NumCast + Num>(&self) -> Hsl<U, Self::Standard>;
102}
103
104impl ToHsl for u32 {
105 type Standard = Srgb;
106 #[inline]
107 fn to_hsl<U:Channel>(&self) -> Hsl<U, Srgb> {
108 panic!("Not yet implemented")
109 }
110}
111
112impl ToHsl for u64 {
113 type Standard = Srgb;
114 #[inline]
115 fn to_hsl<U:Channel + NumCast + Num>(&self) -> Hsl<U, Srgb> {
116 panic!("Not yet implemented")
117 }
118}
119
120impl<T:Channel + NumCast + Num, S: TransferFunction> ToHsl for Hsl<T, S> {
121 type Standard = S;
122 #[inline]
123 fn to_hsl<U:Channel + NumCast + Num>(&self) -> Hsl<U,S> {
124 Hsl::new(Deg(cast(self.h.value())),
125 self.s.to_channel(),
126 self.l.to_channel())
127 }
128}
129
130impl<T: Clone + FloatChannel, S: TransferFunction> ToRgba for Hsl<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 Hsl<T, S> {
139 type Standard = S;
140 fn to_rgb<U:Channel>(&self) -> Rgb<U, S> {
141 if self.l.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.l);
145 Rgb::new(gray, gray, gray)
146 } else {
147 let a: f32 = Channel::from(self.s.normalized_mul(self.l.channel_min(T::CHANNEL_MAX - self.l)));
148 let f = |n| {
149 let hue: f32 = cast(self.h.wrap().value());
150 let hue_six: f32 = hue / 30f32;
151 let k: f32 = (n + hue_six) % 12.;
152 let l: f32 = Channel::from(self.l);
153 <U as Channel>::from(l - a * (k - 3.).min(9. - k).min(1.).max(-1.))
154 };
155 rgb!(f(0.), f(8.), f(4.)).to_standard()
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use {Hsl, ToHsl};
163 use {Rgb, ToRgb};
164 use angle::*;
165
166 #[test]
167 fn test_hsl_to_hsl() {
168 assert_eq!(Hsl::<f64>::new(Deg(0.0), 0.0, 1.0).to_hsl::<f32>(), Hsl::<f32>::new(Deg(0.0), 0.0, 1.0));
169 assert_eq!(Hsl::<f64>::new(Deg(0.0), 1.0, 0.6).to_hsl::<f32>(), Hsl::<f32>::new(Deg(0.0), 1.0, 0.6));
170 assert_eq!(Hsl::<f64>::new(Deg(120.0), 1.0, 0.6).to_hsl::<f32>(), Hsl::<f32>::new(Deg(120.0), 1.0, 0.6));
171 assert_eq!(Hsl::<f64>::new(Deg(240.0), 1.0, 0.6).to_hsl::<f32>(), Hsl::<f32>::new(Deg(240.0), 1.0, 0.6));
172 }
173
174 #[test]
175 fn test_hsl_to_rgb() {
176 assert_eq!(Hsl::<f32>::new(Deg(0.0), 0.0, 1.0).to_rgb::<u8>(), Rgb::<u8>::new(0xFF, 0xFF, 0xFF));
177 assert_eq!(Hsl::<f32>::new(Deg(0.0), 1.0, 0.6).to_rgb::<u8>(), Rgb::<u8>::new(0xFF, 0x33, 0x33));
178 assert_eq!(Hsl::<f32>::new(Deg(120.0), 1.0, 0.6).to_rgb::<u8>(), Rgb::<u8>::new(0x33, 0xff, 0x33));
179 assert_eq!(Hsl::<f32>::new(Deg(240.0), 1.0, 0.6).to_rgb::<u8>(), Rgb::<u8>::new(0x33, 0x33, 0xff));
180 assert_eq!(Hsl::<u16>::new(Deg(0), 0, 65535).to_rgb::<u8>(), Rgb::<u8>::new(0xFF, 0xFF, 0xFF));
181 assert_eq!(Hsl::<u16>::new(Deg(0), 65535, 39321).to_rgb::<u8>(), Rgb::<u8>::new(0xff, 0x33, 0x33));
182 assert_eq!(Hsl::<u16>::new(Deg(120), 65535, 39321).to_rgb::<u8>(), Rgb::<u8>::new(0x33, 0xff, 0x33));
183 assert_eq!(Hsl::<u16>::new(Deg(240), 65535, 39321).to_rgb::<u8>(), Rgb::<u8>::new(0x33, 0x33, 0xff));
184 }
185
186 #[test]
187 fn test_rgb_to_hsl() {
188 assert_eq!(Rgb::<u8>::new(0xFF, 0xFF, 0xFF).to_hsl(), Hsl::<f32>::new(Deg(0.0), 0.0, 1.0));
189 assert_eq!(Rgb::<u8>::new(0xFF, 0x33, 0x33).to_hsl(), Hsl::<f32>::new(Deg(0.0), 1.0, 0.6));
190 assert_eq!(Rgb::<u8>::new(0x33, 0xff, 0x33).to_hsl(), Hsl::<f32>::new(Deg(120.0), 1.0, 0.6));
191 assert_eq!(Rgb::<u8>::new(0x33, 0x33, 0xff).to_hsl(), Hsl::<f32>::new(Deg(240.0), 1.0, 0.6));
192 assert_eq!(Rgb::<u8>::new(0xFF, 0xFF, 0xFF).to_hsl(), Hsl::<u16>::new(Deg(0), 0, 65535));
193 assert_eq!(Rgb::<u8>::new(0xff, 0x33, 0x33).to_hsl(), Hsl::<u16>::new(Deg(0), 65535, 39321));
194 assert_eq!(Rgb::<u8>::new(0x33, 0xff, 0x33).to_hsl(), Hsl::<u16>::new(Deg(120), 65535, 39321));
195 assert_eq!(Rgb::<u8>::new(0x33, 0x33, 0xff).to_hsl(), Hsl::<u16>::new(Deg(240), 65535, 39321));
196 }
197}