#![allow(clippy::unwrap_used, reason = "allow in test files")]
#![allow(clippy::undocumented_unsafe_blocks, reason = "allow in test files")]
#![allow(clippy::indexing_slicing, reason = "allow in test files")]
use std::num::{NonZeroU8, NonZeroUsize};
use crate::{mv_plane::MVPlane, params::Subpel};
fn create_test_mv_plane(
width: usize,
height: usize,
pel: Subpel,
hpad: usize,
vpad: usize,
plane_offset: usize,
) -> MVPlane {
let pitch = width + 2 * hpad;
MVPlane::new(
NonZeroUsize::new(width).unwrap(),
NonZeroUsize::new(height).unwrap(),
pel,
hpad,
vpad,
NonZeroU8::new(8).unwrap(), plane_offset,
NonZeroUsize::new(pitch).unwrap(),
)
.unwrap()
}
fn create_2x_upsampled_frame<T: Copy + From<u8>>(
width: usize,
height: usize,
pitch: usize,
) -> Vec<T> {
let mut frame = vec![T::from(0); pitch * height];
for y in 0..height {
for x in 0..width {
let block_x = x / 2;
let block_y = y / 2;
let sub_x = x % 2;
let sub_y = y % 2;
let base_value = ((block_y * (width / 2) + block_x) % 256) as u8;
let offset = (sub_y * 2 + sub_x) * 10;
let value = base_value.saturating_add(offset as u8);
frame[y * pitch + x] = T::from(value);
}
}
frame
}
fn create_dest_buffer<T: Copy + From<u8>>(plane: &MVPlane, total_windows: usize) -> Vec<T> {
let total_size = plane
.subpel_window_offsets
.get(total_windows - 1)
.copied()
.unwrap_or(0)
+ (plane.height.get() + 2 * plane.vpad) * plane.stride.get();
vec![T::from(0); total_size]
}
fn verify_pel2_interpolation<T: Copy + PartialEq + std::fmt::Debug + From<u8>>(
plane: &MVPlane,
src_2x: &[T],
src_2x_pitch: usize,
dest: &[T],
is_ext_padded: bool,
) {
let mut p1 = plane.subpel_window_offsets[1];
let mut p2 = plane.subpel_window_offsets[2];
let mut p3 = plane.subpel_window_offsets[3];
if !is_ext_padded {
let offset = plane.stride.get() * plane.vpad + plane.hpad;
p1 += offset;
p2 += offset;
p3 += offset;
}
for h in 0..plane.height.get() {
let src_row_base = h * 2 * src_2x_pitch;
for w in 0..plane.width.get() {
let src_col_base = w * 2;
let expected_p1 = src_2x[src_row_base + src_col_base + 1];
assert_eq!(
dest[p1 + w],
expected_p1,
"Subpel window 1 mismatch at ({}, {})",
w,
h
);
let expected_p2 = src_2x[src_row_base + src_2x_pitch + src_col_base];
assert_eq!(
dest[p2 + w],
expected_p2,
"Subpel window 2 mismatch at ({}, {})",
w,
h
);
let expected_p3 = src_2x[src_row_base + src_2x_pitch + src_col_base + 1];
assert_eq!(
dest[p3 + w],
expected_p3,
"Subpel window 3 mismatch at ({}, {})",
w,
h
);
}
p1 += plane.stride.get();
p2 += plane.stride.get();
p3 += plane.stride.get();
}
}
fn verify_pel4_interpolation<T: Copy + PartialEq + std::fmt::Debug + From<u8>>(
plane: &MVPlane,
src_2x: &[T],
src_2x_pitch: usize,
dest: &[T],
is_ext_padded: bool,
) {
let mut pp = [0; 16];
for (ppi, offset) in pp
.iter_mut()
.zip(plane.subpel_window_offsets.iter())
.take(16)
.skip(1)
{
*ppi = *offset;
}
if !is_ext_padded {
let offset = plane.stride.get() * plane.vpad + plane.hpad;
for ppi in pp[1..16].iter_mut() {
*ppi += offset;
}
}
for h in 0..plane.height.get() {
let src_row_base = h * 4 * src_2x_pitch;
for w in 0..plane.width.get() {
let src_col_base = w * 4;
let expected_values = [
src_2x[src_row_base + src_col_base + 1], src_2x[src_row_base + src_col_base + 2], src_2x[src_row_base + src_col_base + 3], src_2x[src_row_base + src_2x_pitch + src_col_base], src_2x[src_row_base + src_2x_pitch + src_col_base + 1], src_2x[src_row_base + src_2x_pitch + src_col_base + 2], src_2x[src_row_base + src_2x_pitch + src_col_base + 3], src_2x[src_row_base + src_2x_pitch * 2 + src_col_base], src_2x[src_row_base + src_2x_pitch * 2 + src_col_base + 1], src_2x[src_row_base + src_2x_pitch * 2 + src_col_base + 2], src_2x[src_row_base + src_2x_pitch * 2 + src_col_base + 3], src_2x[src_row_base + src_2x_pitch * 3 + src_col_base], src_2x[src_row_base + src_2x_pitch * 3 + src_col_base + 1], src_2x[src_row_base + src_2x_pitch * 3 + src_col_base + 2], src_2x[src_row_base + src_2x_pitch * 3 + src_col_base + 3], ];
for i in 1..16 {
assert_eq!(
dest[pp[i] + w],
expected_values[i - 1],
"Subpel window {} mismatch at ({}, {})",
i,
w,
h
);
}
}
for ppi in pp[1..16].iter_mut() {
*ppi += plane.stride.get();
}
}
}
#[test]
fn refine_ext_pel2_u8_basic() {
let width = 4;
let height = 4;
let hpad = 2;
let vpad = 2;
let mut plane = create_test_mv_plane(width, height, Subpel::Half, hpad, vpad, 0);
let src_2x_width = width * 2;
let src_2x_height = height * 2;
let src_2x_pitch = src_2x_width;
let src_2x = create_2x_upsampled_frame::<u8>(src_2x_width, src_2x_height, src_2x_pitch);
let mut dest = create_dest_buffer::<u8>(&plane, 4);
plane.refine_ext_pel2(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true, &mut dest,
);
verify_pel2_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel2_u16_basic() {
let width = 3;
let height = 3;
let hpad = 1;
let vpad = 1;
let mut plane = create_test_mv_plane(width, height, Subpel::Half, hpad, vpad, 0);
let src_2x_width = width * 2;
let src_2x_height = height * 2;
let src_2x_pitch = src_2x_width;
let src_2x = create_2x_upsampled_frame::<u16>(src_2x_width, src_2x_height, src_2x_pitch);
let mut dest = create_dest_buffer::<u16>(&plane, 4);
plane.refine_ext_pel2(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true, &mut dest,
);
verify_pel2_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel2_not_padded() {
let width = 2;
let height = 2;
let hpad = 1;
let vpad = 1;
let mut plane = create_test_mv_plane(width, height, Subpel::Half, hpad, vpad, 0);
let src_2x_width = width * 2;
let src_2x_height = height * 2;
let src_2x_pitch = src_2x_width;
let src_2x = create_2x_upsampled_frame::<u8>(src_2x_width, src_2x_height, src_2x_pitch);
let mut dest = create_dest_buffer::<u8>(&plane, 4);
plane.refine_ext_pel2(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
false, &mut dest,
);
verify_pel2_interpolation(&plane, &src_2x, src_2x_pitch, &dest, false);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel4_u8_basic() {
let width = 2;
let height = 2;
let hpad = 1;
let vpad = 1;
let mut plane = create_test_mv_plane(width, height, Subpel::Quarter, hpad, vpad, 0);
let src_2x_width = width * 4;
let src_2x_height = height * 4;
let src_2x_pitch = src_2x_width;
let src_2x = create_2x_upsampled_frame::<u8>(src_2x_width, src_2x_height, src_2x_pitch);
let mut dest = create_dest_buffer::<u8>(&plane, 16);
plane.refine_ext_pel4(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true, &mut dest,
);
verify_pel4_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel4_u16_basic() {
let width = 3;
let height = 2;
let hpad = 2;
let vpad = 1;
let mut plane = create_test_mv_plane(width, height, Subpel::Quarter, hpad, vpad, 0);
let src_2x_width = width * 4;
let src_2x_height = height * 4;
let src_2x_pitch = src_2x_width;
let src_2x = create_2x_upsampled_frame::<u16>(src_2x_width, src_2x_height, src_2x_pitch);
let mut dest = create_dest_buffer::<u16>(&plane, 16);
plane.refine_ext_pel4(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true, &mut dest,
);
verify_pel4_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel4_not_padded() {
let width = 2;
let height = 1;
let hpad = 1;
let vpad = 1;
let mut plane = create_test_mv_plane(width, height, Subpel::Quarter, hpad, vpad, 0);
let src_2x_width = width * 4;
let src_2x_height = height * 4;
let src_2x_pitch = src_2x_width;
let src_2x = create_2x_upsampled_frame::<u8>(src_2x_width, src_2x_height, src_2x_pitch);
let mut dest = create_dest_buffer::<u8>(&plane, 16);
plane.refine_ext_pel4(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
false, &mut dest,
);
verify_pel4_interpolation(&plane, &src_2x, src_2x_pitch, &dest, false);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel2_larger_frame() {
let width = 8;
let height = 6;
let hpad = 4;
let vpad = 3;
let mut plane = create_test_mv_plane(width, height, Subpel::Half, hpad, vpad, 0);
let src_2x_width = width * 2;
let src_2x_height = height * 2;
let src_2x_pitch = src_2x_width;
let src_2x = create_2x_upsampled_frame::<u8>(src_2x_width, src_2x_height, src_2x_pitch);
let mut dest = create_dest_buffer::<u8>(&plane, 4);
plane.refine_ext_pel2(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true,
&mut dest,
);
verify_pel2_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel4_larger_frame() {
let width = 4;
let height = 3;
let hpad = 2;
let vpad = 2;
let mut plane = create_test_mv_plane(width, height, Subpel::Quarter, hpad, vpad, 0);
let src_2x_width = width * 4;
let src_2x_height = height * 4;
let src_2x_pitch = src_2x_width;
let src_2x = create_2x_upsampled_frame::<u16>(src_2x_width, src_2x_height, src_2x_pitch);
let mut dest = create_dest_buffer::<u16>(&plane, 16);
plane.refine_ext_pel4(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true,
&mut dest,
);
verify_pel4_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel2_minimal_size() {
let width = 1;
let height = 1;
let hpad = 1;
let vpad = 1;
let mut plane = create_test_mv_plane(width, height, Subpel::Half, hpad, vpad, 0);
let src_2x_width = width * 2;
let src_2x_height = height * 2;
let src_2x_pitch = src_2x_width;
let mut src_2x = vec![0u8; src_2x_pitch * src_2x_height];
src_2x[0] = 10; src_2x[1] = 20; src_2x[src_2x_pitch] = 30; src_2x[src_2x_pitch + 1] = 40;
let mut dest = create_dest_buffer::<u8>(&plane, 4);
plane.refine_ext_pel2(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true,
&mut dest,
);
verify_pel2_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel4_minimal_size() {
let width = 1;
let height = 1;
let hpad = 1;
let vpad = 1;
let mut plane = create_test_mv_plane(width, height, Subpel::Quarter, hpad, vpad, 0);
let src_2x_width = width * 4;
let src_2x_height = height * 4;
let src_2x_pitch = src_2x_width;
let mut src_2x = vec![0u8; src_2x_pitch * src_2x_height];
for i in 0..16 {
let y = i / 4;
let x = i % 4;
src_2x[y * src_2x_pitch + x] = ((i + 1) * 10) as u8;
}
let mut dest = create_dest_buffer::<u8>(&plane, 16);
plane.refine_ext_pel4(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true,
&mut dest,
);
verify_pel4_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel2_with_plane_offset() {
let width = 2;
let height = 2;
let hpad = 1;
let vpad = 1;
let plane_offset = 100;
let mut plane = create_test_mv_plane(width, height, Subpel::Half, hpad, vpad, plane_offset);
let src_2x_width = width * 2;
let src_2x_height = height * 2;
let src_2x_pitch = src_2x_width;
let src_2x = create_2x_upsampled_frame::<u8>(src_2x_width, src_2x_height, src_2x_pitch);
let mut dest = create_dest_buffer::<u8>(&plane, 4);
plane.refine_ext_pel2(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true,
&mut dest,
);
verify_pel2_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel4_with_plane_offset() {
let width = 2;
let height = 1;
let hpad = 1;
let vpad = 1;
let plane_offset = 200;
let mut plane = create_test_mv_plane(width, height, Subpel::Quarter, hpad, vpad, plane_offset);
let src_2x_width = width * 4;
let src_2x_height = height * 4;
let src_2x_pitch = src_2x_width;
let src_2x = create_2x_upsampled_frame::<u8>(src_2x_width, src_2x_height, src_2x_pitch);
let mut dest = create_dest_buffer::<u8>(&plane, 16);
plane.refine_ext_pel4(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true,
&mut dest,
);
verify_pel4_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel2_different_pitch() {
let width = 4;
let height = 2;
let hpad = 2;
let vpad = 1;
let mut plane = create_test_mv_plane(width, height, Subpel::Half, hpad, vpad, 0);
let src_2x_width = width * 2;
let src_2x_height = height * 2;
let src_2x_pitch = src_2x_width + 4; let mut src_2x = vec![0u8; src_2x_pitch * src_2x_height];
for y in 0..src_2x_height {
for x in 0..src_2x_width {
let value = ((y * src_2x_width + x) % 256) as u8;
src_2x[y * src_2x_pitch + x] = value;
}
}
let mut dest = create_dest_buffer::<u8>(&plane, 4);
plane.refine_ext_pel2(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true,
&mut dest,
);
verify_pel2_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}
#[test]
fn refine_ext_pel4_different_pitch() {
let width = 2;
let height = 2;
let hpad = 1;
let vpad = 1;
let mut plane = create_test_mv_plane(width, height, Subpel::Quarter, hpad, vpad, 0);
let src_2x_width = width * 4;
let src_2x_height = height * 4;
let src_2x_pitch = src_2x_width + 2; let mut src_2x = vec![0u16; src_2x_pitch * src_2x_height];
for y in 0..src_2x_height {
for x in 0..src_2x_width {
let value = ((y * src_2x_width + x) % 1000) as u16;
src_2x[y * src_2x_pitch + x] = value;
}
}
let mut dest = create_dest_buffer::<u16>(&plane, 16);
plane.refine_ext_pel4(
&src_2x,
NonZeroUsize::new(src_2x_pitch).unwrap(),
true,
&mut dest,
);
verify_pel4_interpolation(&plane, &src_2x, src_2x_pitch, &dest, true);
assert!(plane.is_padded);
}