use std::{fmt::Display, mem::MaybeUninit, num::NonZeroUsize};
use num_traits::{AsPrimitive, PrimInt};
use crate::{
resize::{
SIMPLE_RESIZE_WEIGHT_HALF, SIMPLE_RESIZE_WEIGHT_MAX, SIMPLE_RESIZE_WEIGHT_SHIFT,
SimpleResize,
},
util::uninit_vec,
};
use semisafe::option::unwrap as semisafe_opt_unwrap;
use semisafe::slice::get as semisafe_get;
use semisafe::slice::get_mut as semisafe_get_mut;
pub trait SimpleResizeable:
Copy
+ Clone
+ Default
+ Send
+ Sync
+ PrimInt
+ Display
+ AsPrimitive<i16>
+ AsPrimitive<i32>
+ AsPrimitive<i64>
+ 'static
{
}
impl SimpleResizeable for u8 {}
impl SimpleResizeable for i16 {}
pub(crate) unsafe fn simple_resize<T: SimpleResizeable>(
resizer: &SimpleResize,
mut dest: *mut u8,
dest_stride_bytes: NonZeroUsize,
src: *const u8,
src_stride_bytes: NonZeroUsize,
horizontal_vectors: bool,
) {
let limit_vectors = size_of::<T>() == 2;
let pel = resizer.pel as i32;
let mut minimum = 0;
let mut maximum = resizer.limit_height.get() as i32 * pel - 1;
let horizontal_step = if horizontal_vectors { pel } else { 0 };
let vertical_step = if horizontal_vectors { 0 } else { pel };
let mut work: Vec<MaybeUninit<T>> = uninit_vec(resizer.src_width.get());
for y in 0..resizer.dest_height.get() {
let weight_bottom = *semisafe_get(&resizer.vertical_weights, y);
let weight_top = SIMPLE_RESIZE_WEIGHT_MAX - weight_bottom;
let offset1 = *semisafe_get(&resizer.vertical_offsets, y) * src_stride_bytes.get();
let src_p1: *const T = src.add(offset1).cast();
let src_p2: *const T = src.add(offset1 + src_stride_bytes.get()).cast();
for x in 0..resizer.src_width.get() {
let src1: i32 = (*src_p1.add(x)).as_();
let src2: i32 = (*src_p2.add(x)).as_();
semisafe_get_mut(&mut work, x).write(semisafe_opt_unwrap(T::from(
(src1 * weight_top + src2 * weight_bottom + SIMPLE_RESIZE_WEIGHT_HALF)
>> SIMPLE_RESIZE_WEIGHT_SHIFT,
)));
}
if horizontal_vectors {
minimum = 0;
maximum = resizer.limit_width.get() as i32 * pel - 1;
}
for x in 0..resizer.dest_width.get() {
let weight_right = *semisafe_get(&resizer.horizontal_weights, x);
let weight_left = SIMPLE_RESIZE_WEIGHT_MAX - weight_right;
let offset = *semisafe_get(&resizer.horizontal_offsets, x);
let workp1: i32 = (*unsafe { semisafe_get(&work, offset).assume_init_ref() }).as_();
let workp2: i32 = (*unsafe { semisafe_get(&work, offset + 1).assume_init_ref() }).as_();
let mut result =
(workp1 * weight_left + workp2 * weight_right + SIMPLE_RESIZE_WEIGHT_HALF)
>> SIMPLE_RESIZE_WEIGHT_SHIFT;
if limit_vectors {
result = result.clamp(minimum, maximum);
minimum -= horizontal_step;
maximum -= horizontal_step;
}
let destp: *mut T = dest.cast::<T>();
*unsafe { destp.add(x) } = semisafe_opt_unwrap(T::from(result));
}
dest = dest.add(dest_stride_bytes.get());
if limit_vectors {
minimum -= vertical_step;
maximum -= vertical_step;
}
}
}