5 Commits
1.0 ... 1.1

Author SHA1 Message Date
520d8ddd00 updated README 2023-02-09 14:08:00 +01:00
bb92920e65 updated version 2023-02-09 14:03:23 +01:00
f4d7e60a07 added support for colouring th background instead of the foreground 2023-02-09 14:01:45 +01:00
c138c047ed small rebase of to_ascii function 2023-02-09 13:49:16 +01:00
8de99aefac added grayscale support 2023-02-09 13:46:12 +01:00
7 changed files with 100 additions and 10 deletions

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "img2ascii" name = "img2ascii"
version = "1.0.0" version = "1.1.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

View File

@@ -6,6 +6,8 @@ It can be configured using flags on the command line, a few options of which are
* Complex ASCII (using the map ``` .'`^",:;Il!i><~+_-?][}{1)(|\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$```) * Complex ASCII (using the map ``` .'`^",:;Il!i><~+_-?][}{1)(|\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$```)
* [Braille symbols](https://en.wikipedia.org/wiki/Braille_Patterns) * [Braille symbols](https://en.wikipedia.org/wiki/Braille_Patterns)
* Custom character maps (through the `--map` flag) * Custom character maps (through the `--map` flag)
* Output in full colour
* Output in grayscale
# Prerequisites # Prerequisites
* [Rust](https://www.rust-lang.org/) (building from source only) * [Rust](https://www.rust-lang.org/) (building from source only)
@@ -32,6 +34,8 @@ Of course the output can be customized, by using one (or more) flags.
## options ## options
* -c, --complex: uses the extended character map * -c, --complex: uses the extended character map
* -C, --colour: Display ASCII art in full 24-bit colour * -C, --colour: Display ASCII art in full 24-bit colour
* --background: Colour the background instead of the foreground
* -g, --grayscale: Display ASCII art in grayscale
* -b, --braille: Use braille characters instead of a character map * -b, --braille: Use braille characters instead of a character map
* -d, --debug: Print debugging information * -d, --debug: Print debugging information
* -f, --full: use the full width of the terminal, instead of fitting the image to the terminal dimensions * -f, --full: use the full width of the terminal, instead of fitting the image to the terminal dimensions
@@ -47,14 +51,19 @@ When two or more of these are entered in the same command,
the program uses the one with the highest precedence, in the order the program uses the one with the highest precedence, in the order
`braille > complex > map`. `braille > complex > map`.
The flags `--output` and `--colour` also conflict. The flags `--output`, `--colour` and `--grayscale` also conflict.
When both are entered, the flag `--output` takes precedence When both are entered, the flag `--output` takes precedence
(leading to a simple txt file). (leading to a simple txt file).
Lastly, the flags `--full` and `--width` are mutually exclusive. The flags `--full` and `--width` are mutually exclusive.
When both are entered, the flag `-full` will be applied When both are entered, the flag `-full` will be applied
(`full > width`). (`full > width`).
Lastly, the flags `--colour` and `--grayscale` are not mutually applicable.
When both are entered, the flag `--colour` will be applied.
Note also that the `--background` flag requires either `--colour` or `--grayscale` to do anything.
# Documentation # Documentation
The program has been documented with RustDoc, The program has been documented with RustDoc,
which can be compiled and viewed with which can be compiled and viewed with

View File

@@ -36,8 +36,10 @@ fn to_ascii(char_map: String, image: DynamicImage) -> Vec<Vec<Ascii>> {
let mut out: Vec<Vec<Ascii>> = Vec::new(); let mut out: Vec<Vec<Ascii>> = Vec::new();
for pixel in image.pixels() { for pixel in image.pixels() {
let ch = char_map.as_bytes()[((get_color(pixel.2) as f32-1.0)/255f32 * l) as usize]; let mut ascii = Ascii::new(0, pixel.2[0], pixel.2[1], pixel.2[2]);
str.push(Ascii::new(ch, 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 { if pixel.0 == image.width()-1 {
out.push(str); out.push(str);
@@ -116,7 +118,7 @@ pub fn to_braille_ascii(image: DynamicImage, threshold: u8) -> Vec<Vec<Ascii>> {
} }
} }
} }
str.push(Ascii { char: char::from_u32(braille_value).unwrap(), rgb: [(r/8) as u8, (g/8) as u8, (b/8) as u8] }); 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); out.push(str);
str = Vec::new(); str = Vec::new();

View File

@@ -18,8 +18,20 @@ pub struct Cli {
#[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
///
/// Takes priority over '--grayscale'
#[arg(short = 'C', long)] #[arg(short = 'C', long)]
pub colour: bool, pub colour: bool,
/// Colour the background instead of the foreground
///
/// Requires '--colour' or '--grayscale' to be passed
#[arg(long)]
pub background: bool,
/// Display ASCII art in grayscale
///
/// incompatible with '--color'
#[arg(short= 'g', long)]
pub grayscale: bool,
/// Use braille characters instead of ASCII /// Use braille characters instead of ASCII
/// ///
/// Takes priority over '--complex' and '--map' /// Takes priority over '--complex' and '--map'
@@ -49,7 +61,7 @@ pub struct Cli {
pub width: Option<u32>, 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' /// Incompatible with '--colour' and '--grayscale'
#[arg(short = 'o', long = "output")] #[arg(short = 'o', long = "output")]
pub output: Option<PathBuf>, pub output: Option<PathBuf>,
} }
@@ -62,6 +74,8 @@ impl Cli {
pub fn debug_print(&self) { pub fn debug_print(&self) {
debug!("complex: {}", self.complex); debug!("complex: {}", self.complex);
debug!("colour: {}", self.colour); debug!("colour: {}", self.colour);
debug!("background: {}", self.background);
debug!("grayscale: {}", self.grayscale);
debug!("braille: {}", self.braille); debug!("braille: {}", self.braille);
debug!("debug: {}", self.debug); debug!("debug: {}", self.debug);
debug!("full: {}", self.full); debug!("full: {}", self.full);

View File

@@ -45,6 +45,10 @@ fn main() {
if let Some(output) = cli.output { if let Some(output) = cli.output {
print_file(out, output); print_file(out, output);
} else { } else {
print_terminal(out, cli.colour); if cli.background && (cli.colour || cli.grayscale) {
print_terminal_background(out, cli.colour, cli.grayscale);
} else {
print_terminal(out, cli.colour, cli.grayscale);
}
} }
} }

View File

@@ -1,9 +1,15 @@
//! This module contains the Ascii struct. //! This module contains the Ascii struct.
/// This struct represents one character + rgb colour. /// This struct represents one character + rgb colour.
///
/// attributes:
/// char: char - the character associated with this ASCII value
/// rgb: [u8; 3] - vec of 3 u8 values representing red, green, and blue respectively
/// col_depthL u8 - the depth of the rgb values, calculated through the luminosity method
pub struct Ascii { pub struct Ascii {
pub char: char, pub char: char,
pub rgb: [u8; 3], pub rgb: [u8; 3],
pub col_depth: u8,
} }
impl Ascii { impl Ascii {
@@ -21,6 +27,25 @@ impl Ascii {
Ascii { Ascii {
char: char::from(ch), char: char::from(ch),
rgb: [r, g, b], rgb: [r, g, b],
col_depth: (r as f32 * 0.3 + g as f32 * 0.59 + b as f32 * 0.11) as u8,
}
}
/// This function creates a new Ascii instance.
///
/// Arguments:
/// ch: char - the character
/// r: u8 - Red colour value
/// g: u8 - Green colour value
/// b: u8 - Blue colour value
///
/// returns:
/// self
pub fn new_with_char(ch: char, r: u8, g: u8, b: u8) -> Self {
Ascii {
char: ch,
rgb: [r, g, b],
col_depth: (r as f32 * 0.3 + g as f32 * 0.59 + b as f32 * 0.11) as u8,
} }
} }
} }

View File

@@ -4,7 +4,7 @@ use std::io::{stdout, Write};
use std::path::PathBuf; use std::path::PathBuf;
use std::process::exit; use std::process::exit;
use crossterm::execute; use crossterm::execute;
use crossterm::style::{Color, SetForegroundColor, Print, ResetColor}; use crossterm::style::{Color, SetForegroundColor, SetBackgroundColor, Print, ResetColor};
use log::error; use log::error;
use crate::model_rgb_ascii::Ascii; use crate::model_rgb_ascii::Ascii;
@@ -13,7 +13,8 @@ use crate::model_rgb_ascii::Ascii;
/// arguments: /// arguments:
/// art: Vec<Vec<Ascii>> - vector of vectors representing the art /// art: Vec<Vec<Ascii>> - vector of vectors representing the art
/// in_colour: bool - whether to print in colour or as hard white /// in_colour: bool - whether to print in colour or as hard white
pub fn print_terminal(art: Vec<Vec<Ascii>>, in_colour: bool) { /// grayscale: bool - whether to print grayscale
pub fn print_terminal(art: Vec<Vec<Ascii>>, in_colour: bool, grayscale: bool) {
for line in art { for line in art {
for ascii in line { for ascii in line {
if in_colour { if in_colour {
@@ -21,6 +22,11 @@ pub fn print_terminal(art: Vec<Vec<Ascii>>, in_colour: bool) {
SetForegroundColor(Color::Rgb {r: ascii.rgb[0], g: ascii.rgb[1], b: ascii.rgb[2]}), SetForegroundColor(Color::Rgb {r: ascii.rgb[0], g: ascii.rgb[1], b: ascii.rgb[2]}),
Print(ascii.char), Print(ascii.char),
ResetColor); ResetColor);
} else if grayscale {
let _ = execute!(stdout(),
SetForegroundColor(Color::Rgb {r: ascii.col_depth, g: ascii.col_depth, b: ascii.col_depth}),
Print(ascii.char),
ResetColor);
} else { } else {
print!("{}", ascii.char); print!("{}", ascii.char);
} }
@@ -29,6 +35,36 @@ pub fn print_terminal(art: Vec<Vec<Ascii>>, in_colour: bool) {
} }
} }
/// This function prints ASCII art to the background of stdout.
///
/// arguments:
/// art: Vec<Vec<Ascii>> - vector of vectors representing the art
/// in_colour: bool - whether to print in colour
/// grayscale: bool - whether to print grayscale
pub fn print_terminal_background(art: Vec<Vec<Ascii>>, in_colour: bool, grayscale: bool) {
for line in art {
for ascii in line {
if in_colour {
let _ = execute!(stdout(),
SetBackgroundColor(Color::Rgb {r: ascii.rgb[0], g: ascii.rgb[1], b: ascii.rgb[2]}),
SetForegroundColor(Color::Rgb {r: 255-ascii.rgb[0], g: 255-ascii.rgb[1], b: 255-ascii.rgb[2]}),
Print(ascii.char),
ResetColor);
} else if grayscale {
let _ = execute!(stdout(),
SetBackgroundColor(Color::Rgb {r: ascii.col_depth, g: ascii.col_depth, b: ascii.col_depth}),
SetForegroundColor(Color::Rgb {r: 255-ascii.col_depth, g: 255-ascii.col_depth, b: 255-ascii.col_depth}),
Print(ascii.char),
ResetColor);
} else {
error!("This should be unreachable!");
exit(1);
}
}
println!();
}
}
/// This function prints ASCII art to a file. /// This function prints ASCII art to a file.
/// ///
/// arguments: /// arguments: