fixed resizing image

This commit is contained in:
Djairo Hougee 2023-02-08 17:04:22 +01:00
parent 5a1eda07da
commit b00ef93c1c
5 changed files with 86 additions and 30 deletions

View File

@ -1,4 +1,6 @@
use std::process::exit;
use image::{DynamicImage, GenericImageView, Rgba}; 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? //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>)) -> u8 { fn get_color(pixel: (u32, u32, Rgba<u8>)) -> u8 {
@ -8,7 +10,6 @@ fn get_color(pixel: (u32, u32, Rgba<u8>)) -> u8 {
} }
fn to_ascii(char_map: String, image: DynamicImage) -> Vec<String> { fn to_ascii(char_map: String, image: DynamicImage) -> Vec<String> {
//todo: add color support
let l = char_map.len() as f32; let l = char_map.len() as f32;
let mut str = String::new(); let mut str = String::new();
let mut out: Vec<String> = Vec::new(); let mut out: Vec<String> = Vec::new();
@ -39,6 +40,11 @@ pub fn to_braille_ascii(image: DynamicImage) -> Vec<String> {
} }
pub fn to_custom_ascii(char_map: String, image: DynamicImage) -> Vec<String> { pub fn to_custom_ascii(char_map: String, image: DynamicImage) -> Vec<String> {
//todo: this if char_map.is_empty() {
vec!["not implemented".to_owned()] error!("Custom map can not be empty!");
exit(1);
}
to_ascii(char_map, image)
} }
//todo: replace Vec<String> with a custom struct containing rgb information as well as character (for coloured output)

View File

@ -3,7 +3,6 @@ use std::path::PathBuf;
use std::process::exit; use std::process::exit;
use clap::Parser; use clap::Parser;
use log::{debug, error}; use log::{debug, error};
use image;
/// Convert an image to ASCII art and print it to the terminal. /// Convert an image to ASCII art and print it to the terminal.
/// ///
@ -11,23 +10,40 @@ use image;
#[derive(Parser)] #[derive(Parser)]
pub struct Cli { pub struct Cli {
/// Use a larger range of characters /// Use a larger range of characters
///
/// Takes priority over '--map'
#[arg(short = 'c', long)] #[arg(short = 'c', long)]
pub complex: bool, pub complex: bool,
/// Display ASCII art in full colour /// Display ASCII art in full colour
#[arg(short = 'C', long)] #[arg(short = 'C', long)]
pub colour: bool, pub colour: bool,
/// Use braille characters instead of ASCII /// Use braille characters instead of ASCII
///
/// Takes priority over '--complex' and '--map'
#[arg(short = 'b', long)] #[arg(short = 'b', long)]
pub braille: bool, pub braille: bool,
/// Print debugging information /// Print debugging information
#[arg(short = 'd', long)] #[arg(short = 'd', long)]
pub debug: bool, 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<String>,
/// Image path /// Image path
pub image: PathBuf, pub image: PathBuf,
#[arg(short = 'w', long)]
/// Set the width of the output, instead of using terminal width /// Set the width of the output, instead of using terminal width
pub width: Option<u16>, #[arg(short = 'w', long)]
pub width: Option<u32>,
/// Save the output to a file, instead of printing to terminal /// Save the output to a file, instead of printing to terminal
///
/// Incompatible with '--colour' and '--braille'
#[arg(short = 'o', long = "output")] #[arg(short = 'o', long = "output")]
pub output: Option<PathBuf>, pub output: Option<PathBuf>,
} }
@ -39,7 +55,13 @@ impl Cli {
debug!("colour: {}", self.colour); debug!("colour: {}", self.colour);
debug!("braille: {}", self.braille); debug!("braille: {}", self.braille);
debug!("debug: {}", self.debug); 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()); debug!("image: {}", self.image.display());
if let Some(output) = self.output.clone() { if let Some(output) = self.output.clone() {
debug!("output: {}", output.display()); debug!("output: {}", output.display());

View File

@ -1,15 +1,15 @@
use std::cmp::min;
use termion::terminal_size; use termion::terminal_size;
use log::error; use log::{debug, error, trace};
use std::process::exit; use std::process::exit;
use std::path::PathBuf; use std::path::PathBuf;
use image::DynamicImage; use image::DynamicImage;
use image::imageops::FilterType;
fn get_terminal_size() -> u32 { fn get_terminal_size() -> (u32, u32) {
let size = terminal_size(); let size = terminal_size();
match size { match size {
Ok(size) => { Ok(size) => {
size.0 as u32 (size.0 as u32, size.1 as u32)
} }
Err(e) => { Err(e) => {
error!("Failed to get terminal size: {}", e.to_string()); error!("Failed to get terminal size: {}", e.to_string());
@ -18,12 +18,26 @@ fn get_terminal_size() -> u32 {
} }
} }
pub fn get_size(w: Option<u16>, img_w: u32) -> u16 { pub fn resize_image(img: DynamicImage, full: bool, opt_w: Option<u32>) -> DynamicImage {
if None == w { let (mut w, mut h) = (1,1);
min(get_terminal_size(), img_w) as u16 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 { } 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 { pub fn open_image(path: PathBuf) -> DynamicImage {

View File

@ -1,8 +1,8 @@
use clap::Parser; use clap::Parser;
use image::imageops::FilterType; use crate::ascii_manipulation::*;
use crate::ascii_manipulation::to_simple_ascii;
use crate::cli::Cli; use crate::cli::Cli;
use crate::output::print_terminal; use crate::output::*;
use crate::image_manipulation::*;
mod cli; mod cli;
mod image_manipulation; mod image_manipulation;
@ -10,7 +10,9 @@ mod ascii_manipulation;
mod output; mod output;
//todo: general //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() { fn main() {
@ -21,21 +23,33 @@ fn main() {
cli.init(); cli.init();
cli.validate(); cli.validate();
let mut img = image_manipulation::open_image(cli.image); //preprocess image
let w = image_manipulation::get_size(cli.width, img.width()); 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? //converting to ASCII
let h: u32 = (img.height() as f32 * w as f32 / img.width() as f32 * 0.5) as u32; let out: Vec<String>;
img = img.resize_exact(w as u32, h, FilterType::CatmullRom); 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); //output
if let Some(output) = cli.output {
print_terminal(out); print_file(out, output);
} else {
print_terminal(out, cli.colour);
}
//todo: //todo:
/* function that converts image to braille (if -b) /* function that converts image to braille (if -b)
* something about printing in colour * something about printing in colour
* a function to let the user define a custom map * output to file
*/ */
} }

View File

@ -1,11 +1,11 @@
use std::path::PathBuf; use std::path::PathBuf;
pub fn print_terminal(ascii: Vec<String>) { pub fn print_terminal(ascii: Vec<String>, in_colour: bool) {
for line in ascii { for line in ascii {
println!("{}", line); println!("{}", line);
} }
} }
pub fn print_file(ascii: Vec<String>, out: PathBuf) { pub fn print_file(ascii: Vec<String>, out: PathBuf) {
//todo: output //todo: this
} }