Modular output methods
Using a `std::io::BufWriter` to wrap the output should allow more flexibility.
This commit is contained in:
parent
4d4d246353
commit
b4c7738d69
4 changed files with 56 additions and 58 deletions
|
@ -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)]
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
42
src/main.rs
42
src/main.rs
|
@ -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
20
src/output.rs
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue