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 pub r: f32,
8 pub g: f32,
9 pub b: f32,
10 pub a: f32,
11}
12
13impl 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
121impl 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
132fn 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.
154fn hex_to_u8(hex_string: &str) -> u8 {
155 u8::from_str_radix(hex_string, 16).unwrap_or(default:0)
156}
157