use std::fmt;
use std::io::{self, Write};
use std::os::raw::{c_uchar, c_uint};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RenderError {
InvalidDimensions,
InternalError,
}
impl fmt::Display for RenderError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RenderError::InvalidDimensions => write!(f, "Width or height cannot be zero"),
RenderError::InternalError => write!(f, "An internal rendering error occurred"),
}
}
}
impl std::error::Error for RenderError {}
unsafe extern "C" {
fn generate_braille_cells(
in_pixels: *const c_uchar,
in_width: c_uint,
in_height: c_uint,
out_cells: *mut c_uchar,
target_width: c_uint,
target_height: c_uint,
);
fn generate_terminal_cells(
in_pixels: *const c_uchar,
in_width: c_uint,
in_height: c_uint,
out_cells: *mut c_uchar,
target_width: c_uint,
target_height: c_uint,
);
}
pub fn render_braille(
pixels: &[u8],
in_width: u32,
in_height: u32,
target_width: u32,
target_height: u32,
) -> Result<Vec<u8>, RenderError> {
if target_width == 0 || target_height == 0 || in_width == 0 || in_height == 0 {
return Err(RenderError::InvalidDimensions);
}
let out_size = (target_width * target_height * 8) as usize;
let mut out_cells = vec![0u8; out_size];
unsafe {
generate_braille_cells(
pixels.as_ptr(),
in_width,
in_height,
out_cells.as_mut_ptr(),
target_width,
target_height,
);
}
Ok(out_cells)
}
pub fn render_half_block(
pixels: &[u8],
in_width: u32,
in_height: u32,
target_width: u32,
target_height: u32,
) -> Result<Vec<u8>, RenderError> {
if target_width == 0 || target_height == 0 || in_width == 0 || in_height == 0 {
return Err(RenderError::InvalidDimensions);
}
let out_size = (target_width * target_height * 6) as usize;
let mut out_cells = vec![0u8; out_size];
unsafe {
generate_terminal_cells(
pixels.as_ptr(),
in_width,
in_height,
out_cells.as_mut_ptr(),
target_width,
target_height,
);
}
Ok(out_cells)
}
fn le_bytes_to_char(bytes: &[u8]) -> Option<char> {
if bytes.len() < 4 {
return None;
}
let code_point = u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]);
char::from_u32(code_point)
}
pub fn print_braille_to_terminal(cells: &[u8], target_width: u32, target_height: u32) {
let mut out = io::stdout();
let _ = write!(out, "\x1b[2J\x1b[H");
for cy in 0..target_height {
for cx in 0..target_width {
let idx = ((cy * target_width + cx) * 8) as usize;
let ch = le_bytes_to_char(&cells[idx..idx + 4]);
let r = cells[idx + 4];
let g = cells[idx + 5];
let b = cells[idx + 6];
match ch {
Some(c) if c != '\0' && c != ' ' => {
let _ = write!(out, "\x1b[38;2;{};{};{}m{}", r, g, b, c);
}
_ => {
let _ = write!(out, " ");
}
}
}
let _ = writeln!(out, "\x1b[0m");
}
let _ = out.flush();
}
pub fn print_halfblock_to_terminal(cells: &[u8], target_width: u32, target_height: u32) {
let mut out = io::stdout();
let _ = write!(out, "\x1b[2J\x1b[H");
for cy in 0..target_height {
for cx in 0..target_width {
let idx = ((cy * target_width + cx) * 6) as usize;
let r_fg = cells[idx];
let g_fg = cells[idx + 1];
let b_fg = cells[idx + 2];
let r_bg = cells[idx + 3];
let g_bg = cells[idx + 4];
let b_bg = cells[idx + 5];
let _ = write!(
out,
"\x1b[48;2;{};{};{}m\x1b[38;2;{};{};{}m\u{2580}",
r_fg, g_fg, b_fg, r_bg, g_bg, b_bg
);
}
let _ = writeln!(out, "\x1b[0m");
}
let _ = out.flush();
}