colorutils_rs/
hsl.rs

1/*
2 * // Copyright 2024 (c) the Radzivon Bartoshyk. All rights reserved.
3 * //
4 * // Use of this source code is governed by a BSD-style
5 * // license that can be found in the LICENSE file.
6 */
7use crate::rgb::Rgb;
8
9#[repr(C)]
10#[derive(Debug, Copy, Clone, PartialOrd, PartialEq)]
11/// Represents *HSL* (hue, saturation, lightness) colorspace, H ∈ [0, 360f32], s ∈ [0f32, 1f32], v ∈ [0f32, 1f32]
12pub struct Hsl {
13    /// Hue H ∈ [0, 360f32]
14    pub h: f32,
15    /// Saturation s ∈ [0, 1f32]
16    pub s: f32,
17    /// Lightness v ∈ [0, 1f32]
18    pub l: f32,
19}
20
21impl Hsl {
22    #[inline]
23    pub fn new(h: u16, s: u16, l: u16) -> Hsl {
24        Hsl {
25            h: h as f32,
26            s: s as f32 * (1f32 / 100f32),
27            l: l as f32 * (1f32 / 100f32),
28        }
29    }
30
31    #[inline]
32    pub fn from_components(h: f32, s: f32, l: f32) -> Hsl {
33        Hsl { h, s, l }
34    }
35
36    #[inline]
37    pub fn from_rgb(rgb: Rgb<u8>) -> Hsl {
38        rgb2hsl(rgb.r, rgb.g, rgb.b)
39    }
40
41    #[inline]
42    pub fn to_rgb8(&self) -> Rgb<u8> {
43        let c = (1f32 - (2f32 * self.l - 1f32).abs()) * self.s;
44        let x = c * (1f32 - ((self.h / 60f32) % 2f32 - 1f32).abs());
45        let m = self.l - c / 2f32;
46
47        let (r, g, b) = if self.h >= 0f32 && self.h < 60f32 {
48            (c, x, 0f32)
49        } else if self.h >= 60f32 && self.h < 120f32 {
50            (x, c, 0f32)
51        } else if self.h >= 120f32 && self.h < 180f32 {
52            (0f32, c, x)
53        } else if self.h >= 180f32 && self.h < 240f32 {
54            (0f32, x, c)
55        } else if self.h >= 240f32 && self.h < 300f32 {
56            (x, 0f32, c)
57        } else {
58            (c, 0f32, x)
59        };
60
61        Rgb::<u8> {
62            r: ((r + m) * 255f32).round() as u8,
63            g: ((g + m) * 255f32).round() as u8,
64            b: ((b + m) * 255f32).round() as u8,
65        }
66    }
67
68    #[inline]
69    pub fn to_rgb(&self) -> Rgb<u8> {
70        self.to_rgb8()
71    }
72
73    #[inline]
74    pub fn get_saturation(&self) -> u16 {
75        ((self.s * 100f32) as u16).min(100u16)
76    }
77
78    #[inline]
79    pub fn get_lightness(&self) -> u16 {
80        ((self.l * 100f32) as u16).min(100u16)
81    }
82
83    #[inline]
84    pub fn get_hue(&self) -> u16 {
85        (self.h as u16).min(360)
86    }
87}
88
89#[inline]
90fn rgb2hsl(o_r: u8, o_g: u8, o_b: u8) -> Hsl {
91    let r = o_r as f32 / 255f32;
92    let g = o_g as f32 / 255f32;
93    let b = o_b as f32 / 255f32;
94
95    let c_max = r.max(g).max(b);
96    let c_min = r.min(g).min(b);
97    let delta = c_max - c_min;
98
99    let mut h = if delta == 0f32 {
100        0f32
101    } else if c_max == r {
102        60f32 * (((g - b) / delta) % 6f32)
103    } else if c_max == g {
104        60f32 * (((b - r) / delta) + 2f32)
105    } else {
106        60f32 * (((r - g) / delta) + 4f32)
107    };
108
109    if h < 0f32 {
110        h += 360f32;
111    }
112
113    let l = 0.5f32 * (c_max + c_min);
114    let s = delta / (1f32 - (2f32 * l - 1f32).abs());
115
116    Hsl { h, s, l }
117}