use embedded_graphics_core::{
draw_target::DrawTarget,
geometry::{OriginDimensions, Point},
pixelcolor::{Bgr888, IntoStorage},
};
pub struct DmaReadyFramebuffer<const W: usize, const H: usize> {
pub framebuffer: *mut [[u32; W]; H], big_endian: bool,
}
impl<const W: usize, const H: usize> DmaReadyFramebuffer<W, H> {
pub fn new(
raw_framebuffer: *mut ::core::ffi::c_void,
big_endian: bool,
) -> DmaReadyFramebuffer<W, H> {
if raw_framebuffer.is_null() {
panic!("Failed to allocate framebuffer");
}
DmaReadyFramebuffer {
framebuffer: raw_framebuffer as *mut [[u32; W]; H],
big_endian,
}
}
pub fn set_pixel(&mut self, point: Point, color: Bgr888) {
if point.x >= 0 && point.x < W as i32 && point.y >= 0 && point.y < H as i32 {
unsafe {
let framebuffer = &mut *self.framebuffer;
if self.big_endian {
framebuffer[point.y as usize][point.x as usize] = color.into_storage().to_be();
} else {
framebuffer[point.y as usize][point.x as usize] = color.into_storage();
}
}
}
}
pub fn as_slice(&self) -> &[u32] {
unsafe { core::slice::from_raw_parts(self.framebuffer as *const u32, W * H) }
}
pub fn as_mut_slice(&mut self) -> &mut [u32] {
unsafe { core::slice::from_raw_parts_mut(self.framebuffer as *mut u32, W * H) }
}
pub fn as_mut_ptr(&mut self) -> *mut [u32] {
self.as_slice() as *const [u32] as *mut [u32]
}
}
impl<const W: usize, const H: usize> DrawTarget for DmaReadyFramebuffer<W, H> {
type Color = Bgr888;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = embedded_graphics_core::prelude::Pixel<Self::Color>>,
{
for pixel in pixels {
let embedded_graphics_core::prelude::Pixel(point, color) = pixel;
self.set_pixel(point, color);
}
Ok(())
}
fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
if self.big_endian {
self.as_mut_slice().fill(color.into_storage().to_be());
} else {
self.as_mut_slice().fill(color.into_storage());
}
Ok(())
}
}
impl<const W: usize, const H: usize> OriginDimensions for DmaReadyFramebuffer<W, H> {
fn size(&self) -> embedded_graphics_core::geometry::Size {
embedded_graphics_core::geometry::Size::new(W as u32, H as u32)
}
}
#[cfg(test)]
mod tests {
use super::*;
use embedded_graphics_core::prelude::*;
use std::mem::MaybeUninit;
fn create_test_buffer<const W: usize, const H: usize>() -> Box<[[u32; W]; H]> {
let buffer = Box::new(unsafe { MaybeUninit::<[[u32; W]; H]>::zeroed().assume_init() });
buffer
}
#[test]
fn test_framebuffer_creation() {
const WIDTH: usize = 64;
const HEIGHT: usize = 32;
let buffer = create_test_buffer::<WIDTH, HEIGHT>();
let raw_ptr = buffer.as_ptr() as *mut ::core::ffi::c_void;
let fb_le = DmaReadyFramebuffer::<WIDTH, HEIGHT>::new(raw_ptr, false);
assert!(!fb_le.big_endian);
let fb_be = DmaReadyFramebuffer::<WIDTH, HEIGHT>::new(raw_ptr, true);
assert!(fb_be.big_endian);
}
#[test]
#[should_panic(expected = "Failed to allocate framebuffer")]
fn test_framebuffer_creation_null_pointer() {
let _fb = DmaReadyFramebuffer::<10, 10>::new(std::ptr::null_mut(), false);
}
#[test]
fn test_set_pixel() {
const WIDTH: usize = 32;
const HEIGHT: usize = 32;
let buffer = create_test_buffer::<WIDTH, HEIGHT>();
let raw_ptr = buffer.as_ptr() as *mut ::core::ffi::c_void;
let mut fb = DmaReadyFramebuffer::<WIDTH, HEIGHT>::new(raw_ptr, false);
let color = Bgr888::new(64, 128, 255); let point = Point::new(5, 10);
fb.set_pixel(point, color);
let value = fb.as_slice()[10 * WIDTH + 5];
let expected = color.into_storage();
assert_eq!(value, expected);
}
#[test]
fn test_set_pixel_big_endian() {
const WIDTH: usize = 32;
const HEIGHT: usize = 32;
let buffer = create_test_buffer::<WIDTH, HEIGHT>();
let raw_ptr = buffer.as_ptr() as *mut ::core::ffi::c_void;
let mut fb = DmaReadyFramebuffer::<WIDTH, HEIGHT>::new(raw_ptr, true);
let color = Bgr888::new(64, 128, 255); let point = Point::new(5, 10);
fb.set_pixel(point, color);
let value = fb.as_slice()[10 * WIDTH + 5];
let expected = color.into_storage().to_be();
assert_eq!(value, expected);
}
#[test]
fn test_set_pixel_out_of_bounds() {
const WIDTH: usize = 32;
const HEIGHT: usize = 32;
let buffer = create_test_buffer::<WIDTH, HEIGHT>();
let raw_ptr = buffer.as_ptr() as *mut ::core::ffi::c_void;
let mut fb = DmaReadyFramebuffer::<WIDTH, HEIGHT>::new(raw_ptr, false);
let color = Bgr888::new(255, 0, 0);
fb.set_pixel(Point::new(-1, 10), color);
fb.set_pixel(Point::new(WIDTH as i32, 10), color);
fb.set_pixel(Point::new(5, -1), color);
fb.set_pixel(Point::new(5, HEIGHT as i32), color);
}
#[test]
fn test_as_slice() {
const WIDTH: usize = 4;
const HEIGHT: usize = 2;
let buffer = create_test_buffer::<WIDTH, HEIGHT>();
let raw_ptr = buffer.as_ptr() as *mut ::core::ffi::c_void;
let mut fb = DmaReadyFramebuffer::<WIDTH, HEIGHT>::new(raw_ptr, false);
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.as_slice();
assert_eq!(slice.len(), WIDTH * HEIGHT);
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()); }
#[test]
fn test_as_mut_slice() {
const WIDTH: usize = 4;
const HEIGHT: usize = 2;
let buffer = create_test_buffer::<WIDTH, HEIGHT>();
let raw_ptr = buffer.as_ptr() as *mut ::core::ffi::c_void;
let mut fb = DmaReadyFramebuffer::<WIDTH, HEIGHT>::new(raw_ptr, false);
let slice = fb.as_mut_slice();
assert_eq!(slice.len(), WIDTH * HEIGHT);
slice[0] = 0x00FF0000; slice[1] = 0x0000FF00;
let check_slice = fb.as_slice();
assert_eq!(check_slice[0], 0x00FF0000);
assert_eq!(check_slice[1], 0x0000FF00);
}
#[test]
fn test_draw_target_draw_iter() {
const WIDTH: usize = 32;
const HEIGHT: usize = 32;
let buffer = create_test_buffer::<WIDTH, HEIGHT>();
let raw_ptr = buffer.as_ptr() as *mut ::core::ffi::c_void;
let mut fb = DmaReadyFramebuffer::<WIDTH, HEIGHT>::new(raw_ptr, false);
let pixels = [
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)), ];
fb.draw_iter(pixels).unwrap();
let slice = fb.as_slice();
assert_eq!(slice[1 * WIDTH + 1], Bgr888::new(0, 0, 255).into_storage());
assert_eq!(slice[1 * WIDTH + 2], Bgr888::new(0, 255, 0).into_storage());
assert_eq!(slice[1 * WIDTH + 3], Bgr888::new(255, 0, 0).into_storage());
}
#[test]
fn test_draw_target_clear() {
const WIDTH: usize = 16;
const HEIGHT: usize = 16;
let buffer = create_test_buffer::<WIDTH, HEIGHT>();
let raw_ptr = buffer.as_ptr() as *mut ::core::ffi::c_void;
let mut fb = DmaReadyFramebuffer::<WIDTH, HEIGHT>::new(raw_ptr, false);
fb.set_pixel(Point::new(0, 0), Bgr888::new(255, 0, 0));
fb.set_pixel(Point::new(1, 1), Bgr888::new(0, 255, 0));
fb.clear(Bgr888::new(0, 0, 128)).unwrap();
let slice = fb.as_slice();
let blue_value = Bgr888::new(0, 0, 128).into_storage();
for &pixel in slice {
assert_eq!(pixel, blue_value);
}
}
#[test]
fn test_draw_target_clear_big_endian() {
const WIDTH: usize = 16;
const HEIGHT: usize = 16;
let buffer = create_test_buffer::<WIDTH, HEIGHT>();
let raw_ptr = buffer.as_ptr() as *mut ::core::ffi::c_void;
let mut fb = DmaReadyFramebuffer::<WIDTH, HEIGHT>::new(raw_ptr, true);
let color = Bgr888::new(10, 20, 30);
fb.clear(color).unwrap();
let slice = fb.as_slice();
let expected_value = color.into_storage().to_be();
for &pixel in slice {
assert_eq!(pixel, expected_value);
}
}
#[test]
fn test_origin_dimensions() {
const WIDTH: usize = 64;
const HEIGHT: usize = 32;
let buffer = create_test_buffer::<WIDTH, HEIGHT>();
let raw_ptr = buffer.as_ptr() as *mut ::core::ffi::c_void;
let fb = DmaReadyFramebuffer::<WIDTH, HEIGHT>::new(raw_ptr, false);
let size = fb.size();
assert_eq!(size.width, WIDTH as u32);
assert_eq!(size.height, HEIGHT as u32);
}
}