diff --git a/Cargo.lock b/Cargo.lock index c75e9ab..cfe5499 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -67,6 +67,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + [[package]] name = "backtrace" version = "0.3.75" @@ -82,12 +88,30 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "bytemuck" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + [[package]] name = "cfg-if" version = "1.0.0" @@ -173,6 +197,15 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + [[package]] name = "eyre" version = "0.6.12" @@ -183,6 +216,25 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "gimli" version = "0.31.1" @@ -195,6 +247,31 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "image" +version = "0.25.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +dependencies = [ + "bytemuck", + "byteorder-lite", + "image-webp", + "num-traits", + "png", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6970fe7a5300b4b42e62c52efa0187540a5bef546c60edaf554ef595d2e6f0b" +dependencies = [ + "byteorder-lite", + "quick-error", +] + [[package]] name = "indenter" version = "0.3.3" @@ -232,6 +309,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ "adler2", + "simd-adler32", ] [[package]] @@ -240,12 +318,21 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags", + "bitflags 2.9.1", "cfg-if", "cfg_aliases", "libc", ] +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "object" version = "0.36.7" @@ -286,9 +373,23 @@ dependencies = [ "clap", "color-eyre", "eyre", + "image", "nix", ] +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + [[package]] name = "proc-macro2" version = "1.0.95" @@ -298,6 +399,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quote" version = "1.0.40" @@ -322,6 +429,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "strsim" version = "0.11.1" @@ -480,3 +593,18 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + +[[package]] +name = "zune-jpeg" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9e525af0a6a658e031e95f14b7f889976b74a11ba0eca5a5fc9ac8a1c43a6a" +dependencies = [ + "zune-core", +] diff --git a/Cargo.toml b/Cargo.toml index 0af2544..fa1341d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,5 @@ edition = "2024" clap = { version = "4.5.39", features = ["derive"] } color-eyre = "0.6.5" eyre = "0.6.12" +image = { version = "0.25.6", default-features = false, features = ["jpeg", "png", "webp"] } nix = { version = "0.30.1", features = ["ioctl"] } diff --git a/src/main.rs b/src/main.rs index 242c5b8..bd7bef5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,12 @@ +//! Image format is a buffer of `SCREEN_W * SCREEN_H + 1` bytes. +//! The 4 least-significant bits of each byte is one pixel. The 4 most-significant bits are unused. +//! Images are stored in landscape format, i.e. the first byte is the top-left pixel, and the +//! second-to-last byte is the bottom-right pixel. The final byte is a null-terminator, and must be 0. use clap::Parser; -use eyre::{Context, ensure, eyre}; +use eyre::{Context, bail, eyre}; +use image::ImageReader; use nix::ioctl_write_ptr; -use std::{ - fs::File, - io::Read, - os::fd::AsRawFd, - path::{Path, PathBuf}, -}; +use std::{fs::File, os::fd::AsRawFd, path::PathBuf}; #[derive(Parser)] struct Opt { @@ -14,11 +14,10 @@ struct Opt { pub path: PathBuf, } -const SCREEN_W: usize = 1404; -const SCREEN_H: usize = 1872; +const SCREEN_W: usize = 1872; +const SCREEN_H: usize = 1404; -const BITS_PER_PIXEL: usize = 4; -const FILE_SIZE: usize = SCREEN_W * SCREEN_H * BITS_PER_PIXEL / 8; +const FILE_SIZE: usize = SCREEN_W * SCREEN_H + 1; // const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h // const SPI_IOC_TYPE_MESSAGE: u8 = 0; @@ -46,12 +45,29 @@ fn main() -> eyre::Result<()> { let opt = Opt::parse(); color_eyre::install()?; - let image = - read_file(&opt.path).wrap_err_with(|| eyre!("Failed to read file at {:?}", opt.path))?; + let mut image = ImageReader::open(&opt.path)?.decode()?; + + let image_dimensions = (image.width() as usize, image.height() as usize); + + match image_dimensions { + (SCREEN_W, SCREEN_H) => {} + (SCREEN_H, SCREEN_W) => image = image.rotate270(), + _ => bail!("Image must be {SCREEN_W}x{SCREEN_H}"), + } + + let image = image.to_luma8(); + let mut pixels = image.into_vec(); + for pixel in &mut pixels { + *pixel >>= 4; // convert 8-bit colorspace to 4-bits. + } + pixels.push(0u8); // add a null-terminator + + // sanity check buffer length + assert!(pixels.len() == FILE_SIZE); let set_off_screen_data = DrmRockchipEbcOffScreen { info1: 0, // TODO: what is this? - ptr_screen_content: image.as_ptr(), + ptr_screen_content: pixels.as_ptr(), }; let driver_file = File::options() @@ -64,16 +80,3 @@ fn main() -> eyre::Result<()> { Ok(()) } - -fn read_file(path: &Path) -> eyre::Result> { - let mut file = File::open(path)?; - let mut buf = vec![0u8; FILE_SIZE]; - file.read_exact(&mut buf[..]) - .wrap_err_with(|| eyre!("Expected a raw image file, of exactly {FILE_SIZE} bytes"))?; - - // Try to read a single byte. If n != 0, it means the file was larger than FILE_SIZE - let n = file.read(&mut [0u8])?; - ensure!(n == 0, "File must be exacly {FILE_SIZE} bytes long"); - - Ok(buf) -}