From b00ef93c1cc9f9f54cb6ddbd929611fb33c7e464 Mon Sep 17 00:00:00 2001 From: djairoh Date: Wed, 8 Feb 2023 17:04:22 +0100 Subject: [PATCH] fixed resizing image --- src/ascii_manipulation.rs | 12 +++++++++--- src/cli.rs | 30 +++++++++++++++++++++++++---- src/image_manipulation.rs | 30 +++++++++++++++++++++-------- src/main.rs | 40 ++++++++++++++++++++++++++------------- src/output.rs | 4 ++-- 5 files changed, 86 insertions(+), 30 deletions(-) diff --git a/src/ascii_manipulation.rs b/src/ascii_manipulation.rs index 4d0ff25..9250597 100644 --- a/src/ascii_manipulation.rs +++ b/src/ascii_manipulation.rs @@ -1,4 +1,6 @@ +use std::process::exit; use image::{DynamicImage, GenericImageView, Rgba}; +use log::error; //todo: consider how to take care of the a channel => do we want to render that as background? fn get_color(pixel: (u32, u32, Rgba)) -> u8 { @@ -8,7 +10,6 @@ fn get_color(pixel: (u32, u32, Rgba)) -> u8 { } fn to_ascii(char_map: String, image: DynamicImage) -> Vec { - //todo: add color support let l = char_map.len() as f32; let mut str = String::new(); let mut out: Vec = Vec::new(); @@ -39,6 +40,11 @@ pub fn to_braille_ascii(image: DynamicImage) -> Vec { } pub fn to_custom_ascii(char_map: String, image: DynamicImage) -> Vec { - //todo: this - vec!["not implemented".to_owned()] + if char_map.is_empty() { + error!("Custom map can not be empty!"); + exit(1); + } + to_ascii(char_map, image) } + +//todo: replace Vec with a custom struct containing rgb information as well as character (for coloured output) \ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index 0b6cff5..c311c8b 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -3,7 +3,6 @@ use std::path::PathBuf; use std::process::exit; use clap::Parser; use log::{debug, error}; -use image; /// Convert an image to ASCII art and print it to the terminal. /// @@ -11,23 +10,40 @@ use image; #[derive(Parser)] pub struct Cli { /// Use a larger range of characters + /// + /// Takes priority over '--map' #[arg(short = 'c', long)] pub complex: bool, /// Display ASCII art in full colour #[arg(short = 'C', long)] pub colour: bool, /// Use braille characters instead of ASCII + /// + /// Takes priority over '--complex' and '--map' #[arg(short = 'b', long)] pub braille: bool, /// Print debugging information #[arg(short = 'd', long)] pub debug: bool, + /// use the full width of the terminal + /// + /// Takes priority over '--width' + #[arg(short = 'f', long)] + pub full: bool, + /// use a custom character map for output + /// + /// The character map is interpreted from dark(first character) to light(last character). + /// The map can be any size, but at least 2 characters is recommended. + #[arg(short = 'M', long)] + pub map: Option, /// Image path pub image: PathBuf, - #[arg(short = 'w', long)] /// Set the width of the output, instead of using terminal width - pub width: Option, + #[arg(short = 'w', long)] + pub width: Option, /// Save the output to a file, instead of printing to terminal + /// + /// Incompatible with '--colour' and '--braille' #[arg(short = 'o', long = "output")] pub output: Option, } @@ -39,7 +55,13 @@ impl Cli { debug!("colour: {}", self.colour); debug!("braille: {}", self.braille); debug!("debug: {}", self.debug); - debug!("width: {}", self.width.unwrap_or(u16::MAX)); + debug!("full: {}", self.full); + if let Some(map) = self.map.clone() { + debug!("map: \"{}\"", map); + } else { + debug!("map: None"); + } + debug!("width: {}", self.width.unwrap_or(u32::MAX)); debug!("image: {}", self.image.display()); if let Some(output) = self.output.clone() { debug!("output: {}", output.display()); diff --git a/src/image_manipulation.rs b/src/image_manipulation.rs index 0a9d4de..6b20531 100644 --- a/src/image_manipulation.rs +++ b/src/image_manipulation.rs @@ -1,15 +1,15 @@ -use std::cmp::min; use termion::terminal_size; -use log::error; +use log::{debug, error, trace}; use std::process::exit; use std::path::PathBuf; use image::DynamicImage; +use image::imageops::FilterType; -fn get_terminal_size() -> u32 { +fn get_terminal_size() -> (u32, u32) { let size = terminal_size(); match size { Ok(size) => { - size.0 as u32 + (size.0 as u32, size.1 as u32) } Err(e) => { error!("Failed to get terminal size: {}", e.to_string()); @@ -18,12 +18,26 @@ fn get_terminal_size() -> u32 { } } -pub fn get_size(w: Option, img_w: u32) -> u16 { - if None == w { - min(get_terminal_size(), img_w) as u16 +pub fn resize_image(img: DynamicImage, full: bool, opt_w: Option) -> DynamicImage { + let (mut w, mut h) = (1,1); + if full { + w = get_terminal_size().0; + h = (img.height() as f32 * w as f32 / img.width() as f32 * 0.5) as u32; + } else if let Some(act_w) = opt_w { + w = act_w; + h = (img.height() as f32 * w as f32 / img.width() as f32 * 0.5) as u32; } else { - w.unwrap() + let (max_w, max_h) = get_terminal_size(); + if max_h*max_w/2 > img.height()*img.width() { + h = max_h; + w = (img.width() as f32 * h as f32 / img.height() as f32 * 2.0) as u32; + } else { + w = max_w; + h = (img.height() as f32 * w as f32 / img.height() as f32 * 0.5) as u32; + } } + debug!("Resizing image to (w|h): {} | {}", w, h); + img.resize_exact(w, h, FilterType::CatmullRom) } pub fn open_image(path: PathBuf) -> DynamicImage { diff --git a/src/main.rs b/src/main.rs index 5c9d089..ace5433 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,8 @@ use clap::Parser; -use image::imageops::FilterType; -use crate::ascii_manipulation::to_simple_ascii; +use crate::ascii_manipulation::*; use crate::cli::Cli; -use crate::output::print_terminal; +use crate::output::*; +use crate::image_manipulation::*; mod cli; mod image_manipulation; @@ -10,7 +10,9 @@ mod ascii_manipulation; mod output; //todo: general -/* https://stackoverflow.com/questions/69981449/how-do-i-print-colored-text-to-the-terminal-in-rust +/* Documentation + * Readme + * https://stackoverflow.com/questions/69981449/how-do-i-print-colored-text-to-the-terminal-in-rust */ fn main() { @@ -21,21 +23,33 @@ fn main() { cli.init(); cli.validate(); - let mut img = image_manipulation::open_image(cli.image); - let w = image_manipulation::get_size(cli.width, img.width()); + //preprocess image + let mut img = open_image(cli.image); + img = resize_image(img, cli.full, cli.width); - //todo: change logic to include -full flag for max width, otherwise use max height? - let h: u32 = (img.height() as f32 * w as f32 / img.width() as f32 * 0.5) as u32; - img = img.resize_exact(w as u32, h, FilterType::CatmullRom); + //converting to ASCII + let out: Vec; + if cli.braille { + out = to_braille_ascii(img); + } else if cli.complex { + out = to_complex_ascii(img); + } else if let Some(map) = cli.map { + out = to_custom_ascii(map, img); + } else { + out = to_simple_ascii(img); + } - let out = to_simple_ascii(img); - - print_terminal(out); + //output + if let Some(output) = cli.output { + print_file(out, output); + } else { + print_terminal(out, cli.colour); + } //todo: /* function that converts image to braille (if -b) * something about printing in colour - * a function to let the user define a custom map + * output to file */ } diff --git a/src/output.rs b/src/output.rs index 2b74c41..49d305f 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,11 +1,11 @@ use std::path::PathBuf; -pub fn print_terminal(ascii: Vec) { +pub fn print_terminal(ascii: Vec, in_colour: bool) { for line in ascii { println!("{}", line); } } pub fn print_file(ascii: Vec, out: PathBuf) { - //todo: output + //todo: this } \ No newline at end of file