bones_render/
color.rs

1//! Color components.
2
3use glam::{Vec3, Vec4};
4use std::ops::{Add, AddAssign, Mul, MulAssign};
5use thiserror::Error;
6use type_ulid::TypeUlid;
7
8/// Color type.
9#[derive(Clone, Copy, Debug, TypeUlid)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[ulid = "01GW2T0C0Q97B17MFARADVTZG5"]
12#[repr(C)]
13pub enum Color {
14    /// sRGBA color
15    Rgba {
16        /// Red channel. [0.0, 1.0]
17        red: f32,
18        /// Green channel. [0.0, 1.0]
19        green: f32,
20        /// Blue channel. [0.0, 1.0]
21        blue: f32,
22        /// Alpha channel. [0.0, 1.0]
23        alpha: f32,
24    },
25}
26
27impl Color {
28    /// <div style="background-color:rgb(0%, 0%, 0%); width: 10px; padding: 10px; border: 1px solid;"></div>
29    pub const BLACK: Color = Color::rgb(0.0, 0.0, 0.0);
30    /// <div style="background-color:rgb(0%, 0%, 100%); width: 10px; padding: 10px; border: 1px solid;"></div>
31    pub const BLUE: Color = Color::rgb(0.0, 0.0, 1.0);
32    /// <div style="background-color:rgb(0%, 100%, 100%); width: 10px; padding: 10px; border: 1px solid;"></div>
33    pub const CYAN: Color = Color::rgb(0.0, 1.0, 1.0);
34    /// <div style="background-color:rgb(50%, 50%, 50%); width: 10px; padding: 10px; border: 1px solid;"></div>
35    pub const GRAY: Color = Color::rgb(0.5, 0.5, 0.5);
36    /// <div style="background-color:rgb(0%, 100%, 0%); width: 10px; padding: 10px; border: 1px solid;"></div>
37    pub const GREEN: Color = Color::rgb(0.0, 1.0, 0.0);
38    /// <div style="background-color:rgba(0%, 0%, 0%, 0%); width: 10px; padding: 10px; border: 1px solid;"></div>
39    pub const NONE: Color = Color::rgba(0.0, 0.0, 0.0, 0.0);
40    /// <div style="background-color:rgb(100%, 65%, 0%); width: 10px; padding: 10px; border: 1px solid;"></div>
41    pub const ORANGE: Color = Color::rgb(1.0, 0.65, 0.0);
42    /// <div style="background-color:rgb(100%, 0%, 0%); width: 10px; padding: 10px; border: 1px solid;"></div>
43    pub const RED: Color = Color::rgb(1.0, 0.0, 0.0);
44    /// <div style="background-color:rgb(100%, 100%, 100%); width: 10px; padding: 10px; border: 1px solid;"></div>
45    pub const WHITE: Color = Color::rgb(1.0, 1.0, 1.0);
46    /// <div style="background-color:rgb(100%, 100%, 0%); width: 10px; padding: 10px; border: 1px solid;"></div>
47    pub const YELLOW: Color = Color::rgb(1.0, 1.0, 0.0);
48
49    /// New `Color` from sRGB colorspace.
50    ///
51    /// # Arguments
52    ///
53    /// * `r` - Red channel. [0.0, 1.0]
54    /// * `g` - Green channel. [0.0, 1.0]
55    /// * `b` - Blue channel. [0.0, 1.0]
56    ///
57    /// See also [`Color::rgba`], [`Color::rgb_u8`], [`Color::hex`].
58    ///
59    pub const fn rgb(r: f32, g: f32, b: f32) -> Color {
60        Color::Rgba {
61            red: r,
62            green: g,
63            blue: b,
64            alpha: 1.0,
65        }
66    }
67
68    /// New `Color` from sRGB colorspace.
69    ///
70    /// # Arguments
71    ///
72    /// * `r` - Red channel. [0.0, 1.0]
73    /// * `g` - Green channel. [0.0, 1.0]
74    /// * `b` - Blue channel. [0.0, 1.0]
75    /// * `a` - Alpha channel. [0.0, 1.0]
76    ///
77    /// See also [`Color::rgb`], [`Color::rgba_u8`], [`Color::hex`].
78    ///
79    pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Color {
80        Color::Rgba {
81            red: r,
82            green: g,
83            blue: b,
84            alpha: a,
85        }
86    }
87
88    /// New `Color` from sRGB colorspace.
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// # use bevy_render::color::Color;
94    /// let color = Color::hex("FF00FF").unwrap(); // fuchsia
95    /// let color = Color::hex("FF00FF7F").unwrap(); // partially transparent fuchsia
96    /// ```
97    ///
98    pub fn hex<T: AsRef<str>>(hex: T) -> Result<Color, HexColorError> {
99        let hex = hex.as_ref();
100
101        // RGB
102        if hex.len() == 3 {
103            let mut data = [0; 6];
104            for (i, ch) in hex.chars().enumerate() {
105                data[i * 2] = ch as u8;
106                data[i * 2 + 1] = ch as u8;
107            }
108            return decode_rgb(&data);
109        }
110
111        // RGBA
112        if hex.len() == 4 {
113            let mut data = [0; 8];
114            for (i, ch) in hex.chars().enumerate() {
115                data[i * 2] = ch as u8;
116                data[i * 2 + 1] = ch as u8;
117            }
118            return decode_rgba(&data);
119        }
120
121        // RRGGBB
122        if hex.len() == 6 {
123            return decode_rgb(hex.as_bytes());
124        }
125
126        // RRGGBBAA
127        if hex.len() == 8 {
128            return decode_rgba(hex.as_bytes());
129        }
130
131        Err(HexColorError::Length)
132    }
133
134    /// New `Color` from sRGB colorspace.
135    ///
136    /// # Arguments
137    ///
138    /// * `r` - Red channel. [0, 255]
139    /// * `g` - Green channel. [0, 255]
140    /// * `b` - Blue channel. [0, 255]
141    ///
142    /// See also [`Color::rgb`], [`Color::rgba_u8`], [`Color::hex`].
143    ///
144    pub fn rgb_u8(r: u8, g: u8, b: u8) -> Color {
145        Color::rgba_u8(r, g, b, u8::MAX)
146    }
147
148    // Float operations in const fn are not stable yet
149    // see https://github.com/rust-lang/rust/issues/57241
150    /// New `Color` from sRGB colorspace.
151    ///
152    /// # Arguments
153    ///
154    /// * `r` - Red channel. [0, 255]
155    /// * `g` - Green channel. [0, 255]
156    /// * `b` - Blue channel. [0, 255]
157    /// * `a` - Alpha channel. [0, 255]
158    ///
159    /// See also [`Color::rgba`], [`Color::rgb_u8`], [`Color::hex`].
160    ///
161    pub fn rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Color {
162        Color::rgba(
163            r as f32 / u8::MAX as f32,
164            g as f32 / u8::MAX as f32,
165            b as f32 / u8::MAX as f32,
166            a as f32 / u8::MAX as f32,
167        )
168    }
169
170    /// Get red in sRGB colorspace.
171    pub fn r(&self) -> f32 {
172        match self.as_rgba() {
173            Color::Rgba { red, .. } => red,
174        }
175    }
176
177    /// Get green in sRGB colorspace.
178    pub fn g(&self) -> f32 {
179        match self.as_rgba() {
180            Color::Rgba { green, .. } => green,
181        }
182    }
183
184    /// Get blue in sRGB colorspace.
185    pub fn b(&self) -> f32 {
186        match self.as_rgba() {
187            Color::Rgba { blue, .. } => blue,
188        }
189    }
190
191    /// Set red in sRGB colorspace.
192    pub fn set_r(&mut self, r: f32) -> &mut Self {
193        *self = self.as_rgba();
194        match self {
195            Color::Rgba { red, .. } => *red = r,
196        }
197        self
198    }
199
200    /// Set green in sRGB colorspace.
201    pub fn set_g(&mut self, g: f32) -> &mut Self {
202        *self = self.as_rgba();
203        match self {
204            Color::Rgba { green, .. } => *green = g,
205        }
206        self
207    }
208
209    /// Set blue in sRGB colorspace.
210    pub fn set_b(&mut self, b: f32) -> &mut Self {
211        *self = self.as_rgba();
212        match self {
213            Color::Rgba { blue, .. } => *blue = b,
214        }
215        self
216    }
217
218    /// Get alpha.
219    #[inline(always)]
220    pub fn a(&self) -> f32 {
221        match self {
222            Color::Rgba { alpha, .. } => *alpha,
223        }
224    }
225
226    /// Set alpha.
227    pub fn set_a(&mut self, a: f32) -> &mut Self {
228        match self {
229            Color::Rgba { alpha, .. } => {
230                *alpha = a;
231            }
232        }
233        self
234    }
235
236    /// Converts a `Color` to variant `Color::Rgba`
237    pub fn as_rgba(self: &Color) -> Color {
238        match self {
239            Color::Rgba { .. } => *self,
240        }
241    }
242
243    /// Converts a `Color` to a `[f32; 4]` from sRGB colorspace
244    pub fn as_rgba_f32(self: Color) -> [f32; 4] {
245        match self {
246            Color::Rgba {
247                red,
248                green,
249                blue,
250                alpha,
251            } => [red, green, blue, alpha],
252        }
253    }
254}
255
256impl Default for Color {
257    fn default() -> Self {
258        Color::WHITE
259    }
260}
261
262impl AddAssign<Color> for Color {
263    fn add_assign(&mut self, rhs: Color) {
264        match self {
265            Color::Rgba {
266                red,
267                green,
268                blue,
269                alpha,
270            } => {
271                let rhs = rhs.as_rgba_f32();
272                *red += rhs[0];
273                *green += rhs[1];
274                *blue += rhs[2];
275                *alpha += rhs[3];
276            }
277        }
278    }
279}
280
281impl Add<Color> for Color {
282    type Output = Color;
283
284    fn add(self, rhs: Color) -> Self::Output {
285        match self {
286            Color::Rgba {
287                red,
288                green,
289                blue,
290                alpha,
291            } => {
292                let rhs = rhs.as_rgba_f32();
293                Color::Rgba {
294                    red: red + rhs[0],
295                    green: green + rhs[1],
296                    blue: blue + rhs[2],
297                    alpha: alpha + rhs[3],
298                }
299            }
300        }
301    }
302}
303
304impl From<Color> for [f32; 4] {
305    fn from(color: Color) -> Self {
306        color.as_rgba_f32()
307    }
308}
309
310impl From<[f32; 4]> for Color {
311    fn from([r, g, b, a]: [f32; 4]) -> Self {
312        Color::rgba(r, g, b, a)
313    }
314}
315
316impl From<[f32; 3]> for Color {
317    fn from([r, g, b]: [f32; 3]) -> Self {
318        Color::rgb(r, g, b)
319    }
320}
321
322impl From<Color> for Vec4 {
323    fn from(color: Color) -> Self {
324        let color: [f32; 4] = color.into();
325        Vec4::new(color[0], color[1], color[2], color[3])
326    }
327}
328
329impl From<Vec4> for Color {
330    fn from(vec4: Vec4) -> Self {
331        Color::rgba(vec4.x, vec4.y, vec4.z, vec4.w)
332    }
333}
334
335impl Mul<f32> for Color {
336    type Output = Color;
337
338    fn mul(self, rhs: f32) -> Self::Output {
339        match self {
340            Color::Rgba {
341                red,
342                green,
343                blue,
344                alpha,
345            } => Color::Rgba {
346                red: red * rhs,
347                green: green * rhs,
348                blue: blue * rhs,
349                alpha,
350            },
351        }
352    }
353}
354
355impl MulAssign<f32> for Color {
356    fn mul_assign(&mut self, rhs: f32) {
357        match self {
358            Color::Rgba {
359                red, green, blue, ..
360            } => {
361                *red *= rhs;
362                *green *= rhs;
363                *blue *= rhs;
364            }
365        }
366    }
367}
368
369impl Mul<Vec4> for Color {
370    type Output = Color;
371
372    fn mul(self, rhs: Vec4) -> Self::Output {
373        match self {
374            Color::Rgba {
375                red,
376                green,
377                blue,
378                alpha,
379            } => Color::Rgba {
380                red: red * rhs.x,
381                green: green * rhs.y,
382                blue: blue * rhs.z,
383                alpha: alpha * rhs.w,
384            },
385        }
386    }
387}
388
389impl MulAssign<Vec4> for Color {
390    fn mul_assign(&mut self, rhs: Vec4) {
391        match self {
392            Color::Rgba {
393                red,
394                green,
395                blue,
396                alpha,
397            } => {
398                *red *= rhs.x;
399                *green *= rhs.y;
400                *blue *= rhs.z;
401                *alpha *= rhs.w;
402            }
403        }
404    }
405}
406
407impl Mul<Vec3> for Color {
408    type Output = Color;
409
410    fn mul(self, rhs: Vec3) -> Self::Output {
411        match self {
412            Color::Rgba {
413                red,
414                green,
415                blue,
416                alpha,
417            } => Color::Rgba {
418                red: red * rhs.x,
419                green: green * rhs.y,
420                blue: blue * rhs.z,
421                alpha,
422            },
423        }
424    }
425}
426
427impl MulAssign<Vec3> for Color {
428    fn mul_assign(&mut self, rhs: Vec3) {
429        match self {
430            Color::Rgba {
431                red, green, blue, ..
432            } => {
433                *red *= rhs.x;
434                *green *= rhs.y;
435                *blue *= rhs.z;
436            }
437        }
438    }
439}
440
441impl Mul<[f32; 4]> for Color {
442    type Output = Color;
443
444    fn mul(self, rhs: [f32; 4]) -> Self::Output {
445        match self {
446            Color::Rgba {
447                red,
448                green,
449                blue,
450                alpha,
451            } => Color::Rgba {
452                red: red * rhs[0],
453                green: green * rhs[1],
454                blue: blue * rhs[2],
455                alpha: alpha * rhs[3],
456            },
457        }
458    }
459}
460
461impl MulAssign<[f32; 4]> for Color {
462    fn mul_assign(&mut self, rhs: [f32; 4]) {
463        match self {
464            Color::Rgba {
465                red,
466                green,
467                blue,
468                alpha,
469            } => {
470                *red *= rhs[0];
471                *green *= rhs[1];
472                *blue *= rhs[2];
473                *alpha *= rhs[3];
474            }
475        }
476    }
477}
478
479impl Mul<[f32; 3]> for Color {
480    type Output = Color;
481
482    fn mul(self, rhs: [f32; 3]) -> Self::Output {
483        match self {
484            Color::Rgba {
485                red,
486                green,
487                blue,
488                alpha,
489            } => Color::Rgba {
490                red: red * rhs[0],
491                green: green * rhs[1],
492                blue: blue * rhs[2],
493                alpha,
494            },
495        }
496    }
497}
498
499impl MulAssign<[f32; 3]> for Color {
500    fn mul_assign(&mut self, rhs: [f32; 3]) {
501        match self {
502            Color::Rgba {
503                red, green, blue, ..
504            } => {
505                *red *= rhs[0];
506                *green *= rhs[1];
507                *blue *= rhs[2];
508            }
509        }
510    }
511}
512
513/// Error type for hex color decoding
514#[derive(Debug, Error)]
515pub enum HexColorError {
516    /// Error for unexpected length of hex string
517    #[error("Unexpected length of hex string")]
518    Length,
519    /// Error for hex crate errors
520    #[error("Error parsing hex value")]
521    Hex(#[from] hex::FromHexError),
522}
523
524fn decode_rgb(data: &[u8]) -> Result<Color, HexColorError> {
525    let mut buf = [0; 3];
526    match hex::decode_to_slice(data, &mut buf) {
527        Ok(_) => {
528            let r = buf[0] as f32 / 255.0;
529            let g = buf[1] as f32 / 255.0;
530            let b = buf[2] as f32 / 255.0;
531            Ok(Color::rgb(r, g, b))
532        }
533        Err(err) => Err(HexColorError::Hex(err)),
534    }
535}
536
537fn decode_rgba(data: &[u8]) -> Result<Color, HexColorError> {
538    let mut buf = [0; 4];
539    match hex::decode_to_slice(data, &mut buf) {
540        Ok(_) => {
541            let r = buf[0] as f32 / 255.0;
542            let g = buf[1] as f32 / 255.0;
543            let b = buf[2] as f32 / 255.0;
544            let a = buf[3] as f32 / 255.0;
545            Ok(Color::rgba(r, g, b, a))
546        }
547        Err(err) => Err(HexColorError::Hex(err)),
548    }
549}