Very nearly get qoi image rendering
This commit is contained in:
parent
0a71347771
commit
d22be14de0
BIN
images/Image.qoi
BIN
images/Image.qoi
Binary file not shown.
BIN
images/Image.xcf
BIN
images/Image.xcf
Binary file not shown.
@ -42,6 +42,10 @@ impl Rgb565 {
|
|||||||
pub fn cyan() -> Rgb565 {
|
pub fn cyan() -> Rgb565 {
|
||||||
Rgb565(0, 255, 255)
|
Rgb565(0, 255, 255)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn qoi_hash(&self) -> usize {
|
||||||
|
((self.0 as u32 * 3 + self.1 as u32 * 5 + self.2 as u32 * 7 + 255 * 11) % 64) as usize
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mul<f32> for Rgb565 {
|
impl Mul<f32> for Rgb565 {
|
||||||
@ -83,7 +87,7 @@ pub struct Display<T: DelayNs, DCPin: PinOps, RSTPin: PinOps> {
|
|||||||
pub delay: T,
|
pub delay: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Command {
|
pub enum Command {
|
||||||
NOP = 0x0,
|
NOP = 0x0,
|
||||||
SoftReset = 0x1,
|
SoftReset = 0x1,
|
||||||
RddID = 0x4,
|
RddID = 0x4,
|
||||||
@ -138,7 +142,7 @@ enum ColorMode {
|
|||||||
ColorMode16M = 0x07,
|
ColorMode16M = 0x07,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Writeable<'d> {
|
pub enum Writeable<'d> {
|
||||||
Command(Command),
|
Command(Command),
|
||||||
Data(&'d [u8]),
|
Data(&'d [u8]),
|
||||||
}
|
}
|
||||||
@ -151,7 +155,7 @@ enum Rotation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<T: DelayNs, DCPin: PinOps, RSTPin: PinOps> Display<T, DCPin, RSTPin> {
|
impl<T: DelayNs, DCPin: PinOps, RSTPin: PinOps> Display<T, DCPin, RSTPin> {
|
||||||
fn write(&mut self, writeable: Writeable) {
|
pub fn write(&mut self, writeable: Writeable) {
|
||||||
self.cs.set_low().unwrap();
|
self.cs.set_low().unwrap();
|
||||||
match writeable {
|
match writeable {
|
||||||
Writeable::Command(cmd) => {
|
Writeable::Command(cmd) => {
|
||||||
@ -242,7 +246,7 @@ impl<T: DelayNs, DCPin: PinOps, RSTPin: PinOps> Display<T, DCPin, RSTPin> {
|
|||||||
self.write(Writeable::Data(&[start1, start2, end1, end2]));
|
self.write(Writeable::Data(&[start1, start2, end1, end2]));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_window(&mut self, pos0: Position, pos1: Position) {
|
pub fn set_window(&mut self, pos0: Position, pos1: Position) {
|
||||||
self.set_columns(pos0.x, pos1.x);
|
self.set_columns(pos0.x, pos1.x);
|
||||||
self.set_rows(pos0.y, pos1.y);
|
self.set_rows(pos0.y, pos1.y);
|
||||||
self.write(Writeable::Command(Command::RamWR));
|
self.write(Writeable::Command(Command::RamWR));
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
// https://qoiformat.org/qoi-specification.pdf
|
|
||||||
const image: &[u8; 294] = include_bytes!("../images/Image.qoi");
|
|
27
src/main.rs
27
src/main.rs
@ -3,6 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
#![feature(iter_array_chunks)]
|
||||||
|
|
||||||
use atmega_hal::{
|
use atmega_hal::{
|
||||||
Adc, Usart,
|
Adc, Usart,
|
||||||
@ -16,12 +17,13 @@ use panic_halt as _;
|
|||||||
use crate::{
|
use crate::{
|
||||||
display::{Display, Position, Rgb565},
|
display::{Display, Position, Rgb565},
|
||||||
peripherals::{Button, Knob},
|
peripherals::{Button, Knob},
|
||||||
|
qoi::draw_image,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod display;
|
mod display;
|
||||||
mod font;
|
mod font;
|
||||||
mod image;
|
|
||||||
mod peripherals;
|
mod peripherals;
|
||||||
|
mod qoi;
|
||||||
|
|
||||||
type CoreClock = atmega_hal::clock::MHz8;
|
type CoreClock = atmega_hal::clock::MHz8;
|
||||||
|
|
||||||
@ -96,13 +98,20 @@ fn main() -> ! {
|
|||||||
idx = (idx + 1) % colors.len();
|
idx = (idx + 1) % colors.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
if last_input != input || button_poll {
|
// if last_input != input || button_poll {
|
||||||
last_input = input;
|
// last_input = input;
|
||||||
display.draw_rect(
|
// display.draw_rect(
|
||||||
Position { x: 0, y: 0 },
|
// Position { x: 0, y: 0 },
|
||||||
Position { x: 200, y: 200 },
|
// Position { x: 200, y: 200 },
|
||||||
(colors[idx] * input).as_color(),
|
// (colors[idx] * input).as_color(),
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
match draw_image(&mut serial, &mut display, Position { x: 0, y: 0 }) {
|
||||||
|
Ok(_) => ufmt::uwriteln!(serial, "Successfully read QOI").unwrap(),
|
||||||
|
Err(e) => ufmt::uwriteln!(serial, "Error: {:?}", e).unwrap(),
|
||||||
|
}
|
||||||
|
|
||||||
|
delay.delay_ms(1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
243
src/qoi.rs
Normal file
243
src/qoi.rs
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
use core::slice::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},
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://qoiformat.org/qoi-specification.pdf
|
||||||
|
const image: &[u8; 288] = include_bytes!("../images/Image.qoi");
|
||||||
|
const BUFFER_SIZE: usize = 64;
|
||||||
|
|
||||||
|
#[derive(Debug, uDebug)]
|
||||||
|
pub enum QoiErr {
|
||||||
|
InvalidMagicNumber,
|
||||||
|
UnexpectedEOF,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 mut iter = image.iter();
|
||||||
|
|
||||||
|
if let (Some(113), Some(111), Some(105), Some(102)) =
|
||||||
|
(iter.next(), iter.next(), iter.next(), iter.next())
|
||||||
|
{
|
||||||
|
fn next<'a>(iter: &mut Iter<'a, u8>) -> Result<&'a 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 + width,
|
||||||
|
y: position.y + height,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
let qoi_iter = QoiIterator::from(iter, serial);
|
||||||
|
let mut prev_pixels = [0u8; 64];
|
||||||
|
|
||||||
|
let mut chunks = qoi_iter.array_chunks::<BUFFER_SIZE>();
|
||||||
|
while let Some(array) = chunks.next() {
|
||||||
|
let mut colors = [0u8; BUFFER_SIZE * 2];
|
||||||
|
for (i, pixel) in array.iter().enumerate() {
|
||||||
|
let [c1, c2] = pixel.as_color().bytes;
|
||||||
|
colors[i * 2] = c1;
|
||||||
|
colors[i * 2 + 1] = c2;
|
||||||
|
}
|
||||||
|
display.write(Writeable::Data(&colors));
|
||||||
|
}
|
||||||
|
if let Some(remainder) = chunks.into_remainder() {
|
||||||
|
for pixel in remainder {
|
||||||
|
let [c1, c2] = pixel.as_color().bytes;
|
||||||
|
display.write(Writeable::Data(&[c1, c2]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// display.draw_rect(
|
||||||
|
// position,
|
||||||
|
// Position {
|
||||||
|
// x: position.x + 100,
|
||||||
|
// y: position.y + 100,
|
||||||
|
// },
|
||||||
|
// Rgb565::green().as_color(),
|
||||||
|
// );
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(QoiErr::InvalidMagicNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct QoiIterator<'a> {
|
||||||
|
inner: Iter<'a, u8>,
|
||||||
|
serial: &'a mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>,
|
||||||
|
prev_pixels: [Rgb565; 64],
|
||||||
|
prev_alphas: [u8; 64],
|
||||||
|
last_pixel: Rgb565,
|
||||||
|
last_alpha: u8,
|
||||||
|
repeat: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> QoiIterator<'a> {
|
||||||
|
fn from(
|
||||||
|
bytes: Iter<'a, u8>,
|
||||||
|
serial: &'a mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>,
|
||||||
|
) -> Self {
|
||||||
|
QoiIterator {
|
||||||
|
inner: bytes,
|
||||||
|
serial,
|
||||||
|
prev_pixels: [Rgb565(255, 255, 255); 64],
|
||||||
|
prev_alphas: [255; 64],
|
||||||
|
last_pixel: Rgb565(0, 0, 0),
|
||||||
|
last_alpha: 255,
|
||||||
|
repeat: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for QoiIterator<'a> {
|
||||||
|
type Item = Rgb565;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.repeat > 0 {
|
||||||
|
self.repeat -= 1;
|
||||||
|
return Some(self.last_pixel);
|
||||||
|
}
|
||||||
|
if let Some(byte) = self.inner.next() {
|
||||||
|
let (color, alpha) = 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();
|
||||||
|
ufmt::uwriteln!(self.serial, "RGBA: {} {} {} {}", red, green, blue, alpha).unwrap();
|
||||||
|
(Rgb565(*red, *green, *blue), *alpha)
|
||||||
|
} else if *byte == 0b11111110 {
|
||||||
|
let red = self.inner.next().unwrap();
|
||||||
|
let green = self.inner.next().unwrap();
|
||||||
|
let blue = self.inner.next().unwrap();
|
||||||
|
ufmt::uwriteln!(self.serial, "RGB: {} {} {}", red, green, blue).unwrap();
|
||||||
|
(Rgb565(*red, *green, *blue), self.last_alpha)
|
||||||
|
} else {
|
||||||
|
let tag = (0b11000000 & byte) >> 6;
|
||||||
|
let data = 0b00111111 & byte;
|
||||||
|
|
||||||
|
ufmt::uwriteln!(self.serial, "Tag: {}", tag).unwrap();
|
||||||
|
|
||||||
|
if tag == 0 {
|
||||||
|
(
|
||||||
|
self.prev_pixels[data as usize],
|
||||||
|
self.prev_alphas[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;
|
||||||
|
ufmt::uwriteln!(
|
||||||
|
self.serial,
|
||||||
|
"Diffs: {} {} {}",
|
||||||
|
((0b110000 & data) >> 4),
|
||||||
|
((0b001100 & data) >> 2),
|
||||||
|
(0b000011 & data)
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
(
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
self.last_alpha,
|
||||||
|
)
|
||||||
|
} else if tag == 2 {
|
||||||
|
let second = self.inner.next().unwrap();
|
||||||
|
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;
|
||||||
|
ufmt::uwriteln!(
|
||||||
|
self.serial,
|
||||||
|
"Last: {} {} {}",
|
||||||
|
self.last_pixel.0 as i8,
|
||||||
|
self.last_pixel.1 as i8,
|
||||||
|
self.last_pixel.2 as i8
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
ufmt::uwriteln!(self.serial, "Values: {} {} {}", dr_dg, dg, db_dg).unwrap();
|
||||||
|
ufmt::uwriteln!(self.serial, "Diffs: {} {} {}", dr, dg, db).unwrap();
|
||||||
|
ufmt::uwriteln!(
|
||||||
|
self.serial,
|
||||||
|
"Res: {} {} {}",
|
||||||
|
self.last_pixel.0 as i8 + dr,
|
||||||
|
self.last_pixel.1 as i8 + dg,
|
||||||
|
self.last_pixel.2 as i8 + db
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
(
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
self.last_alpha,
|
||||||
|
)
|
||||||
|
} else if tag == 3 {
|
||||||
|
// QOI_OP_RUN
|
||||||
|
self.repeat = data;
|
||||||
|
ufmt::uwriteln!(self.serial, "Repeat: {}", self.repeat).unwrap();
|
||||||
|
(self.last_pixel, self.last_alpha)
|
||||||
|
} else {
|
||||||
|
(Rgb565::green(), 255)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
self.last_pixel = color;
|
||||||
|
self.last_alpha = alpha;
|
||||||
|
let hash =
|
||||||
|
((color.0 as u32 * 3 + color.1 as u32 * 5 + color.2 as u32 * 7 + alpha as u32 * 11)
|
||||||
|
% 64) as usize;
|
||||||
|
|
||||||
|
ufmt::uwriteln!(
|
||||||
|
self.serial,
|
||||||
|
"Color: {} {} {} {}, Hash: {}",
|
||||||
|
color.0,
|
||||||
|
color.1,
|
||||||
|
color.2,
|
||||||
|
alpha,
|
||||||
|
hash
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
self.prev_pixels[hash] = color;
|
||||||
|
Some(color)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user