diff --git a/out.png b/out.png new file mode 100644 index 0000000..e0285af Binary files /dev/null and b/out.png differ diff --git a/small.png b/small.png new file mode 100644 index 0000000..df37483 Binary files /dev/null and b/small.png differ diff --git a/src/dithering.rs b/src/dithering.rs index 8006276..bc33b6f 100644 --- a/src/dithering.rs +++ b/src/dithering.rs @@ -1,44 +1,31 @@ -use image::DynamicImage; +use image::{ + DynamicImage, GrayImage, + imageops::{self, colorops}, +}; +pub fn auto_brighten(gray: &mut GrayImage) -> () { + let avg = gray.pixels().map(|p| p.0[0] as u32).sum::() / gray.len() as u32; + + if avg < 100 { + colorops::brighten_in_place(gray, 40); + colorops::contrast_in_place(gray, 25.0); + } else if avg > 180 { + colorops::brighten_in_place(gray, -15); + } +} + +pub fn dither(gray: &mut GrayImage) -> Vec { + imageops::dither(gray, &imageops::colorops::BiLevel); -pub fn atkinson_mono(img: &DynamicImage) -> Vec { - let mut gray = img.to_luma8(); let (w, h) = (gray.width() as usize, gray.height() as usize); let pitch = (w + 7) >> 3; let mut bits = vec![0u8; pitch * h]; - let buf = gray.as_mut(); for y in 0..h { - let row = y * w; for x in 0..w { - let i = row + x; - let old = buf[i]; - let new = if old < 128 { 0 } else { 255 }; - buf[i] = new; - let bit = new == 0; - if bit { - let byte = y * pitch + (x >> 3); - bits[byte] |= 128 >> (x & 7); - } - let err = ((old as i16) - (new as i16)) / 8; - - if x + 1 < w { - buf[i + 1] = (buf[i + 1] as i16 + err).clamp(0, 255) as u8; - } - if x + 2 < w { - buf[i + 2] = (buf[i + 2] as i16 + err).clamp(0, 255) as u8; - } - if y + 1 < h { - let down = i + w; - if x > 0 { - buf[down - 1] = (buf[down - 1] as i16 + err).clamp(0, 255) as u8; - } - buf[down] = (buf[down] as i16 + err).clamp(0, 255) as u8; - if x + 1 < w { - buf[down + 1] = (buf[down + 1] as i16 + err).clamp(0, 255) as u8; - } - if y + 2 < h { - buf[down + w] = (buf[down + w] as i16 + err).clamp(0, 255) as u8; - } + let byte = y * pitch + (x >> 3); + let bit = 7 - (x & 7); + if gray[(x as u32, y as u32)].0[0] == 0 { + bits[byte] |= 1 << bit; } } } diff --git a/src/escpos.rs b/src/escpos.rs index d5c1b67..ffd49b2 100644 --- a/src/escpos.rs +++ b/src/escpos.rs @@ -1,5 +1,8 @@ -use crate::dithering::atkinson_mono; -use image::{DynamicImage, imageops::FilterType}; +use crate::dithering::{auto_brighten, dither}; +use image::{ + DynamicImage, + imageops::{FilterType, colorops}, +}; const MAX_WIDTH: u32 = 384; @@ -26,7 +29,10 @@ pub fn escpos_raster( let img = if swap { &img.rotate90() } else { &img }; let img = img.resize(w, h, FilterType::Triangle); - let mono = atkinson_mono(&img); + let mut gray = img.to_luma8(); + auto_brighten(&mut gray); + + let mono = dither(&mut gray); let (width_px, height_px) = (img.width() as u16, img.height() as u16); let width_bytes = ((width_px + 7) >> 3) as u16;