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
108
109
110
extern crate image;
use image::{imageops::FilterType, GenericImageView};
use std::{ffi::OsStr, path::Path};
#[cfg(feature = "fetch")]
use reqwest::IntoUrl;
#[cfg(feature = "fetch")]
use std::{fs::File, io::Write};
#[cfg(feature = "fetch")]
use tempfile::tempfile;
const CHARACTER_SET: [&str; 11] = [" ", "'", ",", ".", ":", ";", "/", "O", "0", "#", "@"];
pub struct Dimension {
width: u32,
height: u32,
}
impl Dimension {
pub fn new(width: u32, height: u32) -> Self {
Dimension { width, height }
}
pub fn width(&self) -> u32 {
self.width
}
pub fn height(&self) -> u32 {
self.height
}
}
pub fn convert_to_ascii<S: AsRef<OsStr> + ?Sized>(
path: &S,
dimensions: Option<Dimension>,
) -> String {
let path = Path::new(path);
let mut art = String::new();
if let Ok(mut image) = image::open(&path) {
let mut last_y = 0;
let dimensions = dimensions.unwrap_or(Dimension {
width: 250,
height: 250,
});
if image.width() > dimensions.width() || image.height() > dimensions.height() {
image = image.resize(dimensions.width(), dimensions.height(), FilterType::Nearest);
}
for pixel in image.pixels() {
if last_y != pixel.1 {
art.push_str("\n");
last_y = pixel.1;
}
let rgba = pixel.2;
let brightness: f64 = ((0.2126 * rgba[0] as f64)
+ (0.7152 * rgba[1] as f64)
+ (0.0722 * rgba[2] as f64)) as f64;
let position =
((brightness / 255.0) * (CHARACTER_SET.len() - 1) as f64).round() as usize;
art.push_str(CHARACTER_SET[position])
}
}
art
}
#[cfg(feature = "fetch")]
pub async fn fetch_remote_image<T: IntoUrl>(url: T) -> Result<File, Box<dyn std::error::Error>> {
let bytes = reqwest::get(url).await?.bytes().await?;
let mut out_file = tempfile()?;
out_file.write_all(&bytes)?;
Ok(out_file)
}