298 lines
8.4 KiB
Rust
298 lines
8.4 KiB
Rust
use core::{
|
|
hint::black_box,
|
|
slice::{Chunks, Iter},
|
|
};
|
|
|
|
use atmega_hal::{
|
|
Atmega, Usart,
|
|
pac::USART0,
|
|
port::{PB3, PB5, PD0, PD1, Pin, PinOps, mode},
|
|
};
|
|
use embedded_hal::delay::DelayNs;
|
|
use ufmt::derive::uDebug;
|
|
|
|
use crate::{
|
|
CoreClock,
|
|
display::{Color, Display, Position, Rgb565, Writeable},
|
|
};
|
|
|
|
#[repr(u8)]
|
|
enum NonBssByte {
|
|
V = 1,
|
|
}
|
|
|
|
pub struct NonBss<Buf> {
|
|
_hack: NonBssByte,
|
|
pub buf: Buf,
|
|
}
|
|
|
|
impl<const N: usize> NonBss<[u8; N]> {
|
|
pub const fn new(buf: [u8; N]) -> Self {
|
|
Self {
|
|
_hack: NonBssByte::V,
|
|
buf,
|
|
}
|
|
}
|
|
}
|
|
|
|
macro_rules! non_bss_statics {(
|
|
$(
|
|
$(#$attr:tt)*
|
|
$pub:vis
|
|
static ref $NAME:ident : [$u8:ty] = *include_bytes!($($args:tt)*);
|
|
)*
|
|
) => (
|
|
$(
|
|
|
|
$(#$attr)*
|
|
$pub
|
|
const $NAME: &[u8] = {
|
|
static $NAME: NonBss<[$u8; ::core::include_bytes!($($args)*).len()]> =
|
|
NonBss::new(*::core::include_bytes!($($args)*));
|
|
&$NAME.buf
|
|
};
|
|
)*
|
|
)}
|
|
|
|
// https://qoiformat.org/qoi-specification.pdf
|
|
// static LARGE: [u8; 1966] = *include_bytes!("../images/large.qoi");
|
|
// static SHEEP: [u8; 852] = *include_bytes!("../images/sheep.qoi");
|
|
|
|
// non_bss_statics! {
|
|
// static ref SHEEP: [u8] = *include_bytes!("../images/sheep.qoi");
|
|
// // static ref LARGE: [u8] = *include_bytes!("../images/large.qoi");
|
|
// }
|
|
static SHEEP: [u8; 446] = *include_bytes!("../images/sheep.qoi");
|
|
|
|
// const LARGE_C1: [u8; 512] = LARGE.as_chunks().0[0];
|
|
// const LARGE_C2: [u8; 512] = LARGE.as_chunks().0[1];
|
|
// const LARGE_C3: [u8; 512] = LARGE.as_chunks().0[2];
|
|
// const LARGE_C4: &[u8] = LARGE.as_chunks::<512>().1;
|
|
|
|
#[derive(Debug, uDebug)]
|
|
pub enum QoiErr {
|
|
InvalidMagicNumber,
|
|
UnexpectedEOF,
|
|
}
|
|
|
|
// #[derive(Default)]
|
|
// struct LargeIter<const N: usize> {
|
|
// index: usize,
|
|
// }
|
|
|
|
// impl<const N: usize> Iterator for LargeIter<N> {
|
|
// type Item = u8;
|
|
|
|
// fn next(&mut self) -> Option<Self::Item> {
|
|
// if self.index >= N {
|
|
// return None;
|
|
// }
|
|
// let old_idx = self.index;
|
|
// self.index += 1;
|
|
// Some(SHEEP[old_idx])
|
|
// }
|
|
// }
|
|
|
|
pub fn draw_image<T: DelayNs, DCPin: PinOps, RSTPin: PinOps>(
|
|
serial: &mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>,
|
|
display: &mut Display<T, DCPin, RSTPin>,
|
|
position: Position,
|
|
) -> Result<(), QoiErr> {
|
|
// let a = LARGE[black_box(50)];
|
|
// ufmt::uwriteln!(serial, "Successfully read QOI header {}", a).unwrap();
|
|
|
|
let mut iter = SHEEP.iter().map(|v| *v);
|
|
|
|
if let (Some(113), Some(111), Some(105), Some(102)) =
|
|
(iter.next(), iter.next(), iter.next(), iter.next())
|
|
{
|
|
fn next(iter: &mut dyn Iterator<Item = u8>) -> Result<u8, QoiErr> {
|
|
iter.next().ok_or(QoiErr::UnexpectedEOF)
|
|
}
|
|
|
|
let width = u32::from_be_bytes([
|
|
next(&mut iter)?,
|
|
next(&mut iter)?,
|
|
next(&mut iter)?,
|
|
next(&mut iter)?,
|
|
]) as u16;
|
|
let height = u32::from_be_bytes([
|
|
next(&mut iter)?,
|
|
next(&mut iter)?,
|
|
next(&mut iter)?,
|
|
next(&mut iter)?,
|
|
]) as u16;
|
|
let _channels = next(&mut iter)?;
|
|
let _colorspace = next(&mut iter)?;
|
|
|
|
ufmt::uwriteln!(serial, "Successfully read QOI header").unwrap();
|
|
|
|
display.set_window(
|
|
position,
|
|
Position {
|
|
x: position.x + 191,
|
|
y: position.y + 191,
|
|
},
|
|
);
|
|
|
|
let qoi_iter = QoiIterator::from(&mut iter, width * height);
|
|
let scale_iter = ScaleIterator {
|
|
qoi: qoi_iter,
|
|
last_row: [Rgb565::yellow(); 64],
|
|
scale_factor: 3,
|
|
width: width as usize,
|
|
counter: 0,
|
|
index: 0,
|
|
};
|
|
|
|
let mut counter = 0u32;
|
|
for pixel in scale_iter {
|
|
let [c1, c2] = pixel.as_color().bytes;
|
|
display.write(Writeable::Data(&[c1, c2]));
|
|
counter += 1;
|
|
}
|
|
ufmt::uwriteln!(serial, "Counter: {}", counter).unwrap();
|
|
|
|
Ok(())
|
|
} else {
|
|
Err(QoiErr::InvalidMagicNumber)
|
|
}
|
|
}
|
|
|
|
struct ScaleIterator<'a> {
|
|
qoi: QoiIterator<'a>,
|
|
last_row: [Rgb565; 64],
|
|
width: usize,
|
|
scale_factor: usize,
|
|
counter: usize,
|
|
index: usize,
|
|
}
|
|
|
|
impl<'a> Iterator for ScaleIterator<'a> {
|
|
type Item = Rgb565;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.index >= self.width * self.scale_factor {
|
|
self.counter = (self.counter + 1) % self.scale_factor;
|
|
self.index = 0;
|
|
}
|
|
let index_div = self.index / self.scale_factor;
|
|
|
|
if self.counter % self.scale_factor == 0 {
|
|
if (self.index % self.scale_factor) == 0 {
|
|
if let Some(pixel) = self.qoi.next() {
|
|
self.last_row[index_div] = pixel;
|
|
self.index += 1;
|
|
Some(pixel)
|
|
} else {
|
|
None
|
|
}
|
|
} else {
|
|
let pixel = self.last_row[index_div];
|
|
self.index += 1;
|
|
Some(pixel)
|
|
}
|
|
} else {
|
|
let pixel = self.last_row[index_div];
|
|
self.index += 1;
|
|
Some(pixel)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct QoiIterator<'a> {
|
|
inner: &'a mut dyn Iterator<Item = u8>,
|
|
prev_pixels: [Rgb565; 64],
|
|
last_pixel: Rgb565,
|
|
repeat: u8,
|
|
expected_colors: u16,
|
|
parsed_colors: u16,
|
|
}
|
|
|
|
impl<'a> QoiIterator<'a> {
|
|
fn from(bytes: &'a mut dyn Iterator<Item = u8>, expected: u16) -> Self {
|
|
QoiIterator {
|
|
inner: bytes,
|
|
prev_pixels: [Rgb565(0, 255, 0); 64],
|
|
last_pixel: Rgb565(0, 0, 0),
|
|
repeat: 0,
|
|
expected_colors: expected,
|
|
parsed_colors: 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator for QoiIterator<'a> {
|
|
type Item = Rgb565;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.parsed_colors >= self.expected_colors {
|
|
return None;
|
|
}
|
|
self.parsed_colors += 1;
|
|
|
|
if self.repeat > 0 {
|
|
self.repeat -= 1;
|
|
return Some(self.last_pixel);
|
|
}
|
|
if let Some(byte) = self.inner.next() {
|
|
let color = if byte == 0xff {
|
|
let red = self.inner.next().unwrap();
|
|
let green = self.inner.next().unwrap();
|
|
let blue = self.inner.next().unwrap();
|
|
let _alpha = self.inner.next().unwrap();
|
|
Rgb565(red, green, blue)
|
|
} else if byte == 0b11111110 {
|
|
let red = self.inner.next()?;
|
|
let green = self.inner.next()?;
|
|
let blue = self.inner.next()?;
|
|
Rgb565(red, green, blue)
|
|
} else {
|
|
let tag = (0b11000000 & byte) >> 6;
|
|
let data = 0b00111111 & byte;
|
|
|
|
if tag == 0 {
|
|
self.prev_pixels[data as usize]
|
|
} else if tag == 1 {
|
|
let dr = ((0b110000 & data) >> 4) as i8 - 2;
|
|
let dg = ((0b001100 & data) >> 2) as i8 - 2;
|
|
let db = (0b000011 & data) as i8 - 2;
|
|
Rgb565(
|
|
(self.last_pixel.0 as i8).wrapping_add(dr) as u8,
|
|
(self.last_pixel.1 as i8).wrapping_add(dg) as u8,
|
|
(self.last_pixel.2 as i8).wrapping_add(db) as u8,
|
|
)
|
|
} else if tag == 2 {
|
|
let second = self.inner.next()?;
|
|
let dg = data as i8 - 32;
|
|
let dr_dg = ((0b11110000 & second) >> 4) as i8 - 8;
|
|
let db_dg = (0b00001111 & second) as i8 - 8;
|
|
let dr = dr_dg + dg;
|
|
let db = db_dg + dg;
|
|
|
|
Rgb565(
|
|
(self.last_pixel.0 as i8 + dr) as u8,
|
|
(self.last_pixel.1 as i8 + dg) as u8,
|
|
(self.last_pixel.2 as i8 + db) as u8,
|
|
)
|
|
} else if tag == 3 {
|
|
// QOI_OP_RUN
|
|
self.repeat = data;
|
|
self.last_pixel
|
|
} else {
|
|
Rgb565::green()
|
|
}
|
|
};
|
|
self.last_pixel = color;
|
|
let hash =
|
|
((color.0 as u32 * 3 + color.1 as u32 * 5 + color.2 as u32 * 7 + 255 as u32 * 11)
|
|
% 64) as usize;
|
|
|
|
self.prev_pixels[hash] = color;
|
|
Some(color)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|