From 2b9817080e5281b3a540b6ed878925227208fdc2 Mon Sep 17 00:00:00 2001 From: djairoh Date: Wed, 8 Feb 2023 22:02:49 +0100 Subject: [PATCH] implemented braille support, including --threshold flag --- src/ascii_manipulation.rs | 52 ++++++++++++++++++++++++++++++--------- src/cli.rs | 5 ++++ src/image_manipulation.rs | 10 ++++++-- src/main.rs | 4 +-- 4 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/ascii_manipulation.rs b/src/ascii_manipulation.rs index 6cf4cbf..70887ea 100644 --- a/src/ascii_manipulation.rs +++ b/src/ascii_manipulation.rs @@ -1,12 +1,13 @@ use std::process::exit; -use image::{DynamicImage, GenericImageView, Rgba}; +use image::{DynamicImage, GenericImageView, imageops, Rgba}; use log::error; use crate::model_rgb_ascii::Ascii; -fn get_color(pixel: (u32, u32, Rgba)) -> u8 { - let vec = pixel.2.0; +const BRAILLE_TABLE: [[u8; 2]; 4] = [[1u8, 8u8], [2u8, 16u8], [4u8, 32u8], [64u8, 128u8]]; + +fn get_color(pixel: Rgba) -> u8 { //luminosity method of getting lightness - (vec[0] as f32 * 0.3 + vec[1] as f32 * 0.59 + vec[2] as f32 * 0.11) as u8 + (pixel[0] as f32 * 0.3 + pixel[1] as f32 * 0.59 + pixel[2] as f32 * 0.11) as u8 } fn to_ascii(char_map: String, image: DynamicImage) -> Vec> { @@ -15,8 +16,8 @@ fn to_ascii(char_map: String, image: DynamicImage) -> Vec> { let mut out: Vec> = Vec::new(); for pixel in image.pixels() { - let ch = char_map.as_bytes()[((get_color(pixel) as f32-1.0)/255f32 * l) as usize]; - //fixme: might break with non-ASCII char_map (ie braille chars, possibly) + let ch = char_map.as_bytes()[((get_color(pixel.2) as f32-1.0)/255f32 * l) as usize]; + //fixme: might break with non-ASCII char_map str.push(Ascii::new(ch, pixel.2[0], pixel.2[1], pixel.2[2])); if pixel.0 == image.width()-1 { @@ -35,15 +36,42 @@ pub fn to_complex_ascii(image: DynamicImage) -> Vec> { to_ascii(" .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$".to_owned(), image) } -pub fn to_braille_ascii(image: DynamicImage) -> Vec> { - //todo: figure out braille symbols - vec![vec![Ascii::new(0, 0, 0, 0)]] -} - pub fn to_custom_ascii(char_map: String, image: DynamicImage) -> Vec> { if char_map.is_empty() { error!("Custom map can not be empty!"); exit(1); } to_ascii(char_map, image) -} \ No newline at end of file +} + +pub fn to_braille_ascii(mut image: DynamicImage, threshold: u8) -> Vec> { + let mut str: Vec = Vec::new(); + let mut out: Vec> = Vec::new(); + + //you know your code is good when you have a quadruple nested for loop + for y in (0..image.height()).step_by(4) { + for x in (0..image.width()).step_by(2) { + let (mut r, mut g, mut b) = (0u16, 0u16, 0u16); + let mut braille_value: u32 = 10240; + for nx in 0..2 { + for ny in 0..4 { + let pixel = image.get_pixel(x+nx, y+ny); + r = r+pixel[0] as u16; + g = g+pixel[1] as u16; + b = b+pixel[2] as u16; + if get_color(pixel) >= threshold { + braille_value += BRAILLE_TABLE[ny as usize][nx as usize] as u32; + } + } + } + str.push(Ascii { char: char::from_u32(braille_value).unwrap(), rgb: [(r/8) as u8, (g/8) as u8, (b/8) as u8] }); + } + out.push(str); + str = Vec::new(); + } + out +} +pub fn to_box_ascii(image: DynamicImage) -> Vec> { + //todo: this one + vec![vec![Ascii::new(0, 0, 0,0 )]] +} diff --git a/src/cli.rs b/src/cli.rs index c311c8b..d8f2b6b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -30,6 +30,11 @@ pub struct Cli { /// Takes priority over '--width' #[arg(short = 'f', long)] pub full: bool, + /// use a custom threshold when converting to braille + /// + /// default is 128 + #[arg(default_value_t = 128, long)] + pub threshold: u8, /// use a custom character map for output /// /// The character map is interpreted from dark(first character) to light(last character). diff --git a/src/image_manipulation.rs b/src/image_manipulation.rs index 627e40f..bfbbb82 100644 --- a/src/image_manipulation.rs +++ b/src/image_manipulation.rs @@ -1,5 +1,5 @@ use termion::terminal_size; -use log::{debug, error}; +use log::{debug, error, trace}; use std::process::exit; use std::path::PathBuf; use image::DynamicImage; @@ -18,7 +18,7 @@ fn get_terminal_size() -> (u32, u32) { } } -pub fn resize_image(img: DynamicImage, full: bool, opt_w: Option) -> DynamicImage { +pub fn resize_image(img: DynamicImage, full: bool, braille: bool, opt_w: Option) -> DynamicImage { let (mut w, mut h) = (1,1); let (max_w, max_h) = get_terminal_size(); if full { @@ -36,6 +36,12 @@ pub fn resize_image(img: DynamicImage, full: bool, opt_w: Option) -> Dynami h = (img.height() as f32 * w as f32 / img.width() as f32 * 0.5) as u32; } } + + //braille has a 2x4 character map + if braille { + w *= 2; + h *= 4; + } debug!("Resizing image to (w|h): {} | {}", w, h); img.resize_exact(w, h, FilterType::CatmullRom) } diff --git a/src/main.rs b/src/main.rs index f373371..e0d2550 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,12 +28,12 @@ fn main() { //preprocess image let mut img = open_image(cli.image); - img = resize_image(img, cli.full, cli.width); + img = resize_image(img, cli.full, cli.braille, cli.width); //converting to ASCII let out: Vec>; if cli.braille { - out = to_braille_ascii(img); + out = to_braille_ascii(img, cli.threshold); } else if cli.complex { out = to_complex_ascii(img); } else if let Some(map) = cli.map {