use display_interface::{DisplayError, WriteOnlyDataCommand};
use hal::{delay::DelayNs, digital::OutputPin};
use crate::{
displayrotation::DisplayRotation, mode::displaymode::DisplayModeTrait,
properties::DisplayProperties,
};
const BUFFER_SIZE: usize = 160 * 160 / 8;
pub struct GraphicsMode<DI>
where
DI: WriteOnlyDataCommand,
{
properties: DisplayProperties<DI>,
buffer: [u8; BUFFER_SIZE],
}
impl<DI> DisplayModeTrait<DI> for GraphicsMode<DI>
where
DI: WriteOnlyDataCommand,
{
fn new(properties: DisplayProperties<DI>) -> Self {
GraphicsMode {
properties,
buffer: [0; BUFFER_SIZE],
}
}
fn release(self) -> DisplayProperties<DI> {
self.properties
}
}
impl<DI> GraphicsMode<DI>
where
DI: WriteOnlyDataCommand,
{
pub fn clear(&mut self) {
self.buffer = [0; BUFFER_SIZE];
}
pub fn reset<RST, DELAY, PinE>(&mut self, rst: &mut RST, delay: &mut DELAY) -> Result<(), PinE>
where
RST: OutputPin<Error = PinE>,
DELAY: DelayNs,
{
rst.set_high()?;
delay.delay_ms(1);
rst.set_low()?;
delay.delay_ms(1);
rst.set_high()?;
delay.delay_ms(1);
Ok(())
}
pub fn flush(&mut self) -> Result<(), DisplayError> {
let display_size = self.properties.get_size();
let (display_width, display_height) = display_size.dimensions();
let column_offset = display_size.column_offset();
self.properties.set_draw_area(
(column_offset, 0),
(display_width + column_offset, display_height),
)?;
let length = (display_width as usize) * (display_height as usize) / 8;
self.properties.draw(&self.buffer[..length])
}
pub fn set_pixel(&mut self, x: u32, y: u32, value: u8) {
let (display_width, _) = self.properties.get_size().dimensions();
let display_rotation = self.properties.get_rotation();
let idx = match display_rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
if x >= display_width as u32 {
return;
}
((y as usize) / 8 * display_width as usize) + (x as usize)
}
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
if y >= display_width as u32 {
return;
}
((x as usize) / 8 * display_width as usize) + (y as usize)
}
};
if idx >= self.buffer.len() {
return;
}
let (byte, bit) = match display_rotation {
DisplayRotation::Rotate0 | DisplayRotation::Rotate180 => {
let byte =
&mut self.buffer[((y as usize) / 8 * display_width as usize) + (x as usize)];
let bit = 1 << (y % 8);
(byte, bit)
}
DisplayRotation::Rotate90 | DisplayRotation::Rotate270 => {
let byte =
&mut self.buffer[((x as usize) / 8 * display_width as usize) + (y as usize)];
let bit = 1 << (x % 8);
(byte, bit)
}
};
if value == 0 {
*byte &= !bit;
} else {
*byte |= bit;
}
}
pub fn init(&mut self) -> Result<(), DisplayError> {
self.properties.init_column_mode()
}
pub fn get_dimensions(&self) -> (u8, u8) {
self.properties.get_dimensions()
}
pub fn set_rotation(&mut self, rot: DisplayRotation) -> Result<(), DisplayError> {
self.properties.set_rotation(rot)
}
pub fn display_on(&mut self, on: bool) -> Result<(), DisplayError> {
self.properties.display_on(on)
}
pub fn set_contrast(&mut self, contrast: u8) -> Result<(), DisplayError> {
self.properties.set_contrast(contrast)
}
}
#[cfg(feature = "graphics")]
use embedded_graphics_core::{
draw_target::DrawTarget,
geometry::{Dimensions, OriginDimensions, Size},
pixelcolor::BinaryColor,
Pixel,
};
#[cfg(feature = "graphics")]
impl<DI> DrawTarget for GraphicsMode<DI>
where
DI: WriteOnlyDataCommand,
{
type Color = BinaryColor;
type Error = DisplayError;
fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>
where
I: IntoIterator<Item = Pixel<Self::Color>>,
{
let bb = self.bounding_box();
pixels
.into_iter()
.filter(|Pixel(pos, _color)| bb.contains(*pos))
.for_each(|Pixel(pos, color)| {
self.set_pixel(pos.x as u32, pos.y as u32, color.is_on().into())
});
Ok(())
}
}
#[cfg(feature = "graphics")]
impl<DI> OriginDimensions for GraphicsMode<DI>
where
DI: WriteOnlyDataCommand,
{
fn size(&self) -> Size {
let (w, h) = self.get_dimensions();
Size::new(w.into(), h.into())
}
}