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