bones_framework/render/
color.rs

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