From ad2426561ff5075d558c1261d1c67d124f28f085 Mon Sep 17 00:00:00 2001 From: saji Date: Thu, 1 Aug 2024 21:15:13 -0500 Subject: [PATCH] image resizing; auto-detection of hardware --- Cargo.lock | 77 +++++++++++++++++++++++++++++++++++------------------ src/api.rs | 25 +++++++---------- src/main.rs | 31 +++++++++++---------- test.hurl | 3 ++- 4 files changed, 78 insertions(+), 58 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 56b8d96..024a7ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -26,7 +26,7 @@ dependencies = [ "cfg-if", "once_cell", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -311,9 +311,9 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.16.1" +version = "1.16.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" +checksum = "102087e286b4677862ea56cf8fc58bb2cdfa8725c40ffb80fe3a008eb7f2fc83" [[package]] name = "byteorder" @@ -329,9 +329,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.6.1" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12916984aab3fa6e39d655a33e09c0071eb36d6ab3aea5c2d78551f1df6d952" +checksum = "8318a53db07bb3f8dca91a600466bdb3f2eaadeedfdbcf02e1accbad9271ba50" [[package]] name = "cast" @@ -341,9 +341,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" +checksum = "26a5c3fd7bfa1ce3897a3a3501d362b2d87b7f2583ebcb4a949ec25911025cbc" dependencies = [ "jobserver", "libc", @@ -394,9 +394,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35723e6a11662c2afb578bcf0b88bf6ea8e21282a953428f240574fcc3a2b5b3" +checksum = "0fbb260a053428790f3de475e304ff84cdbc4face759ea7a3e64c1edd938a7fc" dependencies = [ "clap_builder", "clap_derive", @@ -404,9 +404,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49eb96cbfa7cfa35017b7cd548c75b14c3118c98b423041d70562665e07fb0fa" +checksum = "64b17d7ea74e9f833c7dbf2cbe4fb12ff26783eda4782a8975b72f895c9b4d99" dependencies = [ "anstream", "anstyle", @@ -416,9 +416,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.11" +version = "4.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d029b67f89d30bbb547c89fd5161293c0aec155fc691d7924b64550662db93e" +checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -910,9 +910,9 @@ checksum = "44feda355f4159a7c757171a77de25daf6411e217b4cabd03bd6650690468126" [[package]] name = "indexmap" -version = "2.2.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "de3fc2e30ba82dd1b3911c8de1ffc143c74a914a14e99514d7637e3099df5ea0" dependencies = [ "equivalent", "hashbrown", @@ -1143,9 +1143,9 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "minijinja" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f7e8e35b6c7b169bf40b0176d2c79291ab8ee53290b84e0668ab21d841aa9d" +checksum = "f4bf71af278c578cbcc91d0b1ff092910208bc86f7b3750364642bd424e3dcd3" dependencies = [ "serde", ] @@ -1555,9 +1555,12 @@ dependencies = [ [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "dee4364d9f3b902ef14fab8a1ddffb783a1cb6b4bba3bfc1fa3922732c7de97f" +dependencies = [ + "zerocopy 0.6.6", +] [[package]] name = "proc-macro2" @@ -1826,11 +1829,12 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.120" +version = "1.0.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" +checksum = "784b6203951c57ff748476b126ccb5e8e2959a5c19e5c617ab1956be3dbc68da" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] @@ -2034,9 +2038,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.12.15" +version = "0.12.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4873307b7c257eddcb50c9bedf158eb669578359fb28428bef438fec8e6ba7c2" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "thiserror" @@ -2091,9 +2095,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.39.1" +version = "1.39.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d040ac2b29ab03b09d4129c2f5bbd012a3ac2f79d38ff506a4bf8dd34b0eac8a" +checksum = "daa4fb1bc778bd6f04cbfc4bb2d06a7396a8f299dc33ea1900cedaa316f467b1" dependencies = [ "backtrace", "bytes", @@ -2510,13 +2514,34 @@ dependencies = [ "memchr", ] +[[package]] +name = "zerocopy" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854e949ac82d619ee9a14c66a1b674ac730422372ccb759ce0c39cabcf2bf8e6" +dependencies = [ + "byteorder", + "zerocopy-derive 0.6.6", +] + [[package]] name = "zerocopy" version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy-derive" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "125139de3f6b9d625c39e2efdd73d41bdac468ccd556556440e322be0e1bbd91" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] diff --git a/src/api.rs b/src/api.rs index 15042eb..3c30ac6 100644 --- a/src/api.rs +++ b/src/api.rs @@ -6,14 +6,13 @@ use axum::extract::{FromRequest, Multipart, State}; use axum::http::{header, StatusCode}; use axum::response::IntoResponse; use axum::{response::Response, routing::post, Router}; -use image::{ImageReader, RgbImage}; +use image::{imageops::resize, imageops::FilterType, ImageReader, RgbImage}; use std::io::Cursor; use std::str; use std::str::FromStr; use std::sync::mpsc; use std::sync::Arc; use std::thread::JoinHandle; -use std::time::Duration; use tracing::{error, info, instrument}; #[derive(thiserror::Error, Debug)] @@ -128,21 +127,20 @@ where } } -#[instrument(skip(ctx))] +#[instrument(skip_all)] async fn set_image( State(ctx): State, img_req: ImageRequest, ) -> Result { // FIXME: resize image to 800x480 to match the eink panel. - let mut buf = DitheredImage::new( - img_req.image.width(), - img_req.image.height(), - img_req.palette.value().to_vec(), - ); + info!("Got image"); + let mut buf = DitheredImage::new(800, 480, img_req.palette.value().to_vec()); + let resized = resize(&*img_req.image, 800, 480, FilterType::Lanczos3); { let mut dither = img_req.dither_method.get_ditherer(); - dither.dither(&img_req.image, &mut buf); + dither.dither(&resized, &mut buf); } + info!("image resized, pushing to channel"); ctx.display_channel.send(Box::new(buf))?; Ok(StatusCode::OK) } @@ -150,14 +148,11 @@ async fn set_image( /// generates a dithered image based on the given image and the dithering parameters. /// Can be used to see how the dithering and palette choices affect the result. async fn preview_image(img_req: ImageRequest) -> Result { - let mut buf = DitheredImage::new( - img_req.image.width(), - img_req.image.height(), - img_req.palette.value().to_vec(), - ); + let mut buf = DitheredImage::new(800, 480, img_req.palette.value().to_vec()); + let resized = resize(&*img_req.image, 800, 480, FilterType::Lanczos3); { let mut dither = img_req.dither_method.get_ditherer(); - dither.dither(&img_req.image, &mut buf); + dither.dither(&resized, &mut buf); } // Convert buf into a png image. let img = buf.into_rgbimage(); diff --git a/src/main.rs b/src/main.rs index d385dd1..cbc5edb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,8 +3,8 @@ pub mod display; pub mod dither; pub mod eink; -use std::path::PathBuf; use serde::Deserialize; +use std::path::PathBuf; use toml; use crate::display::{EInkPanel, FakeEInk, Wrapper}; @@ -12,16 +12,13 @@ use crate::dither::{DitherMethod, DitheredImage}; use crate::eink::Palette; use clap::{Args, Parser, Subcommand}; use image::RgbImage; -use tracing::{error, info}; - - +use tracing::{error, event, info, warn, Level}; /// Application config, including sqlite db path, scan folders, and scheduling. #[derive(Deserialize, Debug)] struct Config { database_path: PathBuf, search_paths: Vec, - } /// Display images on E-Ink Displays @@ -38,8 +35,6 @@ enum Command { Convert(ConvertArgs), /// Load a single image Show, - /// Display a test pattern - Test, /// Start the HTTP server Serve, } @@ -57,6 +52,18 @@ async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); let cli = Cli::parse(); + let mut display: Box = match Wrapper::new() { + Ok(w) => { + info!("Found real hardware, using it"); + Box::new(w) + } + Err(e) => { + event!(Level::WARN, "Error opening display SPI interface: {e}"); + warn!("Falling back to fake display"); + Box::new(FakeEInk {}) + } + }; + match cli.command { Command::Convert(a) => { let input = image::ImageReader::open(a.input_file)? @@ -76,7 +83,6 @@ async fn main() -> anyhow::Result<()> { Command::Show => { let img: RgbImage = image::ImageReader::open("image.png")?.decode()?.into(); error!("HI"); - let mut display = FakeEInk {}; let mut eink_buf = crate::eink::new_image(); let mut dither = DitherMethod::Atkinson.get_ditherer(); @@ -84,15 +90,8 @@ async fn main() -> anyhow::Result<()> { dither.dither(&img, &mut eink_buf); display.display(&eink_buf)?; } - Command::Test => { - let mut display = Wrapper::new()?; - - display.test()?; - } Command::Serve => { - let display = FakeEInk {}; - - let ctx = api::AppState::new(Box::new(display)); + let ctx = api::AppState::new(display); let app = api::router().with_state(ctx); let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?; diff --git a/test.hurl b/test.hurl index aaeece0..efd58a1 100644 --- a/test.hurl +++ b/test.hurl @@ -1,3 +1,4 @@ POST http://192.168.0.185:3000/setimage [MultipartFormData] -image: file,image.png; +image: file,{{image}}; +dither_method: Atkinson