1use super::{deg, percent, Angle, Color, Ratio, RGB, RGBA};
2use std::fmt;
3
4pub fn hsl(h: i32, s: u8, l: u8) -> HSL {
23 HSL {
24 h: deg(h),
25 s: percent(s),
26 l: percent(l),
27 }
28}
29
30pub fn hsla(h: i32, s: u8, l: u8, a: f32) -> HSLA {
51 HSLA {
52 h: deg(h),
53 s: percent(s),
54 l: percent(l),
55 a: Ratio::from_f32(a),
56 }
57}
58
59#[derive(Debug, Copy, Clone, PartialEq)]
60pub struct HSL {
68 pub h: Angle,
70
71 pub s: Ratio,
73
74 pub l: Ratio,
76}
77
78impl fmt::Display for HSL {
79 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
80 write!(f, "hsl({}, {}, {})", self.h.degrees(), self.s, self.l)
81 }
82}
83
84impl Color for HSL {
85 type Alpha = HSLA;
86
87 fn to_css(self) -> String {
88 self.to_string()
89 }
90
91 fn to_rgb(self) -> RGB {
92 self.to_hsla().to_rgb()
93 }
94
95 fn to_rgba(self) -> RGBA {
96 self.to_hsla().to_rgba()
97 }
98
99 fn to_hsl(self) -> HSL {
100 self
101 }
102
103 fn to_hsla(self) -> HSLA {
104 let HSL { h, s, l } = self;
105
106 HSLA {
107 h,
108 s,
109 l,
110 a: percent(100),
111 }
112 }
113
114 fn saturate(self, amount: Ratio) -> Self {
115 self.to_hsla().saturate(amount).to_hsl()
116 }
117
118 fn desaturate(self, amount: Ratio) -> Self {
119 self.to_hsla().desaturate(amount).to_hsl()
120 }
121
122 fn lighten(self, amount: Ratio) -> Self {
123 self.to_hsla().lighten(amount).to_hsl()
124 }
125
126 fn darken(self, amount: Ratio) -> Self {
127 self.to_hsla().darken(amount).to_hsl()
128 }
129
130 fn fadein(self, amount: Ratio) -> Self::Alpha {
131 self.to_hsla().fadein(amount)
132 }
133
134 fn fadeout(self, amount: Ratio) -> Self::Alpha {
135 self.to_hsla().fadeout(amount)
136 }
137
138 fn fade(self, amount: Ratio) -> Self::Alpha {
139 self.to_hsla().fade(amount)
140 }
141
142 fn spin(self, amount: Angle) -> Self {
143 self.to_hsla().spin(amount).to_hsl()
144 }
145
146 fn mix<T: Color>(self, other: T, weight: Ratio) -> Self::Alpha {
147 self.to_hsla().mix(other, weight)
148 }
149
150 fn tint(self, weight: Ratio) -> Self {
151 self.to_hsla().tint(weight).to_hsl()
152 }
153
154 fn shade(self, weight: Ratio) -> Self {
155 self.to_hsla().shade(weight).to_hsl()
156 }
157
158 fn greyscale(self) -> Self {
159 self.to_hsla().greyscale().to_hsl()
160 }
161}
162
163fn to_rgb_value(val: u16, temp_1: f32, temp_2: f32) -> f32 {
165 let value = val as f32 / 360.0;
166
167 if value > (2.0 / 3.0) {
168 temp_2
170 } else if value > (1.0 / 2.0) {
171 temp_2 + ((temp_1 - temp_2) * ((2.0 / 3.0) - value) * 6.0)
173 } else if value > (1.0 / 6.0) {
174 temp_1
176 } else {
177 temp_2 + ((temp_1 - temp_2) * value * 6.0)
179 }
180}
181
182#[derive(Debug, Copy, Clone, PartialEq)]
183pub struct HSLA {
192 pub h: Angle,
194
195 pub s: Ratio,
197
198 pub l: Ratio,
200
201 pub a: Ratio,
203}
204
205impl fmt::Display for HSLA {
206 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
207 write!(
208 f,
209 "hsla({}, {}, {}, {:.02})",
210 self.h.degrees(),
211 self.s,
212 self.l,
213 self.a.as_f32()
214 )
215 }
216}
217
218impl Color for HSLA {
219 type Alpha = Self;
220
221 fn to_css(self) -> String {
222 self.to_string()
223 }
224
225 fn to_rgb(self) -> RGB {
226 self.to_rgba().to_rgb()
227 }
228
229 fn to_rgba(self) -> RGBA {
230 let HSLA { h, s, l, a } = self;
231
232 if s == percent(0) {
235 return RGBA {
236 r: l,
237 g: l,
238 b: l,
239 a,
240 };
241 }
242
243 let s = s.as_f32();
244 let l = l.as_f32();
245
246 let temp_1 = if l < 0.5 {
250 l * (1.0 + s)
251 } else {
252 (l + s) - (l * s)
253 };
254
255 let temp_2 = (2.0 * l) - temp_1;
257
258 let rotation = Angle::new(120);
260
261 let temporary_r = (h + rotation).degrees();
263 let temporary_g = h.degrees();
264 let temporary_b = (h - rotation).degrees();
265
266 let red = to_rgb_value(temporary_r, temp_1, temp_2);
267 let green = to_rgb_value(temporary_g, temp_1, temp_2);
268 let blue = to_rgb_value(temporary_b, temp_1, temp_2);
269
270 RGBA {
271 r: Ratio::from_f32(red),
272 g: Ratio::from_f32(green),
273 b: Ratio::from_f32(blue),
274 a,
275 }
276 }
277
278 fn to_hsl(self) -> HSL {
279 let HSLA { h, s, l, .. } = self;
280 HSL { h, s, l }
281 }
282
283 fn to_hsla(self) -> HSLA {
284 self
285 }
286
287 fn saturate(self, amount: Ratio) -> Self {
288 let HSLA { h, s, l, a } = self;
289
290 HSLA {
291 h,
292 s: s + amount,
293 l,
294 a,
295 }
296 }
297
298 fn desaturate(self, amount: Ratio) -> Self {
299 let HSLA { h, s, l, a } = self;
300
301 HSLA {
302 h,
303 s: s - amount,
304 l,
305 a,
306 }
307 }
308
309 fn lighten(self, amount: Ratio) -> Self {
310 let HSLA { h, s, l, a } = self;
311
312 HSLA {
313 h,
314 s,
315 l: l + amount,
316 a,
317 }
318 }
319
320 fn darken(self, amount: Ratio) -> Self {
321 let HSLA { h, s, l, a } = self;
322
323 HSLA {
324 h,
325 s,
326 l: l - amount,
327 a,
328 }
329 }
330
331 fn fadein(self, amount: Ratio) -> Self {
332 self.fade(self.a + amount)
333 }
334
335 fn fadeout(self, amount: Ratio) -> Self {
336 self.fade(self.a - amount)
337 }
338
339 fn fade(self, amount: Ratio) -> Self::Alpha {
340 let HSLA { h, s, l, .. } = self;
341 HSLA { h, s, l, a: amount }
342 }
343
344 fn spin(self, amount: Angle) -> Self {
345 let HSLA { h, s, l, a } = self;
346
347 HSLA {
348 h: h + amount,
349 s,
350 l,
351 a,
352 }
353 }
354
355 fn mix<T: Color>(self, other: T, weight: Ratio) -> Self::Alpha {
356 self.to_rgba().mix(other, weight).to_hsla()
357 }
358
359 fn tint(self, weight: Ratio) -> Self {
360 self.to_rgba().tint(weight).to_hsla()
361 }
362
363 fn shade(self, weight: Ratio) -> Self {
364 self.to_rgba().shade(weight).to_hsla()
365 }
366
367 fn greyscale(self) -> Self {
368 let HSLA { h, l, a, .. } = self;
369
370 HSLA {
371 h,
372 s: percent(0),
373 l,
374 a,
375 }
376 }
377}