pub fn convert_bitmap_to_unicode(w: usize, h: usize, data: Vec) -> Vec> { const CHARS: [&str; 4] = [ " ⠁⠂⠃⠄⠅⠆⠇⠈⠉⠊⠋⠌⠍⠎⠏⠐⠑⠒⠓⠔⠕⠖⠗⠘⠙⠚⠛⠜⠝⠞⠟⠠⠡⠢⠣⠤⠥⠦⠧⠨⠩⠪⠫⠬⠭⠮⠯⠰⠱⠲⠳⠴⠵⠶⠷⠸⠹⠺⠻⠼⠽⠾⠿", "⡀⡁⡂⡃⡄⡅⡆⡇⡈⡉⡊⡋⡌⡍⡎⡏⡐⡑⡒⡓⡔⡕⡖⡗⡘⡙⡚⡛⡜⡝⡞⡟⡠⡡⡢⡣⡤⡥⡦⡧⡨⡩⡪⡫⡬⡭⡮⡯⡰⡱⡲⡳⡴⡵⡶⡷⡸⡹⡺⡻⡼⡽⡾⡿", "⢀⢁⢂⢃⢄⢅⢆⢇⢈⢉⢊⢋⢌⢍⢎⢏⢐⢑⢒⢓⢔⢕⢖⢗⢘⢙⢚⢛⢜⢝⢞⢟⢠⢡⢢⢣⢤⢥⢦⢧⢨⢩⢪⢫⢬⢭⢮⢯⢰⢱⢲⢳⢴⢵⢶⢷⢸⢹⢺⢻⢼⢽⢾⢿", "⣀⣁⣂⣃⣄⣅⣆⣇⣈⣉⣊⣋⣌⣍⣎⣏⣐⣑⣒⣓⣔⣕⣖⣗⣘⣙⣚⣛⣜⣝⣞⣟⣠⣡⣢⣣⣤⣥⣦⣧⣨⣩⣪⣫⣬⣭⣮⣯⣰⣱⣲⣳⣴⣵⣶⣷⣸⣹⣺⣻⣼⣽⣾⣿", ]; let bitchars = CHARS.iter().flat_map(|t| t.chars()).collect::>(); 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) { 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::>().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::()); 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"); } _ => { } } }