use embedded_graphics::pixelcolor::{raw::RawU16, IntoStorage, PixelColor};
pub trait FrameBufferBackend {
type Color: PixelColor;
fn set(&mut self, index: usize, color: Self::Color);
fn get(&self, index: usize) -> Self::Color;
fn nr_elements(&self) -> usize;
}
impl<'a, C: PixelColor, const N: usize> FrameBufferBackend for &'a mut [C; N] {
type Color = C;
fn set(&mut self, index: usize, color: C) {
self[index] = color
}
fn get(&self, index: usize) -> C {
self[index]
}
fn nr_elements(&self) -> usize {
self.len()
}
}
impl<C: PixelColor, const N: usize> FrameBufferBackend for [C; N] {
type Color = C;
fn set(&mut self, index: usize, color: C) {
self[index] = color
}
fn get(&self, index: usize) -> C {
self[index]
}
fn nr_elements(&self) -> usize {
self.len()
}
}
pub unsafe trait DMACapableFrameBufferBackend: FrameBufferBackend {
fn data_ptr(&self) -> *const Self::Color;
}
unsafe impl<'a, C: PixelColor, const N: usize> DMACapableFrameBufferBackend for &'a mut [C; N] {
fn data_ptr(&self) -> *const C {
self.as_ptr()
}
}
#[derive(PartialEq, Eq)]
pub enum EndianCorrection {
ToLittleEndian,
ToBigEndian,
}
pub struct EndianCorrectedBuffer<'a, C: PixelColor> {
data: &'a mut [C],
endian: EndianCorrection,
}
impl<'a, C: PixelColor> EndianCorrectedBuffer<'a, C> {
pub fn new(data: &'a mut [C], endian: EndianCorrection) -> Self {
Self { data, endian }
}
}
impl<'a, C> FrameBufferBackend for EndianCorrectedBuffer<'a, C>
where
C: IntoStorage<Storage = u16> + PixelColor,
RawU16: From<C>,
C: core::convert::From<RawU16>,
{
type Color = C;
fn set(&mut self, index: usize, color: C) {
self.data[index] = match self.endian {
EndianCorrection::ToBigEndian => RawU16::new(color.into_storage().to_be()).into(),
EndianCorrection::ToLittleEndian => RawU16::new(color.into_storage().to_le()).into(),
}
}
fn get(&self, index: usize) -> C {
match self.endian {
EndianCorrection::ToBigEndian => {
C::from(RawU16::new(u16::from_be(self.data[index].into_storage())))
}
EndianCorrection::ToLittleEndian => {
C::from(RawU16::new(u16::from_le(self.data[index].into_storage())))
}
}
}
fn nr_elements(&self) -> usize {
self.data.len()
}
}
unsafe impl<'a, C> DMACapableFrameBufferBackend for EndianCorrectedBuffer<'a, C>
where
C: IntoStorage<Storage = u16> + PixelColor,
RawU16: From<C>,
C: core::convert::From<RawU16>,
{
fn data_ptr(&self) -> *const C {
self.data.as_ptr()
}
}
#[cfg(test)]
mod tests {
extern crate std;
use super::*;
use crate::FrameBuf;
use embedded_graphics::pixelcolor::{raw::RawU16, Rgb565};
use embedded_graphics::prelude::{Point, RawData, RgbColor};
#[test]
fn test_no_endian_correction() {
let mut data = [Rgb565::BLUE; 2 * 3];
let mut fbuf = FrameBuf::new(&mut data, 2, 3);
fbuf.set_color_at(Point::new(1, 0), Rgb565::RED);
fbuf.set_color_at(Point::new(2, 0), Rgb565::BLUE);
assert_eq!(RawU16::from(fbuf.data[0]).into_inner(), 0b00000000_00011111);
assert_eq!(RawU16::from(fbuf.data[1]).into_inner(), 0b11111000_00000000);
assert_eq!(RawU16::from(fbuf.data[2]).into_inner(), 0b00000000_00011111);
}
#[test]
fn test_big_endian_correction() {
let mut data = [Rgb565::BLUE; 2 * 3];
let mut fbuf = FrameBuf::new(
EndianCorrectedBuffer::new(&mut data, EndianCorrection::ToBigEndian),
2,
3,
);
fbuf.set_color_at(Point::new(1, 0), Rgb565::RED);
fbuf.set_color_at(Point::new(2, 0), Rgb565::BLUE);
assert_eq!(fbuf.get_color_at(Point::new(1, 0)), Rgb565::RED);
assert_eq!(fbuf.get_color_at(Point::new(2, 0)), Rgb565::BLUE);
assert_eq!(
RawU16::from(fbuf.data.data[1]).into_inner(),
0b00000000_11111000
);
assert_eq!(
RawU16::from(fbuf.data.data[2]).into_inner(),
0b00011111_00000000
);
}
#[test]
fn test_little_endian_correction() {
let mut data = [Rgb565::BLUE; 2 * 3];
let mut fbuf = FrameBuf::new(
EndianCorrectedBuffer::new(&mut data, EndianCorrection::ToLittleEndian),
2,
3,
);
fbuf.set_color_at(Point::new(1, 0), Rgb565::RED);
fbuf.set_color_at(Point::new(2, 0), Rgb565::BLUE);
assert_eq!(fbuf.get_color_at(Point::new(1, 0)), Rgb565::RED);
assert_eq!(fbuf.get_color_at(Point::new(2, 0)), Rgb565::BLUE);
assert_eq!(
RawU16::from(fbuf.data.data[1]).into_inner(),
0b11111000_00000000
);
assert_eq!(
RawU16::from(fbuf.data.data[2]).into_inner(),
0b00000000_00011111
);
}
}