Skip to main content

astrelis_render/
color.rs

1/// An RGBA color with `f32` components in the `0.0..=1.0` range.
2///
3/// Colors are stored in linear RGBA order and can be constructed from floats,
4/// `u8` values, or hex codes:
5///
6/// ```
7/// use astrelis_render::Color;
8///
9/// let red = Color::rgb(1.0, 0.0, 0.0);
10/// let semi_transparent = Color::rgba(1.0, 1.0, 1.0, 0.5);
11/// let from_hex = Color::from_hex(0xFF8800);
12/// let from_bytes = Color::from_rgba_u8(128, 64, 32, 255);
13/// ```
14///
15/// The struct is `#[repr(C)]` and implements `bytemuck::Pod`, so it can be
16/// used directly in GPU uniform/vertex buffers.
17#[repr(C)]
18#[derive(Debug, Clone, Copy, PartialEq, bytemuck::Pod, bytemuck::Zeroable)]
19pub struct Color {
20    pub r: f32,
21    pub g: f32,
22    pub b: f32,
23    pub a: f32,
24}
25
26impl Color {
27    pub const WHITE: Color = Color::rgb(1.0, 1.0, 1.0);
28    pub const BLACK: Color = Color::rgb(0.0, 0.0, 0.0);
29    pub const RED: Color = Color::rgb(1.0, 0.0, 0.0);
30    pub const GREEN: Color = Color::rgb(0.0, 1.0, 0.0);
31    pub const BLUE: Color = Color::rgb(0.0, 0.0, 1.0);
32    pub const YELLOW: Color = Color::rgb(1.0, 1.0, 0.0);
33    pub const CYAN: Color = Color::rgb(0.0, 1.0, 1.0);
34    pub const MAGENTA: Color = Color::rgb(1.0, 0.0, 1.0);
35    pub const TRANSPARENT: Color = Color::rgba(0.0, 0.0, 0.0, 0.0);
36
37    /// Create a color from RGB components with full opacity (alpha = 1.0).
38    pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
39        Self { r, g, b, a: 1.0 }
40    }
41
42    /// Create a color from RGBA components.
43    pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
44        Self { r, g, b, a }
45    }
46
47    /// Create a color from 8-bit RGBA values (0–255 mapped to 0.0–1.0).
48    pub fn from_rgba_u8(r: u8, g: u8, b: u8, a: u8) -> Self {
49        Self {
50            r: r as f32 / 255.0,
51            g: g as f32 / 255.0,
52            b: b as f32 / 255.0,
53            a: a as f32 / 255.0,
54        }
55    }
56
57    /// Create a color from 8-bit RGB values with full opacity.
58    pub fn from_rgb_u8(r: u8, g: u8, b: u8) -> Self {
59        Self::from_rgba_u8(r, g, b, 255)
60    }
61
62    /// Create a color from a 24-bit RGB hex value (e.g. `0xFF8800`).
63    pub fn from_hex(hex: u32) -> Self {
64        let r = ((hex >> 16) & 0xFF) as u8;
65        let g = ((hex >> 8) & 0xFF) as u8;
66        let b = (hex & 0xFF) as u8;
67        Self::from_rgb_u8(r, g, b)
68    }
69
70    /// Create a color from a 32-bit RGBA hex value (e.g. `0xFF880080`).
71    pub fn from_hex_alpha(hex: u32) -> Self {
72        let r = ((hex >> 24) & 0xFF) as u8;
73        let g = ((hex >> 16) & 0xFF) as u8;
74        let b = ((hex >> 8) & 0xFF) as u8;
75        let a = (hex & 0xFF) as u8;
76        Self::from_rgba_u8(r, g, b, a)
77    }
78
79    /// Convert to the equivalent `wgpu::Color` (f64 components).
80    pub fn to_wgpu(self) -> wgpu::Color {
81        wgpu::Color {
82            r: self.r as f64,
83            g: self.g as f64,
84            b: self.b as f64,
85            a: self.a as f64,
86        }
87    }
88
89    /// Convert to an `[r, g, b, a]` array.
90    pub fn to_array(self) -> [f32; 4] {
91        [self.r, self.g, self.b, self.a]
92    }
93}
94
95impl Default for Color {
96    fn default() -> Self {
97        Self::WHITE
98    }
99}
100
101impl From<[f32; 4]> for Color {
102    fn from(arr: [f32; 4]) -> Self {
103        Self {
104            r: arr[0],
105            g: arr[1],
106            b: arr[2],
107            a: arr[3],
108        }
109    }
110}
111
112impl From<[f32; 3]> for Color {
113    fn from(arr: [f32; 3]) -> Self {
114        Self {
115            r: arr[0],
116            g: arr[1],
117            b: arr[2],
118            a: 1.0,
119        }
120    }
121}
122
123impl From<Color> for [f32; 4] {
124    fn from(color: Color) -> Self {
125        color.to_array()
126    }
127}