1use core::marker::PhantomData;
2use embedded_graphics::geometry::OriginDimensions;
3use embedded_graphics::pixelcolor::raw::RawU16;
4use embedded_graphics::pixelcolor::*;
5use embedded_graphics::prelude::*;
6use embedded_graphics::Pixel;
7
8#[derive(PartialEq, Clone, Copy)]
10pub struct Rgb16(pub u8, pub u8);
11
12impl Rgb16 {
13 pub const fn from_rgb(r: u16, g: u16, b: u16) -> Self {
14 let r = r >> 3;
15 let g = g >> 2;
16 let b = b >> 3;
17 let raw = (b << 11) | ((g & 0b_0011_1111) << 5) | (r & 0b_0001_1111);
18 let raw = raw.to_le_bytes();
19 Self(!raw[0], !raw[1])
20 }
21
22 pub const fn into_rgb(self) -> (u8, u8, u8) {
23 let raw = u16::from_le_bytes([!self.0, !self.1]);
24 let r = (raw << 3) as u8;
25 let g = ((raw >> 5) << 2) as u8;
26 let b = ((raw >> 11) << 3) as u8;
27 (r, g, b)
28 }
29}
30
31impl PixelColor for Rgb16 {
32 type Raw = RawU16;
33}
34
35impl RgbColor for Rgb16 {
36 const MAX_R: u8 = 32;
37 const MAX_G: u8 = 64;
38 const MAX_B: u8 = 32;
39 const BLACK: Self = Self::from_rgb(0, 0, 0);
40 const RED: Self = Self::from_rgb(255, 0, 0);
41 const GREEN: Self = Self::from_rgb(0, 255, 0);
42 const BLUE: Self = Self::from_rgb(0, 0, 255);
43 const YELLOW: Self = Self::from_rgb(255, 255, 0);
44 const MAGENTA: Self = Self::from_rgb(255, 0, 255);
45 const CYAN: Self = Self::from_rgb(0, 255, 255);
46 const WHITE: Self = Self::from_rgb(255, 255, 255);
47
48 fn r(&self) -> u8 {
49 let (r, _, _) = self.into_rgb();
50 r
51 }
52
53 fn g(&self) -> u8 {
54 let (_, g, _) = self.into_rgb();
55 g
56 }
57
58 fn b(&self) -> u8 {
59 let (_, _, b) = self.into_rgb();
60 b
61 }
62}
63
64impl From<Rgb888> for Rgb16 {
65 fn from(c: Rgb888) -> Self {
66 let r = u16::from(c.r());
67 let g = u16::from(c.g());
68 let b = u16::from(c.b());
69 Self::from_rgb(r, g, b)
70 }
71}
72
73impl From<Rgb16> for Rgb888 {
74 fn from(c: Rgb16) -> Self {
75 let (mut r, mut g, mut b) = c.into_rgb();
76 if r == 0b1111_1000 {
77 r = 0xff;
78 }
79 if g == 0b1111_1100 {
80 g = 0xff;
81 }
82 if b == 0b1111_1000 {
83 b = 0xff;
84 }
85 Self::new(r, g, b)
86 }
87}
88
89pub(crate) struct BPPAdapter<'a, D, C>
91where
92 D: DrawTarget<Color = Gray4> + OriginDimensions,
93 C: PixelColor + IntoStorage<Storage = u8>,
94{
95 target: &'a mut D,
96 swaps: [Option<Gray4>; 16],
97 color: PhantomData<C>,
98}
99
100impl<'a, D, C> BPPAdapter<'a, D, C>
101where
102 D: DrawTarget<Color = Gray4> + OriginDimensions,
103 C: PixelColor + IntoStorage<Storage = u8>,
104{
105 pub fn new(target: &'a mut D, transp: u8, swaps: &'_ [u8]) -> Self {
106 Self {
107 target,
108 swaps: parse_swaps(transp, swaps),
109 color: PhantomData,
110 }
111 }
112}
113
114impl<D, C> OriginDimensions for BPPAdapter<'_, D, C>
116where
117 D: DrawTarget<Color = Gray4> + OriginDimensions,
118 C: PixelColor + IntoStorage<Storage = u8>,
119{
120 fn size(&self) -> embedded_graphics::prelude::Size {
121 self.target.size()
122 }
123}
124
125impl<D, C> DrawTarget for BPPAdapter<'_, D, C>
126where
127 D: DrawTarget<Color = Gray4> + OriginDimensions,
128 C: PixelColor + IntoStorage<Storage = u8>,
129{
130 type Color = C;
131 type Error = D::Error;
132
133 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
134 where
135 I: IntoIterator<Item = Pixel<Self::Color>>,
136 {
137 let iter = pixels.into_iter().filter_map(|Pixel(p, c)| {
138 let c = c.into_storage();
139 match self.swaps.get(c as usize) {
140 Some(Some(c)) => Some(Pixel(p, *c)),
141 _ => None,
142 }
143 });
144 self.target.draw_iter(iter)
145 }
146}
147
148pub trait FromRGB {
150 const BG: Self;
152 const PRIMARY: Self;
154 const ACCENT: Self;
156 const DANGER: Self;
158 const MUTED: Self;
160
161 fn from_rgb(rgb: Rgb16) -> Self;
162}
163
164impl FromRGB for Rgb16 {
165 const BG: Self = Self::from_rgb(0xf4, 0xf4, 0xf4);
166 const PRIMARY: Self = Self::from_rgb(0x1a, 0x1c, 0x2c);
167 const ACCENT: Self = Self::from_rgb(0x38, 0xb7, 0x64);
168 const DANGER: Self = Self::from_rgb(0xb1, 0x3e, 0x53);
169 const MUTED: Self = Self::from_rgb(0x94, 0xb0, 0xc2);
170
171 fn from_rgb(rgb: Self) -> Self {
172 rgb
173 }
174}
175
176impl FromRGB for Rgb565 {
177 const BG: Self = new_rgb565(0xf4, 0xf4, 0xf4);
178 const PRIMARY: Self = new_rgb565(0x1a, 0x1c, 0x2c);
179 const ACCENT: Self = new_rgb565(0x38, 0xb7, 0x64);
180 const DANGER: Self = new_rgb565(0xb1, 0x3e, 0x53);
181 const MUTED: Self = new_rgb565(0x94, 0xb0, 0xc2);
182
183 fn from_rgb(rgb: Rgb16) -> Self {
184 let (r, g, b) = rgb.into_rgb();
185 Self::new(r, g, b)
186 }
187}
188
189impl FromRGB for Rgb888 {
190 const BG: Self = Self::new(0xf4, 0xf4, 0xf4);
191 const PRIMARY: Self = Self::new(0x1a, 0x1c, 0x2c);
192 const ACCENT: Self = Self::new(0x38, 0xb7, 0x64);
193 const DANGER: Self = Self::new(0xb1, 0x3e, 0x53);
194 const MUTED: Self = Self::new(0x94, 0xb0, 0xc2);
195
196 fn from_rgb(rgb: Rgb16) -> Self {
197 rgb.into()
198 }
199}
200
201const fn new_rgb565(r: u8, g: u8, b: u8) -> Rgb565 {
202 let r = r as u32 * Rgb565::MAX_R as u32 / Rgb888::MAX_R as u32;
203 let g = g as u32 * Rgb565::MAX_G as u32 / Rgb888::MAX_G as u32;
204 let b = b as u32 * Rgb565::MAX_B as u32 / Rgb888::MAX_B as u32;
205 debug_assert!(r < 256);
206 debug_assert!(g < 256);
207 debug_assert!(b < 256);
208 Rgb565::new(r as u8, g as u8, b as u8)
209}
210
211#[allow(clippy::get_first)]
212pub(crate) fn parse_swaps(transp: u8, swaps: &[u8]) -> [Option<Gray4>; 16] {
213 [
214 parse_color_l(transp, swaps.get(0)),
216 parse_color_r(transp, swaps.get(0)),
217 parse_color_l(transp, swaps.get(1)),
218 parse_color_r(transp, swaps.get(1)),
219 parse_color_l(transp, swaps.get(2)),
221 parse_color_r(transp, swaps.get(2)),
222 parse_color_l(transp, swaps.get(3)),
223 parse_color_r(transp, swaps.get(3)),
224 parse_color_l(transp, swaps.get(4)),
226 parse_color_r(transp, swaps.get(4)),
227 parse_color_l(transp, swaps.get(5)),
228 parse_color_r(transp, swaps.get(5)),
229 parse_color_l(transp, swaps.get(6)),
231 parse_color_r(transp, swaps.get(6)),
232 parse_color_l(transp, swaps.get(7)),
233 parse_color_r(transp, swaps.get(7)),
234 ]
235}
236
237fn parse_color_r(transp: u8, c: Option<&u8>) -> Option<Gray4> {
239 let c = c?;
240 let c = c & 0b1111;
241 if c == transp {
242 return None;
243 }
244 Some(Gray4::new(c))
245}
246
247fn parse_color_l(transp: u8, c: Option<&u8>) -> Option<Gray4> {
249 let c = c?;
250 let c = (c >> 4) & 0b1111;
251 if c == transp {
252 return None;
253 }
254 Some(Gray4::new(c))
255}