use std::num::NonZeroUsize;
use crate::util::Pixel;
pub(super) fn reduce_cubic<T: Pixel>(
dest: &mut [T],
src: &[T],
dest_pitch: NonZeroUsize,
src_pitch: NonZeroUsize,
dest_width: NonZeroUsize,
dest_height: NonZeroUsize,
) {
debug_assert!(src.len() >= src_pitch.get() * dest_height.get() * 2);
debug_assert!(dest.len() >= dest_pitch.get() * dest_height.get());
unsafe {
reduce_cubic_vertical(
dest,
src,
dest_pitch,
src_pitch,
dest_width.saturating_mul(NonZeroUsize::new_unchecked(2)),
dest_height,
);
reduce_cubic_horizontal_inplace(dest, dest_pitch, dest_width, dest_height);
}
}
unsafe fn reduce_cubic_vertical<T: Pixel>(
dest: &mut [T],
src: &[T],
dest_pitch: NonZeroUsize,
src_pitch: NonZeroUsize,
dest_width: NonZeroUsize,
dest_height: NonZeroUsize,
) {
let mut dest = dest.as_mut_ptr();
let src = src.as_ptr();
for x in 0..dest_width.get() {
let a: u32 = (*src.add(x)).as_();
let b: u32 = (*src.add(x + src_pitch.get())).as_();
*dest.add(x) = T::from_u32_or_max_value((a + b + 1) / 2);
}
dest = dest.add(dest_pitch.get());
for y in 1..(dest_height.get() - 1) {
let src_row_offset = y * 2 * src_pitch.get();
for x in 0..dest_width.get() {
let mut m0: u32 = (*src.add(src_row_offset + x - src_pitch.get() * 2)).as_();
let mut m1: u32 = (*src.add(src_row_offset + x - src_pitch.get())).as_();
let mut m2: u32 = (*src.add(src_row_offset + x)).as_();
let m3: u32 = (*src.add(src_row_offset + x + src_pitch.get())).as_();
let m4: u32 = (*src.add(src_row_offset + x + src_pitch.get() * 2)).as_();
let m5: u32 = (*src.add(src_row_offset + x + src_pitch.get() * 3)).as_();
m2 = (m2 + m3) * 10;
m1 = (m1 + m4) * 5;
m0 += m5 + m2 + m1 + 16;
m0 >>= 5;
*dest.add(x) = T::from_u32_or_max_value(m0);
}
dest = dest.add(dest_pitch.get());
}
if dest_height.get() > 1 {
let src_row_offset = (dest_height.get() - 1) * 2 * src_pitch.get();
for x in 0..dest_width.get() {
let a: u32 = (*src.add(src_row_offset + x)).as_();
let b: u32 = (*src.add(src_row_offset + x + src_pitch.get())).as_();
*dest.add(x) = T::from_u32_or_max_value((a + b + 1) / 2);
}
}
}
unsafe fn reduce_cubic_horizontal_inplace<T: Pixel>(
dest: &mut [T],
dest_pitch: NonZeroUsize,
dest_width: NonZeroUsize,
dest_height: NonZeroUsize,
) {
let mut dest = dest.as_mut_ptr();
for _y in 0..dest_height.get() {
let a: u32 = (*dest).as_();
let b: u32 = (*dest.add(1)).as_();
let src0 = (a + b + 1) / 2;
for x in 1..(dest_width.get() - 1) {
let mut m0: u32 = (*dest.add(x * 2 - 2)).as_();
let mut m1: u32 = (*dest.add(x * 2 - 1)).as_();
let mut m2: u32 = (*dest.add(x * 2)).as_();
let m3: u32 = (*dest.add(x * 2 + 1)).as_();
let m4: u32 = (*dest.add(x * 2 + 2)).as_();
let m5: u32 = (*dest.add(x * 2 + 3)).as_();
m2 = (m2 + m3) * 10;
m1 = (m1 + m4) * 5;
m0 += m5 + m2 + m1 + 16;
m0 >>= 5;
*dest.add(x) = T::from_u32_or_max_value(m0);
}
*dest = T::from_u32_or_max_value(src0);
if dest_width.get() > 1 {
let x = dest_width.get() - 1;
let a: u32 = (*dest.add(x * 2)).as_();
let b: u32 = (*dest.add(x * 2 + 1)).as_();
*dest.add(x) = T::from_u32_or_max_value((a + b + 1) / 2);
}
dest = dest.add(dest_pitch.get());
}
}