Compare commits
No commits in common. "main" and "d22be14de0c0ec00cecec0e19a3c5108ed3b2576" have entirely different histories.
main
...
d22be14de0
@ -1,6 +1,6 @@
|
|||||||
[build]
|
[build]
|
||||||
target = "avr-none"
|
target = "avr-none"
|
||||||
rustflags = ["-C", "target-cpu=atmega328p", "--emit=llvm-ir"]
|
rustflags = ["-C", "target-cpu=atmega328p"]
|
||||||
|
|
||||||
[target.'cfg(target_arch = "avr")']
|
[target.'cfg(target_arch = "avr")']
|
||||||
runner = "ravedude"
|
runner = "ravedude"
|
||||||
|
79
Cargo.lock
generated
79
Cargo.lock
generated
@ -50,12 +50,24 @@ dependencies = [
|
|||||||
"unwrap-infallible",
|
"unwrap-infallible",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "az"
|
||||||
|
version = "1.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bare-metal"
|
name = "bare-metal"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
|
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.3"
|
version = "1.0.3"
|
||||||
@ -68,6 +80,16 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-graphics-core"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba9ecd261f991856250d2207f6d8376946cd9f412a2165d3b75bc87a0bc7a044"
|
||||||
|
dependencies = [
|
||||||
|
"az",
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-hal"
|
name = "embedded-hal"
|
||||||
version = "0.2.7"
|
version = "0.2.7"
|
||||||
@ -100,6 +122,37 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "723dce4e9f25b6e6c5f35628e144794e5b459216ed7da97b7c4b66cdb3fa82ca"
|
checksum = "723dce4e9f25b6e6c5f35628e144794e5b459216ed7da97b7c4b66cdb3fa82ca"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad"
|
||||||
|
dependencies = [
|
||||||
|
"hash32",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "mipidsi"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ba34dcbf61182ffa6992b5a4d9b566d5a99df127fd93f6d314213347329e92f"
|
||||||
|
dependencies = [
|
||||||
|
"embedded-graphics-core",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
"heapless",
|
||||||
|
"nb 1.1.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nb"
|
name = "nb"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@ -146,15 +199,10 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slideshow"
|
name = "stable_deref_trait"
|
||||||
version = "0.1.0"
|
version = "1.2.0"
|
||||||
dependencies = [
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"atmega-hal",
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
"avr-device",
|
|
||||||
"embedded-hal 1.0.0",
|
|
||||||
"panic-halt",
|
|
||||||
"ufmt",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
@ -167,6 +215,19 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tests"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"atmega-hal",
|
||||||
|
"avr-device",
|
||||||
|
"embedded-graphics-core",
|
||||||
|
"embedded-hal 1.0.0",
|
||||||
|
"mipidsi",
|
||||||
|
"panic-halt",
|
||||||
|
"ufmt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ufmt"
|
name = "ufmt"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
14
Cargo.toml
14
Cargo.toml
@ -1,5 +1,5 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "slideshow"
|
name = "tests"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
@ -10,6 +10,13 @@ embedded-hal = "1.0.0"
|
|||||||
atmega-hal = { git = "https://github.com/Rahix/avr-hal?tab=readme-ov-file", rev="cd3edea", version = "0.1.0", features=["atmega328p"] }
|
atmega-hal = { git = "https://github.com/Rahix/avr-hal?tab=readme-ov-file", rev="cd3edea", version = "0.1.0", features=["atmega328p"] }
|
||||||
|
|
||||||
ufmt = "0.2.0"
|
ufmt = "0.2.0"
|
||||||
|
mipidsi = "0.9.0"
|
||||||
|
embedded-graphics-core = "0.4.0"
|
||||||
|
# nb = "1.1.0"
|
||||||
|
# pwm-pca9685 = "1.0.0"
|
||||||
|
# infrared = "0.14.1"
|
||||||
|
# embedded-storage = "0.2"
|
||||||
|
# arduino-hal = { git = "https://github.com/Rahix/avr-hal?tab=readme-ov-file", version = "0.1.0", features=["arduino-uno"] }
|
||||||
|
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
@ -17,9 +24,6 @@ panic = "abort"
|
|||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
panic = "abort"
|
panic = "abort"
|
||||||
opt-level = "s" # Size is more important than performance on MSP430.
|
|
||||||
codegen-units = 1 # Better size optimization.
|
|
||||||
lto = "fat" # _Much_ better size optimization.
|
|
||||||
|
|
||||||
[build]
|
[build]
|
||||||
rustflags = ["-C", "link-args=-lc", "--emit=llvm-ir"]
|
rustflags = ["-C", "link-args=-lc"]
|
||||||
|
22
README.md
22
README.md
@ -1,22 +0,0 @@
|
|||||||
# Slideshow
|
|
||||||
|
|
||||||
This project is a relatively simple slideshow running on the Adafruit Feather
|
|
||||||
328P (an ATmega328p-microcontroller) using a simple button and a 240x240 LCD
|
|
||||||
display with the ST7789-driver over
|
|
||||||
[SPI](https://en.wikipedia.org/wiki/Serial_Peripheral_Interface).
|
|
||||||
|
|
||||||
This project is written on Rust and uses a very minimal amount of libraries with
|
|
||||||
the most significant being [`atmega-hal`](https://github.com/Rahix/avr-hal), a
|
|
||||||
hardware abstraction layer for ATmega-microcontrollers. Images are stored in the
|
|
||||||
[program memory](https://en.wikipedia.org/wiki/Flash_memory) of the device and
|
|
||||||
manually read using the `lpm`-instruction using inline-assembly because Rust
|
|
||||||
itself does not produce this instruction in any scenario. The images are stored
|
|
||||||
in the [QOI-specification](https://qoiformat.org/qoi-specification.pdf) to save
|
|
||||||
space, and they are decoded on-the-fly when rendering the images. Communication
|
|
||||||
between the display and the microprocessor is done manually using a [minimal
|
|
||||||
SPI-utility](https://rahix.github.io/avr-hal/atmega_hal/spi/type.Spi.html)
|
|
||||||
within the `atmega-hal` crate.
|
|
||||||
|
|
||||||
## Demonstration
|
|
||||||
|
|
||||||
<video controls src="./demonstration.mp4" alt="demonstration">
|
|
@ -1,14 +1,7 @@
|
|||||||
[general]
|
[general]
|
||||||
port = "/dev/ttyUSB0"
|
board = "uno"
|
||||||
serial-baudrate = 57600
|
serial-baudrate = 57600
|
||||||
open-console = true
|
open-console = true
|
||||||
|
|
||||||
[board.avrdude]
|
|
||||||
# avrdude configuration
|
|
||||||
programmer = "arduino"
|
|
||||||
partno = "m328p"
|
|
||||||
baudrate = 57600
|
|
||||||
do-chip-erase = true
|
|
||||||
|
|
||||||
# For documentation about this file, check here:
|
# For documentation about this file, check here:
|
||||||
# https://github.com/Rahix/avr-hal/blob/main/ravedude/README.md#ravedudetoml-format
|
# https://github.com/Rahix/avr-hal/blob/main/ravedude/README.md#ravedudetoml-format
|
||||||
|
Binary file not shown.
BIN
images/Image.bmp
Normal file
BIN
images/Image.bmp
Normal file
Binary file not shown.
After Width: | Height: | Size: 8.1 KiB |
BIN
images/Image.qoi
Normal file
BIN
images/Image.qoi
Normal file
Binary file not shown.
BIN
images/Image.xcf
Normal file
BIN
images/Image.xcf
Normal file
Binary file not shown.
BIN
images/cat.qoi
BIN
images/cat.qoi
Binary file not shown.
Binary file not shown.
BIN
images/rick.qoi
BIN
images/rick.qoi
Binary file not shown.
Binary file not shown.
82
src/main.rs
82
src/main.rs
@ -4,23 +4,20 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(iter_array_chunks)]
|
#![feature(iter_array_chunks)]
|
||||||
#![feature(const_slice_make_iter)]
|
|
||||||
#![feature(slice_as_chunks)]
|
|
||||||
#![feature(asm_experimental_arch)]
|
|
||||||
|
|
||||||
use core::ptr::addr_of;
|
|
||||||
|
|
||||||
use atmega_hal::{
|
use atmega_hal::{
|
||||||
Usart,
|
Adc, Usart,
|
||||||
|
port::{Pin, mode},
|
||||||
spi::{self, Settings},
|
spi::{self, Settings},
|
||||||
usart::Baudrate,
|
usart::Baudrate,
|
||||||
};
|
};
|
||||||
|
use embedded_hal::delay::DelayNs;
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
display::{Display, Position},
|
display::{Display, Position, Rgb565},
|
||||||
peripherals::Button,
|
peripherals::{Button, Knob},
|
||||||
qoi::{Image, LARGE_CAT_UNSAFE, PRESS_BTN_UNSAFE, RICK_UNSAFE, XP_DESKTOP_UNSAFE, draw_image},
|
qoi::draw_image,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod display;
|
mod display;
|
||||||
@ -45,11 +42,8 @@ fn main() -> ! {
|
|||||||
Baudrate::<CoreClock>::new(57600),
|
Baudrate::<CoreClock>::new(57600),
|
||||||
);
|
);
|
||||||
|
|
||||||
// let eeprom = Eeprom::new(dp.EEPROM);
|
|
||||||
// ufmt::uwriteln!(serial, "Eeprom capacity: {}", eeprom.capacity()).unwrap();
|
|
||||||
|
|
||||||
let cs = pins.pb2.into_output();
|
let cs = pins.pb2.into_output();
|
||||||
let (spi, cs) = spi::Spi::new(
|
let (mut spi, mut cs) = spi::Spi::new(
|
||||||
dp.SPI,
|
dp.SPI,
|
||||||
pins.pb5.into_output(),
|
pins.pb5.into_output(),
|
||||||
pins.pb3.into_output(),
|
pins.pb3.into_output(),
|
||||||
@ -75,39 +69,49 @@ fn main() -> ! {
|
|||||||
|
|
||||||
display.init();
|
display.init();
|
||||||
|
|
||||||
|
let mut adc: Adc<CoreClock> = atmega_hal::Adc::new(dp.ADC, Default::default());
|
||||||
|
|
||||||
let mut button = Button::from(pins.pd5.into_pull_up_input());
|
let mut button = Button::from(pins.pd5.into_pull_up_input());
|
||||||
|
let mut knob = Knob {
|
||||||
|
pin: pins.pc1.into_analog_input(&mut adc),
|
||||||
|
adc,
|
||||||
|
};
|
||||||
|
|
||||||
let mut idx = 0;
|
let colors = [
|
||||||
let images = [
|
Rgb565::red(),
|
||||||
Image::from(addr_of!(LARGE_CAT_UNSAFE)),
|
Rgb565::green(),
|
||||||
Image::from(addr_of!(XP_DESKTOP_UNSAFE)),
|
Rgb565::blue(),
|
||||||
Image::from(addr_of!(RICK_UNSAFE)),
|
Rgb565::cyan(),
|
||||||
|
Rgb565::magenta(),
|
||||||
|
Rgb565::yellow(),
|
||||||
];
|
];
|
||||||
let len = images.len();
|
let mut idx = 0;
|
||||||
|
|
||||||
match draw_image(
|
let mut delay = atmega_hal::delay::Delay::<CoreClock>::new();
|
||||||
&mut serial,
|
|
||||||
&Image::from(addr_of!(PRESS_BTN_UNSAFE)),
|
|
||||||
&mut display,
|
|
||||||
Position { x: 0, y: 0 },
|
|
||||||
) {
|
|
||||||
Ok(_) => ufmt::uwriteln!(serial, "Successfully read QOI").unwrap(),
|
|
||||||
Err(e) => ufmt::uwriteln!(serial, "Error: {:?}", e).unwrap(),
|
|
||||||
}
|
|
||||||
|
|
||||||
|
let mut last_input = 0f32;
|
||||||
loop {
|
loop {
|
||||||
if button.poll() {
|
let input = knob.poll();
|
||||||
match draw_image(
|
let button_poll = button.poll();
|
||||||
&mut serial,
|
|
||||||
&images[idx],
|
|
||||||
&mut display,
|
|
||||||
Position { x: 0, y: 0 },
|
|
||||||
) {
|
|
||||||
Ok(_) => ufmt::uwriteln!(serial, "Successfully read QOI").unwrap(),
|
|
||||||
Err(e) => ufmt::uwriteln!(serial, "Error: {:?}", e).unwrap(),
|
|
||||||
}
|
|
||||||
|
|
||||||
idx = (idx + 1) % len;
|
if button_poll {
|
||||||
|
idx = (idx + 1) % colors.len();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if last_input != input || button_poll {
|
||||||
|
// last_input = input;
|
||||||
|
// display.draw_rect(
|
||||||
|
// Position { x: 0, y: 0 },
|
||||||
|
// Position { x: 200, y: 200 },
|
||||||
|
// (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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
301
src/qoi.rs
301
src/qoi.rs
@ -1,77 +1,21 @@
|
|||||||
use core::{arch::asm, ptr::addr_of};
|
use core::slice::Iter;
|
||||||
|
|
||||||
use atmega_hal::{
|
use atmega_hal::{
|
||||||
Usart,
|
Atmega, Usart,
|
||||||
pac::USART0,
|
pac::USART0,
|
||||||
port::{PD0, PD1, Pin, PinOps, mode},
|
port::{PB3, PB5, PD0, PD1, Pin, PinOps, mode},
|
||||||
};
|
};
|
||||||
use embedded_hal::delay::DelayNs;
|
use embedded_hal::delay::DelayNs;
|
||||||
use ufmt::derive::uDebug;
|
use ufmt::derive::uDebug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
CoreClock,
|
CoreClock,
|
||||||
display::{Display, Position, Rgb565, Writeable},
|
display::{Color, Display, Position, Rgb565, Writeable},
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://qoiformat.org/qoi-specification.pdf
|
// https://qoiformat.org/qoi-specification.pdf
|
||||||
|
const image: &[u8; 288] = include_bytes!("../images/Image.qoi");
|
||||||
#[unsafe(link_section = ".progmem.data")]
|
const BUFFER_SIZE: usize = 64;
|
||||||
pub static LARGE_CAT_UNSAFE: [u8; 8765] = *include_bytes!("../images/cat.qoi");
|
|
||||||
#[unsafe(link_section = ".progmem.data")]
|
|
||||||
pub static XP_DESKTOP_UNSAFE: [u8; 7360] = *include_bytes!("../images/xp_desktop.qoi");
|
|
||||||
#[unsafe(link_section = ".progmem.data")]
|
|
||||||
pub static RICK_UNSAFE: [u8; 2391] = *include_bytes!("../images/rick.qoi");
|
|
||||||
#[unsafe(link_section = ".progmem.data")]
|
|
||||||
pub static PRESS_BTN_UNSAFE: [u8; 1225] = *include_bytes!("../images/press_btn.qoi");
|
|
||||||
|
|
||||||
pub struct Image {
|
|
||||||
ptr: *const u8,
|
|
||||||
length: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ImageIterator {
|
|
||||||
ptr: *const u8,
|
|
||||||
length: usize,
|
|
||||||
index: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Image {
|
|
||||||
pub const fn from<const N: usize>(addr: *const [u8; N]) -> Image {
|
|
||||||
Image {
|
|
||||||
ptr: addr.cast(),
|
|
||||||
length: N,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn iter(&self) -> ImageIterator {
|
|
||||||
ImageIterator {
|
|
||||||
ptr: self.ptr,
|
|
||||||
length: self.length,
|
|
||||||
index: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Iterator for ImageIterator {
|
|
||||||
type Item = u8;
|
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
if self.index >= self.length {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let elem_ptr = self.ptr.wrapping_add(self.index);
|
|
||||||
let res: u8;
|
|
||||||
unsafe {
|
|
||||||
asm!(
|
|
||||||
"lpm {}, Z",
|
|
||||||
out(reg) res,
|
|
||||||
in("Z") elem_ptr,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
self.index += 1;
|
|
||||||
Some(res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, uDebug)]
|
#[derive(Debug, uDebug)]
|
||||||
pub enum QoiErr {
|
pub enum QoiErr {
|
||||||
@ -81,132 +25,100 @@ pub enum QoiErr {
|
|||||||
|
|
||||||
pub fn draw_image<T: DelayNs, DCPin: PinOps, RSTPin: PinOps>(
|
pub fn draw_image<T: DelayNs, DCPin: PinOps, RSTPin: PinOps>(
|
||||||
serial: &mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>,
|
serial: &mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>,
|
||||||
image: &Image,
|
|
||||||
display: &mut Display<T, DCPin, RSTPin>,
|
display: &mut Display<T, DCPin, RSTPin>,
|
||||||
position: Position,
|
position: Position,
|
||||||
) -> Result<(), QoiErr> {
|
) -> Result<(), QoiErr> {
|
||||||
let mut iter = image.iter();
|
let mut iter = image.iter();
|
||||||
|
|
||||||
if let (Some(113), Some(111), Some(105), Some(102)) =
|
if let (Some(113), Some(111), Some(105), Some(102)) =
|
||||||
(iter.next(), iter.next(), iter.next(), iter.next())
|
(iter.next(), iter.next(), iter.next(), iter.next())
|
||||||
{
|
{
|
||||||
fn next(iter: &mut dyn Iterator<Item = u8>) -> Result<u8, QoiErr> {
|
fn next<'a>(iter: &mut Iter<'a, u8>) -> Result<&'a u8, QoiErr> {
|
||||||
iter.next().ok_or(QoiErr::UnexpectedEOF)
|
iter.next().ok_or(QoiErr::UnexpectedEOF)
|
||||||
}
|
}
|
||||||
|
|
||||||
let width = u32::from_be_bytes([
|
let width = u32::from_be_bytes([
|
||||||
next(&mut iter)?,
|
*next(&mut iter)?,
|
||||||
next(&mut iter)?,
|
*next(&mut iter)?,
|
||||||
next(&mut iter)?,
|
*next(&mut iter)?,
|
||||||
next(&mut iter)?,
|
*next(&mut iter)?,
|
||||||
]) as u16;
|
]) as u16;
|
||||||
let height = u32::from_be_bytes([
|
let height = u32::from_be_bytes([
|
||||||
next(&mut iter)?,
|
*next(&mut iter)?,
|
||||||
next(&mut iter)?,
|
*next(&mut iter)?,
|
||||||
next(&mut iter)?,
|
*next(&mut iter)?,
|
||||||
next(&mut iter)?,
|
*next(&mut iter)?,
|
||||||
]) as u16;
|
]) as u16;
|
||||||
let _channels = next(&mut iter)?;
|
let channels = *next(&mut iter)?;
|
||||||
let _colorspace = next(&mut iter)?;
|
let colorspace = *next(&mut iter)?;
|
||||||
|
|
||||||
ufmt::uwriteln!(serial, "Successfully read QOI header").unwrap();
|
ufmt::uwriteln!(serial, "Successfully read QOI header").unwrap();
|
||||||
|
|
||||||
let scale_factor = 240 / width;
|
|
||||||
|
|
||||||
display.set_window(
|
display.set_window(
|
||||||
position,
|
position,
|
||||||
Position {
|
Position {
|
||||||
x: position.x + (width * scale_factor) - 1,
|
x: position.x + width,
|
||||||
y: position.y + (height * scale_factor) - 1,
|
y: position.y + height,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let qoi_iter = QoiIterator::from(&mut iter, width * height);
|
let qoi_iter = QoiIterator::from(iter, serial);
|
||||||
let scale_iter = ScaleIterator {
|
let mut prev_pixels = [0u8; 64];
|
||||||
qoi: qoi_iter,
|
|
||||||
last_row: [Rgb565::yellow(); 120],
|
|
||||||
scale_factor: scale_factor as usize,
|
|
||||||
width: width as usize,
|
|
||||||
counter: 0,
|
|
||||||
index: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut counter = 0u32;
|
let mut chunks = qoi_iter.array_chunks::<BUFFER_SIZE>();
|
||||||
for pixel in scale_iter {
|
while let Some(array) = chunks.next() {
|
||||||
let [c1, c2] = pixel.as_color().bytes;
|
let mut colors = [0u8; BUFFER_SIZE * 2];
|
||||||
display.write(Writeable::Data(&[c1, c2]));
|
for (i, pixel) in array.iter().enumerate() {
|
||||||
counter += 1;
|
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]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
ufmt::uwriteln!(serial, "Counter: {}", counter).unwrap();
|
|
||||||
|
|
||||||
|
// display.draw_rect(
|
||||||
|
// position,
|
||||||
|
// Position {
|
||||||
|
// x: position.x + 100,
|
||||||
|
// y: position.y + 100,
|
||||||
|
// },
|
||||||
|
// Rgb565::green().as_color(),
|
||||||
|
// );
|
||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(QoiErr::InvalidMagicNumber)
|
Err(QoiErr::InvalidMagicNumber)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ScaleIterator<'a> {
|
|
||||||
qoi: QoiIterator<'a>,
|
|
||||||
last_row: [Rgb565; 120],
|
|
||||||
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.scale_factor == 1 {
|
|
||||||
return self.qoi.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
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> {
|
struct QoiIterator<'a> {
|
||||||
inner: &'a mut dyn Iterator<Item = u8>,
|
inner: Iter<'a, u8>,
|
||||||
|
serial: &'a mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>,
|
||||||
prev_pixels: [Rgb565; 64],
|
prev_pixels: [Rgb565; 64],
|
||||||
|
prev_alphas: [u8; 64],
|
||||||
last_pixel: Rgb565,
|
last_pixel: Rgb565,
|
||||||
|
last_alpha: u8,
|
||||||
repeat: u8,
|
repeat: u8,
|
||||||
expected_colors: u16,
|
|
||||||
parsed_colors: u16,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> QoiIterator<'a> {
|
impl<'a> QoiIterator<'a> {
|
||||||
fn from(bytes: &'a mut dyn Iterator<Item = u8>, expected: u16) -> Self {
|
fn from(
|
||||||
|
bytes: Iter<'a, u8>,
|
||||||
|
serial: &'a mut Usart<USART0, Pin<mode::Input, PD0>, Pin<mode::Output, PD1>, CoreClock>,
|
||||||
|
) -> Self {
|
||||||
QoiIterator {
|
QoiIterator {
|
||||||
inner: bytes,
|
inner: bytes,
|
||||||
prev_pixels: [Rgb565(0, 255, 0); 64],
|
serial,
|
||||||
|
prev_pixels: [Rgb565(255, 255, 255); 64],
|
||||||
|
prev_alphas: [255; 64],
|
||||||
last_pixel: Rgb565(0, 0, 0),
|
last_pixel: Rgb565(0, 0, 0),
|
||||||
|
last_alpha: 255,
|
||||||
repeat: 0,
|
repeat: 0,
|
||||||
expected_colors: expected,
|
|
||||||
parsed_colors: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,68 +127,113 @@ impl<'a> Iterator for QoiIterator<'a> {
|
|||||||
type Item = Rgb565;
|
type Item = Rgb565;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.parsed_colors >= self.expected_colors {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
self.parsed_colors += 1;
|
|
||||||
|
|
||||||
if self.repeat > 0 {
|
if self.repeat > 0 {
|
||||||
self.repeat -= 1;
|
self.repeat -= 1;
|
||||||
return Some(self.last_pixel);
|
return Some(self.last_pixel);
|
||||||
}
|
}
|
||||||
if let Some(byte) = self.inner.next() {
|
if let Some(byte) = self.inner.next() {
|
||||||
let color = if byte == 0xff {
|
let (color, alpha) = if *byte == 0xff {
|
||||||
let red = self.inner.next().unwrap();
|
let red = self.inner.next().unwrap();
|
||||||
let green = self.inner.next().unwrap();
|
let green = self.inner.next().unwrap();
|
||||||
let blue = self.inner.next().unwrap();
|
let blue = self.inner.next().unwrap();
|
||||||
let _alpha = self.inner.next().unwrap();
|
let alpha = self.inner.next().unwrap();
|
||||||
Rgb565(red, green, blue)
|
ufmt::uwriteln!(self.serial, "RGBA: {} {} {} {}", red, green, blue, alpha).unwrap();
|
||||||
} else if byte == 0b11111110 {
|
(Rgb565(*red, *green, *blue), *alpha)
|
||||||
let red = self.inner.next()?;
|
} else if *byte == 0b11111110 {
|
||||||
let green = self.inner.next()?;
|
let red = self.inner.next().unwrap();
|
||||||
let blue = self.inner.next()?;
|
let green = self.inner.next().unwrap();
|
||||||
Rgb565(red, green, blue)
|
let blue = self.inner.next().unwrap();
|
||||||
|
ufmt::uwriteln!(self.serial, "RGB: {} {} {}", red, green, blue).unwrap();
|
||||||
|
(Rgb565(*red, *green, *blue), self.last_alpha)
|
||||||
} else {
|
} else {
|
||||||
let tag = (0b11000000 & byte) >> 6;
|
let tag = (0b11000000 & byte) >> 6;
|
||||||
let data = 0b00111111 & byte;
|
let data = 0b00111111 & byte;
|
||||||
|
|
||||||
|
ufmt::uwriteln!(self.serial, "Tag: {}", tag).unwrap();
|
||||||
|
|
||||||
if tag == 0 {
|
if tag == 0 {
|
||||||
self.prev_pixels[data as usize]
|
(
|
||||||
|
self.prev_pixels[data as usize],
|
||||||
|
self.prev_alphas[data as usize],
|
||||||
|
)
|
||||||
} else if tag == 1 {
|
} else if tag == 1 {
|
||||||
let dr = ((0b110000 & data) >> 4) as i8 - 2;
|
let dr = ((0b110000 & data) >> 4) as i8 - 2;
|
||||||
let dg = ((0b001100 & data) >> 2) as i8 - 2;
|
let dg = ((0b001100 & data) >> 2) as i8 - 2;
|
||||||
let db = (0b000011 & data) as i8 - 2;
|
let db = (0b000011 & data) as i8 - 2;
|
||||||
Rgb565(
|
ufmt::uwriteln!(
|
||||||
(self.last_pixel.0 as i8).wrapping_add(dr) as u8,
|
self.serial,
|
||||||
(self.last_pixel.1 as i8).wrapping_add(dg) as u8,
|
"Diffs: {} {} {}",
|
||||||
(self.last_pixel.2 as i8).wrapping_add(db) as u8,
|
((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 {
|
} else if tag == 2 {
|
||||||
let second = self.inner.next()?;
|
let second = self.inner.next().unwrap();
|
||||||
let dg = data as i8 - 32;
|
let dg = data as i8 - 32;
|
||||||
let dr_dg = ((0b11110000 & second) >> 4) as i8 - 8;
|
let dr_dg = ((0b11110000 & second) >> 4) as i8 - 8;
|
||||||
let db_dg = (0b00001111 & second) as i8 - 8;
|
let db_dg = (0b00001111 & second) as i8 - 8;
|
||||||
let dr = dr_dg + dg;
|
let dr = dr_dg + dg;
|
||||||
let db = db_dg + dg;
|
let db = db_dg + dg;
|
||||||
|
ufmt::uwriteln!(
|
||||||
Rgb565(
|
self.serial,
|
||||||
(self.last_pixel.0 as i8 + dr) as u8,
|
"Last: {} {} {}",
|
||||||
(self.last_pixel.1 as i8 + dg) as u8,
|
self.last_pixel.0 as i8,
|
||||||
(self.last_pixel.2 as i8 + db) as u8,
|
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 {
|
} else if tag == 3 {
|
||||||
// QOI_OP_RUN
|
// QOI_OP_RUN
|
||||||
self.repeat = data;
|
self.repeat = data;
|
||||||
self.last_pixel
|
ufmt::uwriteln!(self.serial, "Repeat: {}", self.repeat).unwrap();
|
||||||
|
(self.last_pixel, self.last_alpha)
|
||||||
} else {
|
} else {
|
||||||
Rgb565::green()
|
(Rgb565::green(), 255)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
self.last_pixel = color;
|
self.last_pixel = color;
|
||||||
|
self.last_alpha = alpha;
|
||||||
let hash =
|
let hash =
|
||||||
((color.0 as u32 * 3 + color.1 as u32 * 5 + color.2 as u32 * 7 + 255 as u32 * 11)
|
((color.0 as u32 * 3 + color.1 as u32 * 5 + color.2 as u32 * 7 + alpha as u32 * 11)
|
||||||
% 64) as usize;
|
% 64) as usize;
|
||||||
|
|
||||||
|
ufmt::uwriteln!(
|
||||||
|
self.serial,
|
||||||
|
"Color: {} {} {} {}, Hash: {}",
|
||||||
|
color.0,
|
||||||
|
color.1,
|
||||||
|
color.2,
|
||||||
|
alpha,
|
||||||
|
hash
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
self.prev_pixels[hash] = color;
|
self.prev_pixels[hash] = color;
|
||||||
Some(color)
|
Some(color)
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user