embedded_graphics_core/pixelcolor/
rgb_color.rs

1use crate::pixelcolor::{
2    raw::{RawData, RawU16, RawU24},
3    PixelColor,
4};
5use core::fmt;
6
7/// RGB color.
8pub trait RgbColor: PixelColor {
9    /// Returns the red channel value.
10    fn r(&self) -> u8;
11
12    /// Returns the green channel value.
13    fn g(&self) -> u8;
14
15    /// Returns the blue channel value.
16    fn b(&self) -> u8;
17
18    /// The maximum value in the red channel.
19    const MAX_R: u8;
20
21    /// The maximum value in the green channel.
22    const MAX_G: u8;
23
24    /// The maximum value in the blue channel.
25    const MAX_B: u8;
26
27    /// Black color (R: 0%, G: 0%, B: 0%)
28    const BLACK: Self;
29
30    /// Red color (R: 100%, G: 0%, B: 0%)
31    const RED: Self;
32
33    /// Green color (R: 0%, G: 100%, B: 0%)
34    const GREEN: Self;
35
36    /// Blue color (R: 0%, G: 0%, B: 100%)
37    const BLUE: Self;
38
39    /// Yellow color (R: 100%, G: 100%, B: 0%)
40    const YELLOW: Self;
41
42    /// Magenta color (R: 100%, G: 0%, B: 100%)
43    const MAGENTA: Self;
44
45    /// Cyan color (R: 0%, G: 100%, B: 100%)
46    const CYAN: Self;
47
48    /// White color (R: 100%, G: 100%, B: 100%)
49    const WHITE: Self;
50}
51
52/// Macro to implement a RgbColor type with the given channel bit positions.
53macro_rules! impl_rgb_color {
54    (
55        $type:ident,
56        $data_type:ty,
57        $storage_type:ty,
58        ($r_bits:expr, $g_bits:expr, $b_bits:expr),
59        ($r_pos:expr, $g_pos:expr, $b_pos:expr),
60        $type_str:expr
61    ) => {
62        #[doc = $type_str]
63        #[doc = "color."]
64        #[doc = ""]
65        #[doc = "Use the methods provided by the [`RgbColor`] trait to access"]
66        #[doc = "individual color channels and predefined color constants."]
67        #[doc = ""]
68        #[doc = "See the [module-level documentation](super) for more information about"]
69        #[doc = "conversion between this type and raw data."]
70        #[doc = ""]
71        #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)]
72        pub struct $type($storage_type);
73
74        impl $type {
75            const R_MASK: $storage_type = ($type::MAX_R as $storage_type) << $r_pos;
76            const G_MASK: $storage_type = ($type::MAX_G as $storage_type) << $g_pos;
77            const B_MASK: $storage_type = ($type::MAX_B as $storage_type) << $b_pos;
78            const RGB_MASK: $storage_type = Self::R_MASK | Self::B_MASK | Self::G_MASK;
79        }
80
81        impl fmt::Debug for $type {
82            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
83                write!(
84                    f,
85                    "{}(r: {}, g: {}, b: {})",
86                    stringify!($type),
87                    self.r(),
88                    self.g(),
89                    self.b()
90                )
91            }
92        }
93
94        #[cfg(feature = "defmt")]
95        impl ::defmt::Format for $type {
96            fn format(&self, f: ::defmt::Formatter) {
97                ::defmt::write!(
98                    f,
99                    "{}(r: {=u8}, g: {=u8}, b: {=u8})",
100                    stringify!($type),
101                    self.r(),
102                    self.g(),
103                    self.b()
104                )
105            }
106        }
107
108        impl $type
109        where
110            Self: RgbColor,
111        {
112            #[doc = "Creates a new"]
113            #[doc = $type_str]
114            #[doc = "color.\n"]
115            #[doc = "Too large channel values will be limited by setting the"]
116            #[doc = "unused most significant bits to zero."]
117            pub const fn new(r: u8, g: u8, b: u8) -> Self {
118                let r_shifted = (r & Self::MAX_R) as $storage_type << $r_pos;
119                let g_shifted = (g & Self::MAX_G) as $storage_type << $g_pos;
120                let b_shifted = (b & Self::MAX_B) as $storage_type << $b_pos;
121
122                Self(r_shifted | g_shifted | b_shifted)
123            }
124        }
125
126        impl RgbColor for $type {
127            fn r(&self) -> u8 {
128                #![allow(trivial_numeric_casts)]
129
130                (self.0 >> $r_pos) as u8 & Self::MAX_R
131            }
132
133            fn g(&self) -> u8 {
134                #![allow(trivial_numeric_casts)]
135
136                (self.0 >> $g_pos) as u8 & Self::MAX_G
137            }
138
139            fn b(&self) -> u8 {
140                #![allow(trivial_numeric_casts)]
141
142                (self.0 >> $b_pos) as u8 & Self::MAX_B
143            }
144
145            const MAX_R: u8 = ((1usize << $r_bits) - 1) as u8;
146            const MAX_G: u8 = ((1usize << $g_bits) - 1) as u8;
147            const MAX_B: u8 = ((1usize << $b_bits) - 1) as u8;
148
149            const BLACK: Self = Self::new(0, 0, 0);
150            const RED: Self = Self::new(Self::MAX_R, 0, 0);
151            const GREEN: Self = Self::new(0, Self::MAX_G, 0);
152            const BLUE: Self = Self::new(0, 0, Self::MAX_B);
153            const YELLOW: Self = Self::new(Self::MAX_R, Self::MAX_G, 0);
154            const MAGENTA: Self = Self::new(Self::MAX_R, 0, Self::MAX_B);
155            const CYAN: Self = Self::new(0, Self::MAX_G, Self::MAX_B);
156            const WHITE: Self = Self::new(Self::MAX_R, Self::MAX_G, Self::MAX_B);
157        }
158
159        impl PixelColor for $type {
160            type Raw = $data_type;
161        }
162
163        impl From<$data_type> for $type {
164            fn from(data: $data_type) -> Self {
165                let data = data.into_inner();
166
167                Self(data & Self::RGB_MASK)
168            }
169        }
170
171        impl From<$type> for $data_type {
172            fn from(color: $type) -> Self {
173                Self::new(color.0)
174            }
175        }
176    };
177
178    // Recursive macro to stringify the type.
179    (
180        $type:ident,
181        $data_type:ty,
182        $storage_type:ty,
183        ($r_bits:expr, $g_bits:expr, $b_bits:expr),
184        ($r_pos:expr, $g_pos:expr, $b_pos:expr)
185    ) => {
186        impl_rgb_color!(
187            $type,
188            $data_type,
189            $storage_type,
190            ($r_bits, $g_bits, $b_bits),
191            ($r_pos, $g_pos, $b_pos),
192            stringify!($type)
193        );
194    };
195}
196
197/// Helper macro to calculate bit positions for RGB and BGR colors
198macro_rules! rgb_color {
199    (
200        $type:ident,
201        $data_type:ty,
202        $storage_type:ty,Rgb =
203        ($r_bits:expr, $g_bits:expr, $b_bits:expr)
204    ) => {
205        impl_rgb_color!(
206            $type,
207            $data_type,
208            $storage_type,
209            ($r_bits, $g_bits, $b_bits),
210            ($g_bits + $b_bits, $b_bits, 0)
211        );
212    };
213
214    (
215        $type:ident,
216        $data_type:ty,
217        $storage_type:ty,Bgr =
218        ($r_bits:expr, $g_bits:expr, $b_bits:expr)
219    ) => {
220        impl_rgb_color!(
221            $type,
222            $data_type,
223            $storage_type,
224            ($r_bits, $g_bits, $b_bits),
225            (0, $r_bits, $r_bits + $g_bits)
226        );
227    };
228}
229
230rgb_color!(Rgb555, RawU16, u16, Rgb = (5, 5, 5));
231rgb_color!(Bgr555, RawU16, u16, Bgr = (5, 5, 5));
232rgb_color!(Rgb565, RawU16, u16, Rgb = (5, 6, 5));
233rgb_color!(Bgr565, RawU16, u16, Bgr = (5, 6, 5));
234
235rgb_color!(Rgb666, RawU24, u32, Rgb = (6, 6, 6));
236rgb_color!(Bgr666, RawU24, u32, Bgr = (6, 6, 6));
237rgb_color!(Rgb888, RawU24, u32, Rgb = (8, 8, 8));
238rgb_color!(Bgr888, RawU24, u32, Bgr = (8, 8, 8));
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243    use crate::pixelcolor::IntoStorage;
244
245    /// Convert color to integer and back again to test bit positions
246    fn test_bpp16<C>(color: C, value: u16)
247    where
248        C: RgbColor + From<RawU16> + Into<RawU16> + core::fmt::Debug,
249    {
250        let value = RawU16::new(value);
251
252        assert_eq!(color.into(), value);
253        assert_eq!(C::from(value), color);
254    }
255
256    /// Convert color to integer and back again to test bit positions
257    fn test_bpp24<C>(color: C, value: u32)
258    where
259        C: RgbColor + From<RawU24> + Into<RawU24> + core::fmt::Debug,
260    {
261        let value = RawU24::new(value);
262
263        assert_eq!(color.into(), value);
264        assert_eq!(C::from(value), color);
265    }
266
267    #[test]
268    pub fn bit_positions_rgb555() {
269        test_bpp16(Rgb555::new(0b10001, 0, 0), 0b10001 << 5 + 5);
270        test_bpp16(Rgb555::new(0, 0b10001, 0), 0b10001 << 5);
271        test_bpp16(Rgb555::new(0, 0, 0b10001), 0b10001 << 0);
272    }
273
274    #[test]
275    pub fn bit_positions_bgr555() {
276        test_bpp16(Bgr555::new(0b10001, 0, 0), 0b10001 << 0);
277        test_bpp16(Bgr555::new(0, 0b10001, 0), 0b10001 << 5);
278        test_bpp16(Bgr555::new(0, 0, 0b10001), 0b10001 << 5 + 5);
279    }
280
281    #[test]
282    pub fn bit_positions_rgb565() {
283        test_bpp16(Rgb565::new(0b10001, 0, 0), 0b10001 << 5 + 6);
284        test_bpp16(Rgb565::new(0, 0b100001, 0), 0b100001 << 5);
285        test_bpp16(Rgb565::new(0, 0, 0b10001), 0b10001 << 0);
286    }
287
288    #[test]
289    pub fn bit_positions_bgr565() {
290        test_bpp16(Bgr565::new(0b10001, 0, 0), 0b10001 << 0);
291        test_bpp16(Bgr565::new(0, 0b100001, 0), 0b100001 << 5);
292        test_bpp16(Bgr565::new(0, 0, 0b10001), 0b10001 << 5 + 6);
293    }
294
295    #[test]
296    pub fn bit_positions_rgb666() {
297        test_bpp24(Rgb666::new(0b100001, 0, 0), 0b100001 << 6 + 6);
298        test_bpp24(Rgb666::new(0, 0b100001, 0), 0b100001 << 6);
299        test_bpp24(Rgb666::new(0, 0, 0b100001), 0b100001 << 0);
300    }
301
302    #[test]
303    pub fn bit_positions_bgr666() {
304        test_bpp24(Bgr666::new(0b100001, 0, 0), 0b100001 << 0);
305        test_bpp24(Bgr666::new(0, 0b100001, 0), 0b100001 << 6);
306        test_bpp24(Bgr666::new(0, 0, 0b100001), 0b100001 << 6 + 6);
307    }
308
309    #[test]
310    pub fn bit_positions_rgb888() {
311        test_bpp24(Rgb888::new(0b10000001, 0, 0), 0b10000001 << 8 + 8);
312        test_bpp24(Rgb888::new(0, 0b10000001, 0), 0b10000001 << 8);
313        test_bpp24(Rgb888::new(0, 0, 0b10000001), 0b10000001 << 0);
314    }
315
316    #[test]
317    pub fn bit_positions_bgr888() {
318        test_bpp24(Bgr888::new(0b10000001, 0, 0), 0b10000001 << 0);
319        test_bpp24(Bgr888::new(0, 0b10000001, 0), 0b10000001 << 8);
320        test_bpp24(Bgr888::new(0, 0, 0b10000001), 0b10000001 << 8 + 8);
321    }
322
323    #[test]
324    pub fn unused_bits_are_ignored() {
325        let color: Rgb555 = RawU16::from(0xFFFF).into();
326        assert_eq!(RawU16::from(color).into_inner(), 0x7FFF);
327
328        let color: Bgr555 = RawU16::from(0xFFFF).into();
329        assert_eq!(RawU16::from(color).into_inner(), 0x7FFF);
330    }
331
332    #[test]
333    fn convert_to_raw() {
334        let color = Rgb888::new(0xAA, 0xBB, 0xCC);
335
336        assert_eq!(color.into_storage(), 0x00AABBCC);
337    }
338}