chromashift/
hsl.rs

1use crate::Srgb;
2
3/// An colour represented as Hue, Saturation, and Lightness expressed in the sRGB colour space.
4/// The components are:
5/// - Hue - a number between 0.0 and 360.0
6/// - Saturation - a number between 0.0 and 100.0
7/// - Brightness - a number between 0.0 and 100.0
8/// - Alpha - a number between 0.0 and 100.0
9#[derive(Debug, Clone, Copy, PartialEq)]
10pub struct Hsl {
11	pub hue: f32,
12	pub saturation: f32,
13	pub lightness: f32,
14	pub alpha: f32,
15}
16
17impl Hsl {
18	pub fn new(hue: f32, saturation: f32, lightness: f32, alpha: f32) -> Self {
19		Self {
20			hue: hue.rem_euclid(360.0),
21			saturation: saturation.clamp(0.0, 100.0),
22			lightness: lightness.clamp(0.0, 100.0),
23			alpha: alpha.clamp(0.0, 100.0),
24		}
25	}
26}
27
28impl From<Srgb> for Hsl {
29	fn from(value: Srgb) -> Self {
30		let r = value.red as f32 / 255.0;
31		let g = value.green as f32 / 255.0;
32		let b = value.blue as f32 / 255.0;
33		let max = r.max(g).max(b);
34		let min = r.min(g).min(b);
35		let delta = max - min;
36		let lightness = (max + min) / 2.0;
37		let saturation = if delta == 0.0 { 0.0 } else { delta / (1.0 - (2.0 * lightness - 1.0).abs()) };
38		let hue = if delta == 0.0 {
39			0.0
40		} else if max == r {
41			60.0 * (((g - b) / delta) % 6.0)
42		} else if max == g {
43			60.0 * ((b - r) / delta + 2.0)
44		} else {
45			60.0 * ((r - g) / delta + 4.0)
46		};
47		let hue = if hue < 0.0 { hue + 360.0 } else { hue };
48		Hsl::new(hue, saturation * 100.0, lightness * 100.0, value.alpha)
49	}
50}
51
52impl From<Hsl> for Srgb {
53	fn from(value: Hsl) -> Self {
54		let h = value.hue / 60.0;
55		let s = value.saturation / 100.0;
56		let l = value.lightness / 100.0;
57		let c = (1.0 - (2.0 * l - 1.0).abs()) * s;
58		let x = c * (1.0 - (h % 2.0 - 1.0).abs());
59		let m = l - c / 2.0;
60		let (r_prime, g_prime, b_prime) = if h < 1.0 {
61			(c, x, 0.0)
62		} else if h < 2.0 {
63			(x, c, 0.0)
64		} else if h < 3.0 {
65			(0.0, c, x)
66		} else if h < 4.0 {
67			(0.0, x, c)
68		} else if h < 5.0 {
69			(x, 0.0, c)
70		} else {
71			(c, 0.0, x)
72		};
73		Srgb::new(
74			((r_prime + m) * 255.0).clamp(0.0, 255.0).round() as u8,
75			((g_prime + m) * 255.0).clamp(0.0, 255.0).round() as u8,
76			((b_prime + m) * 255.0).clamp(0.0, 255.0).round() as u8,
77			value.alpha,
78		)
79	}
80}