use tracing::{debug, info};
use crate::error::DotmaxError;
use crate::grid::BrailleGrid;
use super::threshold::BinaryImage;
#[allow(clippy::similar_names)] #[allow(clippy::cast_possible_truncation)] pub fn pixels_to_braille(
binary: &BinaryImage,
_cell_width: usize,
_cell_height: usize,
) -> Result<BrailleGrid, DotmaxError> {
if binary.width == 0 || binary.height == 0 {
return Err(DotmaxError::InvalidImageDimensions {
width: binary.width,
height: binary.height,
});
}
let grid_width = ((binary.width + 1) / 2) as usize;
let grid_height = ((binary.height + 3) / 4) as usize;
info!(
"Mapping {}×{} binary image to {}×{} braille grid",
binary.width, binary.height, grid_width, grid_height
);
let mut grid = BrailleGrid::new(grid_width, grid_height)?;
debug!(
"Created BrailleGrid with dimensions {}×{}",
grid_width, grid_height
);
for cell_y in 0..grid_height {
for cell_x in 0..grid_width {
let pixel_x_start = (cell_x * 2) as u32;
let pixel_y_start = (cell_y * 4) as u32;
for dot_y in 0..4 {
for dot_x in 0..2 {
let pixel_x = pixel_x_start + dot_x;
let pixel_y = pixel_y_start + dot_y;
let pixel_value = if pixel_x < binary.width && pixel_y < binary.height {
let pixel_index = (pixel_y * binary.width + pixel_x) as usize;
binary.pixels[pixel_index]
} else {
false
};
let dot_x_abs = pixel_x as usize;
let dot_y_abs = pixel_y as usize;
if pixel_value {
grid.set_dot(dot_x_abs, dot_y_abs)?;
}
}
}
}
}
info!(
"Braille mapping complete: {}×{} grid with {} total dots",
grid.width(),
grid.height(),
grid.width() * grid.height() * 8
);
Ok(grid)
}
#[cfg(test)]
mod tests {
use super::*;
fn create_test_image(width: u32, height: u32, pixels: Vec<bool>) -> BinaryImage {
assert_eq!(
pixels.len(),
(width * height) as usize,
"Pixel count must match width × height"
);
BinaryImage {
width,
height,
pixels,
}
}
#[test]
fn test_empty_image_returns_error() {
let binary = BinaryImage {
width: 0,
height: 0,
pixels: vec![],
};
let result = pixels_to_braille(&binary, 0, 0);
assert!(result.is_err());
match result {
Err(DotmaxError::InvalidImageDimensions { width, height }) => {
assert_eq!(width, 0);
assert_eq!(height, 0);
}
_ => panic!("Expected InvalidImageDimensions error"),
}
}
#[test]
fn test_all_black_2x4_block_all_dots_on() {
let pixels = vec![true; 8]; let binary = create_test_image(2, 4, pixels);
let grid = pixels_to_braille(&binary, 1, 1).unwrap();
assert_eq!(grid.width(), 1);
assert_eq!(grid.height(), 1);
let ch = grid.get_char(0, 0);
assert_eq!(
ch, '\u{28FF}',
"All black pixels should produce U+28FF (all dots on)"
);
}
#[test]
fn test_all_white_2x4_block_all_dots_off() {
let pixels = vec![false; 8]; let binary = create_test_image(2, 4, pixels);
let grid = pixels_to_braille(&binary, 1, 1).unwrap();
assert_eq!(grid.width(), 1);
assert_eq!(grid.height(), 1);
let ch = grid.get_char(0, 0);
assert_eq!(
ch, '\u{2800}',
"All white pixels should produce U+2800 (blank braille)"
);
}
#[test]
fn test_single_pixel_1x1_image() {
let pixels = vec![true];
let binary = create_test_image(1, 1, pixels);
let grid = pixels_to_braille(&binary, 1, 1).unwrap();
assert_eq!(grid.width(), 1);
assert_eq!(grid.height(), 1);
let ch = grid.get_char(0, 0);
assert_eq!(
ch, '\u{2801}',
"Single pixel at (0,0) should produce U+2801 (dot 1 only)"
);
}
#[test]
fn test_padding_5x5_image() {
let pixels = vec![true; 25]; let binary = create_test_image(5, 5, pixels);
let grid = pixels_to_braille(&binary, 3, 2).unwrap();
assert_eq!(grid.width(), 3);
assert_eq!(grid.height(), 2);
let ch_00 = grid.get_char(0, 0);
assert_eq!(ch_00, '\u{28FF}', "Cell (0,0) should have all dots on");
let ch_20 = grid.get_char(2, 0);
assert_eq!(ch_20, '\u{2847}', "Cell (2,0) should have left column only");
let ch_01 = grid.get_char(0, 1);
assert_eq!(ch_01, '\u{2809}', "Cell (0,1) should have top row only");
}
#[test]
fn test_pixel_0_0_maps_to_dot_1() {
let mut pixels = vec![false; 8];
pixels[0] = true;
let binary = create_test_image(2, 4, pixels);
let grid = pixels_to_braille(&binary, 1, 1).unwrap();
let ch = grid.get_char(0, 0);
assert_eq!(ch, '\u{2801}', "Pixel (0,0) should map to dot 1 (U+2801)");
}
#[test]
fn test_pixel_1_0_maps_to_dot_4() {
let mut pixels = vec![false; 8];
pixels[1] = true;
let binary = create_test_image(2, 4, pixels);
let grid = pixels_to_braille(&binary, 1, 1).unwrap();
let ch = grid.get_char(0, 0);
assert_eq!(ch, '\u{2808}', "Pixel (1,0) should map to dot 4 (U+2808)");
}
#[test]
fn test_checkerboard_pattern() {
let pixels = vec![
true, false, false, true, true, false, false, true, ];
let binary = create_test_image(2, 4, pixels);
let grid = pixels_to_braille(&binary, 1, 1).unwrap();
let ch = grid.get_char(0, 0);
assert_eq!(ch, '\u{2895}', "Checkerboard should produce U+2895");
}
#[test]
fn test_grid_dimensions_160x96_pixels() {
let pixels = vec![false; 160 * 96];
let binary = create_test_image(160, 96, pixels);
let grid = pixels_to_braille(&binary, 80, 24).unwrap();
assert_eq!(grid.width(), 80);
assert_eq!(grid.height(), 24);
}
#[test]
fn test_non_divisible_width() {
let pixels = vec![false; 20]; let binary = create_test_image(5, 4, pixels);
let grid = pixels_to_braille(&binary, 3, 1).unwrap();
assert_eq!(grid.width(), 3);
assert_eq!(grid.height(), 1);
}
#[test]
fn test_non_divisible_height() {
let pixels = vec![false; 20]; let binary = create_test_image(4, 5, pixels);
let grid = pixels_to_braille(&binary, 2, 2).unwrap();
assert_eq!(grid.width(), 2);
assert_eq!(grid.height(), 2);
}
#[test]
fn test_very_small_2x2_image() {
let pixels = vec![true, false, false, true]; let binary = create_test_image(2, 2, pixels);
let grid = pixels_to_braille(&binary, 1, 1).unwrap();
assert_eq!(grid.width(), 1);
assert_eq!(grid.height(), 1);
let ch = grid.get_char(0, 0);
assert_eq!(
ch, '\u{2811}',
"2×2 pattern should produce U+2811 (dots 1,5)"
);
}
}