use crate::array::svec::SVec;
use crate::enums::{DotType, PixelType};
use crate::errors::HalftoneError;
use crate::ops::svec_ops::halftone::dot::dot_create;
use crate::ops::svec_ops::halftone::utils::{HalftonePixel, compute_cos_sin, rotate_pixel_coordinates};
use std::fmt::Debug;
fn apply_halftone<T>(img: &mut SVec, dot_sizes: &[usize], dot_type: &[DotType]) -> Result<(), HalftoneError>
where
T: HalftonePixel + Debug,
{
let (height, width, channels_opt) = img.shape();
let data = img
.get_data_mut::<T>()
.map_err(|e| HalftoneError::GetDataError(format!("{:?}", e)))?;
let channels = channels_opt.ok_or(HalftoneError::NoChannelsError)?;
if dot_sizes.len() < channels || dot_type.len() < channels {
return Err(HalftoneError::DotSizeMismatch(dot_sizes.len(), channels));
}
let mut biases = Vec::with_capacity(channels);
let mut double_sizes = Vec::with_capacity(channels);
let mut dot_matrices = Vec::with_capacity(channels);
for index in 0..channels {
let size = dot_sizes[index];
let bias = size / 2;
let doubled = size * 2;
let matrix = if size > 0 {
let kernel = dot_create(size, &dot_type[index]);
let kernel_data = kernel
.get_data::<f32>()
.map_err(|e| HalftoneError::GetDataError(format!("{:?}", e)))?;
T::prepare_dot_matrix(kernel_data)
} else {
Vec::new()
};
biases.push(bias);
double_sizes.push(doubled);
dot_matrices.push(matrix);
}
for y in 0..height {
for x in 0..width {
for c in 0..channels {
let ds = double_sizes[c];
if ds == 0 {
continue;
}
let bias = biases[c];
let offset_y = (y + bias) % ds;
let idx_in_matrix = (x + bias) % ds + offset_y * ds;
let idx = (y * width + x) * channels + c;
data[idx] = if data[idx] < dot_matrices[c][idx_in_matrix] {
T::MIN_VALUE
} else {
T::MAX_VALUE
};
}
}
}
Ok(())
}
fn apply_rotate_halftone<T>(
img: &mut SVec,
dot_sizes: &[usize],
angles: &[f32],
dot_type: &[DotType],
) -> Result<(), HalftoneError>
where
T: HalftonePixel + Debug,
{
let (height, width, channels_opt) = img.shape();
let data = img
.get_data_mut::<T>()
.map_err(|e| HalftoneError::GetDataError(format!("{:?}", e)))?;
let channels = channels_opt.ok_or(HalftoneError::NoChannelsError)?;
if dot_sizes.len() < channels || angles.len() < channels || dot_type.len() < channels {
return Err(HalftoneError::DotSizeMismatch(dot_sizes.len().max(angles.len()), channels));
}
let x_center = width as f32 / 2.0;
let y_center = height as f32 / 2.0;
let mut double_sizes = Vec::with_capacity(channels);
let mut dot_matrices = Vec::with_capacity(channels);
let mut cos_sin = Vec::with_capacity(channels);
for i in 0..channels {
let size = dot_sizes[i];
let doubled = size * 2;
let matrix = if size > 0 {
let kernel = dot_create(size, &dot_type[i]);
let kernel_data = kernel
.get_data::<f32>()
.map_err(|e| HalftoneError::GetDataError(format!("{:?}", e)))?;
T::prepare_dot_matrix(kernel_data)
} else {
Vec::new()
};
double_sizes.push(doubled);
dot_matrices.push(matrix);
cos_sin.push(compute_cos_sin(angles[i].to_radians()));
}
for y in 0..height {
for x in 0..width {
for c in 0..channels {
let ds = double_sizes[c];
if ds == 0 {
continue;
}
let (rx, ry) = rotate_pixel_coordinates(x as f32, y as f32, x_center, y_center, cos_sin[c][0], cos_sin[c][1]);
let ix = rx % ds;
let iy = ry % ds;
let idx_in_matrix = ix + iy * ds;
let idx = (y * width + x) * channels + c;
data[idx] = if data[idx] < dot_matrices[c][idx_in_matrix] {
T::MIN_VALUE
} else {
T::MAX_VALUE
};
}
}
}
Ok(())
}
pub fn halftone(img: &mut SVec, dot_sizes: &[usize], dot_type: &[DotType]) -> Result<(), HalftoneError> {
match img.pixel_type() {
PixelType::F32 => apply_halftone::<f32>(img, dot_sizes, dot_type),
PixelType::U8 => apply_halftone::<u8>(img, dot_sizes, dot_type),
PixelType::U16 => apply_halftone::<u16>(img, dot_sizes, dot_type),
}
}
pub fn rotate_halftone(img: &mut SVec, dot_sizes: &[usize], angles: &[f32], dot_type: &[DotType]) -> Result<(), HalftoneError> {
match img.pixel_type() {
PixelType::F32 => apply_rotate_halftone::<f32>(img, dot_sizes, angles, dot_type),
PixelType::U8 => apply_rotate_halftone::<u8>(img, dot_sizes, angles, dot_type),
PixelType::U16 => apply_rotate_halftone::<u16>(img, dot_sizes, angles, dot_type),
}
}