1#[derive(Copy, Clone, Debug, Default, PartialEq)]
13pub struct Color {
14 pub r: f32,
15 pub g: f32,
16 pub b: f32,
17 pub a: f32,
18}
19
20impl Color {
21 pub const TRANSPARENT: Self = Self::rgba(0.0, 0.0, 0.0, 0.0);
22 pub const BLACK: Self = Self::rgb(0.0, 0.0, 0.0);
23 pub const WHITE: Self = Self::rgb(1.0, 1.0, 1.0);
24 pub const GRAY: Self = Self::rgb(0.5, 0.5, 0.5);
25 pub const RED: Self = Self::rgb(1.0, 0.0, 0.0);
26 pub const GREEN: Self = Self::rgb(0.0, 1.0, 0.0);
27 pub const BLUE: Self = Self::rgb(0.0, 0.0, 1.0);
28 pub const YELLOW: Self = Self::rgb(1.0, 1.0, 0.0);
29 pub const CYAN: Self = Self::rgb(0.0, 1.0, 1.0);
30 pub const MAGENTA: Self = Self::rgb(1.0, 0.0, 1.0);
31 pub const ORANGE: Self = Self::rgb(1.0, 0.5, 0.0);
32 pub const GOLD: Self = Self::rgb(1.0, 0.84, 0.0);
33
34 pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
36 Self::rgba(r, g, b, 1.0)
37 }
38
39 pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
41 Self { r, g, b, a }
42 }
43
44 #[cfg(feature = "indexed")]
50 pub const fn index(i: u8) -> Self {
51 Self::rgba(i as f32 / 255.0, 0.0, 0.0, 1.0)
52 }
53
54 pub const fn rgb8(r: u8, g: u8, b: u8) -> Self {
56 Self::rgba8(r, g, b, 255)
57 }
58
59 pub const fn rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
61 Self::rgba(
62 r as f32 / 255.0,
63 g as f32 / 255.0,
64 b as f32 / 255.0,
65 a as f32 / 255.0,
66 )
67 }
68
69 pub const fn hex(rgb: u32) -> Self {
71 Self::rgb8((rgb >> 16) as u8, (rgb >> 8) as u8, rgb as u8)
72 }
73
74 pub const fn hex_rgba(rgba: u32) -> Self {
76 Self::rgba8(
77 (rgba >> 24) as u8,
78 (rgba >> 16) as u8,
79 (rgba >> 8) as u8,
80 rgba as u8,
81 )
82 }
83
84 pub const fn with_alpha(self, a: f32) -> Self {
86 Self { a, ..self }
87 }
88
89 pub fn lerp(self, other: Self, t: f32) -> Self {
91 let mix = |a: f32, b: f32| a + (b - a) * t;
92 Self::rgba(
93 mix(self.r, other.r),
94 mix(self.g, other.g),
95 mix(self.b, other.b),
96 mix(self.a, other.a),
97 )
98 }
99
100 pub const fn to_array(self) -> [f32; 4] {
102 [self.r, self.g, self.b, self.a]
103 }
104}
105
106impl From<[f32; 4]> for Color {
107 fn from([r, g, b, a]: [f32; 4]) -> Self {
108 Self { r, g, b, a }
109 }
110}
111
112impl From<[f32; 3]> for Color {
113 fn from([r, g, b]: [f32; 3]) -> Self {
114 Self::rgb(r, g, b)
115 }
116}
117
118impl From<Color> for [f32; 4] {
119 fn from(c: Color) -> Self {
120 c.to_array()
121 }
122}
123
124#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
129pub struct Layer(pub i16);
130
131impl Layer {
132 pub const DEFAULT: Self = Self(0);
134 pub const BOTTOM: Self = Self(i16::MIN);
136 pub const TOP: Self = Self(i16::MAX);
138}
139
140impl From<i16> for Layer {
141 fn from(layer: i16) -> Self {
142 Self(layer)
143 }
144}
145
146impl From<Layer> for i16 {
147 fn from(layer: Layer) -> Self {
148 layer.0
149 }
150}
151
152#[cfg(test)]
153mod tests {
154 use super::*;
155
156 #[test]
157 fn hex_and_rgba8() {
158 assert_eq!(Color::hex(0xff0000), Color::RED);
159 assert_eq!(
160 Color::hex_rgba(0x00ff0080),
161 Color::GREEN.with_alpha(128.0 / 255.0)
162 );
163 assert_eq!(Color::rgb8(0, 0, 255), Color::BLUE);
164 }
165
166 #[test]
167 fn array_conversions_roundtrip() {
168 let c: Color = [0.1, 0.2, 0.3, 0.4].into();
169 assert_eq!(<[f32; 4]>::from(c), [0.1, 0.2, 0.3, 0.4]);
170 assert_eq!(Color::from([0.1, 0.2, 0.3]).a, 1.0);
171 }
172
173 #[test]
174 fn lerp_endpoints() {
175 assert_eq!(Color::BLACK.lerp(Color::WHITE, 0.0), Color::BLACK);
176 assert_eq!(Color::BLACK.lerp(Color::WHITE, 1.0), Color::WHITE);
177 assert_eq!(Color::BLACK.lerp(Color::WHITE, 0.5), Color::GRAY);
178 }
179
180 #[test]
181 fn layer_from_literal() {
182 fn take(l: impl Into<Layer>) -> Layer {
183 l.into()
184 }
185 assert_eq!(take(3), Layer(3));
186 assert!(Layer::BOTTOM < Layer::DEFAULT && Layer::DEFAULT < Layer::TOP);
187 }
188}