pepecore 0.1.3

A Rust library for image decoding, encoding, and processing using an efficient SVec data structure.
Documentation
//! Module implementing screentone and rotated screentone effects on single-channel `SVec` images.
//!
//! Screentone mimics comic-style dot screening by thresholding pixels against a dot matrix.
//!
//! # Examples
//!
//! ```rust
//! use pepecore::{screentone, rotate_screentone};
//! use pepecore::array::svec::SVec;
//! use pepecore::enums::{DotType, PixelType, ImgData};
//! use pepecore::svec::Shape;
//!
//! // Create a grayscale SVec (2x2, u8)
//! let mut img = SVec::new(Shape::new(2, 2, None), ImgData::U8(vec![50, 200, 150, 100]));
//! // Apply screentone with dot size 4 and circular dots
//! screentone(&mut img, 4, &DotType::CIRCLE);
//!
//! // Apply rotated screentone at 45 degrees on f32 image
//! let mut img_f32 = SVec::new(Shape::new(3, 3, None), ImgData::F32(vec![0.2; 9]));
//! rotate_screentone(&mut img_f32, 5, 45.0, &DotType::ELLIPSE);
//! ```
use crate::array::svec::SVec;
use crate::enums::{DotType, PixelType};
use crate::ops::svec_ops::halftone::dot::dot_create;
use crate::ops::svec_ops::halftone::utils::{HalftonePixel, compute_cos_sin, rotate_pixel_coordinates};
/// Apply screentone (non-rotated) to a single-channel image in-place.
///
/// - Uses a symmetric dot matrix of total size `dot_size*2` generated by `dot_type`.
/// - For each pixel, compares its value to the corresponding dot matrix threshold:
///   - If pixel < threshold, sets to `MIN_VALUE`.
///   - Otherwise, sets to `MAX_VALUE`.
///
/// # Parameters
///
/// - `img`: Mutable reference to a single-channel `SVec`.
/// - `dot_size`: Radius of dot pattern (matrix will be `2*dot_size`).
/// - `dot_type`: `DotType` enum specifying shape of dots.
fn apply_screentone<T: HalftonePixel>(img: &mut SVec, dot_size: usize, dot_type: &DotType) {
    let (h, w, _) = img.shape();
    let mut_img = img.get_data_mut::<T>().unwrap();

    let dot_matrix = dot_create(dot_size, dot_type);
    let dot_matrix_data = dot_matrix.get_data::<f32>().unwrap();
    let lx_bias = dot_size / 2;
    let ly_bias = dot_size / 2;
    let dot_size = dot_size * 2;

    let dot_matrix_converted = T::prepare_dot_matrix(dot_matrix_data);

    for ly in 0..h {
        let ly2 = (ly + ly_bias) % dot_size;

        for lx in 0..w {
            let idx = ly * w + lx;
            let dot_idx = (lx + lx_bias) % dot_size + ly2 * dot_size;

            mut_img[idx] = if mut_img[idx] < dot_matrix_converted[dot_idx] {
                T::MIN_VALUE
            } else {
                T::MAX_VALUE
            };
        }
    }
}
/// Apply rotated screentone effect to a single-channel image in-place.
///
/// - Rotates sampling coordinates by `angle` around image center before thresholding.
///
/// # Parameters
///
/// - `img`: Mutable reference to a single-channel `SVec`.
/// - `dot_size`: Radius of dot pattern.
/// - `angle`: Rotation angle in degrees.
/// - `dot_type`: `DotType` specifying shape.

fn apply_rotate_screentone<T: HalftonePixel>(img: &mut SVec, dot_size: usize, angle: f32, dot_type: &DotType) {
    let (h, w, _) = img.shape();
    let mut_img = img.get_data_mut::<T>().unwrap();
    let cos_sin = compute_cos_sin(angle.to_radians());
    let dot_matrix = dot_create(dot_size, dot_type);
    let dot_matrix_data = dot_matrix.get_data::<f32>().unwrap();
    let new_dot_matrix_data = T::prepare_dot_matrix(dot_matrix_data);
    let lx_bias = w / 2;
    let ly_bias = h / 2;
    let dot_size = dot_size * 2;

    for ly in 0..h {
        let ly2 = ly + ly_bias;

        for lx in 0..w {
            let lx2 = lx + lx_bias;
            let value = &mut mut_img[ly * w + lx];
            let rot = rotate_pixel_coordinates(lx2 as f32, ly2 as f32, w as f32, h as f32, cos_sin[0], cos_sin[1]);
            *value = if *value < new_dot_matrix_data[rot.0 % dot_size + (rot.1 % dot_size) * dot_size] {
                T::MIN_VALUE
            } else {
                T::MAX_VALUE
            };
        }
    }
}

/// Public API: apply non-rotated screentone, dispatching by pixel type.
pub fn screentone(img: &mut SVec, dot_size: usize, dot_type: &DotType) {
    match img.pixel_type() {
        PixelType::F32 => apply_screentone::<f32>(img, dot_size, dot_type),
        PixelType::U8 => apply_screentone::<u8>(img, dot_size, dot_type),
        PixelType::U16 => apply_screentone::<u16>(img, dot_size, dot_type),
    }
}

/// Public API: apply rotated screentone, dispatching by pixel type.
pub fn rotate_screentone(img: &mut SVec, dot_size: usize, angle: f32, dot_type: &DotType) {
    match img.pixel_type() {
        PixelType::F32 => apply_rotate_screentone::<f32>(img, dot_size, angle, dot_type),
        PixelType::U8 => apply_rotate_screentone::<u8>(img, dot_size, angle, dot_type),
        PixelType::U16 => apply_rotate_screentone::<u16>(img, dot_size, angle, dot_type),
    }
}