diff --git a/.gitignore b/.gitignore index ea8c4bf..e39cff4 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +*.png \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 55efab5..845dbb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8,10 +8,61 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" +dependencies = [ + "windows-sys 0.52.0", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys 0.52.0", +] + [[package]] name = "ascii-gen" version = "0.1.0" dependencies = [ + "clap", + "colored", "image", ] @@ -51,12 +102,68 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "clap" +version = "4.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b3edb18336f4df585bc9aa31dd99c036dfa5dc5e9a2939a722a188f3a8970d" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1c09dd5ada6c6c78075d6fd0da3f90d8080651e2d6cc8eb2f1aaa4034ced708" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" + [[package]] name = "color_quant" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "crc32fast" version = "1.4.2" @@ -167,6 +274,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "image" version = "0.24.9" @@ -185,6 +298,12 @@ dependencies = [ "tiff", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "jpeg-decoder" version = "0.3.1" @@ -194,6 +313,12 @@ dependencies = [ "rayon", ] +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "lebe" version = "0.5.2" @@ -242,6 +367,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + [[package]] name = "qoi" version = "0.4.1" @@ -251,6 +385,15 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + [[package]] name = "rayon" version = "1.10.0" @@ -298,6 +441,23 @@ dependencies = [ "lock_api", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "tiff" version = "0.9.1" @@ -309,12 +469,163 @@ dependencies = [ "weezl", ] +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "weezl" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.5", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +dependencies = [ + "windows_aarch64_gnullvm 0.52.5", + "windows_aarch64_msvc 0.52.5", + "windows_i686_gnu 0.52.5", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.5", + "windows_x86_64_gnu 0.52.5", + "windows_x86_64_gnullvm 0.52.5", + "windows_x86_64_msvc 0.52.5", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" + [[package]] name = "zune-inflate" version = "0.2.54" diff --git a/Cargo.toml b/Cargo.toml index 95c6624..299a586 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] +clap = { version = "4.5.8", features = ["derive"] } +colored = "2.1.0" image = "0.24.9" diff --git a/src/libs.rs b/src/libs.rs new file mode 100644 index 0000000..43ab1d2 --- /dev/null +++ b/src/libs.rs @@ -0,0 +1,3 @@ +pub mod args; +pub mod image; +pub mod ascii; \ No newline at end of file diff --git a/src/libs/args.rs b/src/libs/args.rs new file mode 100644 index 0000000..1461103 --- /dev/null +++ b/src/libs/args.rs @@ -0,0 +1,23 @@ +use clap::Parser; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +pub struct Args { + #[arg(short, long)] + pub image: String, + + #[arg(long, default_value_t = false)] + pub invert: bool, + + #[arg(long, default_value_t = false)] + pub colorful: bool, + + #[arg(long, default_value_t = 80)] + pub width: usize, + + #[arg(long, default_value_t = 25)] + pub height: usize, + + #[arg(long, default_value_t = 2)] + pub pixel: usize, +} \ No newline at end of file diff --git a/src/libs/ascii.rs b/src/libs/ascii.rs new file mode 100644 index 0000000..dcfc3c8 --- /dev/null +++ b/src/libs/ascii.rs @@ -0,0 +1,99 @@ +use image; +use image::GenericImageView; +use crate::libs::args; +use colored::Colorize; + +fn grayscale_ascii(img: &image::DynamicImage, width: u32, height: u32, invert: bool, pixel_size: usize) -> String { + let mut ascii_img = String::new(); + + for y in 0..height { + for x in 0..width { + let pixel = img.get_pixel(x, y); + + let red = pixel[0]; + let green = pixel[1]; + let blue = pixel[2]; + + let pixel_iterator: Vec = vec![red.into(), green.into(), blue.into()]; + let darkest_color: &u16 = pixel_iterator.iter().min().unwrap(); + let brightest_color: &u16 = pixel_iterator.iter().max().unwrap(); + + let mut brightness = ((darkest_color + brightest_color) / 2) as u8; + + if invert { + brightness = 255 - brightness; + } + + let char_pixel = select_char(&brightness).repeat(pixel_size); + + ascii_img.push_str(&char_pixel); + } + ascii_img.push('\n'); + } + + ascii_img +} + +fn colorful_ascii(img: &image::DynamicImage, width: u32, height: u32, pixel_size: usize) -> String { + let mut ascii_img = String::new(); + + for y in 0..height { + for x in 0..width { + let pixel = img.get_pixel(x, y); + + let red = pixel[0]; + let green = pixel[1]; + let blue = pixel[2]; + + let pixel_iterator: Vec = vec![red.into(), green.into(), blue.into()]; + let darkest_color: &u16 = pixel_iterator.iter().min().unwrap(); + let brightest_color: &u16 = pixel_iterator.iter().max().unwrap(); + + let brightness = ((darkest_color + brightest_color) / 2) as u8; + + let mut char_pixel = select_char(&brightness).repeat(pixel_size); + + char_pixel = select_dominant_color((red, green, blue), char_pixel); + + ascii_img.push_str(&char_pixel); + } + ascii_img.push('\n'); + } + + ascii_img +} + +fn select_char(brightness: &u8) -> String { + let char = match brightness { + 0..=25 => " ", + 26..=50 => ".", + 51..=75 => ":", + 76..=100 => "-", + 101..=125 => "=", + 126..=150 => "+", + 151..=175 => "*", + 176..=200 => "#", + 201..=225 => "%", + 226..=255 => "@", + }; + + char.into() +} + +fn select_dominant_color(pixel : (u8, u8, u8), char_pixel: String) -> String { + let (red, green, blue) = pixel; + + let char_pixel = char_pixel.truecolor(red, green, blue).to_string(); + + char_pixel +} + +pub fn to_ascii(img: &image::DynamicImage, args: &args::Args) -> String { + let (width, height) = img.dimensions(); + + if args.colorful { + return colorful_ascii(img, width, height, args.pixel); + } + + grayscale_ascii(img, width, height, args.invert, args.pixel) +} \ No newline at end of file diff --git a/src/libs/image.rs b/src/libs/image.rs new file mode 100644 index 0000000..a3a67b9 --- /dev/null +++ b/src/libs/image.rs @@ -0,0 +1,18 @@ +use image; +use image::GenericImageView; + +pub fn load_image(file_name: &str) -> image::DynamicImage { + let img = image::open(file_name).expect("File not found!"); + println!("Image loaded: {file_name}"); + + img +} + +pub fn print_size(img: &image::DynamicImage) { + let (width, height) = img.dimensions(); + println!("Image dimensions: {width}x{height}"); +} + +pub fn resize_image(img: &image::DynamicImage, nwidth: usize, nheight: usize) -> image::DynamicImage { + img.resize(nwidth as u32, nheight as u32, image::imageops::FilterType::Lanczos3) +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 2a4773e..ec6b031 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,85 +1,19 @@ -use std::env; -use image; -use image::GenericImageView; - -fn parse_args() -> Vec { - let args: Vec = env::args().collect(); - - if args.len() < 2 { - println!("Usage: {} ", args[0]); - std::process::exit(1); - } - - args -} - -fn load_image(file_name: &str) -> image::DynamicImage { - let img = image::open(file_name).expect("File not found!"); - println!("Image loaded: {file_name}"); - - img -} - -fn print_size(img: &image::DynamicImage) { - let (width, height) = img.dimensions(); - println!("Image dimensions: {width}x{height}"); -} - -fn select_char(brightness: &u8) -> &str { - match brightness { - 0..=25 => " ", - 26..=50 => "..", - 51..=75 => "::", - 76..=100 => "--", - 101..=125 => "==", - 126..=150 => "++", - 151..=175 => "**", - 176..=200 => "##", - 201..=225 => "%%", - 226..=255 => "@@", - } -} - -fn to_ascii(img: &image::DynamicImage) -> String { - let (width, height) = img.dimensions(); - - let mut ascii_img = String::new(); - - for y in 0..height { - for x in 0..width { - let pixel = img.get_pixel(x, y); - - let red = pixel[0] as u16; - let green = pixel[1] as u16; - let blue = pixel[2] as u16; - - let brightness = ((red + green + blue) / 3) as u8; - - ascii_img.push_str(select_char(&brightness)); - } - ascii_img.push('\n'); - } - - ascii_img -} - -fn resize_image(img: &image::DynamicImage, nwidth: u32, nheight: u32) -> image::DynamicImage { - img.resize(nwidth, nheight, image::imageops::FilterType::Lanczos3) -} +use clap::Parser; +mod libs; fn main() { println!("ASCII Generator\n----------------"); - let args: Vec = parse_args(); + let args = libs::args::Args::parse(); - let img = load_image(&args[1]); - print_size(&img); + let img = libs::image::load_image(&args.image); + libs::image::print_size(&img); - let resized_img = resize_image(&img, 80, 25); + let resized_img = libs::image::resize_image(&img, args.width, args.height); println!("Image resized"); - print_size(&resized_img); + libs::image::print_size(&resized_img); - let ascii_img = to_ascii(&resized_img); + let ascii_img = libs::ascii::to_ascii(&resized_img, &args); println!("ASCII image created"); println!("{ascii_img}");