//! This module contains all functions related to converting images to ASCII. use std::process::exit; use image::{DynamicImage, GenericImageView, Rgba}; use log::error; use crate::model_rgb_ascii::Ascii; /// This constant is used to calculate braille values. const BRAILLE_TABLE: [[u8; 2]; 4] = [[1u8, 8u8], [2u8, 16u8], [4u8, 32u8], [64u8, 128u8]]; /// This function returns the colour depth of an input pixel. /// /// It does so using the luminosity method /// (0.3*r + 0.59*g + 0.11*b) /// /// arguments: /// pixel: Rgba - pixel to return colour from /// /// returns: /// u8 representing colour depth of pixel fn get_color(pixel: Rgba) -> u8 { //luminosity method of getting lightness (pixel[0] as f32 * 0.3 + pixel[1] as f32 * 0.59 + pixel[2] as f32 * 0.11) as u8 } /// This function converts a given image into ASCII. /// /// arguments: /// char_map: String - the characters to convert the image into /// image: DynamicImage - the image to convert into char_map's characters /// /// returns: /// Vec> containing the converted image fn to_ascii(char_map: String, image: DynamicImage) -> Vec> { let l = char_map.len() as f32; let mut str: Vec = Vec::new(); let mut out: Vec> = Vec::new(); for pixel in image.pixels() { let mut ascii = Ascii::new(0, pixel.2[0], pixel.2[1], pixel.2[2]); let ch = char_map.as_bytes()[((ascii.col_depth as f32-1.0)/255f32 * l) as usize]; ascii.char = char::from(ch); str.push(ascii); if pixel.0 == image.width()-1 { out.push(str); str = Vec::new(); } } out } /// This wrapper function converts an image to standard ASCII. /// /// It uses the char_map " .:-=+*#%@" /// /// arguments: /// image: DynamicImage - the image to convert /// /// returns: /// Vec> containing the converted image pub fn to_simple_ascii(image: DynamicImage) -> Vec> { to_ascii(" .:-=+*#%@".to_owned(), image) } /// This wrapper function converts an image to extended ASCII. /// /// It uses the char_map " .'`^",:;Il!i><~+_-?][}{1)(|\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$" /// /// arguments: /// image: DynamicImage - the image to convert /// /// returns: /// Vec> containing the converted image pub fn to_complex_ascii(image: DynamicImage) -> Vec> { to_ascii(" .'`^\",:;Il!i><~+_-?][}{1)(|\\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$".to_owned(), image) } /// This wrapper function converts an image to custom ASCII. /// /// arguments: /// char_map: String - the custom character map to convert image into /// image: DynamicImage - the image to convert /// /// returns: /// Vec> containing the converted image 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) } /// This function converts an image to braille ASCII. /// /// arguments: /// image: DynamicImage - the image to convert /// /// returns: /// Vec> containing the converted image pub fn to_braille_ascii(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::new_with_char(char::from_u32(braille_value).unwrap(), (r/8) as u8, (g/8) as u8, (b/8) as u8)); } out.push(str); str = Vec::new(); } out }