1mod pixel_map;
4mod rotation;
5mod window;
6
7use core::{convert::Infallible, marker::PhantomData};
8
9use embedded_graphics_core::{
10 Pixel,
11 draw_target::DrawTarget,
12 geometry::{OriginDimensions, Point, Size},
13 pixelcolor::BinaryColor,
14 primitives::Rectangle,
15};
16
17pub use pixel_map::{Landscape, PixelMap};
18pub use rotation::Rotation;
19pub use window::Window;
20
21pub struct FrameBuffer<M: PixelMap, const N: usize> {
29 buf: [u8; N],
30 rotation: Rotation,
31 map: PhantomData<M>,
32}
33
34impl<M: PixelMap, const N: usize> FrameBuffer<M, N> {
35 pub const fn new() -> Self {
38 const { assert!(N == M::BUFFER_LEN) };
39 Self {
40 buf: [0x00; N],
41 rotation: Rotation::Deg0,
42 map: PhantomData,
43 }
44 }
45
46 #[must_use]
48 pub const fn with_rotation(mut self, rotation: Rotation) -> Self {
49 self.rotation = rotation;
50 self
51 }
52
53 pub const fn rotation(&self) -> Rotation {
55 self.rotation
56 }
57
58 pub fn set_rotation(&mut self, rotation: Rotation) {
61 self.rotation = rotation;
62 }
63
64 pub const fn logical_size(&self) -> (u32, u32) {
66 self.rotation.size(M::WIDTH, M::HEIGHT)
67 }
68
69 fn locate(&self, x: u32, y: u32) -> Option<(usize, u8)> {
72 let (xn, yn) = self.rotation.to_native(M::WIDTH, M::HEIGHT, x, y)?;
73 M::locate(xn, yn)
74 }
75
76 pub fn set_pixel(&mut self, x: u32, y: u32, on: bool) {
79 let Some((i, bit)) = self.locate(x, y) else {
80 return;
81 };
82
83 if on {
84 self.buf[i] |= 1 << bit;
85 } else {
86 self.buf[i] &= !(1 << bit);
87 }
88 }
89
90 pub fn get_pixel(&self, x: u32, y: u32) -> Option<bool> {
92 let (i, bit) = self.locate(x, y)?;
93 Some((self.buf[i] >> bit) & 0b1 == 1)
94 }
95
96 pub fn fill(&mut self, on: bool) {
99 self.buf.fill(if on { 0xFF } else { 0x00 });
100 }
101
102 pub fn as_bytes(&self) -> &[u8] {
104 &self.buf
105 }
106
107 pub fn window(&self, area: Rectangle) -> Option<Window> {
111 let (w, h) = self.logical_size();
112 let area = area.intersection(&Rectangle::new(Point::zero(), Size::new(w, h)));
113 let bottom_right = area.bottom_right()?; let to_native = |p: Point| {
118 self.rotation
119 .to_native(M::WIDTH, M::HEIGHT, p.x as u32, p.y as u32)
120 .map(|(x, y)| Point::new(x as i32, y as i32))
121 };
122 let (a, b) = (to_native(area.top_left)?, to_native(bottom_right)?);
123 Window::covering::<M>(Rectangle::with_corners(
124 Point::new(a.x.min(b.x), a.y.min(b.y)),
125 Point::new(a.x.max(b.x), a.y.max(b.y)),
126 ))
127 }
128
129 pub fn copy_window<'a>(&self, window: &Window, out: &'a mut [u8]) -> &'a [u8] {
137 window.copy_from(&self.buf, M::HEIGHT, out)
138 }
139
140 pub fn as_bytes_mut(&mut self) -> &mut [u8] {
143 &mut self.buf
144 }
145
146 pub fn fill_with(&mut self, mut f: impl FnMut() -> u8) {
148 self.buf.iter_mut().for_each(|b| *b = f());
149 }
150
151 pub fn read_row(&self, y: u32, out: &mut [bool]) -> bool {
158 let (w, h) = self.logical_size();
159 assert!(y < h && out.len() == w as usize);
160
161 match self.rotation {
162 Rotation::Deg0 => M::read_row(&self.buf, y, out),
163 Rotation::Deg180 => {
164 let any = M::read_row(&self.buf, M::HEIGHT - 1 - y, out);
165 out.reverse();
166 any
167 }
168 Rotation::Deg90 => M::read_col(&self.buf, M::WIDTH - 1 - y, out),
169 Rotation::Deg270 => {
170 let any = M::read_col(&self.buf, y, out);
171 out.reverse();
172 any
173 }
174 }
175 }
176
177 pub fn write_row(&mut self, y: u32, row: &[bool]) {
181 let (w, h) = self.logical_size();
182 assert!(y < h && row.len() == w as usize);
183
184 match self.rotation {
185 Rotation::Deg0 => M::write_row(&mut self.buf, y, row),
186 Rotation::Deg180 => M::write_row_rev(&mut self.buf, M::HEIGHT - 1 - y, row),
187 Rotation::Deg90 => M::write_col(&mut self.buf, M::WIDTH - 1 - y, row),
188 Rotation::Deg270 => M::write_col_rev(&mut self.buf, y, row),
189 }
190 }
191}
192
193impl<M: PixelMap, const N: usize> core::fmt::Debug for FrameBuffer<M, N> {
195 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
196 f.debug_struct("FrameBuffer")
197 .field("width", &M::WIDTH)
198 .field("height", &M::HEIGHT)
199 .field("rotation", &self.rotation)
200 .finish_non_exhaustive()
201 }
202}
203
204impl<M: PixelMap, const N: usize> Default for FrameBuffer<M, N> {
205 fn default() -> Self {
206 Self::new()
207 }
208}
209
210impl<M: PixelMap, const N: usize> OriginDimensions for FrameBuffer<M, N> {
211 fn size(&self) -> Size {
212 let (w, h) = self.logical_size();
213 Size::new(w, h)
214 }
215}
216
217impl<M: PixelMap, const N: usize> DrawTarget for FrameBuffer<M, N> {
218 type Color = BinaryColor;
219
220 type Error = Infallible;
221
222 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
223 where
224 I: IntoIterator<Item = Pixel<Self::Color>>,
225 {
226 for Pixel(Point { x, y }, color) in pixels {
227 if let (Ok(x), Ok(y)) = (u32::try_from(x), u32::try_from(y)) {
228 self.set_pixel(x, y, color.is_on());
229 }
230 }
231 Ok(())
232 }
233
234 fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
236 self.fill(color.is_on());
237 Ok(())
238 }
239}