1use embedded_graphics_core::pixelcolor::raw::RawU24;
2use embedded_graphics_core::{
3 draw_target::DrawTarget,
4 geometry::{OriginDimensions, Point},
5 pixelcolor::{Bgr888, IntoStorage},
6};
7
8pub struct DmaReadyFramebuffer {
9 pub width: usize,
10 pub height: usize,
11 pub framebuffer: Box<[u32]>,
12 big_endian: bool,
13}
14
15impl DmaReadyFramebuffer {
16 pub fn new(width: usize, height: usize, big_endian: bool) -> DmaReadyFramebuffer {
17 DmaReadyFramebuffer {
18 framebuffer: vec![0u32; width * height].into_boxed_slice(),
19 width,
20 height,
21 big_endian,
22 }
23 }
24
25 pub fn set_pixel(&mut self, point: Point, color: Bgr888) {
26 if point.x >= 0
27 && point.x < self.width as i32
28 && point.y >= 0
29 && point.y < self.height as i32
30 {
31 let framebuffer = &mut *self.framebuffer;
32
33 if self.big_endian {
34 framebuffer[point.y as usize * self.width + point.x as usize] =
35 color.into_storage().to_be();
36 } else {
37 framebuffer[point.y as usize * self.width + point.x as usize] =
38 color.into_storage();
39 }
40 }
41 }
42
43 pub fn get_pixel(&mut self, point: Point) -> Option<Bgr888> {
44 if point.x >= 0
45 && point.x < self.width as i32
46 && point.y >= 0
47 && point.y < self.height as i32
48 {
49 if self.big_endian {
50 Some(Bgr888::from(RawU24::new(u32::from_be(
51 self.framebuffer[point.y as usize * self.width + point.x as usize],
52 ))))
53 } else {
54 Some(Bgr888::from(RawU24::new(
55 self.framebuffer[point.y as usize * self.width + point.x as usize],
56 )))
57 }
58 } else {
59 None
60 }
61 }
62}
63
64impl DrawTarget for DmaReadyFramebuffer {
65 type Color = Bgr888;
66 type Error = core::convert::Infallible;
67
68 fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
69 where
70 I: IntoIterator<Item = embedded_graphics_core::prelude::Pixel<Self::Color>>,
71 {
72 for pixel in pixels {
73 let embedded_graphics_core::prelude::Pixel(point, color) = pixel;
74
75 self.set_pixel(point, color);
76 }
77 Ok(())
78 }
79
80 fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
81 if self.big_endian {
82 self.framebuffer.fill(color.into_storage().to_be());
83 } else {
84 self.framebuffer.fill(color.into_storage());
85 }
86
87 Ok(())
88 }
89}
90
91impl OriginDimensions for DmaReadyFramebuffer {
92 fn size(&self) -> embedded_graphics_core::geometry::Size {
93 embedded_graphics_core::geometry::Size::new(self.width as u32, self.height as u32)
94 }
95}
96
97unsafe impl Send for DmaReadyFramebuffer {}
102
103#[cfg(test)]
104mod tests {
105 use super::*;
106 use embedded_graphics_core::prelude::*;
107
108 #[test]
109 fn test_framebuffer_creation() {
110 const WIDTH: usize = 64;
111 const HEIGHT: usize = 32;
112
113 let fb_le = DmaReadyFramebuffer::new(WIDTH, HEIGHT, false);
115 assert!(!fb_le.big_endian);
116
117 let fb_be = DmaReadyFramebuffer::new(WIDTH, HEIGHT, true);
119 assert!(fb_be.big_endian);
120 }
121
122 #[test]
123 fn test_set_pixel() {
124 const WIDTH: usize = 32;
125 const HEIGHT: usize = 32;
126
127 let mut fb = DmaReadyFramebuffer::new(WIDTH, HEIGHT, false);
129
130 let color = Bgr888::new(64, 128, 255); let point = Point::new(5, 10);
134 fb.set_pixel(point, color);
135
136 let value = fb.framebuffer[10 * WIDTH + 5]; let expected = color.into_storage();
141 assert_eq!(value, expected);
142 }
143
144 #[test]
145 fn test_set_pixel_big_endian() {
146 const WIDTH: usize = 32;
147 const HEIGHT: usize = 32;
148
149 let mut fb = DmaReadyFramebuffer::new(WIDTH, HEIGHT, true);
151
152 let color = Bgr888::new(64, 128, 255); let point = Point::new(5, 10);
156 fb.set_pixel(point, color);
157
158 let value = fb.framebuffer[10 * WIDTH + 5]; let expected = color.into_storage().to_be();
163 assert_eq!(value, expected);
164 }
165
166 #[test]
167 fn test_get_pixel_roundtrip() {
168 const WIDTH: usize = 32;
169 const HEIGHT: usize = 32;
170
171 let mut fb_le = DmaReadyFramebuffer::new(WIDTH, HEIGHT, false);
173 let color = Bgr888::new(64, 128, 255);
174 let point = Point::new(5, 10);
175 fb_le.set_pixel(point, color);
176 assert_eq!(fb_le.get_pixel(point), Some(color));
177
178 let mut fb_be = DmaReadyFramebuffer::new(WIDTH, HEIGHT, true);
180 fb_be.set_pixel(point, color);
181 assert_eq!(fb_be.get_pixel(point), Some(color));
182 }
183
184 #[test]
185 fn test_set_pixel_out_of_bounds() {
186 const WIDTH: usize = 32;
187 const HEIGHT: usize = 32;
188
189 let mut fb = DmaReadyFramebuffer::new(WIDTH, HEIGHT, false);
191
192 let color = Bgr888::new(255, 0, 0);
194
195 fb.set_pixel(Point::new(-1, 10), color);
197 fb.set_pixel(Point::new(WIDTH as i32, 10), color);
198
199 fb.set_pixel(Point::new(5, -1), color);
201 fb.set_pixel(Point::new(5, HEIGHT as i32), color);
202
203 }
205
206 #[test]
207 fn test_as_slice() {
208 const WIDTH: usize = 4;
209 const HEIGHT: usize = 2;
210
211 let mut fb = DmaReadyFramebuffer::new(WIDTH, HEIGHT, false);
213
214 fb.set_pixel(Point::new(0, 0), Bgr888::new(0, 0, 1)); fb.set_pixel(Point::new(1, 0), Bgr888::new(0, 0, 2)); fb.set_pixel(Point::new(0, 1), Bgr888::new(0, 0, 3)); let slice = fb.framebuffer;
221 assert_eq!(slice.len(), WIDTH * HEIGHT);
222
223 assert_eq!(slice[0], Bgr888::new(0, 0, 1).into_storage()); assert_eq!(slice[1], Bgr888::new(0, 0, 2).into_storage()); assert_eq!(slice[WIDTH], Bgr888::new(0, 0, 3).into_storage()); }
228
229 #[test]
230 fn test_as_mut_slice() {
231 const WIDTH: usize = 4;
232 const HEIGHT: usize = 2;
233
234 let mut fb = DmaReadyFramebuffer::new(WIDTH, HEIGHT, false);
236
237 let slice = &mut fb.framebuffer;
239 assert_eq!(slice.len(), WIDTH * HEIGHT);
240
241 slice[0] = 0x00FF0000; slice[1] = 0x0000FF00; let check_slice = fb.framebuffer;
247 assert_eq!(check_slice[0], 0x00FF0000);
248 assert_eq!(check_slice[1], 0x0000FF00);
249 }
250
251 #[test]
252 fn test_draw_target_draw_iter() {
253 const WIDTH: usize = 32;
254 const HEIGHT: usize = 32;
255
256 let mut fb = DmaReadyFramebuffer::new(WIDTH, HEIGHT, false);
258
259 let pixels = [
261 Pixel(Point::new(1, 1), Bgr888::new(0, 0, 255)), Pixel(Point::new(2, 1), Bgr888::new(0, 255, 0)), Pixel(Point::new(3, 1), Bgr888::new(255, 0, 0)), ];
265
266 fb.draw_iter(pixels).unwrap();
268
269 let slice = fb.framebuffer;
271
272 assert_eq!(slice[1 * WIDTH + 1], Bgr888::new(0, 0, 255).into_storage());
274
275 assert_eq!(slice[1 * WIDTH + 2], Bgr888::new(0, 255, 0).into_storage());
277
278 assert_eq!(slice[1 * WIDTH + 3], Bgr888::new(255, 0, 0).into_storage());
280 }
281
282 #[test]
283 fn test_draw_target_clear() {
284 const WIDTH: usize = 16;
285 const HEIGHT: usize = 16;
286
287 let mut fb = DmaReadyFramebuffer::new(WIDTH, HEIGHT, false);
289
290 fb.set_pixel(Point::new(0, 0), Bgr888::new(255, 0, 0));
292 fb.set_pixel(Point::new(1, 1), Bgr888::new(0, 255, 0));
293
294 fb.clear(Bgr888::new(0, 0, 128)).unwrap();
296
297 let slice = fb.framebuffer;
299 let blue_value = Bgr888::new(0, 0, 128).into_storage();
300
301 for pixel in slice {
302 assert_eq!(pixel, blue_value);
303 }
304 }
305
306 #[test]
307 fn test_draw_target_clear_big_endian() {
308 const WIDTH: usize = 16;
309 const HEIGHT: usize = 16;
310
311 let mut fb = DmaReadyFramebuffer::new(WIDTH, HEIGHT, true);
313
314 let color = Bgr888::new(10, 20, 30);
316 fb.clear(color).unwrap();
317
318 let slice = fb.framebuffer;
320 let expected_value = color.into_storage().to_be();
321
322 for pixel in slice {
323 assert_eq!(pixel, expected_value);
324 }
325 }
326
327 #[test]
328 fn test_origin_dimensions() {
329 const WIDTH: usize = 64;
330 const HEIGHT: usize = 32;
331
332 let fb = DmaReadyFramebuffer::new(WIDTH, HEIGHT, false);
334
335 let size = fb.framebuffer.len();
337 assert_eq!(size, WIDTH * HEIGHT);
338 }
339}