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 cb25727..bc33b6f 100644 --- a/src/dithering.rs +++ b/src/dithering.rs @@ -1,35 +1,32 @@ -use image::{DynamicImage, imageops}; +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; -pub fn atkinson_mono(img: &DynamicImage) -> Vec { - let mut gray = img.to_luma8(); - imageops::dither(&mut gray, &imageops::colorops::BiLevel); + 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); let (w, h) = (gray.width() as usize, gray.height() as usize); - let stride = (w + 7) >> 3; - let mut bits = vec![0u8; stride * h]; - let pix = gray.as_raw(); + let pitch = (w + 7) >> 3; + let mut bits = vec![0u8; pitch * h]; for y in 0..h { - let row_off = y * w; - let bit_off = y * stride; - let chunks = w >> 3; - let tail = w & 7; - - for x8 in 0..chunks { - let p = &pix[row_off + (x8 << 3)..][..8]; - let mut byte = 0u8; - for (bit, &v) in p.iter().enumerate() { - byte |= ((v < 128) as u8) << (7 - bit); + for x in 0..w { + 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; } - bits[bit_off + x8] = byte; - } - - if tail != 0 { - let mut last = 0u8; - for (bit, &v) in pix[row_off + (chunks << 3)..][..tail].iter().enumerate() { - last |= ((v < 128) as u8) << (7 - bit); - } - bits[bit_off + chunks] = last; } } bits 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;