use embedded_graphics::{
draw_target::DrawTarget,
geometry::{Dimensions, OriginDimensions},
pixelcolor::{raw::RawU16, PixelColor, RgbColor},
prelude::*,
primitives::Rectangle,
Pixel,
};
pub trait IntoRawBytes: PixelColor + Sized {
const BYTES_PER_PIXEL: usize;
type Raw: AsRef<[u8]> + AsMut<[u8]> + Copy + Default;
fn into_raw_bytes(self) -> <Self as IntoRawBytes>::Raw;
}
impl IntoRawBytes for embedded_graphics::pixelcolor::Rgb565 {
const BYTES_PER_PIXEL: usize = 2;
type Raw = [u8; 2];
fn into_raw_bytes(self) -> <Self as IntoRawBytes>::Raw {
RawU16::from(self).into_inner().to_be_bytes()
}
}
impl IntoRawBytes for embedded_graphics::pixelcolor::Rgb666 {
const BYTES_PER_PIXEL: usize = 3;
type Raw = [u8; 3];
fn into_raw_bytes(self) -> <Self as IntoRawBytes>::Raw {
[self.r() * 4, self.g() * 4, self.b() * 4]
}
}
impl IntoRawBytes for embedded_graphics::pixelcolor::Rgb888 {
const BYTES_PER_PIXEL: usize = 3;
type Raw = [u8; 3];
fn into_raw_bytes(self) -> <Self as IntoRawBytes>::Raw {
[self.r(), self.g(), self.b()]
}
}
pub trait RawBufferBackendMut {
fn as_mut_u8_slice(&mut self) -> &mut [u8];
fn as_u8_slice(&self) -> &[u8];
fn u8_len(&self) -> usize;
}
impl RawBufferBackendMut for &mut [u8] {
fn as_mut_u8_slice(&mut self) -> &mut [u8] {
self
}
fn as_u8_slice(&self) -> &[u8] {
self
}
fn u8_len(&self) -> usize {
self.len()
}
}
pub struct RawFrameBuf<C, BUF>
where
C: IntoRawBytes,
BUF: RawBufferBackendMut,
{
buffer: BUF,
width: usize,
height: usize,
_phantom_color: core::marker::PhantomData<C>,
}
impl<C, BUF> RawFrameBuf<C, BUF>
where
C: IntoRawBytes,
BUF: RawBufferBackendMut,
{
pub fn new(buffer: BUF, width: usize, height: usize) -> Self {
let expected_len = width * height * C::BYTES_PER_PIXEL;
assert!(
buffer.u8_len() >= expected_len,
"RawFrameBuf underlying buffer is too small. Expected at least {}, got {}.",
expected_len,
buffer.u8_len()
);
Self {
buffer,
width,
height,
_phantom_color: core::marker::PhantomData,
}
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn as_bytes(&self) -> &[u8] {
let expected_len = self.width * self.height * C::BYTES_PER_PIXEL;
&self.buffer.as_u8_slice()[0..expected_len]
}
pub fn as_mut_bytes(&mut self) -> &mut [u8] {
let expected_len = self.width * self.height * C::BYTES_PER_PIXEL;
&mut self.buffer.as_mut_u8_slice()[0..expected_len]
}
}
impl<C, BUF> OriginDimensions for RawFrameBuf<C, BUF>
where
C: IntoRawBytes,
BUF: RawBufferBackendMut,
{
fn size(&self) -> Size {
Size::new(self.width as u32, self.height as u32)
}
}
impl<C, BUF> DrawTarget for RawFrameBuf<C, BUF>
where
C: IntoRawBytes,
BUF: RawBufferBackendMut,
{
type Color = C;
type Error = core::convert::Infallible;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
let bounding_box = self.bounding_box();
let current_width = self.width;
let buffer_slice = self.buffer.as_mut_u8_slice();
let active_buffer_len = self.width * self.height * C::BYTES_PER_PIXEL;
for Pixel(coord, color) in pixels.into_iter() {
if bounding_box.contains(coord) {
let byte_index =
(coord.y as usize * current_width + coord.x as usize) * C::BYTES_PER_PIXEL;
let color_bytes = color.into_raw_bytes();
if byte_index + C::BYTES_PER_PIXEL <= active_buffer_len {
buffer_slice[byte_index..byte_index + C::BYTES_PER_PIXEL]
.copy_from_slice(color_bytes.as_ref());
}
}
}
Ok(())
}
fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {
let color_bytes_array = color.into_raw_bytes();
let color_bytes = color_bytes_array.as_ref();
let buffer_slice = self.buffer.as_mut_u8_slice();
let active_buffer_len = self.width * self.height * C::BYTES_PER_PIXEL;
let active_slice = &mut buffer_slice[0..active_buffer_len];
let all_bytes_same = if let Some(first) = color_bytes.first() {
color_bytes.iter().all(|&b| b == *first)
} else {
true
};
if all_bytes_same && !color_bytes.is_empty() {
active_slice.fill(color_bytes[0]);
} else if C::BYTES_PER_PIXEL > 0 {
for chunk in active_slice.chunks_exact_mut(C::BYTES_PER_PIXEL) {
chunk.copy_from_slice(color_bytes);
}
}
Ok(())
}
fn fill_solid(&mut self, area: &Rectangle, color: Self::Color) -> Result<(), Self::Error> {
let drawable_area = area.intersection(&self.bounding_box());
if drawable_area.is_zero_sized() {
return Ok(());
}
let color_bytes_array = color.into_raw_bytes();
let color_bytes = color_bytes_array.as_ref();
let current_width = self.width;
let buffer_slice = self.buffer.as_mut_u8_slice();
for p in drawable_area.points() {
let byte_index = (p.y as usize * current_width + p.x as usize) * C::BYTES_PER_PIXEL;
if byte_index + C::BYTES_PER_PIXEL <= buffer_slice.len() {
buffer_slice[byte_index..byte_index + C::BYTES_PER_PIXEL]
.copy_from_slice(color_bytes);
}
}
Ok(())
}
}