Skip to main content

dotzuki_engine/render/
color.rs

1/// An RGBA colour with 8 bits per channel.
2///
3/// Each channel (`r`, `g`, `b`, `a`) ranges from `0` to `255`.
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub struct Rgba {
6    /// Red channel.
7    pub r: u8,
8    /// Green channel.
9    pub g: u8,
10    /// Blue channel.
11    pub b: u8,
12    /// Alpha channel (`0` = fully transparent, `255` = fully opaque).
13    pub a: u8,
14}
15
16impl Rgba {
17    /// Pure white, fully opaque.
18    pub const WHITE: Self = Self::rgb(0xFF, 0xFF, 0xFF);
19
20    /// Pure black, fully opaque.
21    pub const BLACK: Self = Self::rgb(0x00, 0x00, 0x00);
22
23    /// Fully transparent black.
24    pub const TRANSPARENT: Self = Self::new(0, 0, 0, 0);
25
26    /// Darkest shade of the default 4-step ink ramp used for menu/text
27    /// rendering (matches the historical `InkColor::Black` RGBA value).
28    pub const INK_BLACK: Self = Self::rgb(0x20, 0x20, 0x20);
29
30    /// Dark-gray shade of the default ink ramp.
31    pub const INK_DARK_GRAY: Self = Self::rgb(0x60, 0x60, 0x60);
32
33    /// Light-gray shade of the default ink ramp.
34    pub const INK_LIGHT_GRAY: Self = Self::rgb(0xA0, 0xA0, 0xA0);
35
36    /// Lightest shade of the default ink ramp (paper background).
37    pub const INK_WHITE: Self = Self::rgb(0xE0, 0xE0, 0xE0);
38
39    /// Create a colour from individual channel values.
40    #[inline]
41    pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
42        Self { r, g, b, a }
43    }
44
45    /// Create an opaque colour from RGB values (alpha = 255).
46    #[inline]
47    pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
48        Self { r, g, b, a: 0xFF }
49    }
50
51    /// Return the channels as a flat `[r, g, b, a]` byte array.
52    #[inline]
53    pub const fn to_array(self) -> [u8; 4] {
54        [self.r, self.g, self.b, self.a]
55    }
56}
57
58impl From<Rgba> for [u8; 4] {
59    fn from(c: Rgba) -> Self {
60        c.to_array()
61    }
62}
63
64impl Default for Rgba {
65    /// Returns [`Rgba::TRANSPARENT`].
66    fn default() -> Self {
67        Self::TRANSPARENT
68    }
69}
70
71impl From<[u8; 4]> for Rgba {
72    /// Convert a `[r, g, b, a]` byte array into an [`Rgba`].
73    fn from(arr: [u8; 4]) -> Self {
74        Self {
75            r: arr[0],
76            g: arr[1],
77            b: arr[2],
78            a: arr[3],
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn const_white() {
89        assert_eq!(
90            Rgba::WHITE,
91            Rgba {
92                r: 0xFF,
93                g: 0xFF,
94                b: 0xFF,
95                a: 0xFF
96            }
97        );
98    }
99
100    #[test]
101    fn const_black() {
102        assert_eq!(
103            Rgba::BLACK,
104            Rgba {
105                r: 0x00,
106                g: 0x00,
107                b: 0x00,
108                a: 0xFF
109            }
110        );
111    }
112
113    #[test]
114    fn const_transparent() {
115        assert_eq!(
116            Rgba::TRANSPARENT,
117            Rgba {
118                r: 0,
119                g: 0,
120                b: 0,
121                a: 0
122            }
123        );
124    }
125
126    #[test]
127    fn new_constructor() {
128        let c = Rgba::new(10, 20, 30, 40);
129        assert_eq!(c.r, 10);
130        assert_eq!(c.g, 20);
131        assert_eq!(c.b, 30);
132        assert_eq!(c.a, 40);
133    }
134
135    #[test]
136    fn rgb_constructor() {
137        let c = Rgba::rgb(10, 20, 30);
138        assert_eq!(c.r, 10);
139        assert_eq!(c.g, 20);
140        assert_eq!(c.b, 30);
141        assert_eq!(c.a, 0xFF);
142    }
143
144    #[test]
145    fn to_array() {
146        let c = Rgba::new(1, 2, 3, 4);
147        assert_eq!(c.to_array(), [1, 2, 3, 4]);
148    }
149
150    #[test]
151    fn from_array() {
152        let c = Rgba::from([10, 20, 30, 40]);
153        assert_eq!(c, Rgba::new(10, 20, 30, 40));
154    }
155
156    #[test]
157    fn default_is_transparent() {
158        assert_eq!(Rgba::default(), Rgba::TRANSPARENT);
159    }
160
161    #[test]
162    fn equality() {
163        assert_eq!(Rgba::new(1, 2, 3, 4), Rgba::new(1, 2, 3, 4));
164        assert_ne!(Rgba::new(1, 2, 3, 4), Rgba::new(5, 2, 3, 4));
165    }
166
167    #[test]
168    fn copy_works() {
169        let a = Rgba::new(1, 2, 3, 4);
170        let b = a;
171        assert_eq!(a, b);
172    }
173}