ascim 1.0.0

Convert images to ASCII art (CLI + library).
Documentation
use crate::argparse::Args;
use image::GenericImageView;

pub struct ImageData {
    pub width: usize,
    pub height: usize,
    pub data: Vec<u8>,
}

pub fn load_and_resize_image(args: &Args) -> Result<ImageData, String> {
    let img = image::open(&args.file_path).map_err(|e| format!("Failed to load image: {}", e))?;

    let (original_width, original_height) = img.dimensions();

    let proposed_height = (original_height as f64 * args.max_width as f64)
        / (args.character_ratio * original_width as f64);

    let (width, height) = if proposed_height <= args.max_height as f64 {
        (args.max_width, proposed_height as usize)
    } else {
        let w = (args.character_ratio * original_width as f64 * args.max_height as f64)
            / original_height as f64;
        (w as usize, args.max_height)
    };

    let resized = img.resize_exact(
        width as u32,
        height as u32,
        image::imageops::FilterType::Triangle,
    );

    let rgb = resized.to_rgba8();
    let data = rgb.as_raw().clone();

    Ok(ImageData {
        width,
        height,
        data,
    })
}

pub fn make_grayscale(image: &ImageData) -> Vec<f64> {
    let mut grayscale = Vec::with_capacity(image.width * image.height);

    for i in 0..(image.width * image.height) {
        let idx = i * 4;
        let r = image.data[idx] as f64 / 255.0;
        let g = image.data[idx + 1] as f64 / 255.0;
        let b = image.data[idx + 2] as f64 / 255.0;

        let gray = 0.2126 * r + 0.7152 * g + 0.0722 * b;
        grayscale.push(gray);
    }

    grayscale
}