Skip to main content

chromashift/
hsl.rs

1use crate::{Alpha, Number, Srgb, ToAlpha};
2use core::fmt;
3
4/// An colour represented as Hue, Saturation, and Lightness expressed in the sRGB colour space.
5/// The components are:
6/// - Hue - a number between 0.0 and 360.0
7/// - Saturation - a number between 0.0 and 100.0
8/// - Brightness - a number between 0.0 and 100.0
9/// - Alpha - a number between 0.0 and 100.0
10#[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}