1use std::ops::{Add, AddAssign, Mul, MulAssign};
6
7use glam::{vec3, vec4, Vec3, Vec4};
8use serde::{Deserialize, Serialize};
9use thiserror::Error;
10
11mod colorspace;
12
13use crate::colorspace::{HslRepresentation, SrgbColorSpace};
14
15#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
16pub enum Color {
17 Rgba {
19 red: f32,
21 green: f32,
23 blue: f32,
25 alpha: f32,
27 },
28 RgbaLinear {
30 red: f32,
32 green: f32,
34 blue: f32,
36 alpha: f32,
38 },
39 Hsla {
41 hue: f32,
43 saturation: f32,
45 lightness: f32,
47 alpha: f32,
49 },
50}
51
52impl Color {
53 pub const WHITE: Color = Color::rgb(1.0, 1.0, 1.0);
54 pub const BLACK: Color = Color::rgb(0.0, 0.0, 0.0);
55 pub const TRANSPARENT: Color = Color::rgba(0.0, 0.0, 0.0, 0.);
56
57 pub const fn rgb(r: f32, g: f32, b: f32) -> Color {
59 Color::Rgba {
60 red: r,
61 green: g,
62 blue: b,
63 alpha: 1.0,
64 }
65 }
66
67 pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
69 Color::Rgba {
70 red: r,
71 green: g,
72 blue: b,
73 alpha: a,
74 }
75 }
76
77 pub const fn rgb_linear(r: f32, g: f32, b: f32) -> Color {
79 Color::RgbaLinear {
80 red: r,
81 green: g,
82 blue: b,
83 alpha: 1.0,
84 }
85 }
86
87 pub const fn rgba_linear(r: f32, g: f32, b: f32, a: f32) -> Color {
89 Color::RgbaLinear {
90 red: r,
91 green: g,
92 blue: b,
93 alpha: a,
94 }
95 }
96
97 pub const fn hsl(hue: f32, saturation: f32, lightness: f32) -> Color {
99 Color::Hsla {
100 hue,
101 saturation,
102 lightness,
103 alpha: 1.0,
104 }
105 }
106
107 pub const fn hsla(hue: f32, saturation: f32, lightness: f32, alpha: f32) -> Color {
109 Color::Hsla {
110 hue,
111 saturation,
112 lightness,
113 alpha,
114 }
115 }
116
117 pub fn hex<T: AsRef<str>>(hex: T) -> Result<Color, HexColorError> {
119 let hex = hex.as_ref();
120 if let Some(hex) = hex.strip_prefix('#') {
121 return Self::hex(hex);
122 }
123
124 if hex.len() == 3 {
126 let mut data = [0; 6];
127 for (i, ch) in hex.chars().enumerate() {
128 data[i * 2] = ch as u8;
129 data[i * 2 + 1] = ch as u8;
130 }
131 return decode_rgb(&data);
132 }
133
134 if hex.len() == 4 {
136 let mut data = [0; 8];
137 for (i, ch) in hex.chars().enumerate() {
138 data[i * 2] = ch as u8;
139 data[i * 2 + 1] = ch as u8;
140 }
141 return decode_rgba(&data);
142 }
143
144 if hex.len() == 6 {
146 return decode_rgb(hex.as_bytes());
147 }
148
149 if hex.len() == 8 {
151 return decode_rgba(hex.as_bytes());
152 }
153
154 Err(HexColorError::Length)
155 }
156
157 pub fn u8_debug(v: u8) -> Self {
158 if v == 0 {
159 Self::rgb(1., 1., 1.)
160 } else if v == 1 {
161 Self::rgb(1., 0., 0.)
162 } else if v == 2 {
163 Self::rgb(0., 1., 0.)
164 } else if v == 3 {
165 Self::rgb(0., 0., 1.)
166 } else if v == 4 {
167 Self::rgb(1., 0., 1.)
168 } else if v == 5 {
169 Self::rgb(0., 1., 1.)
170 } else if v == 6 {
171 Self::rgb(1., 1., 0.)
172 } else {
173 Self::rgb(0.5, 0.5, 0.5)
174 }
175 }
176
177 pub fn rgb_u8(r: u8, g: u8, b: u8) -> Color {
179 Color::rgba_u8(r, g, b, u8::MAX)
180 }
181
182 pub fn rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Color {
186 Color::rgba(
187 r as f32 / u8::MAX as f32,
188 g as f32 / u8::MAX as f32,
189 b as f32 / u8::MAX as f32,
190 a as f32 / u8::MAX as f32,
191 )
192 }
193
194 pub fn r(&self) -> f32 {
196 match self.as_rgba() {
197 Color::Rgba { red, .. } => red,
198 _ => unreachable!(),
199 }
200 }
201
202 pub fn g(&self) -> f32 {
204 match self.as_rgba() {
205 Color::Rgba { green, .. } => green,
206 _ => unreachable!(),
207 }
208 }
209
210 pub fn b(&self) -> f32 {
212 match self.as_rgba() {
213 Color::Rgba { blue, .. } => blue,
214 _ => unreachable!(),
215 }
216 }
217
218 pub fn set_r(&mut self, r: f32) -> &mut Self {
220 *self = self.as_rgba();
221 match self {
222 Color::Rgba { red, .. } => *red = r,
223 _ => unreachable!(),
224 }
225 self
226 }
227
228 pub fn set_g(&mut self, g: f32) -> &mut Self {
230 *self = self.as_rgba();
231 match self {
232 Color::Rgba { green, .. } => *green = g,
233 _ => unreachable!(),
234 }
235 self
236 }
237
238 pub fn set_b(&mut self, b: f32) -> &mut Self {
240 *self = self.as_rgba();
241 match self {
242 Color::Rgba { blue, .. } => *blue = b,
243 _ => unreachable!(),
244 }
245 self
246 }
247
248 pub fn a(&self) -> f32 {
250 match self {
251 Color::Rgba { alpha, .. }
252 | Color::RgbaLinear { alpha, .. }
253 | Color::Hsla { alpha, .. } => *alpha,
254 }
255 }
256
257 pub fn set_a(&mut self, a: f32) -> &mut Self {
259 match self {
260 Color::Rgba { alpha, .. }
261 | Color::RgbaLinear { alpha, .. }
262 | Color::Hsla { alpha, .. } => {
263 *alpha = a;
264 }
265 }
266 self
267 }
268
269 pub fn as_rgba(self: &Color) -> Color {
271 match self {
272 Color::Rgba { .. } => *self,
273 Color::RgbaLinear {
274 red,
275 green,
276 blue,
277 alpha,
278 } => Color::Rgba {
279 red: red.linear_to_nonlinear_srgb(),
280 green: green.linear_to_nonlinear_srgb(),
281 blue: blue.linear_to_nonlinear_srgb(),
282 alpha: *alpha,
283 },
284 Color::Hsla {
285 hue,
286 saturation,
287 lightness,
288 alpha,
289 } => {
290 let [red, green, blue] =
291 HslRepresentation::hsl_to_nonlinear_srgb(*hue, *saturation, *lightness);
292 Color::Rgba {
293 red,
294 green,
295 blue,
296 alpha: *alpha,
297 }
298 }
299 }
300 }
301
302 pub fn as_rgba_linear(self: &Color) -> Color {
304 match self {
305 Color::Rgba {
306 red,
307 green,
308 blue,
309 alpha,
310 } => Color::RgbaLinear {
311 red: red.nonlinear_to_linear_srgb(),
312 green: green.nonlinear_to_linear_srgb(),
313 blue: blue.nonlinear_to_linear_srgb(),
314 alpha: *alpha,
315 },
316 Color::RgbaLinear { .. } => *self,
317 Color::Hsla {
318 hue,
319 saturation,
320 lightness,
321 alpha,
322 } => {
323 let [red, green, blue] =
324 HslRepresentation::hsl_to_nonlinear_srgb(*hue, *saturation, *lightness);
325 Color::RgbaLinear {
326 red: red.nonlinear_to_linear_srgb(),
327 green: green.nonlinear_to_linear_srgb(),
328 blue: blue.nonlinear_to_linear_srgb(),
329 alpha: *alpha,
330 }
331 }
332 }
333 }
334
335 pub fn as_hsla(self: &Color) -> Color {
337 match self {
338 Color::Rgba {
339 red,
340 green,
341 blue,
342 alpha,
343 } => {
344 let (hue, saturation, lightness) =
345 HslRepresentation::nonlinear_srgb_to_hsl([*red, *green, *blue]);
346 Color::Hsla {
347 hue,
348 saturation,
349 lightness,
350 alpha: *alpha,
351 }
352 }
353 Color::RgbaLinear {
354 red,
355 green,
356 blue,
357 alpha,
358 } => {
359 let (hue, saturation, lightness) = HslRepresentation::nonlinear_srgb_to_hsl([
360 red.linear_to_nonlinear_srgb(),
361 green.linear_to_nonlinear_srgb(),
362 blue.linear_to_nonlinear_srgb(),
363 ]);
364 Color::Hsla {
365 hue,
366 saturation,
367 lightness,
368 alpha: *alpha,
369 }
370 }
371 Color::Hsla { .. } => *self,
372 }
373 }
374
375 pub fn as_rgba_f32(self: Color) -> [f32; 4] {
377 match self {
378 Color::Rgba {
379 red,
380 green,
381 blue,
382 alpha,
383 } => [red, green, blue, alpha],
384 Color::RgbaLinear {
385 red,
386 green,
387 blue,
388 alpha,
389 } => [
390 red.linear_to_nonlinear_srgb(),
391 green.linear_to_nonlinear_srgb(),
392 blue.linear_to_nonlinear_srgb(),
393 alpha,
394 ],
395 Color::Hsla {
396 hue,
397 saturation,
398 lightness,
399 alpha,
400 } => {
401 let [red, green, blue] =
402 HslRepresentation::hsl_to_nonlinear_srgb(hue, saturation, lightness);
403 [red, green, blue, alpha]
404 }
405 }
406 }
407
408 #[inline]
410 pub fn as_linear_rgba_f32(self: Color) -> [f32; 4] {
411 match self {
412 Color::Rgba {
413 red,
414 green,
415 blue,
416 alpha,
417 } => [
418 red.nonlinear_to_linear_srgb(),
419 green.nonlinear_to_linear_srgb(),
420 blue.nonlinear_to_linear_srgb(),
421 alpha,
422 ],
423 Color::RgbaLinear {
424 red,
425 green,
426 blue,
427 alpha,
428 } => [red, green, blue, alpha],
429 Color::Hsla {
430 hue,
431 saturation,
432 lightness,
433 alpha,
434 } => {
435 let [red, green, blue] =
436 HslRepresentation::hsl_to_nonlinear_srgb(hue, saturation, lightness);
437 [
438 red.nonlinear_to_linear_srgb(),
439 green.nonlinear_to_linear_srgb(),
440 blue.nonlinear_to_linear_srgb(),
441 alpha,
442 ]
443 }
444 }
445 }
446
447 pub fn as_hsla_f32(self: Color) -> [f32; 4] {
449 match self {
450 Color::Rgba {
451 red,
452 green,
453 blue,
454 alpha,
455 } => {
456 let (hue, saturation, lightness) =
457 HslRepresentation::nonlinear_srgb_to_hsl([red, green, blue]);
458 [hue, saturation, lightness, alpha]
459 }
460 Color::RgbaLinear {
461 red,
462 green,
463 blue,
464 alpha,
465 } => {
466 let (hue, saturation, lightness) = HslRepresentation::nonlinear_srgb_to_hsl([
467 red.linear_to_nonlinear_srgb(),
468 green.linear_to_nonlinear_srgb(),
469 blue.linear_to_nonlinear_srgb(),
470 ]);
471 [hue, saturation, lightness, alpha]
472 }
473 Color::Hsla {
474 hue,
475 saturation,
476 lightness,
477 alpha,
478 } => [hue, saturation, lightness, alpha],
479 }
480 }
481
482 pub fn as_rgba_u32(self: Color) -> u32 {
487 match self {
488 Color::Rgba {
489 red,
490 green,
491 blue,
492 alpha,
493 } => u32::from_le_bytes([
494 (red * 255.0) as u8,
495 (green * 255.0) as u8,
496 (blue * 255.0) as u8,
497 (alpha * 255.0) as u8,
498 ]),
499 Color::RgbaLinear {
500 red,
501 green,
502 blue,
503 alpha,
504 } => u32::from_le_bytes([
505 (red.linear_to_nonlinear_srgb() * 255.0) as u8,
506 (green.linear_to_nonlinear_srgb() * 255.0) as u8,
507 (blue.linear_to_nonlinear_srgb() * 255.0) as u8,
508 (alpha * 255.0) as u8,
509 ]),
510 Color::Hsla {
511 hue,
512 saturation,
513 lightness,
514 alpha,
515 } => {
516 let [red, green, blue] =
517 HslRepresentation::hsl_to_nonlinear_srgb(hue, saturation, lightness);
518 u32::from_le_bytes([
519 (red * 255.0) as u8,
520 (green * 255.0) as u8,
521 (blue * 255.0) as u8,
522 (alpha * 255.0) as u8,
523 ])
524 }
525 }
526 }
527
528 pub fn as_linear_rgba_u32(self: Color) -> u32 {
533 match self {
534 Color::Rgba {
535 red,
536 green,
537 blue,
538 alpha,
539 } => u32::from_le_bytes([
540 (red.nonlinear_to_linear_srgb() * 255.0) as u8,
541 (green.nonlinear_to_linear_srgb() * 255.0) as u8,
542 (blue.nonlinear_to_linear_srgb() * 255.0) as u8,
543 (alpha * 255.0) as u8,
544 ]),
545 Color::RgbaLinear {
546 red,
547 green,
548 blue,
549 alpha,
550 } => u32::from_le_bytes([
551 (red * 255.0) as u8,
552 (green * 255.0) as u8,
553 (blue * 255.0) as u8,
554 (alpha * 255.0) as u8,
555 ]),
556 Color::Hsla {
557 hue,
558 saturation,
559 lightness,
560 alpha,
561 } => {
562 let [red, green, blue] =
563 HslRepresentation::hsl_to_nonlinear_srgb(hue, saturation, lightness);
564 u32::from_le_bytes([
565 (red.nonlinear_to_linear_srgb() * 255.0) as u8,
566 (green.nonlinear_to_linear_srgb() * 255.0) as u8,
567 (blue.nonlinear_to_linear_srgb() * 255.0) as u8,
568 (alpha * 255.0) as u8,
569 ])
570 }
571 }
572 }
573
574 pub fn saturate(self, amount: f32) -> Self {
575 if let Color::Hsla {
576 hue,
577 saturation,
578 lightness,
579 alpha,
580 } = self.as_hsla()
581 {
582 Self::Hsla {
583 hue,
584 saturation: (saturation + amount).clamp(0., 1.),
585 lightness,
586 alpha,
587 }
588 } else {
589 unreachable!()
590 }
591 }
592 pub fn desaturate(self, amount: f32) -> Self {
593 self.saturate(-amount)
594 }
595
596 pub fn lighten(self, amount: f32) -> Self {
597 if let Color::Hsla {
598 hue,
599 saturation,
600 lightness,
601 alpha,
602 } = self.as_hsla()
603 {
604 Self::Hsla {
605 hue,
606 saturation,
607 lightness: (lightness + amount).clamp(0., 1.),
608 alpha,
609 }
610 } else {
611 unreachable!()
612 }
613 }
614 pub fn darken(self, amount: f32) -> Self {
615 self.lighten(-amount)
616 }
617}
618
619impl Default for Color {
620 fn default() -> Self {
621 Color::WHITE
622 }
623}
624
625impl AddAssign<Color> for Color {
626 fn add_assign(&mut self, rhs: Color) {
627 match self {
628 Color::Rgba {
629 red,
630 green,
631 blue,
632 alpha,
633 } => {
634 let rhs = rhs.as_rgba_f32();
635 *red += rhs[0];
636 *green += rhs[1];
637 *blue += rhs[2];
638 *alpha += rhs[3];
639 }
640 Color::RgbaLinear {
641 red,
642 green,
643 blue,
644 alpha,
645 } => {
646 let rhs = rhs.as_linear_rgba_f32();
647 *red += rhs[0];
648 *green += rhs[1];
649 *blue += rhs[2];
650 *alpha += rhs[3];
651 }
652 Color::Hsla {
653 hue,
654 saturation,
655 lightness,
656 alpha,
657 } => {
658 let rhs = rhs.as_linear_rgba_f32();
659 *hue += rhs[0];
660 *saturation += rhs[1];
661 *lightness += rhs[2];
662 *alpha += rhs[3];
663 }
664 }
665 }
666}
667
668impl Add<Color> for Color {
669 type Output = Color;
670
671 fn add(self, rhs: Color) -> Self::Output {
672 match self {
673 Color::Rgba {
674 red,
675 green,
676 blue,
677 alpha,
678 } => {
679 let rhs = rhs.as_rgba_f32();
680 Color::Rgba {
681 red: red + rhs[0],
682 green: green + rhs[1],
683 blue: blue + rhs[2],
684 alpha: alpha + rhs[3],
685 }
686 }
687 Color::RgbaLinear {
688 red,
689 green,
690 blue,
691 alpha,
692 } => {
693 let rhs = rhs.as_linear_rgba_f32();
694 Color::RgbaLinear {
695 red: red + rhs[0],
696 green: green + rhs[1],
697 blue: blue + rhs[2],
698 alpha: alpha + rhs[3],
699 }
700 }
701 Color::Hsla {
702 hue,
703 saturation,
704 lightness,
705 alpha,
706 } => {
707 let rhs = rhs.as_linear_rgba_f32();
708 Color::Hsla {
709 hue: hue + rhs[0],
710 saturation: saturation + rhs[1],
711 lightness: lightness + rhs[2],
712 alpha: alpha + rhs[3],
713 }
714 }
715 }
716 }
717}
718
719impl AddAssign<Vec4> for Color {
720 fn add_assign(&mut self, rhs: Vec4) {
721 let rhs: Color = rhs.into();
722 *self += rhs;
723 }
724}
725
726impl Add<Vec4> for Color {
727 type Output = Color;
728
729 fn add(self, rhs: Vec4) -> Self::Output {
730 let rhs: Color = rhs.into();
731 self + rhs
732 }
733}
734
735impl From<Color> for [f32; 4] {
736 fn from(color: Color) -> Self {
737 color.as_rgba_f32()
738 }
739}
740
741impl From<[f32; 4]> for Color {
742 fn from([r, g, b, a]: [f32; 4]) -> Self {
743 Color::rgba(r, g, b, a)
744 }
745}
746
747impl From<[f32; 3]> for Color {
748 fn from([r, g, b]: [f32; 3]) -> Self {
749 Color::rgb(r, g, b)
750 }
751}
752
753impl From<Color> for Vec4 {
754 fn from(color: Color) -> Self {
755 let color: [f32; 4] = color.into();
756 vec4(color[0], color[1], color[2], color[3])
757 }
758}
759impl From<Color> for Vec3 {
760 fn from(color: Color) -> Self {
761 let color: [f32; 4] = color.into();
762 vec3(color[0], color[1], color[2])
763 }
764}
765
766impl From<Vec4> for Color {
767 fn from(vec4: Vec4) -> Self {
768 Color::rgba(vec4.x, vec4.y, vec4.z, vec4.w)
769 }
770}
771
772#[cfg(feature = "wgpu")]
773impl From<Color> for wgpu::Color {
774 fn from(color: Color) -> Self {
775 if let Color::RgbaLinear {
776 red,
777 green,
778 blue,
779 alpha,
780 } = color.as_rgba_linear()
781 {
782 wgpu::Color {
783 r: red as f64,
784 g: green as f64,
785 b: blue as f64,
786 a: alpha as f64,
787 }
788 } else {
789 unreachable!()
790 }
791 }
792}
793
794impl Mul<f32> for Color {
795 type Output = Color;
796
797 fn mul(self, rhs: f32) -> Self::Output {
798 match self {
799 Color::Rgba {
800 red,
801 green,
802 blue,
803 alpha,
804 } => Color::Rgba {
805 red: red * rhs,
806 green: green * rhs,
807 blue: blue * rhs,
808 alpha,
809 },
810 Color::RgbaLinear {
811 red,
812 green,
813 blue,
814 alpha,
815 } => Color::RgbaLinear {
816 red: red * rhs,
817 green: green * rhs,
818 blue: blue * rhs,
819 alpha,
820 },
821 Color::Hsla {
822 hue,
823 saturation,
824 lightness,
825 alpha,
826 } => Color::Hsla {
827 hue: hue * rhs,
828 saturation: saturation * rhs,
829 lightness: lightness * rhs,
830 alpha,
831 },
832 }
833 }
834}
835
836impl MulAssign<f32> for Color {
837 fn mul_assign(&mut self, rhs: f32) {
838 match self {
839 Color::Rgba {
840 red, green, blue, ..
841 }
842 | Color::RgbaLinear {
843 red, green, blue, ..
844 } => {
845 *red *= rhs;
846 *green *= rhs;
847 *blue *= rhs;
848 }
849 Color::Hsla {
850 hue,
851 saturation,
852 lightness,
853 ..
854 } => {
855 *hue *= rhs;
856 *saturation *= rhs;
857 *lightness *= rhs;
858 }
859 }
860 }
861}
862
863impl Mul<Vec4> for Color {
864 type Output = Color;
865
866 fn mul(self, rhs: Vec4) -> Self::Output {
867 match self {
868 Color::Rgba {
869 red,
870 green,
871 blue,
872 alpha,
873 } => Color::Rgba {
874 red: red * rhs.x,
875 green: green * rhs.y,
876 blue: blue * rhs.z,
877 alpha: alpha * rhs.w,
878 },
879 Color::RgbaLinear {
880 red,
881 green,
882 blue,
883 alpha,
884 } => Color::RgbaLinear {
885 red: red * rhs.x,
886 green: green * rhs.y,
887 blue: blue * rhs.z,
888 alpha: alpha * rhs.w,
889 },
890 Color::Hsla {
891 hue,
892 saturation,
893 lightness,
894 alpha,
895 } => Color::Hsla {
896 hue: hue * rhs.x,
897 saturation: saturation * rhs.y,
898 lightness: lightness * rhs.z,
899 alpha: alpha * rhs.w,
900 },
901 }
902 }
903}
904
905impl MulAssign<Vec4> for Color {
906 fn mul_assign(&mut self, rhs: Vec4) {
907 match self {
908 Color::Rgba {
909 red,
910 green,
911 blue,
912 alpha,
913 }
914 | Color::RgbaLinear {
915 red,
916 green,
917 blue,
918 alpha,
919 } => {
920 *red *= rhs.x;
921 *green *= rhs.y;
922 *blue *= rhs.z;
923 *alpha *= rhs.w;
924 }
925 Color::Hsla {
926 hue,
927 saturation,
928 lightness,
929 alpha,
930 } => {
931 *hue *= rhs.x;
932 *saturation *= rhs.y;
933 *lightness *= rhs.z;
934 *alpha *= rhs.w;
935 }
936 }
937 }
938}
939
940impl Mul<Vec3> for Color {
941 type Output = Color;
942
943 fn mul(self, rhs: Vec3) -> Self::Output {
944 match self {
945 Color::Rgba {
946 red,
947 green,
948 blue,
949 alpha,
950 } => Color::Rgba {
951 red: red * rhs.x,
952 green: green * rhs.y,
953 blue: blue * rhs.z,
954 alpha,
955 },
956 Color::RgbaLinear {
957 red,
958 green,
959 blue,
960 alpha,
961 } => Color::RgbaLinear {
962 red: red * rhs.x,
963 green: green * rhs.y,
964 blue: blue * rhs.z,
965 alpha,
966 },
967 Color::Hsla {
968 hue,
969 saturation,
970 lightness,
971 alpha,
972 } => Color::Hsla {
973 hue: hue * rhs.x,
974 saturation: saturation * rhs.y,
975 lightness: lightness * rhs.z,
976 alpha,
977 },
978 }
979 }
980}
981
982impl MulAssign<Vec3> for Color {
983 fn mul_assign(&mut self, rhs: Vec3) {
984 match self {
985 Color::Rgba {
986 red, green, blue, ..
987 }
988 | Color::RgbaLinear {
989 red, green, blue, ..
990 } => {
991 *red *= rhs.x;
992 *green *= rhs.y;
993 *blue *= rhs.z;
994 }
995 Color::Hsla {
996 hue,
997 saturation,
998 lightness,
999 ..
1000 } => {
1001 *hue *= rhs.x;
1002 *saturation *= rhs.y;
1003 *lightness *= rhs.z;
1004 }
1005 }
1006 }
1007}
1008
1009impl Mul<[f32; 4]> for Color {
1010 type Output = Color;
1011
1012 fn mul(self, rhs: [f32; 4]) -> Self::Output {
1013 match self {
1014 Color::Rgba {
1015 red,
1016 green,
1017 blue,
1018 alpha,
1019 } => Color::Rgba {
1020 red: red * rhs[0],
1021 green: green * rhs[1],
1022 blue: blue * rhs[2],
1023 alpha: alpha * rhs[3],
1024 },
1025 Color::RgbaLinear {
1026 red,
1027 green,
1028 blue,
1029 alpha,
1030 } => Color::RgbaLinear {
1031 red: red * rhs[0],
1032 green: green * rhs[1],
1033 blue: blue * rhs[2],
1034 alpha: alpha * rhs[3],
1035 },
1036 Color::Hsla {
1037 hue,
1038 saturation,
1039 lightness,
1040 alpha,
1041 } => Color::Hsla {
1042 hue: hue * rhs[0],
1043 saturation: saturation * rhs[1],
1044 lightness: lightness * rhs[2],
1045 alpha: alpha * rhs[3],
1046 },
1047 }
1048 }
1049}
1050
1051impl MulAssign<[f32; 4]> for Color {
1052 fn mul_assign(&mut self, rhs: [f32; 4]) {
1053 match self {
1054 Color::Rgba {
1055 red,
1056 green,
1057 blue,
1058 alpha,
1059 }
1060 | Color::RgbaLinear {
1061 red,
1062 green,
1063 blue,
1064 alpha,
1065 } => {
1066 *red *= rhs[0];
1067 *green *= rhs[1];
1068 *blue *= rhs[2];
1069 *alpha *= rhs[3];
1070 }
1071 Color::Hsla {
1072 hue,
1073 saturation,
1074 lightness,
1075 alpha,
1076 } => {
1077 *hue *= rhs[0];
1078 *saturation *= rhs[1];
1079 *lightness *= rhs[2];
1080 *alpha *= rhs[3];
1081 }
1082 }
1083 }
1084}
1085
1086impl Mul<[f32; 3]> for Color {
1087 type Output = Color;
1088
1089 fn mul(self, rhs: [f32; 3]) -> Self::Output {
1090 match self {
1091 Color::Rgba {
1092 red,
1093 green,
1094 blue,
1095 alpha,
1096 } => Color::Rgba {
1097 red: red * rhs[0],
1098 green: green * rhs[1],
1099 blue: blue * rhs[2],
1100 alpha,
1101 },
1102 Color::RgbaLinear {
1103 red,
1104 green,
1105 blue,
1106 alpha,
1107 } => Color::RgbaLinear {
1108 red: red * rhs[0],
1109 green: green * rhs[1],
1110 blue: blue * rhs[2],
1111 alpha,
1112 },
1113 Color::Hsla {
1114 hue,
1115 saturation,
1116 lightness,
1117 alpha,
1118 } => Color::Hsla {
1119 hue: hue * rhs[0],
1120 saturation: saturation * rhs[1],
1121 lightness: lightness * rhs[2],
1122 alpha,
1123 },
1124 }
1125 }
1126}
1127
1128impl MulAssign<[f32; 3]> for Color {
1129 fn mul_assign(&mut self, rhs: [f32; 3]) {
1130 match self {
1131 Color::Rgba {
1132 red, green, blue, ..
1133 }
1134 | Color::RgbaLinear {
1135 red, green, blue, ..
1136 } => {
1137 *red *= rhs[0];
1138 *green *= rhs[1];
1139 *blue *= rhs[2];
1140 }
1141 Color::Hsla {
1142 hue,
1143 saturation,
1144 lightness,
1145 ..
1146 } => {
1147 *hue *= rhs[0];
1148 *saturation *= rhs[1];
1149 *lightness *= rhs[2];
1150 }
1151 }
1152 }
1153}
1154
1155#[derive(Debug, Error)]
1156pub enum HexColorError {
1157 #[error("Unexpected length of hex string")]
1158 Length,
1159 #[error("Error parsing hex value")]
1160 Hex(#[from] hex::FromHexError),
1161}
1162
1163fn decode_rgb(data: &[u8]) -> Result<Color, HexColorError> {
1164 let mut buf = [0; 3];
1165 match hex::decode_to_slice(data, &mut buf) {
1166 Ok(_) => {
1167 let r = buf[0] as f32 / 255.0;
1168 let g = buf[1] as f32 / 255.0;
1169 let b = buf[2] as f32 / 255.0;
1170 Ok(Color::rgb(r, g, b))
1171 }
1172 Err(err) => Err(HexColorError::Hex(err)),
1173 }
1174}
1175
1176fn decode_rgba(data: &[u8]) -> Result<Color, HexColorError> {
1177 let mut buf = [0; 4];
1178 match hex::decode_to_slice(data, &mut buf) {
1179 Ok(_) => {
1180 let r = buf[0] as f32 / 255.0;
1181 let g = buf[1] as f32 / 255.0;
1182 let b = buf[2] as f32 / 255.0;
1183 let a = buf[3] as f32 / 255.0;
1184 Ok(Color::rgba(r, g, b, a))
1185 }
1186 Err(err) => Err(HexColorError::Hex(err)),
1187 }
1188}
1189
1190#[cfg(test)]
1191mod tests {
1192 use super::*;
1193
1194 #[test]
1195 fn hex_color() {
1196 assert_eq!(Color::hex("FFF").unwrap(), Color::rgb(1.0, 1.0, 1.0));
1197 assert_eq!(Color::hex("000").unwrap(), Color::rgb(0.0, 0.0, 0.0));
1198 assert!(Color::hex("---").is_err());
1199
1200 assert_eq!(Color::hex("FFFF").unwrap(), Color::rgba(1.0, 1.0, 1.0, 1.0));
1201 assert_eq!(Color::hex("0000").unwrap(), Color::rgba(0.0, 0.0, 0.0, 0.0));
1202 assert!(Color::hex("----").is_err());
1203
1204 assert_eq!(Color::hex("FFFFFF").unwrap(), Color::rgb(1.0, 1.0, 1.0));
1205 assert_eq!(Color::hex("000000").unwrap(), Color::rgb(0.0, 0.0, 0.0));
1206 assert!(Color::hex("------").is_err());
1207
1208 assert_eq!(
1209 Color::hex("FFFFFFFF").unwrap(),
1210 Color::rgba(1.0, 1.0, 1.0, 1.0)
1211 );
1212 assert_eq!(
1213 Color::hex("00000000").unwrap(),
1214 Color::rgba(0.0, 0.0, 0.0, 0.0)
1215 );
1216 assert!(Color::hex("--------").is_err());
1217
1218 assert!(Color::hex("1234567890").is_err());
1219 }
1220
1221 #[test]
1222 fn conversions_vec4() {
1223 let starting_vec4 = Vec4::new(0.4, 0.5, 0.6, 1.0);
1224 let starting_color = Color::from(starting_vec4);
1225
1226 assert_eq!(starting_vec4, Vec4::from(starting_color),);
1227
1228 let transformation = Vec4::new(0.5, 0.5, 0.5, 1.0);
1229
1230 assert_eq!(
1231 starting_color * transformation,
1232 Color::from(starting_vec4 * transformation),
1233 );
1234 }
1235
1236 #[test]
1237 fn mul_and_mulassign_f32() {
1238 let transformation = 0.5;
1239 let starting_color = Color::rgba(0.4, 0.5, 0.6, 1.0);
1240
1241 assert_eq!(
1242 starting_color * transformation,
1243 Color::rgba(0.4 * 0.5, 0.5 * 0.5, 0.6 * 0.5, 1.0),
1244 );
1245
1246 let mut mutated_color = starting_color;
1247 mutated_color *= transformation;
1248
1249 assert_eq!(starting_color * transformation, mutated_color,);
1250 }
1251
1252 #[test]
1253 fn mul_and_mulassign_f32by3() {
1254 let transformation = [0.4, 0.5, 0.6];
1255 let starting_color = Color::rgba(0.4, 0.5, 0.6, 1.0);
1256
1257 assert_eq!(
1258 starting_color * transformation,
1259 Color::rgba(0.4 * 0.4, 0.5 * 0.5, 0.6 * 0.6, 1.0),
1260 );
1261
1262 let mut mutated_color = starting_color;
1263 mutated_color *= transformation;
1264
1265 assert_eq!(starting_color * transformation, mutated_color,);
1266 }
1267
1268 #[test]
1269 fn mul_and_mulassign_f32by4() {
1270 let transformation = [0.4, 0.5, 0.6, 0.9];
1271 let starting_color = Color::rgba(0.4, 0.5, 0.6, 1.0);
1272
1273 assert_eq!(
1274 starting_color * transformation,
1275 Color::rgba(0.4 * 0.4, 0.5 * 0.5, 0.6 * 0.6, 1.0 * 0.9),
1276 );
1277
1278 let mut mutated_color = starting_color;
1279 mutated_color *= transformation;
1280
1281 assert_eq!(starting_color * transformation, mutated_color,);
1282 }
1283
1284 #[test]
1285 fn mul_and_mulassign_vec3() {
1286 let transformation = Vec3::new(0.2, 0.3, 0.4);
1287 let starting_color = Color::rgba(0.4, 0.5, 0.6, 1.0);
1288
1289 assert_eq!(
1290 starting_color * transformation,
1291 Color::rgba(0.4 * 0.2, 0.5 * 0.3, 0.6 * 0.4, 1.0),
1292 );
1293
1294 let mut mutated_color = starting_color;
1295 mutated_color *= transformation;
1296
1297 assert_eq!(starting_color * transformation, mutated_color,);
1298 }
1299
1300 #[test]
1301 fn mul_and_mulassign_vec4() {
1302 let transformation = Vec4::new(0.2, 0.3, 0.4, 0.5);
1303 let starting_color = Color::rgba(0.4, 0.5, 0.6, 1.0);
1304
1305 assert_eq!(
1306 starting_color * transformation,
1307 Color::rgba(0.4 * 0.2, 0.5 * 0.3, 0.6 * 0.4, 1.0 * 0.5),
1308 );
1309
1310 let mut mutated_color = starting_color;
1311 mutated_color *= transformation;
1312
1313 assert_eq!(starting_color * transformation, mutated_color,);
1314 }
1315
1316 #[test]
1317 fn leading_hash() {
1318 assert_eq!(
1319 Color::hex("#ff00ff").unwrap(),
1320 Color::hex("ff00ff").unwrap()
1321 );
1322 }
1323}