You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

132 lines
4.7 KiB

pub fn convert_bitmap_to_unicode(w: usize, h: usize, data: Vec<u8>) -> Vec<Vec<char>>
{
const CHARS: [&str; 4] = [
" ⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿",
"⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿",
"⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿",
"⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿",
];
let bitchars = CHARS.iter().flat_map(|t| t.chars()).collect::<Vec<_>>();
let px = |i: usize, j: usize| if i < w && j < h {data[j * w + i]} else {0};
let mut output = vec![];
for j in (0..h).step_by(4)
{
let mut line = vec![];
for i in (0..w).step_by(2)
{
let mut index = 0;
index |= if px(i+0, j+0) < 128 {0} else {1 << 0};
index |= if px(i+0, j+1) < 128 {0} else {1 << 1};
index |= if px(i+0, j+2) < 128 {0} else {1 << 2};
index |= if px(i+0, j+3) < 128 {0} else {1 << 3};
index |= if px(i+1, j+0) < 128 {0} else {1 << 4};
index |= if px(i+1, j+1) < 128 {0} else {1 << 5};
index |= if px(i+1, j+2) < 128 {0} else {1 << 6};
index |= if px(i+1, j+3) < 128 {0} else {1 << 7};
line.push(bitchars[index]);
}
output.push(line);
}
output
}
fn read_jpeg_to_bitmap(file: &str) -> (usize, usize, Vec<u8>)
{
use zune_jpeg::zune_core::{colorspace::ColorSpace, options::DecoderOptions, bytestream::ZCursor};
let data = std::fs::read(file).unwrap();
let options = DecoderOptions::default().jpeg_set_out_colorspace(ColorSpace::Luma);
let mut decoder = zune_jpeg::JpegDecoder::new_with_options(ZCursor::new(&data), options);
let pixels = decoder.decode().unwrap();
let (w, h) = decoder.dimensions().unwrap();
(w, h, pixels)
}
fn main()
{
match std::env::args().collect::<Vec<_>>().as_slice() {
[_, bitmap_file, source_file, output_file] =>
{
let (w, h, pixels) = read_jpeg_to_bitmap(&bitmap_file);
let char_bitmap = convert_bitmap_to_unicode(w, h, pixels);
let source = std::fs::read_to_string(source_file).unwrap();
let max_width = 120;
let mut modified_lines = vec![];
let mut buffer = Vec::with_capacity(1024);
let mut row = 0;
for line in source.lines()
{
buffer.clear();
buffer.extend(line.chars());
if buffer.len() < max_width {
let needed = max_width - buffer.len();
buffer.extend(core::iter::repeat(' ').take(needed));
}
let mut i = 0;
for j in 0..buffer.len()
{
if !buffer[j].is_whitespace()
{
if j - i > 3
{
let bmp_row = &char_bitmap[row];
for k in i..j {
buffer[k] = bmp_row[k % bmp_row.len()];
}
buffer[i ] = '/';
buffer[i+1] = '*';
buffer[j-1] = '/';
buffer[j-2] = '*';
}
i = j + 1;
}
}
let j = buffer.len();
if j - i > 3
{
let bmp_row = &char_bitmap[row];
for k in i..j {
buffer[k] = bmp_row[k % bmp_row.len()];
}
buffer[i ] = '/';
buffer[i+1] = '*';
buffer[j-1] = '/';
buffer[j-2] = '*';
}
modified_lines.push(buffer.iter().collect::<String>());
row += 1;
}
let new_source = modified_lines.join("\n");
println!("{new_source}");
std::fs::write(output_file, new_source).unwrap();
}
[path] =>
{
println!("usage: {path} art.jpg input_src output_src");
}
_ =>
{
}
}
}