1mod const_color;
2
3#[repr(C, align(4))]
5#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
6pub struct Color {
7 _ph: u8,
8 pub blue: u8,
9 pub green: u8,
10 pub red: u8,
11}
12
13pub struct ColorHSV {
15 pub hue: f32,
16 pub saturation: f32,
17 pub value: f32,
18}
19
20pub struct ColorHSL {
22 pub hue: f32,
23 pub saturation: f32,
24 pub lightness: f32,
25}
26
27impl Color {
28 pub const fn new(red: u8, green: u8, blue: u8) -> Color {
30 Color {
31 _ph: 0,
32 blue,
33 green,
34 red,
35 }
36 }
37}
38
39impl Color {
40 pub const fn into_hsv(self) -> ColorHSV {
42 let r = self.red as f32 / 255.0;
43 let g = self.green as f32 / 255.0;
44 let b = self.blue as f32 / 255.0;
45
46 let max = r.max(g).max(b);
47 let min = r.min(g).min(b);
48 let delta = max - min;
49
50 let hue = if delta == 0.0 {
51 0.0
52 } else if max == r {
53 (g - b) / delta
54 } else if max == g {
55 (b - r) / delta + 2.0
56 } else {
57 (r - g) / delta + 4.0
58 };
59 let saturation = if max == 0.0 { 0.0 } else { delta / max };
60 let value = max;
61
62 let mut hue = hue * 60.0;
63 if hue < 0.0 {
64 hue += 360.0;
65 }
66
67 ColorHSV {
68 hue,
69 saturation,
70 value,
71 }
72 }
73
74 pub const fn into_hsl(self) -> ColorHSL {
76 let r = self.red as f32 / 255.0;
77 let g = self.green as f32 / 255.0;
78 let b = self.blue as f32 / 255.0;
79
80 let max = r.max(g).max(b);
81 let min = r.min(g).min(b);
82 let delta = max - min;
83
84 let lightness = (max + min) / 2.0;
85 let saturation = if lightness == 0.0 || lightness == 1.0 {
86 0.0
87 } else {
88 delta / (1.0 - (2.0 * lightness - 1.0).abs())
89 };
90
91 let hue = if delta == 0.0 {
92 0.0
93 } else if max == r {
94 (g - b) / delta
95 } else if max == g {
96 (b - r) / delta + 2.0
97 } else {
98 (r - g) / delta + 4.0
99 };
100
101 let mut hue = hue * 60.0;
102 if hue < 0.0 {
103 hue += 360.0;
104 }
105
106 ColorHSL {
107 hue,
108 saturation,
109 lightness,
110 }
111 }
112
113 pub const fn from_hsv(hsv: ColorHSV) -> Color {
115 let chroma = hsv.value * hsv.saturation;
116 let hue_prime = hsv.hue / 60.0;
117 let x = chroma * (1.0 - (hue_prime % 2.0 - 1.0).abs());
118
119 let (r, g, b) = if hue_prime < 1.0 {
120 (chroma, x, 0.0)
121 } else if hue_prime < 2.0 {
122 (x, chroma, 0.0)
123 } else if hue_prime < 3.0 {
124 (0.0, chroma, x)
125 } else if hue_prime < 4.0 {
126 (0.0, x, chroma)
127 } else if hue_prime < 5.0 {
128 (x, 0.0, chroma)
129 } else {
130 (chroma, 0.0, x)
131 };
132
133 let m = hsv.value - chroma;
134
135 Color::new(
136 ((r + m) * 255.0) as u8,
137 ((g + m) * 255.0) as u8,
138 ((b + m) * 255.0) as u8,
139 )
140 }
141
142 pub const fn from_hsl(hsl: ColorHSL) -> Color {
144 let chroma = (1.0 - (2.0 * hsl.lightness - 1.0).abs()) * hsl.saturation;
145 let hue_prime = hsl.hue / 60.0;
146 let x = chroma * (1.0 - (hue_prime % 2.0 - 1.0).abs());
147
148 let (r, g, b) = if hue_prime < 1.0 {
149 (chroma, x, 0.0)
150 } else if hue_prime < 2.0 {
151 (x, chroma, 0.0)
152 } else if hue_prime < 3.0 {
153 (0.0, chroma, x)
154 } else if hue_prime < 4.0 {
155 (0.0, x, chroma)
156 } else if hue_prime < 5.0 {
157 (x, 0.0, chroma)
158 } else {
159 (chroma, 0.0, x)
160 };
161
162 let m = hsl.lightness - chroma / 2.0;
163
164 Color::new(
165 ((r + m) * 255.0) as u8,
166 ((g + m) * 255.0) as u8,
167 ((b + m) * 255.0) as u8,
168 )
169 }
170}
171
172impl Into<u32> for Color {
173 fn into(self) -> u32 {
174 unsafe { std::mem::transmute(self) }
175 }
176}
177
178impl From<u32> for Color {
179 fn from(color: u32) -> Self {
180 unsafe { std::mem::transmute(color & 0x00ffffff) }
181 }
182}
183
184impl Into<ColorHSV> for Color {
185 fn into(self) -> ColorHSV {
186 self.into_hsv()
187 }
188}
189
190impl From<ColorHSV> for Color {
191 fn from(hsv: ColorHSV) -> Self {
192 Self::from_hsv(hsv)
193 }
194}
195
196impl Into<ColorHSL> for Color {
197 fn into(self) -> ColorHSL {
198 self.into_hsl()
199 }
200}
201
202impl From<ColorHSL> for Color {
203 fn from(hsl: ColorHSL) -> Self {
204 Self::from_hsl(hsl)
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 #[test]
213 fn test_color_hsv() {
214 let color = Color::new(255, 0, 0);
215 let hsv = color.into_hsv();
216 assert_eq!(hsv.hue, 0.0);
217 assert_eq!(hsv.saturation, 1.0);
218 assert_eq!(hsv.value, 1.0);
219
220 let color = Color::new(0, 255, 0);
221 let hsv = color.into_hsv();
222 assert_eq!(hsv.hue, 120.0);
223 assert_eq!(hsv.saturation, 1.0);
224 assert_eq!(hsv.value, 1.0);
225
226 let color = Color::new(0, 0, 255);
227 let hsv = color.into_hsv();
228 assert_eq!(hsv.hue, 240.0);
229 assert_eq!(hsv.saturation, 1.0);
230 assert_eq!(hsv.value, 1.0);
231
232 let color = Color::new(255, 255, 0);
233 let hsv = color.into_hsv();
234 assert_eq!(hsv.hue, 60.0);
235 assert_eq!(hsv.saturation, 1.0);
236 assert_eq!(hsv.value, 1.0);
237
238 let color = Color::new(0, 255, 255);
239 let hsv = color.into_hsv();
240 assert_eq!(hsv.hue, 180.0);
241 assert_eq!(hsv.saturation, 1.0);
242 assert_eq!(hsv.value, 1.0);
243
244 let color = Color::new(255, 0, 255);
245 let hsv = color.into_hsv();
246 assert_eq!(hsv.hue, 300.0);
247 assert_eq!(hsv.saturation, 1.0);
248 assert_eq!(hsv.value, 1.0);
249 }
250
251 #[test]
252 fn test_color_hsl() {
253 let color = Color::new(255, 0, 0);
254 let hsl = color.into_hsl();
255 assert_eq!(hsl.hue, 0.0);
256 assert_eq!(hsl.saturation, 1.0);
257 assert_eq!(hsl.lightness, 0.5);
258
259 let color = Color::new(0, 255, 0);
260 let hsl = color.into_hsl();
261 assert_eq!(hsl.hue, 120.0);
262 assert_eq!(hsl.saturation, 1.0);
263 assert_eq!(hsl.lightness, 0.5);
264
265 let color = Color::new(0, 0, 255);
266 let hsl = color.into_hsl();
267 assert_eq!(hsl.hue, 240.0);
268 assert_eq!(hsl.saturation, 1.0);
269 assert_eq!(hsl.lightness, 0.5);
270
271 let color = Color::new(255, 255, 0);
272 let hsl = color.into_hsl();
273 assert_eq!(hsl.hue, 60.0);
274 assert_eq!(hsl.saturation, 1.0);
275 assert_eq!(hsl.lightness, 0.5);
276 let color = Color::new(0, 255, 255);
277 let hsl = color.into_hsl();
278 assert_eq!(hsl.hue, 180.0);
279 assert_eq!(hsl.saturation, 1.0);
280 assert_eq!(hsl.lightness, 0.5);
281
282 let color = Color::new(255, 0, 255);
283 let hsl = color.into_hsl();
284 assert_eq!(hsl.hue, 300.0);
285 assert_eq!(hsl.saturation, 1.0);
286 assert_eq!(hsl.lightness, 0.5);
287 }
288
289 #[test]
290 fn test_color_from_hsv() {
291 let color = Color::from_hsv(ColorHSV {
292 hue: 0.0,
293 saturation: 1.0,
294 value: 1.0,
295 });
296 assert_eq!(color, Color::new(255, 0, 0));
297
298 let color = Color::from_hsv(ColorHSV {
299 hue: 120.0,
300 saturation: 1.0,
301 value: 1.0,
302 });
303 assert_eq!(color, Color::new(0, 255, 0));
304
305 let color = Color::from_hsv(ColorHSV {
306 hue: 240.0,
307 saturation: 1.0,
308 value: 1.0,
309 });
310 assert_eq!(color, Color::new(0, 0, 255));
311
312 let color = Color::from_hsv(ColorHSV {
313 hue: 60.0,
314 saturation: 1.0,
315 value: 1.0,
316 });
317 assert_eq!(color, Color::new(255, 255, 0));
318
319 let color = Color::from_hsv(ColorHSV {
320 hue: 180.0,
321 saturation: 1.0,
322 value: 1.0,
323 });
324 assert_eq!(color, Color::new(0, 255, 255));
325
326 let color = Color::from_hsv(ColorHSV {
327 hue: 300.0,
328 saturation: 1.0,
329 value: 1.0,
330 });
331 assert_eq!(color, Color::new(255, 0, 255));
332 }
333
334 #[test]
335 fn test_color_from_hsl() {
336 let color = Color::from_hsl(ColorHSL {
337 hue: 0.0,
338 saturation: 1.0,
339 lightness: 0.5,
340 });
341 assert_eq!(color, Color::new(255, 0, 0));
342
343 let color = Color::from_hsl(ColorHSL {
344 hue: 120.0,
345 saturation: 1.0,
346 lightness: 0.5,
347 });
348 assert_eq!(color, Color::new(0, 255, 0));
349
350 let color = Color::from_hsl(ColorHSL {
351 hue: 240.0,
352 saturation: 1.0,
353 lightness: 0.5,
354 });
355 assert_eq!(color, Color::new(0, 0, 255));
356
357 let color = Color::from_hsl(ColorHSL {
358 hue: 60.0,
359 saturation: 1.0,
360 lightness: 0.5,
361 });
362 assert_eq!(color, Color::new(255, 255, 0));
363
364 let color = Color::from_hsl(ColorHSL {
365 hue: 180.0,
366 saturation: 1.0,
367 lightness: 0.5,
368 });
369 assert_eq!(color, Color::new(0, 255, 255));
370
371 let color = Color::from_hsl(ColorHSL {
372 hue: 300.0,
373 saturation: 1.0,
374 lightness: 0.5,
375 });
376 assert_eq!(color, Color::new(255, 0, 255));
377 }
378}