1use crate::pixelcolor::{
2 raw::{RawData, RawU16, RawU24},
3 PixelColor,
4};
5use core::fmt;
6
7pub trait RgbColor: PixelColor {
9 fn r(&self) -> u8;
11
12 fn g(&self) -> u8;
14
15 fn b(&self) -> u8;
17
18 const MAX_R: u8;
20
21 const MAX_G: u8;
23
24 const MAX_B: u8;
26
27 const BLACK: Self;
29
30 const RED: Self;
32
33 const GREEN: Self;
35
36 const BLUE: Self;
38
39 const YELLOW: Self;
41
42 const MAGENTA: Self;
44
45 const CYAN: Self;
47
48 const WHITE: Self;
50}
51
52macro_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 (
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
197macro_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 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 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}