1use core::marker::PhantomData;
9
10use embedded_graphics_core::{
11 pixelcolor::BinaryColor,
12 prelude::{DrawTarget, OriginDimensions, RawData, Size},
13 primitives::Rectangle,
14 Pixel,
15};
16use embedded_hal::spi::SpiBus;
17
18use crate::pixelcolor::Rgb111;
19
20#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
22pub enum Rotation {
23 Deg0,
25 Deg90,
27 Deg180,
29 Deg270,
31}
32
33impl Rotation {
34 #[inline]
35 fn is_column_row_swap(self) -> bool {
36 matches!(self, Rotation::Deg90 | Rotation::Deg270)
37 }
38}
39
40pub(crate) mod sealed {
41 use embedded_hal::spi::SpiBus;
42
43 pub trait FramebufferSpiUpdate {
44 fn update<SPI: SpiBus>(&self, spi: &mut SPI) -> Result<(), SPI::Error>;
45 }
46
47 pub trait DriverVariant {}
48}
49
50pub struct JDI;
51pub struct Sharp;
52
53impl sealed::DriverVariant for JDI {}
54impl sealed::DriverVariant for Sharp {}
55
56pub trait FramebufferType: OriginDimensions + DrawTarget + Default + sealed::FramebufferSpiUpdate {}
57
58pub struct Framebuffer4Bit<const WIDTH: u16, const HEIGHT: u16>
59where
60 [(); WIDTH as usize * HEIGHT as usize / 2]:,
61{
62 data: [u8; WIDTH as usize * HEIGHT as usize / 2],
63 rotation: Rotation,
64}
65
66impl<const WIDTH: u16, const HEIGHT: u16> Default for Framebuffer4Bit<WIDTH, HEIGHT>
67where
68 [(); WIDTH as usize * HEIGHT as usize / 2]:,
69{
70 fn default() -> Self {
71 Self::new()
72 }
73}
74
75impl<const WIDTH: u16, const HEIGHT: u16> sealed::FramebufferSpiUpdate for Framebuffer4Bit<WIDTH, HEIGHT>
76where
77 [(); WIDTH as usize * HEIGHT as usize / 2]:,
78{
79 fn update<SPI: SpiBus>(&self, spi: &mut SPI) -> Result<(), SPI::Error> {
81 for i in 0..HEIGHT {
82 let start = (i as usize) * WIDTH as usize / 2;
83 let end = start + WIDTH as usize / 2;
84 let line_data = &self.data[start..end];
85 spi.write(&[crate::CMD_UPDATE_4BIT, i as u8 + 1])?;
87 spi.write(line_data)?;
88 }
89 spi.write(&[0x00, 0x00])?;
90 Ok(())
91 }
92}
93
94impl<const WIDTH: u16, const HEIGHT: u16> Framebuffer4Bit<WIDTH, HEIGHT>
95where
96 [(); WIDTH as usize * HEIGHT as usize / 2]:,
97{
98 pub fn new() -> Self {
99 Self {
100 data: [0; WIDTH as usize * HEIGHT as usize / 2],
101 rotation: Rotation::Deg0,
102 }
103 }
104
105 pub fn set_rotation(&mut self, rotation: Rotation) {
106 self.rotation = rotation;
107 }
108
109 pub fn get_rotation(&self) -> Rotation {
110 self.rotation
111 }
112
113 pub(crate) fn set_pixel(&mut self, x: u16, y: u16, color: Rgb111) {
114 if self.rotation.is_column_row_swap() {
115 if x >= HEIGHT || y >= WIDTH {
116 return;
117 }
118 } else if y >= HEIGHT || x >= WIDTH {
119 return;
120 }
121 let x = x as usize;
122 let y = y as usize;
123
124 let (x, y) = match self.rotation {
125 Rotation::Deg0 => (x, y),
126 Rotation::Deg90 => (y, HEIGHT as usize - x - 1),
127 Rotation::Deg180 => (WIDTH as usize - x - 1, HEIGHT as usize - y - 1),
128 Rotation::Deg270 => (WIDTH as usize - y - 1, x),
129 };
130
131 let index = (y * WIDTH as usize + x) / 2;
132
133 let color = color.0.into_inner();
134
135 if x % 2 == 0 {
136 self.data[index] = (self.data[index] & 0b00001111) | (color << 4);
137 } else {
138 self.data[index] = (self.data[index] & 0b11110000) | color;
139 }
140 }
141}
142
143impl<const WIDTH: u16, const HEIGHT: u16> FramebufferType for Framebuffer4Bit<WIDTH, HEIGHT> where
144 [(); WIDTH as usize * HEIGHT as usize / 2]:
145{
146}
147
148impl<const WIDTH: u16, const HEIGHT: u16> OriginDimensions for Framebuffer4Bit<WIDTH, HEIGHT>
149where
150 [(); WIDTH as usize * HEIGHT as usize / 2]:,
151{
152 fn size(&self) -> Size {
153 match self.rotation {
154 Rotation::Deg0 | Rotation::Deg180 => Size::new(WIDTH as u32, HEIGHT as u32),
155 Rotation::Deg90 | Rotation::Deg270 => Size::new(HEIGHT as u32, WIDTH as u32),
156 }
157 }
158}
159
160impl<const WIDTH: u16, const HEIGHT: u16> DrawTarget for Framebuffer4Bit<WIDTH, HEIGHT>
161where
162 [(); WIDTH as usize * HEIGHT as usize / 2]:,
163{
164 type Color = Rgb111;
165
166 type Error = core::convert::Infallible;
167
168 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
169 where
170 I: IntoIterator<Item = Pixel<Self::Color>>,
171 {
172 for Pixel(coord, color) in pixels.into_iter() {
173 self.set_pixel(coord.x as u16, coord.y as u16, color);
174 }
175 Ok(())
176 }
177
178 fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
179 let raw = color.0.into_inner() << 4 | color.0.into_inner();
180 self.data.fill(raw);
181 Ok(())
182 }
183
184 fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
185 if self.rotation == Rotation::Deg0 && area.top_left.x % 2 == 0 && area.size.width % 2 == 0 {
186 let w = area.size.width as usize / 2;
187 let x_off = area.top_left.x as usize / 2;
188 let raw_pix = color.0.into_inner() << 4 | color.0.into_inner();
189
190 for y in area.top_left.y..area.top_left.y + area.size.height as i32 {
191 let start = (y as usize) * WIDTH as usize / 2 + x_off;
192 let end = start + w;
193 self.data[start..end].fill(raw_pix);
194 }
195 Ok(())
196 } else {
197 self.fill_contiguous(area, core::iter::repeat(color))
198 }
199 }
200}
201
202pub struct FramebufferBW<const WIDTH: u16, const HEIGHT: u16, TYPE: sealed::DriverVariant>
203where
204 [(); WIDTH as usize * HEIGHT as usize / 8]:,
205{
206 data: [u8; WIDTH as usize * HEIGHT as usize / 8],
207 rotation: Rotation,
208 _type: PhantomData<TYPE>,
209}
210
211impl<const WIDTH: u16, const HEIGHT: u16, TYPE: sealed::DriverVariant> Default for FramebufferBW<WIDTH, HEIGHT, TYPE>
212where
213 [(); WIDTH as usize * HEIGHT as usize / 8]:,
214{
215 fn default() -> Self {
216 Self::new()
217 }
218}
219
220impl<const WIDTH: u16, const HEIGHT: u16, TYPE: sealed::DriverVariant> FramebufferBW<WIDTH, HEIGHT, TYPE>
221where
222 [(); WIDTH as usize * HEIGHT as usize / 8]:,
223{
224 pub fn new() -> Self {
225 Self {
226 data: [0; WIDTH as usize * HEIGHT as usize / 8],
227 rotation: Rotation::Deg0,
228 _type: PhantomData,
229 }
230 }
231
232 pub fn set_rotation(&mut self, rotation: Rotation) {
233 self.rotation = rotation;
234 }
235
236 pub fn get_rotation(&self) -> Rotation {
237 self.rotation
238 }
239
240 pub(crate) fn set_pixel(&mut self, x: u16, y: u16, color: BinaryColor) {
241 if self.rotation.is_column_row_swap() {
242 if x >= HEIGHT || y >= WIDTH {
243 return;
244 }
245 } else if y >= HEIGHT || x >= WIDTH {
246 return;
247 }
248
249 let x = x as usize;
250 let y = y as usize;
251
252 let (x, y) = match self.rotation {
253 Rotation::Deg0 => (x, y),
254 Rotation::Deg90 => (y, HEIGHT as usize - x - 1),
255 Rotation::Deg180 => (WIDTH as usize - x - 1, HEIGHT as usize - y - 1),
256 Rotation::Deg270 => (WIDTH as usize - y - 1, x),
257 };
258
259 if y >= HEIGHT as usize || x >= WIDTH as usize {
260 return;
261 }
262
263 let index = y * WIDTH as usize + x;
264
265 if color.is_on() {
266 self.data[index / 8] |= 1 << (8 - (index % 8) - 1);
267 } else {
268 self.data[index / 8] &= !(1 << (8 - (index % 8) - 1));
269 }
270 }
271}
272
273impl<const WIDTH: u16, const HEIGHT: u16> sealed::FramebufferSpiUpdate for FramebufferBW<WIDTH, HEIGHT, JDI>
274where
275 [(); WIDTH as usize * HEIGHT as usize / 8]:,
276{
277 fn update<SPI: SpiBus>(&self, spi: &mut SPI) -> Result<(), SPI::Error> {
278 for i in 0..HEIGHT {
279 let start = (i as usize) * WIDTH as usize / 8;
280 let end = start + WIDTH as usize / 8;
281 let gate_line = &self.data[start..end];
282 spi.write(&[crate::CMD_UPDATE_1BIT, i as u8 + 1])?;
284 spi.write(gate_line)?;
285 }
286 spi.write(&[0x00, 0x00])?;
287 Ok(())
288 }
289}
290
291impl<const WIDTH: u16, const HEIGHT: u16> sealed::FramebufferSpiUpdate for FramebufferBW<WIDTH, HEIGHT, Sharp>
292where
293 [(); WIDTH as usize * HEIGHT as usize / 8]:,
294{
295 fn update<SPI: SpiBus>(&self, spi: &mut SPI) -> Result<(), SPI::Error> {
296 for i in 0..HEIGHT {
297 let start = (i as usize) * WIDTH as usize / 8;
298 let end = start + WIDTH as usize / 8;
299 let gate_line = &self.data[start..end];
300 spi.write(&[crate::CMD_UPDATE_1BIT, reverse_bits(i as u8 + 1)])?;
302 spi.write(gate_line)?;
303 }
304 spi.write(&[0x00, 0x00])?;
305 Ok(())
306 }
307}
308
309impl<const WIDTH: u16, const HEIGHT: u16, TYPE: sealed::DriverVariant> FramebufferType
310 for FramebufferBW<WIDTH, HEIGHT, TYPE>
311where
312 [(); WIDTH as usize * HEIGHT as usize / 8]:,
313 FramebufferBW<WIDTH, HEIGHT, TYPE>: sealed::FramebufferSpiUpdate,
314{
315}
316
317impl<const WIDTH: u16, const HEIGHT: u16, TYPE: sealed::DriverVariant> OriginDimensions
318 for FramebufferBW<WIDTH, HEIGHT, TYPE>
319where
320 [(); WIDTH as usize * HEIGHT as usize / 8]:,
321{
322 fn size(&self) -> Size {
323 match self.rotation {
324 Rotation::Deg0 | Rotation::Deg180 => Size::new(WIDTH as u32, HEIGHT as u32),
325 Rotation::Deg90 | Rotation::Deg270 => Size::new(HEIGHT as u32, WIDTH as u32),
326 }
327 }
328}
329
330impl<const WIDTH: u16, const HEIGHT: u16, TYPE: sealed::DriverVariant> DrawTarget for FramebufferBW<WIDTH, HEIGHT, TYPE>
331where
332 [(); WIDTH as usize * HEIGHT as usize / 8]:,
333{
334 type Color = BinaryColor;
335
336 type Error = core::convert::Infallible;
337
338 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
339 where
340 I: IntoIterator<Item = Pixel<Self::Color>>,
341 {
342 for Pixel(coord, color) in pixels.into_iter() {
343 self.set_pixel(coord.x as u16, coord.y as u16, color);
344 }
345 Ok(())
346 }
347
348 fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
349 if color.is_on() {
350 self.data.fill(0xFF);
351 } else {
352 self.data.fill(0x00);
353 }
354 Ok(())
355 }
356}
357
358fn reverse_bits(mut b: u8) -> u8 {
360 let mut r = 0;
361 for _ in 0..8 {
362 r <<= 1;
363 r |= b & 1;
364 b >>= 1;
365 }
366 r
367}