ambient_color/
lib.rs

1// This code was adapted from Bevy (MIT/Apache2):
2//
3// https://github.com/bevyengine/bevy/blob/93a131661de507eb711264b11965fe1d4bb13f12/crates/bevy_render/src/color/mod.rs
4
5use 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    /// sRGBA color
18    Rgba {
19        /// Red component. [0.0, 1.0]
20        red: f32,
21        /// Green component. [0.0, 1.0]
22        green: f32,
23        /// Blue component. [0.0, 1.0]
24        blue: f32,
25        /// Alpha component. [0.0, 1.0]
26        alpha: f32,
27    },
28    /// RGBA color in the Linear sRGB colorspace (often colloquially referred to as "linear", "RGB", or "linear RGB").
29    RgbaLinear {
30        /// Red component. [0.0, 1.0]
31        red: f32,
32        /// Green component. [0.0, 1.0]
33        green: f32,
34        /// Blue component. [0.0, 1.0]
35        blue: f32,
36        /// Alpha component. [0.0, 1.0]
37        alpha: f32,
38    },
39    /// HSL (hue, saturation, lightness) color with an alpha channel
40    Hsla {
41        /// Hue component. [0.0, 360.0]
42        hue: f32,
43        /// Saturation component. [0.0, 1.0]
44        saturation: f32,
45        /// Lightness component. [0.0, 1.0]
46        lightness: f32,
47        /// Alpha component. [0.0, 1.0]
48        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    /// New `Color` from sRGB colorspace.
58    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    /// New `Color` from sRGB colorspace.
68    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    /// New `Color` from linear RGB colorspace.
78    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    /// New `Color` from linear RGB colorspace.
88    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    /// New `Color` with HSL representation in sRGB colorspace.
98    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    /// New `Color` with HSL representation in sRGB colorspace.
108    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    /// New `Color` from sRGB colorspace.
118    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        // RGB
125        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        // RGBA
135        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        // RRGGBB
145        if hex.len() == 6 {
146            return decode_rgb(hex.as_bytes());
147        }
148
149        // RRGGBBAA
150        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    /// New `Color` from sRGB colorspace.
178    pub fn rgb_u8(r: u8, g: u8, b: u8) -> Color {
179        Color::rgba_u8(r, g, b, u8::MAX)
180    }
181
182    // Float operations in const fn are not stable yet
183    // see https://github.com/rust-lang/rust/issues/57241
184    /// New `Color` from sRGB colorspace.
185    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    /// Get red in sRGB colorspace.
195    pub fn r(&self) -> f32 {
196        match self.as_rgba() {
197            Color::Rgba { red, .. } => red,
198            _ => unreachable!(),
199        }
200    }
201
202    /// Get green in sRGB colorspace.
203    pub fn g(&self) -> f32 {
204        match self.as_rgba() {
205            Color::Rgba { green, .. } => green,
206            _ => unreachable!(),
207        }
208    }
209
210    /// Get blue in sRGB colorspace.
211    pub fn b(&self) -> f32 {
212        match self.as_rgba() {
213            Color::Rgba { blue, .. } => blue,
214            _ => unreachable!(),
215        }
216    }
217
218    /// Set red in sRGB colorspace.
219    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    /// Set green in sRGB colorspace.
229    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    /// Set blue in sRGB colorspace.
239    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    /// Get alpha.
249    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    /// Set alpha.
258    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    /// Converts a `Color` to variant `Color::Rgba`
270    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    /// Converts a `Color` to variant `Color::RgbaLinear`
303    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    /// Converts a `Color` to variant `Color::Hsla`
336    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    /// Converts a `Color` to a `[f32; 4]` from sRGB colorspace
376    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    /// Converts a `Color` to a `[f32; 4]` from linear RGB colorspace
409    #[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    /// Converts a `Color` to a `[f32; 4]` from HSL colorspace
448    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    /// Converts `Color` to a `u32` from sRGB colorspace.
483    ///
484    /// Maps the RGBA channels in RGBA order to a little-endian byte array (GPUs are little-endian).
485    /// `A` will be the most significant byte and `R` the least significant.
486    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    /// Converts Color to a u32 from linear RGB colorspace.
529    ///
530    /// Maps the RGBA channels in RGBA order to a little-endian byte array (GPUs are little-endian).
531    /// `A` will be the most significant byte and `R` the least significant.
532    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}