1use crate::{Alpha, Number, Srgb, ToAlpha};
2use core::fmt;
3
4#[derive(Debug, Clone, Copy, PartialEq)]
11pub struct Hsl {
12 pub hue: f64,
13 pub saturation: f64,
14 pub lightness: f64,
15 pub alpha: f32,
16}
17
18impl Hsl {
19 pub fn new(hue: f64, saturation: f64, lightness: f64, alpha: f32) -> Self {
20 Self { hue: hue.rem_euclid(360.0), saturation, lightness, alpha: alpha.clamp(0.0, 100.0) }
21 }
22}
23
24impl ToAlpha for Hsl {
25 fn to_alpha(&self) -> f32 {
26 self.alpha
27 }
28}
29
30impl fmt::Display for Hsl {
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 let Self { hue, saturation, lightness, alpha } = *self;
33 write!(f, "hsl({} {}% {}%{})", Number(hue), Number(saturation), Number(lightness), Alpha(alpha))
34 }
35}
36
37impl From<Srgb> for Hsl {
38 fn from(value: Srgb) -> Self {
39 let Srgb { red, green, blue, alpha } = value;
40 let max = red.max(green).max(blue);
41 let min = red.min(green).min(blue);
42 let lightness = (max + min) / 2.0;
43 let mut hue = value.hue();
44 let mut saturation = if max - min <= 1e-9 || lightness == 0.0 || lightness == 1.0 {
45 0.0
46 } else {
47 (max - lightness) / lightness.min(1.0 - lightness)
48 };
49 if saturation < 0.0 {
50 hue += 180.0;
51 saturation = -saturation;
52 }
53 if saturation <= 1e-5 {
54 hue = 0.0;
55 }
56 Hsl::new(hue, saturation * 100.0, lightness * 100.0, alpha)
57 }
58}
59
60impl From<Hsl> for Srgb {
61 fn from(value: Hsl) -> Self {
62 let Hsl { hue, saturation, lightness, alpha } = value;
63 let (saturation, lightness) = (saturation / 100.0, lightness / 100.0);
64 let a = saturation * lightness.min(1.0 - lightness);
65 let f = |n: f64| {
66 let k = (n + hue / 30.0) % 12.0;
67 lightness - a * (k - 3.0).min(9.0 - k).clamp(-1.0, 1.0)
68 };
69 Srgb::new(f(0.0), f(8.0), f(4.0), alpha)
70 }
71}