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 { r: arr[0], g: arr[1], b: arr[2], a: arr[3] }
75    }
76}
77
78#[cfg(test)]
79mod tests {
80    use super::*;
81
82    #[test]
83    fn const_white() {
84        assert_eq!(Rgba::WHITE, Rgba { r: 0xFF, g: 0xFF, b: 0xFF, a: 0xFF });
85    }
86
87    #[test]
88    fn const_black() {
89        assert_eq!(Rgba::BLACK, Rgba { r: 0x00, g: 0x00, b: 0x00, a: 0xFF });
90    }
91
92    #[test]
93    fn const_transparent() {
94        assert_eq!(Rgba::TRANSPARENT, Rgba { r: 0, g: 0, b: 0, a: 0 });
95    }
96
97    #[test]
98    fn new_constructor() {
99        let c = Rgba::new(10, 20, 30, 40);
100        assert_eq!(c.r, 10);
101        assert_eq!(c.g, 20);
102        assert_eq!(c.b, 30);
103        assert_eq!(c.a, 40);
104    }
105
106    #[test]
107    fn rgb_constructor() {
108        let c = Rgba::rgb(10, 20, 30);
109        assert_eq!(c.r, 10);
110        assert_eq!(c.g, 20);
111        assert_eq!(c.b, 30);
112        assert_eq!(c.a, 0xFF);
113    }
114
115    #[test]
116    fn to_array() {
117        let c = Rgba::new(1, 2, 3, 4);
118        assert_eq!(c.to_array(), [1, 2, 3, 4]);
119    }
120
121    #[test]
122    fn from_array() {
123        let c = Rgba::from([10, 20, 30, 40]);
124        assert_eq!(c, Rgba::new(10, 20, 30, 40));
125    }
126
127    #[test]
128    fn default_is_transparent() {
129        assert_eq!(Rgba::default(), Rgba::TRANSPARENT);
130    }
131
132    #[test]
133    fn equality() {
134        assert_eq!(Rgba::new(1, 2, 3, 4), Rgba::new(1, 2, 3, 4));
135        assert_ne!(Rgba::new(1, 2, 3, 4), Rgba::new(5, 2, 3, 4));
136    }
137
138    #[test]
139    fn copy_works() {
140        let a = Rgba::new(1, 2, 3, 4);
141        let b = a;
142        assert_eq!(a, b);
143    }
144}