From 6112a8c02fdc0dc017bdca256a3d9d5660521ff8 Mon Sep 17 00:00:00 2001 From: Joscha Date: Sat, 1 Mar 2025 14:18:52 +0100 Subject: [PATCH] Dither images to correct width --- .../src/documents/lib.typ | 18 ++++-- showbits-typst-plugin/src/lib.rs | 58 ++++++++++++++++++- 2 files changed, 69 insertions(+), 7 deletions(-) diff --git a/showbits-thermal-printer/src/documents/lib.typ b/showbits-thermal-printer/src/documents/lib.typ index f64baf7..66d6287 100644 --- a/showbits-thermal-printer/src/documents/lib.typ +++ b/showbits-thermal-printer/src/documents/lib.typ @@ -26,8 +26,18 @@ #import plugin("plugin.wasm") as p -#let dither(path) = { - let bytes = read(path, encoding: none) - let dithered = p.dither(bytes) - image(dithered) +#let _length_to_bytes(len) = { + let len = len.pt() + let n = if len > 10000 { -1 } else { int(len) } + n.to-bytes(size: 8) } + +#let dither(path) = layout(size => { + let bytes = read(path, encoding: none) + let dithered = p.dither( + bytes, + _length_to_bytes(size.width), + _length_to_bytes(size.height), + ) + image(dithered) +}) diff --git a/showbits-typst-plugin/src/lib.rs b/showbits-typst-plugin/src/lib.rs index d73e6f0..fc3104a 100644 --- a/showbits-typst-plugin/src/lib.rs +++ b/showbits-typst-plugin/src/lib.rs @@ -1,18 +1,57 @@ use std::io::Cursor; -use image::ImageFormat; +use image::{ + ImageFormat, + imageops::{self, FilterType}, +}; use mark::dither::{AlgoFloydSteinberg, Algorithm, DiffEuclid, Palette}; use palette::LinSrgb; use wasm_minimal_protocol::{initiate_protocol, wasm_func}; initiate_protocol!(); +fn i64_from_bytes(bytes: &[u8]) -> Result { + let bytes: [u8; 8] = bytes.try_into().map_err(|it| format!("{it}"))?; + Ok(i64::from_le_bytes(bytes)) +} + +fn size_from_i64(size: i64) -> Result, String> { + if size < 0 { + return Ok(None); // Unlimited width + } + + let size: u32 = size.try_into().map_err(|_| "size too large")?; + Ok(Some(size)) +} + #[wasm_func] -pub fn dither(image: &[u8]) -> Result, String> { - let image = image::load_from_memory(image) +pub fn dither(image: &[u8], max_width: &[u8], max_height: &[u8]) -> Result, String> { + let max_width = size_from_i64(i64_from_bytes(max_width)?)?; + let max_height = size_from_i64(i64_from_bytes(max_height)?)?; + + let mut image = image::load_from_memory(image) .map_err(|it| format!("Failed to read image: {it:?}"))? .to_rgba8(); + let image_width = image.width(); + let image_height = image.height(); + + let scale_factor = match (max_width, max_height) { + (None, None) => 1.0, + (None, Some(height)) => height as f32 / image_height as f32, + (Some(width), None) => width as f32 / image_width as f32, + (Some(width), Some(height)) => { + (width as f32 / image_width as f32).min(height as f32 / image_height as f32) + } + }; + + let target_width = (image_width as f32 * scale_factor) as u32; + let target_height = (image_height as f32 * scale_factor) as u32; + + if image_width != target_width || image_height != target_height { + image = imageops::resize(&image, target_width, target_height, FilterType::CatmullRom); + } + let palette = Palette::new(vec![ LinSrgb::new(0.0, 0.0, 0.0), LinSrgb::new(1.0, 1.0, 1.0), @@ -27,3 +66,16 @@ pub fn dither(image: &[u8]) -> Result, String> { Ok(bytes) } + +#[wasm_func] +pub fn debug(value: &[u8]) -> Result, String> { + // let value: [u8; 4] = value + // .try_into() + // .map_err(|it| format!("incorrect number of bytes: {it}"))?; + + // let be = u32::from_be_bytes(value); + // let le = u32::from_le_bytes(value); + + // Ok(format!("be: {be}, le: {le}").into_bytes()) + Ok(format!("{value:?}").into_bytes()) +}