1use std::ops::Add;
2use std::ops::AddAssign;
3use std::ops::Div;
4use std::ops::DivAssign;
5use std::ops::Mul;
6use std::ops::MulAssign;
7use std::ops::Rem;
8use std::ops::Sub;
9use std::ops::SubAssign;
10
11mod colour_parse_error;
12pub use self::colour_parse_error::*;
13
14pub type Color = Colour;
15
16#[derive(Copy, Clone, Debug, PartialEq)]
17pub struct Colour {
18 rgba: u32,
19}
20
21impl Colour {
22 pub const WHITE: Colour = Colour::new_from_rgba(0xffffffff);
23 pub const LIGHT_GREY: Colour = Colour::new_from_rgba(0xC0C0C0ff);
24 pub const GREY: Colour = Colour::new_from_rgba(0x808080ff);
25 pub const DARK_GREY: Colour = Colour::new_from_rgba(0x404040ff);
26 pub const BLACK: Colour = Colour::new_from_rgba(0x000000ff);
27
28 pub const ORANGE_RED: Colour = Colour::new_from_rgba(0xff4500ff);
29 pub const ORANGE: Colour = Colour::new_from_rgba(0xffA500ff);
30
31 pub const DARK_RED: Colour = Colour::new_from_rgba(0x800000ff);
32 pub const RED: Colour = Colour::new_from_rgba(0xff0000ff);
33 pub const LIGHT_RED: Colour = Colour::new_from_rgba(0xff6666ff);
34
35 pub const DARK_GREEN: Colour = Colour::new_from_rgba(0x006400ff);
36 pub const GREEN: Colour = Colour::new_from_rgba(0x00ff00ff);
37 pub const LIGHT_GREEN: Colour = Colour::new_from_rgba(0x90EE90ff);
38
39 pub const DARK_BLUE: Colour = Colour::new_from_rgba(0x00008Bff);
40 pub const BLUE: Colour = Colour::new_from_rgba(0x0000ffff);
41 pub const LIGHT_BLUE: Colour = Colour::new_from_rgba(0xADD8E6ff);
42
43 pub const MAGENTA: Colour = Colour::new_from_rgba(0xff00ffff);
44 pub const CYAN: Colour = Colour::new_from_rgba(0x00ffffff);
45 pub const YELLOW: Colour = Colour::new_from_rgba(0xffff00ff);
46
47 pub fn from_hex_str(hex: &str) -> Result<Color, ColourParseError> {
68 if hex.len() <= 6 {
69 return Self::from_short_hex_str(hex);
70 }
71
72 Self::from_long_hex_str(hex)
73 }
74
75 fn from_short_hex_str(hex: &str) -> Result<Color, ColourParseError> {
76 let len = hex.len();
77
78 if len < 3 || 6 < len {
79 return Err(ColourParseError::InvalidFormat);
80 }
81
82 if let Some(first) = hex.get(0..1) {
83 if first == "#" {
84 return Self::from_short_hex_str(&hex[1..]);
85 }
86 }
87
88 if let Some(first) = hex.get(0..2) {
89 if first == "0x" || first == "0X" {
90 return Self::from_short_hex_str(&hex[2..]);
91 }
92 }
93
94 if len == 6 {
95 return Self::from_long_hex_str(hex);
96 }
97
98 let red = lengthen_short_hex_num(&hex.get(0..1).ok_or(ColourParseError::InvalidFormat)?)?;
99 let green = lengthen_short_hex_num(&hex.get(1..2).ok_or(ColourParseError::InvalidFormat)?)?;
100 let blue = lengthen_short_hex_num(&hex.get(2..3).ok_or(ColourParseError::InvalidFormat)?)?;
101 let alpha = match len {
102 3 => 255,
103 4 => lengthen_short_hex_num(&hex.get(3..4).ok_or(ColourParseError::InvalidFormat)?)?,
104 _ => return Err(ColourParseError::InvalidFormat),
105 };
106
107 Ok(Color::new_from_u8s(red, green, blue, alpha))
108 }
109
110 fn from_long_hex_str(hex: &str) -> Result<Color, ColourParseError> {
111 let len = hex.len();
112
113 if let Some(first) = hex.get(0..1) {
114 if first == "#" {
115 return Self::from_long_hex_str(&hex[1..]);
116 }
117 }
118
119 if let Some(first) = hex.get(0..2) {
120 if first == "0x" || first == "0X" {
121 return Self::from_long_hex_str(&hex[2..]);
122 }
123 }
124
125 let red = u8::from_str_radix(&hex.get(0..2).ok_or(ColourParseError::InvalidFormat)?, 16)?;
126 let green = u8::from_str_radix(&hex.get(2..4).ok_or(ColourParseError::InvalidFormat)?, 16)?;
127 let blue = u8::from_str_radix(&hex.get(4..6).ok_or(ColourParseError::InvalidFormat)?, 16)?;
128 let alpha = match len {
129 6 => 255,
130 8 => u8::from_str_radix(&hex.get(6..8).ok_or(ColourParseError::InvalidFormat)?, 16)?,
131 _ => return Err(ColourParseError::InvalidFormat),
132 };
133
134 Ok(Color::new_from_u8s(red, green, blue, alpha))
135 }
136
137 #[inline(always)]
138 fn hex_u32_to_f32(val: u32, shift: u32) -> f32 {
139 ((val >> shift) & 0xff) as f32 / 255.0
140 }
141
142 #[inline(always)]
143 fn hex_u32_to_u32(val: u32, shift: u32) -> u32 {
144 (val >> shift) & 0xff
145 }
146
147 #[inline(always)]
148 fn hex_u32_to_u8(val: u32, shift: u32) -> u8 {
149 ((val >> shift) & 0xff) as u8
150 }
151
152 #[inline(always)]
153 #[allow(dead_code)]
154 fn f32_to_hex_u32(val: f32, shift: u8) -> u32 {
155 ((val * 255.0).round() as u32) << shift
156 }
157
158 #[allow(dead_code)]
159 fn clamp_u8_to_f32(val: u8) -> f32 {
160 if val >= 255 {
161 1.0
162 } else if val == 0 {
163 0.0
164 } else {
165 (val as f32) / 255.0
166 }
167 }
168
169 #[allow(dead_code)]
170 fn f32_to_rgba_u8(val: f32) -> u8 {
171 if val >= 1.0 {
172 255
173 } else if val <= 0.0 {
174 0
175 } else {
176 (val * 255.0) as u8
177 }
178 }
179
180 fn f32_to_rgba_u32(val: f32) -> u32 {
181 if val >= 1.0 {
182 255
183 } else if val <= 0.0 {
184 0
185 } else {
186 (val * 255.0) as u32
187 }
188 }
189
190 const fn rgba_u32s_to_rgba_hex(red: u32, green: u32, blue: u32, alpha: u32) -> u32 {
191 (red << 24) | (green << 16) | (blue << 8) | alpha
192 }
193
194 const fn rgba_u8s_to_rgba_hex(red: u8, green: u8, blue: u8, alpha: u8) -> u32 {
195 ((red as u32) << 24) | ((green as u32) << 16) | ((blue as u32) << 8) | (alpha as u32)
196 }
197
198 pub fn new_from_f32s(red: f32, green: f32, blue: f32, alpha: f32) -> Self {
199 let red = Colour::f32_to_rgba_u32(red);
200 let green = Colour::f32_to_rgba_u32(green);
201 let blue = Colour::f32_to_rgba_u32(blue);
202 let alpha = Colour::f32_to_rgba_u32(alpha);
203
204 Self {
205 rgba: Colour::rgba_u32s_to_rgba_hex(red, green, blue, alpha),
206 }
207 }
208
209 pub const fn new_from_u32s(red: u32, green: u32, blue: u32, alpha: u32) -> Self {
210 Self {
211 rgba: Colour::rgba_u32s_to_rgba_hex(red, green, blue, alpha),
212 }
213 }
214
215 pub const fn new_from_u8s(red: u8, green: u8, blue: u8, alpha: u8) -> Self {
216 Self {
217 rgba: Colour::rgba_u8s_to_rgba_hex(red, green, blue, alpha),
218 }
219 }
220
221 pub const fn new_from_rgba(rgba: u32) -> Self {
222 Self { rgba }
223 }
224
225 pub fn red_f32(&self) -> f32 {
226 Colour::hex_u32_to_f32(self.rgba, 24)
227 }
228
229 pub fn red_u32(&self) -> u32 {
230 Colour::hex_u32_to_u32(self.rgba, 24)
231 }
232
233 pub fn red_u8(&self) -> u8 {
234 Colour::hex_u32_to_u8(self.rgba, 24)
235 }
236
237 pub fn green_f32(&self) -> f32 {
238 Colour::hex_u32_to_f32(self.rgba, 16)
239 }
240
241 pub fn green_u32(&self) -> u32 {
242 Colour::hex_u32_to_u32(self.rgba, 16)
243 }
244
245 pub fn green_u8(&self) -> u8 {
246 Colour::hex_u32_to_u8(self.rgba, 16)
247 }
248
249 pub fn blue_f32(&self) -> f32 {
250 Colour::hex_u32_to_f32(self.rgba, 8)
251 }
252
253 pub fn blue_u32(&self) -> u32 {
254 Colour::hex_u32_to_u32(self.rgba, 8)
255 }
256
257 pub fn blue_u8(&self) -> u8 {
258 Colour::hex_u32_to_u8(self.rgba, 8)
259 }
260
261 pub fn alpha_f32(&self) -> f32 {
262 Colour::hex_u32_to_f32(self.rgba, 0)
263 }
264
265 pub fn alpha_u32(&self) -> u32 {
266 Colour::hex_u32_to_u32(self.rgba, 0)
267 }
268
269 pub fn alpha_u8(&self) -> u8 {
270 Colour::hex_u32_to_u8(self.rgba, 0)
271 }
272
273 pub fn mix(self, other: Self, mut amount: f32) -> Self {
274 amount = amount.max(0.0).min(1.0);
275 let inverse_amount = 1.0 - amount;
276
277 (self * inverse_amount) + (other * amount)
278 }
279
280 pub fn mix_no_alpha(self, other: Self, mut amount: f32) -> Self {
281 amount = amount.max(0.0).min(1.0);
282 let inverse_amount = 1.0 - amount;
283
284 let mut result = (self * inverse_amount) + (other * amount);
285 result.rgba = (result.rgba & 0xffffff00) | self.alpha_u32();
286 result
287 }
288
289 pub fn replace_alpha_f32(self, alpha: f32) -> Self {
290 self.replace_alpha_u32(Colour::f32_to_rgba_u32(alpha))
291 }
292
293 pub fn replace_alpha_u32(mut self, alpha: u32) -> Self {
294 self.set_alpha_u32(alpha);
295 self
296 }
297
298 pub fn set_alpha_f32(&mut self, alpha: f32) {
299 self.set_alpha_u32(Colour::f32_to_rgba_u32(alpha));
300 }
301
302 pub fn set_alpha_u32(&mut self, alpha: u32) {
303 self.rgba = (self.rgba & 0xffffff00) | alpha;
304 }
305
306 #[inline(always)]
307 pub fn to_rgba_u32(self) -> u32 {
308 self.rgba
309 }
310}
311
312impl Add<Self> for Colour {
313 type Output = Self;
314
315 fn add(self, other: Self) -> Self {
316 Self::new_from_f32s(
317 self.red_f32() + other.red_f32(),
318 self.green_f32() + other.green_f32(),
319 self.blue_f32() + other.blue_f32(),
320 self.alpha_f32() + other.alpha_f32(),
321 )
322 }
323}
324
325impl AddAssign<Self> for Colour {
326 fn add_assign(&mut self, other: Self) {
327 *self = *self + other;
328 }
329}
330
331impl Sub<Self> for Colour {
332 type Output = Self;
333
334 fn sub(self, other: Self) -> Self {
335 Self::new_from_f32s(
336 self.red_f32() - other.red_f32(),
337 self.green_f32() - other.green_f32(),
338 self.blue_f32() - other.blue_f32(),
339 self.alpha_f32() - other.alpha_f32(),
340 )
341 }
342}
343
344impl SubAssign<Self> for Colour {
345 fn sub_assign(&mut self, other: Self) {
346 *self = *self - other;
347 }
348}
349
350impl Mul<Self> for Colour {
351 type Output = Self;
352
353 fn mul(self, other: Self) -> Self {
354 Self::new_from_f32s(
355 self.red_f32() * other.red_f32(),
356 self.green_f32() * other.green_f32(),
357 self.blue_f32() * other.blue_f32(),
358 self.alpha_f32() * other.alpha_f32(),
359 )
360 }
361}
362
363impl MulAssign<Self> for Colour {
364 fn mul_assign(&mut self, other: Self) {
365 *self = *self * other;
366 }
367}
368
369impl Div<Self> for Colour {
370 type Output = Self;
371
372 fn div(self, other: Self) -> Self {
373 Self::new_from_f32s(
374 self.red_f32() / other.red_f32(),
375 self.green_f32() / other.green_f32(),
376 self.blue_f32() / other.blue_f32(),
377 self.alpha_f32() / other.alpha_f32(),
378 )
379 }
380}
381
382impl DivAssign<Self> for Colour {
383 fn div_assign(&mut self, other: Self) {
384 *self = *self / other;
385 }
386}
387
388impl Rem<Self> for Colour {
389 type Output = Self;
390
391 fn rem(self, other: Self) -> Self {
392 Self::new_from_f32s(
393 self.red_f32() % other.red_f32(),
394 self.green_f32() % other.green_f32(),
395 self.blue_f32() % other.blue_f32(),
396 self.alpha_f32() % other.alpha_f32(),
397 )
398 }
399}
400
401impl Mul<f32> for Colour {
402 type Output = Self;
403
404 fn mul(self, val: f32) -> Self {
405 Self::new_from_f32s(
406 self.red_f32() * val,
407 self.green_f32() * val,
408 self.blue_f32() * val,
409 self.alpha_f32() * val,
410 )
411 }
412}
413
414impl MulAssign<f32> for Colour {
415 fn mul_assign(&mut self, val: f32) {
416 *self = *self * val;
417 }
418}
419
420impl Div<f32> for Colour {
421 type Output = Self;
422
423 fn div(self, val: f32) -> Self {
424 Self::new_from_f32s(
425 self.red_f32() / val,
426 self.green_f32() / val,
427 self.blue_f32() / val,
428 self.alpha_f32() / val,
429 )
430 }
431}
432
433impl DivAssign<f32> for Colour {
434 fn div_assign(&mut self, val: f32) {
435 *self = *self / val;
436 }
437}
438
439fn lengthen_short_hex_num(hex_num: &str) -> Result<u8, ColourParseError> {
440 let num = u8::from_str_radix(&hex_num, 16)?;
441 let lengthened_num = (num << 4) + num;
442
443 Ok(lengthened_num)
444}
445
446#[cfg(test)]
447mod red_xx {
448 use super::*;
449
450 #[test]
451 fn it_should_return_red_given() {
452 let rgba_hex = 0xffa89321;
453 let colour = Colour::new_from_rgba(rgba_hex);
454
455 assert_eq!(colour.red_f32(), 1.0);
456 assert_eq!(colour.red_u32(), 255);
457 assert_eq!(colour.red_u8(), 255);
458 }
459}
460
461#[cfg(test)]
462mod green_xx {
463 use super::*;
464
465 #[test]
466 fn it_should_return_green_given() {
467 let rgba_hex = 0xffa89321;
468 let colour = Colour::new_from_rgba(rgba_hex);
469
470 assert_eq!(colour.green_f32(), 168.0 / 255.0);
471 assert_eq!(colour.green_u32(), 168);
472 assert_eq!(colour.green_u8(), 168);
473 }
474}
475
476#[cfg(test)]
477mod blue_xx {
478 use super::*;
479
480 #[test]
481 fn it_should_return_blue_given() {
482 let rgba_hex = 0xffa89321;
483 let colour = Colour::new_from_rgba(rgba_hex);
484
485 assert_eq!(colour.blue_f32(), 147.0 / 255.0);
486 assert_eq!(colour.blue_u32(), 147);
487 assert_eq!(colour.blue_u8(), 147);
488 }
489}
490
491#[cfg(test)]
492mod alpha_xx {
493 use super::*;
494
495 #[test]
496 fn it_should_return_alpha_given() {
497 let rgba_hex = 0xffa89321;
498 let colour = Colour::new_from_rgba(rgba_hex);
499
500 assert_eq!(colour.alpha_f32(), 33.0 / 255.0);
501 assert_eq!(colour.alpha_u32(), 33);
502 assert_eq!(colour.alpha_u8(), 33);
503 }
504}
505
506#[cfg(test)]
507mod new_rgba_hex {
508 use super::*;
509
510 #[test]
511 fn it_should_have_components_match_those_given() {
512 let rgba_hex = 0xffa89321;
513 let colour = Colour::new_from_rgba(rgba_hex);
514
515 assert_eq!(colour.red_u32(), 255);
516 assert_eq!(colour.green_u32(), 168);
517 assert_eq!(colour.blue_u32(), 147);
518 assert_eq!(colour.alpha_u32(), 33);
519 assert_eq!(colour.to_rgba_u32(), rgba_hex);
520 }
521}
522
523#[cfg(test)]
524mod replace_alpha_f32 {
525 use super::*;
526
527 #[test]
528 fn it_should_replace_alpha_value() {
529 let rgba_hex = 0xffffff11;
530 let mut colour = Colour::new_from_rgba(rgba_hex);
531
532 colour = colour.replace_alpha_f32(0.0);
533 assert_eq!(colour.alpha_u8(), 0);
534
535 colour = colour.replace_alpha_f32(0.5);
536 assert_eq!(colour.alpha_u8(), 127);
537
538 colour = colour.replace_alpha_f32(1.0);
539 assert_eq!(colour.alpha_u8(), 255);
540 }
541
542 #[test]
543 fn it_should_not_replace_other_values() {
544 let rgba_hex = 0xffffff11;
545 let mut colour = Colour::new_from_rgba(rgba_hex);
546
547 colour = colour.replace_alpha_f32(0.0);
548 assert_eq!(colour.red_u8(), 255);
549 assert_eq!(colour.green_u8(), 255);
550 assert_eq!(colour.blue_u8(), 255);
551 }
552}
553
554#[cfg(test)]
555mod from_hex_str {
556 use super::*;
557 use testcat::*;
558
559 describe!("short hex codes", {
560 it!("should parse with no prefix", short_hex::test_no_prefix);
561 it!("should parse with hash prefix", short_hex::test_hash_prefix);
562 it!("should parse with 0x prefix", short_hex::test_zero_x_prefix);
563 it!(
564 "should parse with no prefix, with alpha",
565 short_hex::test_no_prefix_with_alpha
566 );
567 it!(
568 "should parse with hash prefix, with alpha",
569 short_hex::test_hash_prefix_with_alpha
570 );
571 it!(
572 "should parse with 0x prefix, with alpha",
573 short_hex::test_zero_x_prefix_with_alpha
574 );
575 });
576
577 describe!("long hex codes", {
578 it!("should parse with no prefix", long_hex::test_no_prefix);
579 it!("should parse with hash prefix", long_hex::test_hash_prefix);
580 it!("should parse with 0x prefix", long_hex::test_zero_x_prefix);
581 it!(
582 "should parse with no prefix, with alpha",
583 long_hex::test_no_prefix_with_alpha
584 );
585 it!(
586 "should parse with hash prefix, with alpha",
587 long_hex::test_hash_prefix_with_alpha
588 );
589 it!(
590 "should parse with 0x prefix, with alpha",
591 long_hex::test_zero_x_prefix_with_alpha
592 );
593 });
594
595 #[cfg(test)]
596 mod short_hex {
597 use super::*;
598
599 pub fn test_no_prefix() {
600 let colour = Colour::from_hex_str(&"12c").unwrap();
601
602 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 255));
603 }
604
605 pub fn test_hash_prefix() {
606 let colour = Colour::from_hex_str(&"#12c").unwrap();
607
608 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 255));
609 }
610
611 pub fn test_zero_x_prefix() {
612 let colour = Colour::from_hex_str(&"0x12c").unwrap();
613
614 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 255));
615 }
616
617 pub fn test_no_prefix_with_alpha() {
618 let colour = Colour::from_hex_str(&"12c4").unwrap();
619
620 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 68));
621 }
622
623 pub fn test_hash_prefix_with_alpha() {
624 let colour = Colour::from_hex_str(&"#12c4").unwrap();
625
626 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 68));
627 }
628
629 pub fn test_zero_x_prefix_with_alpha() {
630 let colour = Colour::from_hex_str(&"0x12c4").unwrap();
631
632 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 68));
633 }
634 }
635
636 #[cfg(test)]
637 mod long_hex {
638 use super::*;
639
640 pub fn test_no_prefix() {
641 let colour = Colour::from_hex_str(&"1122cc").unwrap();
642
643 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 255));
644 }
645
646 pub fn test_hash_prefix() {
647 let colour = Colour::from_hex_str(&"#1122cc").unwrap();
648
649 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 255));
650 }
651
652 pub fn test_zero_x_prefix() {
653 let colour = Colour::from_hex_str(&"0x1122cc").unwrap();
654
655 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 255));
656 }
657
658 pub fn test_no_prefix_with_alpha() {
659 let colour = Colour::from_hex_str(&"1122cc44").unwrap();
660
661 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 68));
662 }
663
664 pub fn test_hash_prefix_with_alpha() {
665 let colour = Colour::from_hex_str(&"#1122cc44").unwrap();
666
667 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 68));
668 }
669
670 pub fn test_zero_x_prefix_with_alpha() {
671 let colour = Colour::from_hex_str(&"0x1122cc44").unwrap();
672
673 assert_eq!(colour, Colour::new_from_u8s(17, 34, 204, 68));
674 }
675 }
676}