Compare commits
2 commits
35ef64a829
...
ad2426561f
Author | SHA1 | Date | |
---|---|---|---|
saji | ad2426561f | ||
saji | de20da4e86 |
77
Cargo.lock
generated
77
Cargo.lock
generated
|
@ -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]]
|
||||
|
|
25
src/api.rs
25
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<AppState>,
|
||||
img_req: ImageRequest,
|
||||
) -> Result<impl IntoResponse, AppError> {
|
||||
// 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<impl IntoResponse, AppError> {
|
||||
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();
|
||||
|
|
|
@ -39,8 +39,8 @@ pub enum ProcessingError {
|
|||
/// Buffer to be sent to the ``EInk`` display.
|
||||
#[derive(Debug)]
|
||||
pub struct DitheredImage {
|
||||
buf: ImageBuffer<Luma<u8>, Vec<u8>>,
|
||||
palette: Vec<Srgb>,
|
||||
pub buf: ImageBuffer<Luma<u8>, Vec<u8>>,
|
||||
pub palette: Vec<Srgb>,
|
||||
}
|
||||
|
||||
impl DitheredImage {
|
||||
|
@ -86,8 +86,7 @@ pub trait Ditherer {
|
|||
fn dither(&mut self, img: &RgbImage, output: &mut DitheredImage);
|
||||
}
|
||||
|
||||
/// Find the closest approximate palette color to the given sRGB value.
|
||||
/// This uses euclidian distance in linear space.
|
||||
/// Find the closest approximate palette color
|
||||
fn nearest_neighbor(input_color: Lab, palette: &[Lab]) -> (u8, Lab) {
|
||||
let (nearest, _, color_diff) = palette
|
||||
.iter()
|
||||
|
@ -95,7 +94,7 @@ fn nearest_neighbor(input_color: Lab, palette: &[Lab]) -> (u8, Lab) {
|
|||
.map(|(idx, p_color)| {
|
||||
(
|
||||
idx,
|
||||
input_color.difference(*p_color),
|
||||
input_color.difference(*p_color), // this is CIEDIE2000 based and highly accurate.
|
||||
input_color - *p_color,
|
||||
)
|
||||
})
|
||||
|
@ -199,7 +198,7 @@ static STUKI_DITHER_POINTS: &[DiffusionPoint] = &[
|
|||
pub type DiffusionMatrix<'a> = &'a [DiffusionPoint];
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ErrorDiffusion<'a>(&'a [DiffusionPoint]);
|
||||
pub struct ErrorDiffusion<'a>(DiffusionMatrix<'a>);
|
||||
|
||||
impl<'a> ErrorDiffusion<'a> {
|
||||
#[must_use]
|
||||
|
|
31
src/main.rs
31
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<PathBuf>,
|
||||
|
||||
}
|
||||
|
||||
/// 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<dyn EInkPanel + Send> = 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?;
|
||||
|
|
Loading…
Reference in a new issue