diff --git a/Cargo.lock b/Cargo.lock index bb45502..7a59a91 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1544,11 +1544,11 @@ dependencies = [ [[package]] name = "mark" version = "0.0.0" -source = "git+https://github.com/Garmelon/mark.git#2345d80d803e0e9590681a49743491c477d28126" +source = "git+https://github.com/Garmelon/mark.git?rev=2a862a69d69abc64ddd7eefd1e1ff3d05ce3b6e4#2a862a69d69abc64ddd7eefd1e1ff3d05ce3b6e4" dependencies = [ "image", "palette", - "rand 0.8.5", + "rand 0.9.0", ] [[package]] @@ -2537,6 +2537,9 @@ dependencies = [ name = "showbits-typst-plugin" version = "0.0.0" dependencies = [ + "image", + "mark", + "palette", "wasm-minimal-protocol", ] diff --git a/Cargo.toml b/Cargo.toml index f22133d..790495a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,9 +18,8 @@ axum = "0.8.1" clap = { version = "4.5.30", features = ["derive", "deprecated"] } cosmic-text = "0.12.1" escpos = "0.15.0" -image = "0.25.5" +image = { version = "0.25.5", default-features = false } jiff = "0.2.1" -mark.git = "https://github.com/Garmelon/mark.git" mime_guess = "2.0.5" palette = "0.7.6" paste = "1.0.15" @@ -40,6 +39,10 @@ version = "0.7.6" default-features = false features = ["std", "taffy_tree", "flexbox", "grid", "block_layout"] +[workspace.dependencies.mark] +git = "https://github.com/Garmelon/mark.git" +rev = "2a862a69d69abc64ddd7eefd1e1ff3d05ce3b6e4" + [workspace.dependencies.wasm-minimal-protocol] git = "https://github.com/astrale-sharp/wasm-minimal-protocol.git" rev = "90336ebf2d99844fd8f8e99ea7096af96526cbf4" diff --git a/showbits-thermal-printer/Cargo.toml b/showbits-thermal-printer/Cargo.toml index 9a87c7d..f810edd 100644 --- a/showbits-thermal-printer/Cargo.toml +++ b/showbits-thermal-printer/Cargo.toml @@ -8,7 +8,7 @@ anyhow = { workspace = true } axum = { workspace = true, features = ["multipart"] } clap = { workspace = true } escpos = { workspace = true } -image = { workspace = true } +image = { workspace = true, default-features = true } jiff = { workspace = true } mime_guess = { workspace = true } palette = { workspace = true } diff --git a/showbits-thermal-printer/src/documents.rs b/showbits-thermal-printer/src/documents.rs index b296042..9d72262 100644 --- a/showbits-thermal-printer/src/documents.rs +++ b/showbits-thermal-printer/src/documents.rs @@ -1,7 +1,8 @@ use showbits_typst::Typst; -pub use self::text::*; +pub use self::{image::*, text::*}; +mod image; mod text; fn typst_with_lib() -> Typst { diff --git a/showbits-thermal-printer/src/documents/image/image.png b/showbits-thermal-printer/src/documents/image/image.png new file mode 100644 index 0000000..35df62b Binary files /dev/null and b/showbits-thermal-printer/src/documents/image/image.png differ diff --git a/showbits-thermal-printer/src/documents/image/lib.typ b/showbits-thermal-printer/src/documents/image/lib.typ new file mode 120000 index 0000000..ea61142 --- /dev/null +++ b/showbits-thermal-printer/src/documents/image/lib.typ @@ -0,0 +1 @@ +../lib.typ \ No newline at end of file diff --git a/showbits-thermal-printer/src/documents/image/main.typ b/showbits-thermal-printer/src/documents/image/main.typ new file mode 100644 index 0000000..78cbbbd --- /dev/null +++ b/showbits-thermal-printer/src/documents/image/main.typ @@ -0,0 +1,4 @@ +#import "lib.typ"; +#show: it => lib.init(it) + +#lib.dither("image.png") diff --git a/showbits-thermal-printer/src/documents/image/mod.rs b/showbits-thermal-printer/src/documents/image/mod.rs new file mode 100644 index 0000000..80d35d5 --- /dev/null +++ b/showbits-thermal-printer/src/documents/image/mod.rs @@ -0,0 +1,24 @@ +use std::io::Cursor; + +use anyhow::Context; +use image::{ImageFormat, RgbaImage}; +use showbits_typst::Typst; + +pub struct Image { + pub image: RgbaImage, +} + +impl Image { + pub fn into_typst(self) -> anyhow::Result { + let mut bytes: Vec = Vec::new(); + self.image + .write_to(&mut Cursor::new(&mut bytes), ImageFormat::Png) + .context("failed to encode image as png")?; + + let typst = super::typst_with_lib() + .with_file("/image.png", bytes) + .with_main_file(include_str!("main.typ")); + + Ok(typst) + } +} diff --git a/showbits-thermal-printer/src/documents/image/plugin.wasm b/showbits-thermal-printer/src/documents/image/plugin.wasm new file mode 120000 index 0000000..04468b6 --- /dev/null +++ b/showbits-thermal-printer/src/documents/image/plugin.wasm @@ -0,0 +1 @@ +../plugin.wasm \ No newline at end of file diff --git a/showbits-thermal-printer/src/documents/lib.typ b/showbits-thermal-printer/src/documents/lib.typ index f11798c..f64baf7 100644 --- a/showbits-thermal-printer/src/documents/lib.typ +++ b/showbits-thermal-printer/src/documents/lib.typ @@ -20,4 +20,14 @@ // the same size after tearing off the paper. #let feed = v(64pt + 32pt) -#import plugin("plugin.wasm") as plugin +//////////// +// Plugin // +//////////// + +#import plugin("plugin.wasm") as p + +#let dither(path) = { + let bytes = read(path, encoding: none) + let dithered = p.dither(bytes) + image(dithered) +} diff --git a/showbits-thermal-printer/src/main.rs b/showbits-thermal-printer/src/main.rs index da5ef48..0770812 100644 --- a/showbits-thermal-printer/src/main.rs +++ b/showbits-thermal-printer/src/main.rs @@ -1,8 +1,8 @@ +mod documents; mod drawer; mod persistent_printer; mod printer; mod server; -mod documents; use std::{path::PathBuf, time::Duration}; diff --git a/showbits-thermal-printer/src/server.rs b/showbits-thermal-printer/src/server.rs index 602c996..4590281 100644 --- a/showbits-thermal-printer/src/server.rs +++ b/showbits-thermal-printer/src/server.rs @@ -14,7 +14,7 @@ use showbits_common::widgets::DitherAlgorithm; use tokio::{net::TcpListener, sync::mpsc}; use crate::{ - documents::Text, + documents::{Image, Text}, drawer::{ CalendarDrawing, CellsDrawing, ChatMessageDrawing, Command, EggDrawing, ImageDrawing, NewTypstDrawing, PhotoDrawing, TextDrawing, TicTacToeDrawing, TypstDrawing, @@ -40,6 +40,7 @@ pub async fn run(tx: mpsc::Sender, addr: String) -> anyhow::Result<()> .route("/tictactoe", post(post_tictactoe)) .route("/typst", post(post_typst).fallback(get_static_file)) .route("/test", post(post_test).fallback(get_static_file)) + .route("/test2", post(post_test2).fallback(get_static_file)) .fallback(get(get_static_file)) .layer(DefaultBodyLimit::max(32 * 1024 * 1024)) // 32 MiB .with_state(Server { tx }); @@ -235,3 +236,33 @@ async fn post_test(server: State, request: Form) { .send(Command::draw(NewTypstDrawing::new(request.0))) .await; } + +// /test2 + +async fn post_test2(server: State, mut multipart: Multipart) -> somehow::Result { + let mut image = None; + + while let Some(field) = multipart.next_field().await? { + match field.name() { + Some("image") => { + let data = field.bytes().await?; + let decoded = image::load_from_memory(&data)?.into_rgba8(); + image = Some(decoded); + } + _ => {} + } + } + + let Some(image) = image else { + return Ok(status_code(StatusCode::UNPROCESSABLE_ENTITY)); + }; + + let image = Image { image }.into_typst().map_err(somehow::Error)?; + + let _ = server + .tx + .send(Command::draw(NewTypstDrawing::new(image))) + .await; + + Ok(().into_response()) +} diff --git a/showbits-typst-plugin/Cargo.toml b/showbits-typst-plugin/Cargo.toml index af236df..797e871 100644 --- a/showbits-typst-plugin/Cargo.toml +++ b/showbits-typst-plugin/Cargo.toml @@ -6,9 +6,11 @@ edition = { workspace = true } [lib] crate-type = ["cdylib"] -[dependencies.wasm-minimal-protocol] -git = "https://github.com/astrale-sharp/wasm-minimal-protocol.git" -rev = "90336ebf2d99844fd8f8e99ea7096af96526cbf4" +[dependencies] +image = { workspace = true, features = ["png"] } +mark = { workspace = true } +palette = { workspace = true } +wasm-minimal-protocol = { workspace = true } [profile.release] lto = true # Enable link-time optimization diff --git a/showbits-typst-plugin/src/lib.rs b/showbits-typst-plugin/src/lib.rs index 5395d56..d73e6f0 100644 --- a/showbits-typst-plugin/src/lib.rs +++ b/showbits-typst-plugin/src/lib.rs @@ -1,8 +1,29 @@ -use wasm_minimal_protocol::*; +use std::io::Cursor; + +use image::ImageFormat; +use mark::dither::{AlgoFloydSteinberg, Algorithm, DiffEuclid, Palette}; +use palette::LinSrgb; +use wasm_minimal_protocol::{initiate_protocol, wasm_func}; initiate_protocol!(); #[wasm_func] -pub fn debug_print(arg: &[u8]) -> Vec { - format!("{arg:?}").into_bytes() +pub fn dither(image: &[u8]) -> Result, String> { + let image = image::load_from_memory(image) + .map_err(|it| format!("Failed to read image: {it:?}"))? + .to_rgba8(); + + let palette = Palette::new(vec![ + LinSrgb::new(0.0, 0.0, 0.0), + LinSrgb::new(1.0, 1.0, 1.0), + ]); + + let dithered = >::run(image, &palette); + + let mut bytes: Vec = Vec::new(); + dithered + .write_to(&mut Cursor::new(&mut bytes), ImageFormat::Png) + .map_err(|it| format!("Failed to write image: {it:?}"))?; + + Ok(bytes) }