ascii_izer/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
use std::path::Path;

#[cfg(feature = "color")]
use color::Color;
use color::GrayscaleMode;
use image::{DynamicImage, GenericImageView, ImageReader};
#[cfg(feature = "crossterm")]
pub use terminal::put_in_console;
pub use {error::AsciiError, generator::ASCIIGenerator};

mod color;
mod error;
mod generator;
mod terminal;

#[derive(Debug, Clone)]
pub struct Line {
    chars: Vec<char>,
    #[cfg(feature = "color")]
    colors: Vec<Color>,
}

impl Line {
    pub fn new(size: usize) -> Self {
        Line {
            chars: Vec::with_capacity(size),
            #[cfg(feature = "color")]
            colors: vec![Color::white(); size],
        }
    }

    pub fn add_char(&mut self, char: char) {
        self.chars.push(char);
    }

    pub fn add_color(&mut self, color: Color) {
        self.colors.push(color);
    }

    pub fn chars(&self) -> &Vec<char> {
        &self.chars
    }

    #[cfg(feature = "color")]
    pub fn colors(&self) -> &Vec<Color> {
        &self.colors
    }
}

pub fn to_ascii_lines<P: AsRef<Path>>(
    path: P,
    grayscale_mode: GrayscaleMode,
    #[cfg(feature = "color")] with_color: bool,
) -> Result<Vec<Line>, AsciiError> {
    let image = ImageReader::open(path)?.decode()?;
    image_into_lines(
        &image,
        grayscale_mode,
        #[cfg(feature = "color")]
        with_color,
    )
}

pub fn image_into_lines(
    image: &DynamicImage,
    grayscale_mode: GrayscaleMode,
    #[cfg(feature = "color")] with_color: bool,
) -> Result<Vec<Line>, AsciiError> {
    let mut lines: Vec<Line> =
        vec![Line::new(image.dimensions().0 as usize); image.dimensions().1 as usize];
    for (_, y, color) in image.pixels() {
        let color = Color::from(color.0);
        let gray = color.grayscale(grayscale_mode);

        lines
            .get_mut(y as usize)
            .unwrap()
            .add_char(char_from_gray(gray));
        if cfg!(feature = "color") && with_color {
            lines.get_mut(y as usize).unwrap().add_color(color);
        }
    }

    Ok(lines)
}

fn char_from_gray(gray: u8) -> char {
    if gray > 230 {
        ' '
    } else if gray >= 200 {
        '.'
    } else if gray >= 180 {
        '*'
    } else if gray >= 160 {
        ':'
    } else if gray >= 130 {
        'o'
    } else if gray >= 100 {
        '&'
    } else if gray >= 70 {
        '8'
    } else if gray >= 50 {
        '#'
    } else {
        '@'
    }
}