mirror of
https://github.com/waveplate/img2irc.git
synced 2025-04-19 19:28:25 +00:00
380 lines
11 KiB
Rust
380 lines
11 KiB
Rust
use crate::args;
|
|
use crate::palette::{RGB99, RGB88, ANSI232, ANSI256, nearest_hex_color};
|
|
use photon_rs::PhotonImage;
|
|
|
|
// █ full
|
|
const FULL: &str = "\u{2588}";
|
|
|
|
// ▄ down
|
|
const UP: &str = "\u{2580}";
|
|
|
|
// ▀ up
|
|
const DOWN: &str = "\u{2584}";
|
|
|
|
// ▌ left
|
|
const LEFT: &str = "\u{258C}";
|
|
|
|
// ▐ right
|
|
const RIGHT: &str = "\u{2590}";
|
|
|
|
// ▞ diag_right
|
|
const DIAG_RIGHT: &str = "\u{259E}";
|
|
|
|
// ▚ diag_left
|
|
const DIAG_LEFT: &str = "\u{259A}";
|
|
|
|
// ▙ down_left (2596 prev)
|
|
const DOWN_LEFT: &str = "\u{2599}";
|
|
|
|
// ▟ down_right
|
|
const DOWN_RIGHT: &str = "\u{259F}";
|
|
|
|
// ▛ top_left
|
|
const UP_LEFT: &str = "\u{259B}";
|
|
|
|
// ▜ top_right
|
|
const UP_RIGHT: &str = "\u{259C}";
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct AnsiImage {
|
|
pub image: PhotonImage,
|
|
pub bitmap: Vec<Vec<u32>>,
|
|
pub halfblock: Vec<Vec<AnsiPixelPair>>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct AnsiPixelPair {
|
|
pub top: AnsiPixel,
|
|
pub bottom: AnsiPixel,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct AnsiPixel {
|
|
pub orig: u32,
|
|
pub ansi: u8,
|
|
pub ansi232: u8,
|
|
pub irc: u8,
|
|
pub irc88: u8,
|
|
}
|
|
|
|
impl AnsiPixel {
|
|
pub fn new(pixel: &u32) -> AnsiPixel {
|
|
let irc = nearest_hex_color(*pixel, RGB99.to_vec());
|
|
let irc88 = nearest_hex_color(*pixel, RGB88.to_vec());
|
|
let ansi = nearest_hex_color(*pixel, ANSI256.to_vec());
|
|
let ansi232 = nearest_hex_color(*pixel, ANSI232.to_vec());
|
|
AnsiPixel {
|
|
orig: *pixel,
|
|
ansi: ansi,
|
|
ansi232: ansi232,
|
|
irc: irc,
|
|
irc88: irc88,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AnsiImage {
|
|
pub fn new(image: PhotonImage) -> AnsiImage {
|
|
let mut bitmap = image.get_raw_pixels()
|
|
.chunks(4)
|
|
.map(|x| make_rgb_u32(x.to_vec()))
|
|
.collect::<Vec<u32>>()
|
|
.chunks(image.get_width() as usize)
|
|
.map(|x| x.to_vec())
|
|
.collect::<Vec<Vec<u32>>>();
|
|
|
|
if bitmap.len() % 2 != 0 {
|
|
bitmap.push(vec![0; image.get_width() as usize]);
|
|
}
|
|
|
|
let halfblock = halfblock_bitmap(&bitmap);
|
|
|
|
return AnsiImage {
|
|
image: image,
|
|
bitmap: bitmap,
|
|
halfblock: halfblock,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn make_rgb_u8(rgb: u32) -> [u8; 3] {
|
|
let r = (rgb >> 16) as u8;
|
|
let g = (rgb >> 8) as u8;
|
|
let b = rgb as u8;
|
|
|
|
return [r, g, b]
|
|
}
|
|
|
|
pub fn make_rgb_u32(rgb: Vec<u8>) -> u32 {
|
|
let r = *rgb.get(0).unwrap() as u32;
|
|
let g = *rgb.get(1).unwrap() as u32;
|
|
let b = *rgb.get(2).unwrap() as u32;
|
|
|
|
let rgb = (r << 16) + (g << 8) + b;
|
|
|
|
return rgb
|
|
}
|
|
|
|
pub fn halfblock_bitmap(bitmap: &Vec<Vec<u32>>) -> Vec<Vec<AnsiPixelPair>> {
|
|
let ansi_bitmap = bitmap
|
|
.iter()
|
|
.map(|x| {
|
|
x.iter().map(|y| AnsiPixel::new(y)).collect::<Vec<AnsiPixel>>()
|
|
})
|
|
.collect::<Vec<Vec<AnsiPixel>>>();
|
|
|
|
let mut ansi_canvas: Vec<Vec<AnsiPixelPair>> = Vec::new();
|
|
|
|
for two_rows in ansi_bitmap.chunks(2) {
|
|
let rows = two_rows.to_vec();
|
|
let top_row = rows.get(0).unwrap();
|
|
let bottom_row = rows.get(1).unwrap();
|
|
|
|
let mut ansi_row: Vec<AnsiPixelPair> = Vec::new();
|
|
|
|
for i in 0..bitmap.get(0).unwrap().len() {
|
|
let top_pixel = top_row.get(i as usize).unwrap();
|
|
let bottom_pixel = bottom_row.get(i as usize).unwrap();
|
|
|
|
let pixel_pair = AnsiPixelPair {
|
|
top: *top_pixel,
|
|
bottom: *bottom_pixel,
|
|
};
|
|
|
|
ansi_row.push(pixel_pair);
|
|
}
|
|
|
|
ansi_canvas.push(ansi_row);
|
|
}
|
|
|
|
ansi_canvas
|
|
}
|
|
|
|
fn get_qb_char(pixel_pairs: &[AnsiPixelPair]) -> &str {
|
|
let (pair0_top, pair0_bottom) = (&pixel_pairs[0].top.irc, &pixel_pairs[0].bottom.irc);
|
|
let (pair1_top, pair1_bottom) = (&pixel_pairs[1].top.irc, &pixel_pairs[1].bottom.irc);
|
|
|
|
let ups_equal = pair0_top == pair1_top;
|
|
let downs_equal = pair0_bottom == pair1_bottom;
|
|
let lefts_equal = pair0_top == pair0_bottom;
|
|
let rights_equal = pair1_top == pair1_bottom;
|
|
let left_diag = pair0_top == pair1_bottom;
|
|
let right_diag = pair1_top == pair0_bottom;
|
|
|
|
match (ups_equal, downs_equal, lefts_equal, rights_equal, left_diag, right_diag) {
|
|
(true, _, true, true, _, _) => FULL,
|
|
(true, _, true, _, _, _) => UP_LEFT,
|
|
(true, _, _, true, _, _) => UP_RIGHT,
|
|
(true, _, _, _, _, _) => UP,
|
|
(_, true, true, _, _, _) => DOWN_LEFT,
|
|
(_, true, _, true, _, _) => DOWN_RIGHT,
|
|
(_, true, _, _, _, _) => DOWN,
|
|
(_, _, true, false, _, _) => LEFT,
|
|
(_, _, false, true, _, _) => RIGHT,
|
|
(_, _, _, _, true, _) => DIAG_LEFT,
|
|
(_, _, _, _, _, true) => DIAG_RIGHT,
|
|
_ => UP,
|
|
}
|
|
}
|
|
|
|
pub fn ansi_draw_24bit(image: AnsiImage) -> String {
|
|
let mut out: String = String::new();
|
|
for (y, row) in image.halfblock.iter().enumerate() {
|
|
for pixel_pair in row.iter() {
|
|
let fg = make_rgb_u8(pixel_pair.top.orig)
|
|
.to_vec()
|
|
.iter()
|
|
.map(|x| x.to_string())
|
|
.collect::<Vec<String>>();
|
|
|
|
let bg = make_rgb_u8(pixel_pair.bottom.orig)
|
|
.to_vec()
|
|
.iter()
|
|
.map(|x| x.to_string())
|
|
.collect::<Vec<String>>();
|
|
|
|
out.push_str(format!("\x1b[38;2;{}m\x1b[48;2;{}m{}", fg.join(";"), bg.join(";"), UP).as_str());
|
|
}
|
|
out.push_str("\x1b[0m");
|
|
|
|
if y != image.halfblock.len() - 1 {
|
|
out.push_str("\n");
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
pub fn ansi_draw_24bit_qb(image: AnsiImage) -> String {
|
|
let mut out: String = String::new();
|
|
for (y, row) in image.halfblock.iter().enumerate() {
|
|
for pixel_pairs in row.chunks(2) {
|
|
let fg = make_rgb_u8(pixel_pairs[0].top.orig)
|
|
.to_vec()
|
|
.iter()
|
|
.map(|x| x.to_string())
|
|
.collect::<Vec<String>>();
|
|
|
|
let bg = make_rgb_u8(pixel_pairs[0].bottom.orig)
|
|
.to_vec()
|
|
.iter()
|
|
.map(|x| x.to_string())
|
|
.collect::<Vec<String>>();
|
|
|
|
let char = match y {
|
|
_ if y == image.halfblock.len() - 1 => UP,
|
|
_ => get_qb_char(pixel_pairs),
|
|
};
|
|
|
|
out.push_str(format!("\x1b[38;2;{}m\x1b[48;2;{}m{}", fg.join(";"), bg.join(";"), char).as_str());
|
|
}
|
|
out.push_str("\x1b[0m");
|
|
|
|
if y != image.halfblock.len() - 1 {
|
|
out.push_str("\n");
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
pub fn ansi_draw_8bit(image: AnsiImage, args: &args::Args) -> String {
|
|
let mut out: String = String::new();
|
|
for (y, row) in image.halfblock.iter().enumerate() {
|
|
for pixel_pair in row.iter() {
|
|
|
|
let fg = match args.nograyscale {
|
|
true => pixel_pair.top.ansi232,
|
|
false => pixel_pair.top.ansi,
|
|
};
|
|
|
|
let bg = match args.nograyscale {
|
|
true => pixel_pair.bottom.ansi232,
|
|
false => pixel_pair.bottom.ansi,
|
|
};
|
|
|
|
out.push_str(format!("\x1b[38;5;{}m\x1b[48;5;{}m{}", fg, bg, UP).as_str());
|
|
}
|
|
out.push_str("\x1b[0m");
|
|
|
|
if y != image.halfblock.len() - 1 {
|
|
out.push_str("\n");
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
pub fn ansi_draw_8bit_qb(image: AnsiImage, args: &args::Args) -> String {
|
|
let mut out: String = String::new();
|
|
for (y, row) in image.halfblock.iter().enumerate() {
|
|
for pixel_pairs in row.chunks(2) {
|
|
let fg = match args.nograyscale {
|
|
true => pixel_pairs[0].top.ansi232,
|
|
false => pixel_pairs[0].top.ansi,
|
|
};
|
|
|
|
let bg = match args.nograyscale {
|
|
true => pixel_pairs[0].bottom.ansi232,
|
|
false => pixel_pairs[0].bottom.ansi,
|
|
};
|
|
|
|
let char = match y {
|
|
_ if y == image.halfblock.len() - 1 => UP,
|
|
_ => get_qb_char(pixel_pairs),
|
|
};
|
|
|
|
out.push_str(format!("\x1b[38;5;{}m\x1b[48;5;{}m{}", fg, bg, char).as_str());
|
|
}
|
|
out.push_str("\x1b[0m");
|
|
|
|
if y != image.halfblock.len() - 1 {
|
|
out.push_str("\n");
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
pub fn irc_draw(image: AnsiImage, args: &args::Args) -> String {
|
|
let mut out: String = String::new();
|
|
for (y, row) in image.halfblock.iter().enumerate() {
|
|
let mut last_fg: u8 = 0;
|
|
let mut last_bg: u8 = 0;
|
|
for (x, pixel_pair) in row.iter().enumerate() {
|
|
let fg = match args.nograyscale {
|
|
true => pixel_pair.top.irc88,
|
|
false => pixel_pair.top.irc,
|
|
};
|
|
|
|
let bg = match args.nograyscale {
|
|
true => pixel_pair.bottom.irc88,
|
|
false => pixel_pair.bottom.irc,
|
|
};
|
|
|
|
if x != 0 {
|
|
if fg == last_fg && bg == last_bg {
|
|
out.push_str(&format!("{}", UP));
|
|
} else if bg == last_bg {
|
|
out.push_str(&format!("\x03{}{}", fg, UP));
|
|
} else {
|
|
out.push_str(&format!("\x03{},{}{}", fg, bg, UP));
|
|
}
|
|
} else {
|
|
out.push_str(&format!("\x03{},{}{}", fg, bg, UP));
|
|
}
|
|
|
|
last_fg = fg;
|
|
last_bg = bg;
|
|
}
|
|
|
|
out.push_str("\x0f");
|
|
|
|
if y != image.halfblock.len() - 1 {
|
|
out.push_str("\n");
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
pub fn irc_draw_qb(image: AnsiImage, args: &args::Args) -> String {
|
|
let mut out: String = String::new();
|
|
for (y, row) in image.halfblock.iter().enumerate() {
|
|
let mut last_fg: u8 = 0;
|
|
let mut last_bg: u8 = 0;
|
|
for (x, pixel_pairs) in row.chunks(2).enumerate() {
|
|
let fg = match args.nograyscale {
|
|
true => pixel_pairs[0].top.irc88,
|
|
false => pixel_pairs[0].top.irc,
|
|
};
|
|
|
|
let bg = match args.nograyscale {
|
|
true => pixel_pairs[0].bottom.irc88,
|
|
false => pixel_pairs[0].bottom.irc,
|
|
};
|
|
|
|
let char = match y {
|
|
_ if y == image.halfblock.len() - 1 => UP,
|
|
_ => get_qb_char(pixel_pairs),
|
|
};
|
|
|
|
if x == 0 {
|
|
out.push_str(&format!("\x03{},{}{}", fg, bg, char));
|
|
} else {
|
|
if fg == last_fg && bg == last_bg {
|
|
out.push_str(&format!("{}", char));
|
|
} else if bg == last_bg {
|
|
out.push_str(&format!("\x03{}{}", fg, char));
|
|
} else {
|
|
out.push_str(&format!("\x03{},{}{}", fg, bg, char));
|
|
}
|
|
}
|
|
|
|
last_fg = fg;
|
|
last_bg = bg;
|
|
}
|
|
|
|
out.push_str("\x0f");
|
|
|
|
if y != image.halfblock.len() - 1 {
|
|
out.push_str("\n");
|
|
}
|
|
}
|
|
return out
|
|
} |