use std::path::Path;
use crate::core::{ImageFormat, Pix, PixelDepth};
use crate::io::{IoError, IoResult};
#[allow(clippy::too_many_arguments)]
pub fn convert_files_to_1bpp(
dir_in: impl AsRef<Path>,
substr: Option<&str>,
upscaling: u32,
thresh: u32,
first_page: usize,
n_pages: usize,
dir_out: impl AsRef<Path>,
out_format: ImageFormat,
) -> IoResult<()> {
if upscaling != 1 && upscaling != 2 && upscaling != 4 {
return Err(IoError::InvalidData("upscaling must be 1, 2, or 4".into()));
}
let thresh = if thresh == 0 { 128 } else { thresh };
let dir_in = dir_in.as_ref();
let dir_out = dir_out.as_ref();
std::fs::create_dir_all(dir_out).map_err(IoError::Io)?;
let mut paths: Vec<std::path::PathBuf> = std::fs::read_dir(dir_in)
.map_err(IoError::Io)?
.filter_map(|e| e.ok())
.filter(|e| e.path().is_file())
.filter(|e| {
let name = e.file_name().to_string_lossy().to_string();
match substr {
Some(s) => name.contains(s),
None => true,
}
})
.map(|e| e.path())
.collect();
paths.sort();
let end = if n_pages == 0 {
paths.len()
} else {
(first_page + n_pages).min(paths.len())
};
let paths = &paths[first_page.min(paths.len())..end];
let ext = crate::io::get_format_extension(out_format);
for path in paths {
let pix = crate::io::read_image(path)?;
let gray = convert_to_grayscale(&pix)?;
let binary = binarize_with_upscaling(&gray, upscaling, thresh)?;
let stem = path
.file_stem()
.unwrap_or_default()
.to_string_lossy()
.to_string();
let out_path = dir_out.join(format!("{stem}.{ext}"));
crate::io::write_image(&binary, &out_path, out_format)?;
}
Ok(())
}
fn convert_to_grayscale(pix: &Pix) -> IoResult<Pix> {
match pix.depth() {
PixelDepth::Bit1 => Ok(pix.clone()),
PixelDepth::Bit8 => Ok(pix.clone()),
PixelDepth::Bit32 => {
let w = pix.width();
let h = pix.height();
let gray = Pix::new(w, h, PixelDepth::Bit8).map_err(IoError::Core)?;
let mut gray_mut = gray
.try_into_mut()
.map_err(|_| IoError::Core(crate::core::Error::AllocationFailed))?;
for y in 0..h {
for x in 0..w {
let pixel = pix.get_pixel(x, y).unwrap_or(0);
let r = (pixel >> 24) & 0xff;
let g = (pixel >> 16) & 0xff;
let b = (pixel >> 8) & 0xff;
let lum = ((77 * r + 150 * g + 29 * b) >> 8).min(255);
gray_mut.set_pixel(x, y, lum).map_err(IoError::Core)?;
}
}
Ok(gray_mut.into())
}
_depth => {
Ok(pix.clone())
}
}
}
fn binarize_with_upscaling(pix: &Pix, upscaling: u32, thresh: u32) -> IoResult<Pix> {
if pix.depth() == PixelDepth::Bit1 {
return Ok(pix.clone());
}
let w = pix.width();
let h = pix.height();
let (work_pix, work_thresh) = if upscaling > 1 {
let new_w = w * upscaling;
let new_h = h * upscaling;
let scaled = Pix::new(new_w, new_h, pix.depth()).map_err(IoError::Core)?;
let mut scaled_mut = scaled
.try_into_mut()
.map_err(|_| IoError::Core(crate::core::Error::AllocationFailed))?;
for y in 0..new_h {
for x in 0..new_w {
let sx = x / upscaling;
let sy = y / upscaling;
let val = pix.get_pixel(sx, sy).unwrap_or(0);
scaled_mut.set_pixel(x, y, val).map_err(IoError::Core)?;
}
}
(scaled_mut.into(), thresh)
} else {
(pix.clone(), thresh)
};
let final_w = work_pix.width();
let final_h = work_pix.height();
let binary = Pix::new(final_w, final_h, PixelDepth::Bit1).map_err(IoError::Core)?;
let mut binary_mut = binary
.try_into_mut()
.map_err(|_| IoError::Core(crate::core::Error::AllocationFailed))?;
for y in 0..final_h {
for x in 0..final_w {
let val = work_pix.get_pixel(x, y).unwrap_or(0);
if val < work_thresh {
binary_mut.set_pixel(x, y, 1).map_err(IoError::Core)?;
}
}
}
Ok(binary_mut.into())
}