Modular output methods #5

Merged
giulio-Joshi merged 1 commit from bufferedOutput into master 2022-10-10 21:59:52 +00:00
4 changed files with 56 additions and 58 deletions
Showing only changes of commit b4c7738d69 - Show all commits

View file

@ -1,6 +1,6 @@
pub mod args { pub mod args {
use clap::{Parser, arg, ColorChoice};
use super::enums::*; use super::enums::*;
use clap::{arg, ColorChoice, Parser};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None, color = ColorChoice::Always)] #[command(author, version, about, long_about = None, color = ColorChoice::Always)]
@ -29,7 +29,7 @@ pub mod args {
impl Arguments { impl Arguments {
pub fn validate(&self) -> Result<(), String> { pub fn validate(&self) -> Result<(), String> {
if self.characters.len() == 0 { if self.characters.is_empty() {
return Err("No characters provided".to_string()); return Err("No characters provided".to_string());
} else if self.characters.len() == 1 { } else if self.characters.len() == 1 {
if self.mode == Mode::NormalAscii { if self.mode == Mode::NormalAscii {
@ -53,7 +53,7 @@ pub mod enums {
NormalAscii, NormalAscii,
/// Colored ASCII art, the colors are based on the terminal colors /// Colored ASCII art, the colors are based on the terminal colors
#[clap(alias = "c")] #[clap(alias = "c")]
COLORED, Colored,
} }
#[derive(Copy, Clone, ValueEnum, Debug, PartialOrd, Eq, PartialEq)] #[derive(Copy, Clone, ValueEnum, Debug, PartialOrd, Eq, PartialEq)]

View file

@ -1,41 +1,50 @@
use image::{GenericImageView, DynamicImage}; use crate::args::{args::Arguments, enums::Mode};
use colored::{ColoredString, Colorize}; use colored::{ColoredString, Colorize};
use crate::args::{ use image::{DynamicImage, GenericImageView};
args::Arguments,
enums::Mode,
};
pub fn generate_ascii(image: DynamicImage, args: &Arguments) -> Vec<ColoredString> { use std::io::{self, BufWriter, Write};
pub fn generate_ascii<W: Write>(
image: DynamicImage,
args: &Arguments,
mut buffer: BufWriter<W>,
) -> io::Result<()> {
let characters = args.characters.chars().collect::<Vec<char>>(); let characters = args.characters.chars().collect::<Vec<char>>();
trace!("Characters: {:?}, length: {}", characters, characters.len());
let mut output = Vec::new();
let (width, height) = image.dimensions(); let (width, height) = image.dimensions();
for y in 0..height { for y in 0..height {
for x in 0..width { for x in 0..width {
if y % (args.scale * 2) == 0 && x % args.scale == 0 { if y % (args.scale * 2) == 0 && x % args.scale == 0 {
output.push(get_character( let element = get_character(
image.get_pixel(x, y), image.get_pixel(x, y),
&characters, args.mode, &characters,
args.mode,
&args.background, &args.background,
)); );
buffer.write_all(format!("{element}").as_bytes())?;
} }
} }
// Add a new line at the end of each row // Add a new line at the end of each row
if y % (args.scale * 2) == 0 { if y % (args.scale * 2) == 0 {
output.push("\n".into()); buffer.write_all("\n".as_bytes())?;
} }
} }
output Ok(())
} }
fn get_character( fn get_character(
pixel: image::Rgba<u8>, pixel: image::Rgba<u8>,
characters: &Vec<char>, mode: Mode, characters: &Vec<char>,
mode: Mode,
background: &Option<String>, background: &Option<String>,
) -> ColoredString { ) -> ColoredString {
let intent = if pixel[3] == 0 { 0 } else { pixel[0] / 3 + pixel[1] / 3 + pixel[2] / 3 }; let intent = if pixel[3] == 0 {
0
} else {
pixel[0] / 3 + pixel[1] / 3 + pixel[2] / 3
};
let ch = characters[(intent / (32 + 7 - (7 + (characters.len() - 7)) as u8)) as usize]; let ch = characters[(intent / (32 + 7 - (7 + (characters.len() - 7)) as u8)) as usize];
@ -43,14 +52,11 @@ fn get_character(
let ch = match mode { let ch = match mode {
Mode::NormalAscii => ColoredString::from(&*ch), Mode::NormalAscii => ColoredString::from(&*ch),
Mode::COLORED => { Mode::Colored => ch.truecolor(pixel[0], pixel[1], pixel[2]),
ch.to_string()
.truecolor(pixel[0], pixel[1], pixel[2])
}
}; };
match background { match background {
Some(bg) => ch.on_color(bg.to_string()), Some(bg) => ch.on_color(bg.to_string()),
None => ch None => ch,
} }
} }

View file

@ -1,23 +1,22 @@
use std::io::Write;
use clap::Parser; use clap::Parser;
use image::GenericImageView; use image::GenericImageView;
use std::io::Result;
extern crate pretty_env_logger; extern crate pretty_env_logger;
#[macro_use] #[macro_use]
extern crate log; extern crate log;
mod args; mod args;
mod ascii_processor; mod ascii_processor;
mod output;
use crate::args::{ use crate::args::{args::Arguments, enums::OutputMethod};
args::Arguments,
enums::OutputMethod,
};
use crate::ascii_processor::generate_ascii; use crate::ascii_processor::generate_ascii;
fn main() { fn main() -> Result<()> {
// Initialize the logger // Initialize the logger
pretty_env_logger::init(); pretty_env_logger::init();
info!("Successfully initialized logger"); info!("Successfully initialized logger");
@ -51,34 +50,7 @@ fn main() {
info!("Successfully opened image"); info!("Successfully opened image");
trace!("Image dimensions: {:?}", image.dimensions()); trace!("Image dimensions: {:?}", image.dimensions());
// Process the image generate_ascii(image, &arguments, output::prepare_output(&arguments)?)?;
let output = generate_ascii(image, &arguments);
info!("Successfully processed image"); info!("Successfully processed image");
Ok(())
// Output the image
info!("Outputting image");
match arguments.output_method {
OutputMethod::File => {
match std::fs::write(
arguments.output.clone(),
output.iter()
.map(|s| format!("{}", s))
.collect::<String>(),
) {
Ok(_) => info!("Successfully outputted image: {}", arguments.output),
Err(e) => {
error!("Failed to output image: {:?}", e);
eprintln!("Failed to output image: {:?}", e);
std::process::exit(1);
}
}
}
OutputMethod::Stdout => {
for char in output {
print!("{}", char);
std::io::stdout().flush().unwrap();
}
info!("Successfully outputted image");
}
}
} }

20
src/output.rs Normal file
View file

@ -0,0 +1,20 @@
use std::fs::File;
use std::io;
use std::io::BufWriter;
use std::io::Write;
use super::Arguments;
use super::OutputMethod;
pub fn prepare_output(arguments: &Arguments) -> io::Result<BufWriter<Box<dyn Write>>> {
match arguments.output_method {
OutputMethod::File => {
let output_file = Box::new(File::create(&arguments.output)?);
Ok(BufWriter::with_capacity(1024, output_file))
}
OutputMethod::Stdout => {
let output_wrap = Box::new(std::io::stdout().lock());
Ok(BufWriter::with_capacity(1024, output_wrap))
}
}
}