1use std::fmt;
37
38#[derive(Copy, Clone, Default, PartialEq)]
42#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
43pub struct PixelColor {
44 pub red: u8,
45 pub green: u8,
46 pub blue: u8,
47}
48
49impl fmt::Debug for PixelColor {
50 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
51 write!(f, "#{:02X}{:02X}{:02X}", self.red, self.green, self.blue)
52 }
53}
54
55impl PixelColor {
56 pub const BLACK: PixelColor = PixelColor {
57 red: 0,
58 green: 0,
59 blue: 0,
60 };
61
62 pub const RED: PixelColor = PixelColor {
63 red: 0xFF,
64 green: 0,
65 blue: 0,
66 };
67
68 pub const BLUE: PixelColor = PixelColor {
69 red: 0,
70 green: 0,
71 blue: 0xFF,
72 };
73
74 pub const GREEN: PixelColor = PixelColor {
75 red: 0,
76 green: 0xFF,
77 blue: 0,
78 };
79
80 pub const WHITE: PixelColor = PixelColor {
81 red: 0xFF,
82 green: 0xFF,
83 blue: 0xFF,
84 };
85
86 pub const YELLOW: PixelColor = PixelColor {
87 red: 0xFF,
88 green: 0xFF,
89 blue: 0,
90 };
91
92 pub const CYAN: PixelColor = PixelColor {
93 red: 0,
94 green: 0xFF,
95 blue: 0xFF,
96 };
97
98 pub const MAGENTA: PixelColor = PixelColor {
99 red: 0xFF,
100 green: 0,
101 blue: 0xFF,
102 };
103
104 pub fn new(red: u8, green: u8, blue: u8) -> Self {
106 Self { red, green, blue }
107 }
108
109 pub fn from_rgb565_bytes(color: [u8; 2]) -> Self {
111 let rgb565: Rgb565 = color.into();
112 rgb565.into()
113 }
114
115 #[cfg(not(feature = "big-endian"))]
116 pub fn rgb565(&self) -> [u8; 2] {
118 Rgb565::from(self).split_le()
119 }
120
121 #[cfg(feature = "big-endian")]
122 pub fn rgb565(&self) -> [u8; 2] {
124 Rgb565::from(self).split_be()
125 }
126
127 pub fn dim(self, mut scale: f32) -> PixelColor {
132 if scale > 1.0 {
133 scale = 1.0;
134 }
135 if scale < 0.0 {
136 scale = 0.0;
137 }
138 fn scale_byte(b: u8, scale: f32) -> u8 {
139 (f32::from(b) * scale) as u8
140 }
141 PixelColor {
142 red: scale_byte(self.red, scale),
143 green: scale_byte(self.green, scale),
144 blue: scale_byte(self.blue, scale),
145 }
146 }
147}
148
149impl From<Rgb565> for PixelColor {
150 fn from(color: Rgb565) -> Self {
151 let rgb565 = color.to_rgb();
152 PixelColor::new(rgb565.0, rgb565.1, rgb565.2)
153 }
154}
155
156impl From<(u8, u8, u8)> for PixelColor {
157 fn from(color: (u8, u8, u8)) -> Self {
158 PixelColor::new(color.0, color.1, color.2)
159 }
160}
161
162impl Into<(u8, u8, u8)> for PixelColor {
163 fn into(self) -> (u8, u8, u8) {
164 (self.red, self.green, self.blue)
165 }
166}
167
168#[derive(Copy, Clone, Debug, Default, PartialEq)]
192#[cfg_attr(feature = "serde-support", derive(Serialize, Deserialize))]
193pub struct Rgb565(u16);
194
195impl Rgb565 {
196 pub fn from_rgb(red: u8, green: u8, blue: u8) -> Self {
198 let r = u16::from((red >> 3) & 0x1F);
199 let g = u16::from((green >> 2) & 0x3F);
200 let b = u16::from((blue >> 3) & 0x1F);
201 let rgb = (r << 11) + (g << 5) + b;
202 Rgb565(rgb)
203 }
204
205 pub fn to_rgb(self) -> (u8, u8, u8) {
207 let red = (((self.0 & 0b1111_1000_0000_0000) >> 11) << 3) as u8;
208 let green = (((self.0 & 0b0000_0111_1110_0000) >> 5) << 2) as u8;
209 let blue = ((self.0 & 0b0000_0000_0001_1111) << 3) as u8;
210 (red, green, blue)
211 }
212
213 #[cfg(not(feature = "big-endian"))]
214 fn from_le(bytes: [u8; 2]) -> Self {
216 let lo = u16::from(bytes[1]) << 8;
217 let hi = u16::from(bytes[0]);
218 Rgb565(hi | lo)
219 }
220
221 #[cfg(feature = "big-endian")]
222 fn from_be(bytes: [u8; 2]) -> Self {
224 let lo = u16::from(bytes[0]) << 8;
225 let hi = u16::from(bytes[1]);
226 Rgb565(hi | lo)
227 }
228
229 #[cfg(not(feature = "big-endian"))]
230 fn split_le(self) -> [u8; 2] {
232 let lo = (self.0 & 0x00FF) as u8;
233 let hi = (self.0.swap_bytes() & 0x00FF) as u8;
234 [lo, hi]
235 }
236
237 #[cfg(feature = "big-endian")]
238 fn split_be(self) -> [u8; 2] {
240 let lo = (self.0 & 0x00FF) as u8;
241 let hi = (self.0.swap_bytes() & 0x00FF) as u8;
242 [hi, lo]
243 }
244}
245
246impl Into<u16> for Rgb565 {
247 fn into(self) -> u16 {
248 self.0
249 }
250}
251
252impl From<u16> for Rgb565 {
253 fn from(bytes: u16) -> Self {
254 Rgb565(bytes)
255 }
256}
257#[cfg(not(feature = "big-endian"))]
258impl Into<[u8; 2]> for Rgb565 {
259 fn into(self) -> [u8; 2] {
260 Rgb565::split_le(self)
261 }
262}
263
264#[cfg(not(feature = "big-endian"))]
265impl From<[u8; 2]> for Rgb565 {
266 fn from(bytes: [u8; 2]) -> Self {
267 Rgb565::from_le(bytes)
268 }
269}
270
271#[cfg(feature = "big-endian")]
272impl Into<[u8; 2]> for Rgb565 {
273 fn into(self) -> [u8; 2] {
274 Rgb565::split_be(self)
275 }
276}
277
278#[cfg(feature = "big-endian")]
279impl From<[u8; 2]> for Rgb565 {
280 fn from(bytes: [u8; 2]) -> Self {
281 Rgb565::from_be(bytes)
282 }
283}
284
285impl From<(u8, u8, u8)> for Rgb565 {
286 fn from(color: (u8, u8, u8)) -> Self {
287 Rgb565::from_rgb(color.0, color.1, color.2)
288 }
289}
290
291impl Into<(u8, u8, u8)> for Rgb565 {
292 fn into(self) -> (u8, u8, u8) {
293 self.to_rgb()
294 }
295}
296
297impl From<PixelColor> for Rgb565 {
298 fn from(color: PixelColor) -> Self {
299 Rgb565::from_rgb(color.red, color.green, color.blue)
300 }
301}
302
303impl<'a> From<&'a PixelColor> for Rgb565 {
304 fn from(color: &'a PixelColor) -> Self {
305 Rgb565::from_rgb(color.red, color.green, color.blue)
306 }
307}
308
309pub trait BackgroundColor {
312 fn set_background_color(&mut self, color: PixelColor);
314 fn get_background_color(&self) -> PixelColor;
316}
317
318pub trait StrokeColor {
321 fn set_stroke_color(&mut self, color: PixelColor);
323 fn get_stroke_color(&self) -> PixelColor;
325}
326
327#[cfg(test)]
328mod tests {
329 use super::*;
330
331 #[cfg(not(feature = "big-endian"))]
332 #[test]
333 fn color_pixel_encodes_rgb_into_2_bytes_rgb565_with_losses() {
334 assert_eq!(
336 PixelColor::from_rgb565_bytes([0x00, 0x00]),
337 PixelColor::new(0x00, 0x00, 0x00)
338 );
339 assert_eq!(
341 PixelColor::from_rgb565_bytes([0xFF, 0xFF]),
342 PixelColor::new(0xF8, 0xFC, 0xF8)
343 );
344 assert_eq!(
346 PixelColor::from_rgb565_bytes([0xE0, 0x07]),
347 PixelColor::new(0x00, 0xFC, 0x00)
348 );
349 }
350
351 #[cfg(feature = "big-endian")]
352 #[test]
353 fn color_pixel_encodes_rgb_into_2_bytes_rgb565_with_losses() {
354 assert_eq!(
356 PixelColor::from_rgb565_bytes([0x00, 0x00]),
357 PixelColor::new(0x00, 0x00, 0x00)
358 );
359 assert_eq!(
361 PixelColor::from_rgb565_bytes([0xFF, 0xFF]),
362 PixelColor::new(0xF8, 0xFC, 0xF8)
363 );
364 assert_eq!(
366 PixelColor::from_rgb565_bytes([0x07, 0xE0]),
367 PixelColor::new(0x00, 0xFC, 0x00)
368 );
369 }
370
371 #[cfg(not(feature = "big-endian"))]
372 #[test]
373 fn convert_rgb565_to_byte_array() {
374 let bytes = [0xFF, 0xFF];
375 assert_eq!(Rgb565::from(bytes), Rgb565(0xFFFF));
376 let bytes = [0xE0, 0x07];
377 assert_eq!(Rgb565::from(bytes), Rgb565(0x07E0));
378 let bytes = [0x1F, 0x00];
379 assert_eq!(Rgb565::from(bytes), Rgb565(0x001F));
380 }
381
382 #[cfg(feature = "big-endian")]
383 #[test]
384 fn convert_rgb565_to_byte_array() {
385 let bytes = [0xFF, 0xFF];
386 assert_eq!(Rgb565::from(bytes), Rgb565(0xFFFF));
387 let bytes = [0x07, 0xE0];
388 assert_eq!(Rgb565::from(bytes), Rgb565(0x07E0));
389 let bytes = [0x00, 0x1F];
390 assert_eq!(Rgb565::from(bytes), Rgb565(0x001F));
391 }
392
393 #[cfg(not(feature = "big-endian"))]
394 #[test]
395 fn convert_byte_array_to_rgb565() {
396 let rgb: [u8; 2] = Rgb565(0x07E0).into();
397 assert_eq!(rgb, [0xE0, 0x07]);
398 }
399
400 #[cfg(feature = "big-endian")]
401 #[test]
402 fn convert_byte_array_to_rgb565() {
403 let rgb: [u8; 2] = Rgb565(0x07E0).into();
404 assert_eq!(rgb, [0x07, 0xE0]);
405 }
406
407 #[cfg(not(feature = "big-endian"))]
408 #[test]
409 fn color_pixel_converts_rgb_into_2_bytes_rgb565() {
410 let white_pixel = PixelColor::WHITE;
411 assert_eq!(white_pixel.rgb565(), [0xFF, 0xFF]);
412
413 let red_pixel = PixelColor::RED;
414 assert_eq!(red_pixel.rgb565(), [0x00, 0xF8]);
415
416 let green_pixel = PixelColor::GREEN;
417 assert_eq!(green_pixel.rgb565(), [0xE0, 0x07]);
418
419 let blue_pixel = PixelColor::BLUE;
420 assert_eq!(blue_pixel.rgb565(), [0x1F, 0x00]);
421 }
422
423 #[cfg(feature = "big-endian")]
424 #[test]
425 fn color_pixel_converts_rgb_into_2_bytes_rgb565() {
426 let white_pixel = PixelColor::WHITE;
427 assert_eq!(white_pixel.rgb565(), [0xFF, 0xFF]);
428
429 let red_pixel = PixelColor::RED;
430 assert_eq!(red_pixel.rgb565(), [0xF8, 0x00]);
431
432 let green_pixel = PixelColor::GREEN;
433 assert_eq!(green_pixel.rgb565(), [0x07, 0xE0]);
434
435 let blue_pixel = PixelColor::BLUE;
436 assert_eq!(blue_pixel.rgb565(), [0x00, 0x1F]);
437 }
438}