diff --git a/src/main.rs b/src/main.rs index 571bce4..54dd4ab 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,16 @@ -use std::{env, error::Error}; +use std::{ + env, + error::Error, + io::{self, BufWriter, Write}, +}; -use image::{DynamicImage, ImageReader, Luma}; +use image::{ImageReader, Luma}; + +const BRAILLE_FIRST_BYTE: u8 = 0xe2; +const BRAILLE_SECOND_BYTE: u8 = 0xa0; +const BRAILLE_HEIGHT: u8 = 4; +const BRAILLE_BIT_MAPPING: [[u8; BRAILLE_HEIGHT as usize]; 2] = + [[0x1, 0x2, 0x4, 0x40], [0x8, 0x10, 0x20, 0x80]]; #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)] fn main() -> Result<(), Box> { @@ -12,16 +22,25 @@ fn main() -> Result<(), Box> { ]; let src = env::args().nth(1).ok_or("i need a source path")?; - let out_path = env::args().nth(2).ok_or("i need an output path")?; let mut img = ImageReader::open(&src)?.decode()?.to_luma32f(); - let mut out = img.clone(); + + let mut out = BufWriter::new(io::stdout().lock()); + + let mut output_line = vec![0u8; img.width().div_ceil(2) as usize]; + let mut lines_buffered: u8 = 0; + for y in 0..img.height() { for x in 0..img.width() { let old_pixel = img.get_pixel(x, y); let old_luma = old_pixel.0[0]; - let luma = if old_luma < 0.5 { 0. } else { 1. }; + let white = old_luma > 0.5; + let luma = if white { 1. } else { 0. }; - out.put_pixel(x, y, Luma([luma])); + // white pixels are filled because i'm targetting a dark mode site + if white { + output_line[(x / 2) as usize] |= + BRAILLE_BIT_MAPPING[(x % 2) as usize][(y % 4) as usize]; + } let error = old_luma - luma; for (dx, dy, quant) in kernel { @@ -39,7 +58,29 @@ fn main() -> Result<(), Box> { img.put_pixel(x, y, Luma([corrected])); } } + + lines_buffered += 1; + if lines_buffered == BRAILLE_HEIGHT { + for byte in &output_line { + if *byte == 0 { + out.write_all(b" ").unwrap(); + continue; + } + + let upper_2 = byte >> 6; + let lower_6 = byte & 0b111_111; + out.write_all(&[ + BRAILLE_FIRST_BYTE, + BRAILLE_SECOND_BYTE | upper_2, + 0b10_000000 | lower_6, + ]) + .unwrap(); + } + out.write_all(b"\n").unwrap(); + + output_line.fill(0); + lines_buffered = 0; + } } - DynamicImage::from(out).into_luma8().save(out_path)?; Ok(()) }