1use std::u8;
2
3#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
5#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
6pub struct Color {
7 pub r: f32,
9 pub g: f32,
11 pub b: f32,
13 pub a: f32,
15}
16
17impl Color {
18 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 pub fn rgbf(r: f32, g: f32, b: f32) -> Self {
25 Self { r, g, b, a: 1.0 }
26 }
27
28 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 pub fn rgbaf(r: f32, g: f32, b: f32, a: f32) -> Self {
35 Self { r, g, b, a }
36 }
37
38 pub fn hsl(h: f32, s: f32, l: f32) -> Self {
41 Self::hsla(h, s, l, 1.0)
42 }
43
44 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 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 pub fn white() -> Self {
89 Self::rgbaf(1.0, 1.0, 1.0, 1.0)
90 }
91
92 pub fn black() -> Self {
94 Self::rgbaf(0.0, 0.0, 0.0, 1.0)
95 }
96
97 pub fn set_alpha(&mut self, a: u8) {
99 self.set_alphaf(a as f32 / 255.0);
100 }
101
102 pub fn set_alphaf(&mut self, a: f32) {
104 self.a = a;
105 }
106
107 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 pub fn to_array(self) -> [f32; 4] {
119 [self.r, self.g, self.b, self.a]
120 }
121
122 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
160fn hex_to_u8(hex_string: &str) -> u8 {
162 u8::from_str_radix(hex_string, 16).map(|o| o as u8).unwrap_or(0)
163}