mirror of
https://github.com/eRgo35/ascii.git
synced 2026-02-04 04:46:09 +01:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
763c82a89e
|
|||
|
91051a82be
|
|||
|
99f7fa2a28
|
|||
|
a75c300692
|
|||
|
2e87792a7f
|
|||
|
e0f043bb77
|
|||
|
274ceb0b83
|
|||
|
0d7e950503
|
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
*.png
|
*.png
|
||||||
|
*.webm
|
||||||
|
|||||||
1268
Cargo.lock
generated
1268
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,14 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "ascii-gen"
|
name = "ascii"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
atty = "0.2.14"
|
||||||
clap = { version = "4.5.8", features = ["derive"] }
|
clap = { version = "4.5.8", features = ["derive"] }
|
||||||
|
clearscreen = "3.0.0"
|
||||||
colored = "2.1.0"
|
colored = "2.1.0"
|
||||||
|
ffmpeg-next = "7.0.2"
|
||||||
image = "0.24.9"
|
image = "0.24.9"
|
||||||
|
nokhwa = { path = "../../Software/nokhwa", version = "0.10.4", features = ["input-native"] }
|
||||||
|
video-rs = "0.8.1"
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
pub mod args;
|
pub mod args;
|
||||||
pub mod image;
|
pub mod image;
|
||||||
pub mod ascii;
|
pub mod ascii;
|
||||||
|
pub mod gen_handler;
|
||||||
|
pub mod cam_handler;
|
||||||
|
pub mod vid_handler;
|
||||||
110
src/libs/args.rs
110
src/libs/args.rs
@@ -1,23 +1,107 @@
|
|||||||
use clap::Parser;
|
use clap::{Parser, Subcommand};
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
#[arg(short, long)]
|
#[command(subcommand)]
|
||||||
pub image: String,
|
pub command: Option<Subcommands>,
|
||||||
|
}
|
||||||
|
|
||||||
#[arg(long, default_value_t = false)]
|
#[derive(Subcommand, Debug)]
|
||||||
pub invert: bool,
|
pub enum Subcommands {
|
||||||
|
Gen {
|
||||||
|
#[arg(short, long, default_value_t = String::from(""))]
|
||||||
|
image: String,
|
||||||
|
|
||||||
#[arg(long, default_value_t = false)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub colorful: bool,
|
invert: bool,
|
||||||
|
|
||||||
#[arg(long, default_value_t = 80)]
|
#[arg(long, default_value_t = false)]
|
||||||
pub width: usize,
|
colorful: bool,
|
||||||
|
|
||||||
#[arg(long, default_value_t = 25)]
|
#[arg(long, default_value_t = 80)]
|
||||||
pub height: usize,
|
width: usize,
|
||||||
|
|
||||||
#[arg(long, default_value_t = 2)]
|
#[arg(long, default_value_t = 25)]
|
||||||
pub pixel: usize,
|
height: usize,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = 2)]
|
||||||
|
pixel: usize,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
noresize: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
matrix: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
nofill: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = String::from(""))]
|
||||||
|
output: String,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = String::from(""))]
|
||||||
|
lightmap: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Cam {
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
invert: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
colorful: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = 80)]
|
||||||
|
width: usize,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = 25)]
|
||||||
|
height: usize,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = 2)]
|
||||||
|
pixel: usize,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
noresize: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
matrix: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
nofill: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = String::from(""))]
|
||||||
|
lightmap: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
Vid {
|
||||||
|
#[arg(short, long, default_value_t = String::from(""))]
|
||||||
|
input: String,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
invert: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
colorful: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = 80)]
|
||||||
|
width: usize,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = 25)]
|
||||||
|
height: usize,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = 2)]
|
||||||
|
pixel: usize,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
noresize: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
matrix: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = false)]
|
||||||
|
nofill: bool,
|
||||||
|
|
||||||
|
#[arg(long, default_value_t = String::from(""))]
|
||||||
|
lightmap: String,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
@@ -1,99 +1,83 @@
|
|||||||
|
use colored::Colorize;
|
||||||
use image;
|
use image;
|
||||||
use image::GenericImageView;
|
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 {
|
fn calculate_brightness((red, green, blue): (u8, u8, u8), mapping: &str) -> u8 {
|
||||||
let mut ascii_img = String::new();
|
let pixel_iterator: Vec<u16> = vec![red.into(), green.into(), blue.into()];
|
||||||
|
let darkest_color: &u16 = pixel_iterator.iter().min().unwrap();
|
||||||
for y in 0..height {
|
let brightest_color: &u16 = pixel_iterator.iter().max().unwrap();
|
||||||
for x in 0..width {
|
|
||||||
let pixel = img.get_pixel(x, y);
|
|
||||||
|
|
||||||
let red = pixel[0];
|
let mapping: u8 = match mapping {
|
||||||
let green = pixel[1];
|
"fullbright" => 255,
|
||||||
let blue = pixel[2];
|
"luminosity" => (0.21 * red as f32 + 0.72 * green as f32 + 0.07 * blue as f32) as u8,
|
||||||
|
"minmax" | "average" | _ => ((darkest_color + brightest_color) / 2) as u8,
|
||||||
|
};
|
||||||
|
|
||||||
let pixel_iterator: Vec<u16> = vec![red.into(), green.into(), blue.into()];
|
mapping
|
||||||
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<u16> = 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 {
|
fn select_char(brightness: &u8) -> String {
|
||||||
let char = match brightness {
|
let char = match brightness {
|
||||||
0..=25 => " ",
|
0..=25 => " ",
|
||||||
26..=50 => ".",
|
26..=50 => ".",
|
||||||
51..=75 => ":",
|
51..=75 => ":",
|
||||||
76..=100 => "-",
|
76..=100 => "-",
|
||||||
101..=125 => "=",
|
101..=125 => "=",
|
||||||
126..=150 => "+",
|
126..=150 => "+",
|
||||||
151..=175 => "*",
|
151..=175 => "*",
|
||||||
176..=200 => "#",
|
176..=200 => "#",
|
||||||
201..=225 => "%",
|
201..=225 => "%",
|
||||||
226..=255 => "@",
|
226..=255 => "@",
|
||||||
};
|
};
|
||||||
|
|
||||||
char.into()
|
char.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn select_dominant_color(pixel : (u8, u8, u8), char_pixel: String) -> String {
|
fn select_dominant_color(pixel: (u8, u8, u8), char_pixel: String) -> String {
|
||||||
let (red, green, blue) = pixel;
|
let (red, green, blue) = pixel;
|
||||||
|
|
||||||
let char_pixel = char_pixel.truecolor(red, green, blue).to_string();
|
char_pixel.truecolor(red, green, blue).to_string()
|
||||||
|
|
||||||
char_pixel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_ascii(img: &image::DynamicImage, args: &args::Args) -> String {
|
pub fn to_ascii(
|
||||||
let (width, height) = img.dimensions();
|
img: &image::DynamicImage,
|
||||||
|
lightmap: &str,
|
||||||
|
pixel_format: usize,
|
||||||
|
invert: bool,
|
||||||
|
colorful: bool,
|
||||||
|
matrix: bool,
|
||||||
|
) -> String {
|
||||||
|
let mut ascii_img = String::new();
|
||||||
|
let (width, height) = img.dimensions();
|
||||||
|
|
||||||
if args.colorful {
|
for y in 0..height {
|
||||||
return colorful_ascii(img, width, height, args.pixel);
|
for x in 0..width {
|
||||||
}
|
let pixel = img.get_pixel(x, y);
|
||||||
|
let (mut red, mut green, mut blue) = (pixel[0], pixel[1], pixel[2]);
|
||||||
|
|
||||||
grayscale_ascii(img, width, height, args.invert, args.pixel)
|
let mut brightness = calculate_brightness((red, green, blue), lightmap);
|
||||||
}
|
|
||||||
|
if invert {
|
||||||
|
red = 255 - red;
|
||||||
|
green = 255 - green;
|
||||||
|
blue = 255 - blue;
|
||||||
|
brightness = 255 - brightness;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut char_pixel = select_char(&brightness).repeat(pixel_format);
|
||||||
|
|
||||||
|
if colorful {
|
||||||
|
char_pixel = select_dominant_color((red, green, blue), char_pixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if matrix {
|
||||||
|
char_pixel = char_pixel.bright_green().to_string();
|
||||||
|
}
|
||||||
|
|
||||||
|
ascii_img.push_str(&char_pixel);
|
||||||
|
}
|
||||||
|
ascii_img.push('\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
ascii_img
|
||||||
|
}
|
||||||
|
|||||||
53
src/libs/cam_handler.rs
Normal file
53
src/libs/cam_handler.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use crate::libs;
|
||||||
|
use image::DynamicImage;
|
||||||
|
use nokhwa::{
|
||||||
|
pixel_format::RgbFormat,
|
||||||
|
utils::{CameraIndex, RequestedFormat, RequestedFormatType},
|
||||||
|
Camera,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn camera(
|
||||||
|
invert: bool,
|
||||||
|
colorful: bool,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
pixel: usize,
|
||||||
|
noresize: bool,
|
||||||
|
matrix: bool,
|
||||||
|
nofill: bool,
|
||||||
|
lightmap: String,
|
||||||
|
) {
|
||||||
|
let index = CameraIndex::Index(0);
|
||||||
|
|
||||||
|
let requested =
|
||||||
|
RequestedFormat::new::<RgbFormat>(RequestedFormatType::AbsoluteHighestFrameRate);
|
||||||
|
|
||||||
|
let mut camera = Camera::new(index, requested).unwrap();
|
||||||
|
|
||||||
|
camera.open_stream().unwrap();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let frame = camera.frame().unwrap();
|
||||||
|
|
||||||
|
let decoded = frame.decode_image::<RgbFormat>().unwrap();
|
||||||
|
let mut img: DynamicImage = decoded.into();
|
||||||
|
|
||||||
|
if !noresize && !nofill {
|
||||||
|
img = libs::image::resize_image(&img, width, height);
|
||||||
|
eprintln!("Image resized");
|
||||||
|
libs::image::print_size(&img);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !noresize && nofill {
|
||||||
|
img = libs::image::resize_image_no_fill(&img, width, height);
|
||||||
|
eprintln!("Image resized exact");
|
||||||
|
libs::image::print_size(&img);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ascii_img = libs::ascii::to_ascii(&img, &lightmap, pixel, invert, colorful, matrix);
|
||||||
|
eprintln!("ASCII image created");
|
||||||
|
|
||||||
|
clearscreen::clear().expect("Failed to clear screen");
|
||||||
|
print!("{ascii_img}");
|
||||||
|
}
|
||||||
|
}
|
||||||
53
src/libs/gen_handler.rs
Normal file
53
src/libs/gen_handler.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use crate::libs;
|
||||||
|
|
||||||
|
pub fn generator(
|
||||||
|
image_path: String,
|
||||||
|
invert: bool,
|
||||||
|
colorful: bool,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
pixel: usize,
|
||||||
|
noresize: bool,
|
||||||
|
matrix: bool,
|
||||||
|
nofill: bool,
|
||||||
|
output: String,
|
||||||
|
lightmap: String,
|
||||||
|
) {
|
||||||
|
let path = image_path.clone();
|
||||||
|
|
||||||
|
let mut img = if !path.is_empty() {
|
||||||
|
libs::image::load_image(&path)
|
||||||
|
} else {
|
||||||
|
match libs::image::load_image_from_stdin() {
|
||||||
|
Ok(img) => img,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("Error: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
libs::image::print_size(&img);
|
||||||
|
|
||||||
|
if !noresize && !nofill {
|
||||||
|
img = libs::image::resize_image(&img, width, height);
|
||||||
|
eprintln!("Image resized");
|
||||||
|
libs::image::print_size(&img);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !noresize && nofill {
|
||||||
|
img = libs::image::resize_image_no_fill(&img, width, height);
|
||||||
|
eprintln!("Image resized exact");
|
||||||
|
libs::image::print_size(&img);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ascii_img = libs::ascii::to_ascii(&img, &lightmap, pixel, invert, colorful, matrix);
|
||||||
|
eprintln!("ASCII image created");
|
||||||
|
|
||||||
|
if !output.is_empty() {
|
||||||
|
libs::image::save_image(&ascii_img, &output);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("{ascii_img}");
|
||||||
|
}
|
||||||
@@ -1,18 +1,51 @@
|
|||||||
|
use image::io::Reader;
|
||||||
use image;
|
use image;
|
||||||
use image::GenericImageView;
|
use image::GenericImageView;
|
||||||
|
use std::io::{self, BufReader, BufRead, Cursor};
|
||||||
|
use atty::Stream;
|
||||||
|
use std::fs;
|
||||||
|
|
||||||
|
pub fn load_image_from_stdin() -> Result<image::DynamicImage, image::ImageError> {
|
||||||
|
let mut buffer: Vec<u8> = Vec::new();
|
||||||
|
let mut raw_reader: Box<dyn BufRead> = if atty::is(Stream::Stdin) {
|
||||||
|
eprintln!("Error: No image provided");
|
||||||
|
std::process::exit(1);
|
||||||
|
} else {
|
||||||
|
Box::new(BufReader::new(io::stdin()))
|
||||||
|
};
|
||||||
|
|
||||||
|
raw_reader.read_to_end(&mut buffer).unwrap();
|
||||||
|
|
||||||
|
let reader = Reader::new(Cursor::new(buffer))
|
||||||
|
.with_guessed_format()
|
||||||
|
.expect("Failed to read image format");
|
||||||
|
|
||||||
|
eprintln!("Image loaded: stdin");
|
||||||
|
|
||||||
|
reader.decode()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_image(file_name: &str) -> image::DynamicImage {
|
pub fn load_image(file_name: &str) -> image::DynamicImage {
|
||||||
let img = image::open(file_name).expect("File not found!");
|
let img = image::open(file_name).expect("File not found!");
|
||||||
println!("Image loaded: {file_name}");
|
eprintln!("Image loaded: {file_name}");
|
||||||
|
|
||||||
img
|
img
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn save_image(img: &String, file_name: &str) {
|
||||||
|
fs::write(file_name, img).expect("Unable to write file");
|
||||||
|
eprintln!("Image saved: {file_name}");
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print_size(img: &image::DynamicImage) {
|
pub fn print_size(img: &image::DynamicImage) {
|
||||||
let (width, height) = img.dimensions();
|
let (width, height) = img.dimensions();
|
||||||
println!("Image dimensions: {width}x{height}");
|
eprintln!("Image dimensions: {width}x{height}");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn resize_image(img: &image::DynamicImage, nwidth: usize, nheight: usize) -> image::DynamicImage {
|
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)
|
img.resize(nwidth as u32, nheight as u32, image::imageops::FilterType::Lanczos3)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize_image_no_fill(img: &image::DynamicImage, nwidth: usize, nheight: usize) -> image::DynamicImage {
|
||||||
|
img.resize_exact(nwidth as u32, nheight as u32, image::imageops::FilterType::Lanczos3)
|
||||||
}
|
}
|
||||||
116
src/libs/vid_handler.rs
Normal file
116
src/libs/vid_handler.rs
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::libs;
|
||||||
|
use image::{DynamicImage, ImageBuffer};
|
||||||
|
|
||||||
|
use ffmpeg_next::format::{input, Pixel};
|
||||||
|
use ffmpeg_next::media::Type;
|
||||||
|
use ffmpeg_next::software::scaling::{context::Context, flag::Flags};
|
||||||
|
use ffmpeg_next::util::frame::video::Video;
|
||||||
|
|
||||||
|
pub fn video(
|
||||||
|
input_path: String,
|
||||||
|
invert: bool,
|
||||||
|
colorful: bool,
|
||||||
|
width: usize,
|
||||||
|
height: usize,
|
||||||
|
pixel: usize,
|
||||||
|
noresize: bool,
|
||||||
|
matrix: bool,
|
||||||
|
nofill: bool,
|
||||||
|
lightmap: String,
|
||||||
|
) {
|
||||||
|
ffmpeg_next::init().unwrap();
|
||||||
|
|
||||||
|
if input_path.is_empty() {
|
||||||
|
eprintln!("Error: No input path provided");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(mut ictx) = input(&input_path) {
|
||||||
|
let input = ictx
|
||||||
|
.streams()
|
||||||
|
.best(Type::Video)
|
||||||
|
.ok_or(ffmpeg_next::Error::StreamNotFound)
|
||||||
|
.expect("Failed to find video stream");
|
||||||
|
let video_stream_index = input.index();
|
||||||
|
|
||||||
|
let context_decoder =
|
||||||
|
ffmpeg_next::codec::context::Context::from_parameters(input.parameters()).unwrap();
|
||||||
|
let mut decoder = context_decoder.decoder().video().unwrap();
|
||||||
|
|
||||||
|
let mut scaler = Context::get(
|
||||||
|
decoder.format(),
|
||||||
|
decoder.width(),
|
||||||
|
decoder.height(),
|
||||||
|
Pixel::RGB24,
|
||||||
|
decoder.width(),
|
||||||
|
decoder.height(),
|
||||||
|
Flags::BILINEAR,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let frame_rate = input.avg_frame_rate();
|
||||||
|
let frame_duration = Duration::from_secs_f64(frame_rate.denominator() as f64 / frame_rate.numerator() as f64);
|
||||||
|
|
||||||
|
let mut frame_index = 0;
|
||||||
|
|
||||||
|
let mut receive_and_process_decoded_frames =
|
||||||
|
|decoder: &mut ffmpeg_next::decoder::Video| -> Result<(), ffmpeg_next::Error> {
|
||||||
|
let mut decoded = Video::empty();
|
||||||
|
while decoder.receive_frame(&mut decoded).is_ok() {
|
||||||
|
let start = std::time::Instant::now();
|
||||||
|
|
||||||
|
let mut rgb_frame = Video::empty();
|
||||||
|
scaler.run(&decoded, &mut rgb_frame)?;
|
||||||
|
|
||||||
|
let vwidth = rgb_frame.width();
|
||||||
|
let vheight = rgb_frame.height();
|
||||||
|
|
||||||
|
let data = rgb_frame.data(0);
|
||||||
|
|
||||||
|
let buf = ImageBuffer::from_raw(vwidth, vheight, data.to_vec()).unwrap();
|
||||||
|
|
||||||
|
let mut img = DynamicImage::ImageRgb8(buf);
|
||||||
|
|
||||||
|
if !noresize && !nofill {
|
||||||
|
img = libs::image::resize_image(&img, width, height);
|
||||||
|
eprintln!("Image resized");
|
||||||
|
libs::image::print_size(&img);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !noresize && nofill {
|
||||||
|
img = libs::image::resize_image_no_fill(&img, width, height);
|
||||||
|
eprintln!("Image resized exact");
|
||||||
|
libs::image::print_size(&img);
|
||||||
|
}
|
||||||
|
|
||||||
|
let ascii_img =
|
||||||
|
libs::ascii::to_ascii(&img, &lightmap, pixel, invert, colorful, matrix);
|
||||||
|
eprintln!("ASCII image created");
|
||||||
|
|
||||||
|
clearscreen::clear().expect("Failed to clear screen");
|
||||||
|
print!("{ascii_img}");
|
||||||
|
|
||||||
|
frame_index += 1;
|
||||||
|
|
||||||
|
let elapsed = start.elapsed();
|
||||||
|
if elapsed < frame_duration {
|
||||||
|
std::thread::sleep(frame_duration - elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
};
|
||||||
|
|
||||||
|
for (stream, packet) in ictx.packets() {
|
||||||
|
if stream.index() == video_stream_index {
|
||||||
|
decoder.send_packet(&packet).unwrap();
|
||||||
|
receive_and_process_decoded_frames(&mut decoder).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder.send_eof().unwrap();
|
||||||
|
receive_and_process_decoded_frames(&mut decoder).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
73
src/main.rs
73
src/main.rs
@@ -1,20 +1,65 @@
|
|||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
|
|
||||||
mod libs;
|
mod libs;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("ASCII Generator\n----------------");
|
eprintln!("ASCII Generator\n----------------");
|
||||||
|
|
||||||
let args = libs::args::Args::parse();
|
let args = libs::args::Args::parse();
|
||||||
|
|
||||||
let img = libs::image::load_image(&args.image);
|
match args.command {
|
||||||
libs::image::print_size(&img);
|
Some(libs::args::Subcommands::Gen {
|
||||||
|
image,
|
||||||
let resized_img = libs::image::resize_image(&img, args.width, args.height);
|
invert,
|
||||||
println!("Image resized");
|
colorful,
|
||||||
libs::image::print_size(&resized_img);
|
width,
|
||||||
|
height,
|
||||||
let ascii_img = libs::ascii::to_ascii(&resized_img, &args);
|
pixel,
|
||||||
println!("ASCII image created");
|
noresize,
|
||||||
|
matrix,
|
||||||
println!("{ascii_img}");
|
nofill,
|
||||||
}
|
output,
|
||||||
|
lightmap,
|
||||||
|
}) => {
|
||||||
|
libs::gen_handler::generator(
|
||||||
|
image, invert, colorful, width, height, pixel, noresize, matrix, nofill, output,
|
||||||
|
lightmap,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(libs::args::Subcommands::Cam {
|
||||||
|
invert,
|
||||||
|
colorful,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
pixel,
|
||||||
|
noresize,
|
||||||
|
matrix,
|
||||||
|
nofill,
|
||||||
|
lightmap,
|
||||||
|
}) => {
|
||||||
|
libs::cam_handler::camera(
|
||||||
|
invert, colorful, width, height, pixel, noresize, matrix, nofill, lightmap,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Some(libs::args::Subcommands::Vid {
|
||||||
|
input,
|
||||||
|
invert,
|
||||||
|
colorful,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
pixel,
|
||||||
|
noresize,
|
||||||
|
matrix,
|
||||||
|
nofill,
|
||||||
|
lightmap,
|
||||||
|
}) => {
|
||||||
|
libs::vid_handler::video(
|
||||||
|
input, invert, colorful, width, height, pixel, noresize, matrix, nofill, lightmap,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
eprintln!("No subcommand provided. Available subcommands: `gen`, `cam`, and `vid`");
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user