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