vg/
color.rs

1use std::u8;
2
3/// Struct for representing colors.
4#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
5#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6pub struct Color {
7    /// Red component of color
8    pub r: f32,
9    /// Green component of color
10    pub g: f32,
11    /// Blue component of color
12    pub b: f32,
13    /// Alpha component of color
14    pub a: f32,
15}
16
17impl Color {
18    /// Returns a color value from red, green, blue char values. Alpha will be set to 255.
19    pub fn rgb(r: u8, g: u8, b: u8) -> Self {
20        Self::rgbf(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0)
21    }
22
23    /// Returns a color value from red, green, blue float values. Alpha will be set to 1.0.
24    pub fn rgbf(r: f32, g: f32, b: f32) -> Self {
25        Self { r, g, b, a: 1.0 }
26    }
27
28    /// Returns a color value from red, green, blue and alpha char values.
29    pub fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
30        Self::rgbaf(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, a as f32 / 255.0)
31    }
32
33    /// Returns a color value from red, green, blue and alpha char values.
34    pub fn rgbaf(r: f32, g: f32, b: f32, a: f32) -> Self {
35        Self { r, g, b, a }
36    }
37
38    /// Returns color value specified by hue, saturation and lightness.
39    /// HSL values are all in range [0..1], alpha will be set to 1.0.
40    pub fn hsl(h: f32, s: f32, l: f32) -> Self {
41        Self::hsla(h, s, l, 1.0)
42    }
43
44    /// Returns color value specified by hue, saturation, lightness and alpha.
45    /// All values are in range [0..1]
46    pub fn hsla(h: f32, s: f32, l: f32, a: f32) -> Self {
47        let mut h = h % 1.0;
48
49        if h < 0.0 {
50            h += 1.0;
51        }
52
53        let s = s.max(0.0).min(1.0);
54        let l = l.max(0.0).min(1.0);
55
56        let m2 = if l <= 0.5 { l * (1.0 + s) } else { l + s - l * s };
57        let m1 = 2.0 * l - m2;
58
59        Self {
60            r: hue(h + 1.0 / 3.0, m1, m2).max(0.0).min(1.0),
61            g: hue(h, m1, m2).max(0.0).min(1.0),
62            b: hue(h - 1.0 / 3.0, m1, m2).max(0.0).min(1.0),
63            a,
64        }
65    }
66
67    /// Returns color value for a 6-digit (`RRGGBB`) or 8-digit (`RRGGBBAA`)
68    /// HTML hexadecimal string. Any other length produces `rgb(0,0,0)`.
69    /// The “#” is optional.
70    pub fn hex(raw_hex: &str) -> Self {
71        let hex = raw_hex.trim_start_matches('#');
72
73        if hex.len() == 8 {
74            Self::rgba(
75                hex_to_u8(&hex[0..2]),
76                hex_to_u8(&hex[2..4]),
77                hex_to_u8(&hex[4..6]),
78                hex_to_u8(&hex[6..8]),
79            )
80        } else if hex.len() == 6 {
81            Self::rgb(hex_to_u8(&hex[0..2]), hex_to_u8(&hex[2..4]), hex_to_u8(&hex[4..6]))
82        } else {
83            Self::rgb(0, 0, 0)
84        }
85    }
86
87    /// Returns a white color
88    pub fn white() -> Self {
89        Self::rgbaf(1.0, 1.0, 1.0, 1.0)
90    }
91
92    /// Returns a black color
93    pub fn black() -> Self {
94        Self::rgbaf(0.0, 0.0, 0.0, 1.0)
95    }
96
97    /// Sets transparency of a color value.
98    pub fn set_alpha(&mut self, a: u8) {
99        self.set_alphaf(a as f32 / 255.0);
100    }
101
102    /// Sets transparency of a color value.
103    pub fn set_alphaf(&mut self, a: f32) {
104        self.a = a;
105    }
106
107    /// Create premultiplied version of color
108    pub fn premultiplied(self) -> Self {
109        Self {
110            r: self.r * self.a,
111            g: self.g * self.a,
112            b: self.b * self.a,
113            a: self.a,
114        }
115    }
116
117    /// Convert to slice
118    pub fn to_array(self) -> [f32; 4] {
119        [self.r, self.g, self.b, self.a]
120    }
121
122    /// Check the color is black
123    pub fn is_black(&self) -> bool {
124        self.r == 0.0 && self.g == 0.0 && self.b == 0.0 && self.a == 0.0
125    }
126}
127
128impl Default for Color {
129    fn default() -> Self {
130        Self {
131            r: 0.0,
132            g: 0.0,
133            b: 0.0,
134            a: 1.0,
135        }
136    }
137}
138
139fn hue(mut h: f32, m1: f32, m2: f32) -> f32 {
140    if h < 0.0 {
141        h += 1.0;
142    }
143    if h > 1.0 {
144        h -= 1.0;
145    }
146
147    if h < 1.0 / 6.0 {
148        return m1 + (m2 - m1) * h * 6.0;
149    }
150    if h < 3.0 / 6.0 {
151        return m2;
152    }
153    if h < 4.0 / 6.0 {
154        return m1 + (m2 - m1) * (2.0 / 3.0 - h) * 6.0;
155    }
156
157    m1
158}
159
160// Convert a hex string to decimal. Eg. "00" -> 0. "FF" -> 255.
161fn hex_to_u8(hex_string: &str) -> u8 {
162    u8::from_str_radix(hex_string, 16).map(|o| o as u8).unwrap_or(0)
163}