#![allow(dead_code)]
#![allow(clippy::too_many_arguments)]
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
pub enum InterPredError {
#[error("fractional luma component {0} out of range 0..=3")]
LumaFracOutOfRange(u8),
#[error("fractional chroma component {0} out of range 0..=7")]
ChromaFracOutOfRange(u8),
#[error("block dimensions must be multiples of 4, got {w}x{h}")]
InvalidBlockSize { w: u32, h: u32 },
#[error("zero-dim reference plane: src_width={src_width}, src_height={src_height}")]
ZeroDimRef { src_width: usize, src_height: usize },
}
pub type InterPredResult<T> = Result<T, InterPredError>;
#[inline]
fn clip3(x: i32, y: i32, z: i32) -> i32 {
z.max(x).min(y)
}
#[inline]
fn clip1(x: i32, bit_depth: u32) -> i32 {
let max_v = (1i32 << bit_depth) - 1;
clip3(0, max_v, x)
}
#[inline]
fn ref_sample(src: &[i32], src_stride: usize, src_w: usize, src_h: usize, x: i32, y: i32) -> i32 {
let cx = clip3(0, src_w as i32 - 1, x) as usize;
let cy = clip3(0, src_h as i32 - 1, y) as usize;
src[cy * src_stride + cx]
}
#[inline]
fn tap6(s0: i32, s1: i32, s2: i32, s3: i32, s4: i32, s5: i32) -> i32 {
s0 - 5 * s1 + 20 * s2 + 20 * s3 - 5 * s4 + s5
}
#[inline]
fn horiz_b1(src: &[i32], src_stride: usize, src_w: usize, src_h: usize, x: i32, y: i32) -> i32 {
let s0 = ref_sample(src, src_stride, src_w, src_h, x - 2, y);
let s1 = ref_sample(src, src_stride, src_w, src_h, x - 1, y);
let s2 = ref_sample(src, src_stride, src_w, src_h, x, y);
let s3 = ref_sample(src, src_stride, src_w, src_h, x + 1, y);
let s4 = ref_sample(src, src_stride, src_w, src_h, x + 2, y);
let s5 = ref_sample(src, src_stride, src_w, src_h, x + 3, y);
tap6(s0, s1, s2, s3, s4, s5)
}
#[inline]
fn vert_h1(src: &[i32], src_stride: usize, src_w: usize, src_h: usize, x: i32, y: i32) -> i32 {
let s0 = ref_sample(src, src_stride, src_w, src_h, x, y - 2);
let s1 = ref_sample(src, src_stride, src_w, src_h, x, y - 1);
let s2 = ref_sample(src, src_stride, src_w, src_h, x, y);
let s3 = ref_sample(src, src_stride, src_w, src_h, x, y + 1);
let s4 = ref_sample(src, src_stride, src_w, src_h, x, y + 2);
let s5 = ref_sample(src, src_stride, src_w, src_h, x, y + 3);
tap6(s0, s1, s2, s3, s4, s5)
}
#[inline]
fn j1_at(src: &[i32], src_stride: usize, src_w: usize, src_h: usize, x: i32, y: i32) -> i32 {
let b_m2 = horiz_b1(src, src_stride, src_w, src_h, x, y - 2);
let b_m1 = horiz_b1(src, src_stride, src_w, src_h, x, y - 1);
let b_0 = horiz_b1(src, src_stride, src_w, src_h, x, y);
let b_p1 = horiz_b1(src, src_stride, src_w, src_h, x, y + 1);
let b_p2 = horiz_b1(src, src_stride, src_w, src_h, x, y + 2);
let b_p3 = horiz_b1(src, src_stride, src_w, src_h, x, y + 3);
tap6(b_m2, b_m1, b_0, b_p1, b_p2, b_p3)
}
fn luma_pred_one(
src: &[i32],
src_stride: usize,
src_w: usize,
src_h: usize,
ix: i32,
iy: i32,
x_frac: u8,
y_frac: u8,
bit_depth: u32,
) -> i32 {
#[inline]
fn g(s: &[i32], st: usize, w: usize, h: usize, x: i32, y: i32) -> i32 {
ref_sample(s, st, w, h, x, y)
}
#[inline]
fn b_at(s: &[i32], st: usize, w: usize, h: usize, x: i32, y: i32, bd: u32) -> i32 {
let b1 = horiz_b1(s, st, w, h, x, y);
clip1((b1 + 16) >> 5, bd)
}
#[inline]
fn h_at(s: &[i32], st: usize, w: usize, h: usize, x: i32, y: i32, bd: u32) -> i32 {
let h1 = vert_h1(s, st, w, h, x, y);
clip1((h1 + 16) >> 5, bd)
}
#[inline]
fn j_at(s: &[i32], st: usize, w: usize, h: usize, x: i32, y: i32, bd: u32) -> i32 {
let j1 = j1_at(s, st, w, h, x, y);
clip1((j1 + 512) >> 10, bd)
}
match (x_frac, y_frac) {
(0, 0) => g(src, src_stride, src_w, src_h, ix, iy),
(0, 1) => {
let g = g(src, src_stride, src_w, src_h, ix, iy);
let h_s = h_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
(g + h_s + 1) >> 1
}
(0, 2) => h_at(src, src_stride, src_w, src_h, ix, iy, bit_depth),
(0, 3) => {
let m = g(src, src_stride, src_w, src_h, ix, iy + 1);
let h_s = h_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
(m + h_s + 1) >> 1
}
(1, 0) => {
let g = g(src, src_stride, src_w, src_h, ix, iy);
let b_s = b_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
(g + b_s + 1) >> 1
}
(1, 1) => {
let b_s = b_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
let h_s = h_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
(b_s + h_s + 1) >> 1
}
(1, 2) => {
let h_s = h_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
let j_s = j_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
(h_s + j_s + 1) >> 1
}
(1, 3) => {
let h_s = h_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
let s_b1 = horiz_b1(src, src_stride, src_w, src_h, ix, iy + 1);
let s_s = clip1((s_b1 + 16) >> 5, bit_depth);
(h_s + s_s + 1) >> 1
}
(2, 0) => b_at(src, src_stride, src_w, src_h, ix, iy, bit_depth),
(2, 1) => {
let b_s = b_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
let j_s = j_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
(b_s + j_s + 1) >> 1
}
(2, 2) => j_at(src, src_stride, src_w, src_h, ix, iy, bit_depth),
(2, 3) => {
let j_s = j_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
let s_b1 = horiz_b1(src, src_stride, src_w, src_h, ix, iy + 1);
let s_s = clip1((s_b1 + 16) >> 5, bit_depth);
(j_s + s_s + 1) >> 1
}
(3, 0) => {
let h_int = g(src, src_stride, src_w, src_h, ix + 1, iy);
let b_s = b_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
(h_int + b_s + 1) >> 1
}
(3, 1) => {
let b_s = b_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
let m_h1 = vert_h1(src, src_stride, src_w, src_h, ix + 1, iy);
let m_s = clip1((m_h1 + 16) >> 5, bit_depth);
(b_s + m_s + 1) >> 1
}
(3, 2) => {
let j_s = j_at(src, src_stride, src_w, src_h, ix, iy, bit_depth);
let m_h1 = vert_h1(src, src_stride, src_w, src_h, ix + 1, iy);
let m_s = clip1((m_h1 + 16) >> 5, bit_depth);
(j_s + m_s + 1) >> 1
}
(3, 3) => {
let m_h1 = vert_h1(src, src_stride, src_w, src_h, ix + 1, iy);
let m_s = clip1((m_h1 + 16) >> 5, bit_depth);
let s_b1 = horiz_b1(src, src_stride, src_w, src_h, ix, iy + 1);
let s_s = clip1((s_b1 + 16) >> 5, bit_depth);
(m_s + s_s + 1) >> 1
}
_ => unreachable!(),
}
}
pub fn interpolate_luma(
src: &[i32],
src_stride: usize,
src_width: usize,
src_height: usize,
int_x: i32,
int_y: i32,
x_frac: u8,
y_frac: u8,
w: u32,
h: u32,
bit_depth: u32,
dst: &mut [i32],
dst_stride: usize,
) -> InterPredResult<()> {
if x_frac > 3 {
return Err(InterPredError::LumaFracOutOfRange(x_frac));
}
if y_frac > 3 {
return Err(InterPredError::LumaFracOutOfRange(y_frac));
}
if w == 0 || h == 0 || w % 4 != 0 || h % 4 != 0 {
return Err(InterPredError::InvalidBlockSize { w, h });
}
if src_width == 0 || src_height == 0 {
return Err(InterPredError::ZeroDimRef {
src_width,
src_height,
});
}
for yl in 0..h as i32 {
for xl in 0..w as i32 {
let v = luma_pred_one(
src,
src_stride,
src_width,
src_height,
int_x + xl,
int_y + yl,
x_frac,
y_frac,
bit_depth,
);
dst[(yl as usize) * dst_stride + xl as usize] = v;
}
}
Ok(())
}
pub fn interpolate_chroma(
src: &[i32],
src_stride: usize,
src_width: usize,
src_height: usize,
int_x: i32,
int_y: i32,
x_frac: u8,
y_frac: u8,
w: u32,
h: u32,
_bit_depth: u32,
dst: &mut [i32],
dst_stride: usize,
) -> InterPredResult<()> {
if x_frac > 7 {
return Err(InterPredError::ChromaFracOutOfRange(x_frac));
}
if y_frac > 7 {
return Err(InterPredError::ChromaFracOutOfRange(y_frac));
}
if w == 0 || h == 0 {
return Err(InterPredError::InvalidBlockSize { w, h });
}
if src_width == 0 || src_height == 0 {
return Err(InterPredError::ZeroDimRef {
src_width,
src_height,
});
}
let xf = x_frac as i32;
let yf = y_frac as i32;
let w8mxf = 8 - xf;
let w8myf = 8 - yf;
for yl in 0..h as i32 {
for xl in 0..w as i32 {
let ix = int_x + xl;
let iy = int_y + yl;
let sa = ref_sample(src, src_stride, src_width, src_height, ix, iy);
let sb = ref_sample(src, src_stride, src_width, src_height, ix + 1, iy);
let sc = ref_sample(src, src_stride, src_width, src_height, ix, iy + 1);
let sd = ref_sample(src, src_stride, src_width, src_height, ix + 1, iy + 1);
let v =
(w8mxf * w8myf * sa + xf * w8myf * sb + w8mxf * yf * sc + xf * yf * sd + 32) >> 6;
dst[(yl as usize) * dst_stride + xl as usize] = v;
}
}
Ok(())
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub struct WeightedEntry {
pub weight: i32,
pub offset: i32,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BiPredMode {
L0Only,
L1Only,
Bipred,
}
pub fn weighted_pred_default(
pred_l0: &[i32],
pred_l1: &[i32],
stride: usize,
w: u32,
h: u32,
_bit_depth: u32,
dst: &mut [i32],
dst_stride: usize,
) {
for y in 0..h as usize {
for x in 0..w as usize {
let a = pred_l0[y * stride + x];
let b = pred_l1[y * stride + x];
dst[y * dst_stride + x] = (a + b + 1) >> 1;
}
}
}
pub fn weighted_pred_explicit(
pred_l0: Option<&[i32]>,
pred_l1: Option<&[i32]>,
stride: usize,
w: u32,
h: u32,
mode: BiPredMode,
weight_l0: WeightedEntry,
weight_l1: WeightedEntry,
log2_wd: u32,
bit_depth: u32,
dst: &mut [i32],
dst_stride: usize,
) {
match mode {
BiPredMode::L0Only => {
let l0 = pred_l0.expect("L0 buffer required in L0Only mode");
for y in 0..h as usize {
for x in 0..w as usize {
let p = l0[y * stride + x];
let v = if log2_wd >= 1 {
((p * weight_l0.weight + (1i32 << (log2_wd - 1))) >> log2_wd)
+ weight_l0.offset
} else {
p * weight_l0.weight + weight_l0.offset
};
dst[y * dst_stride + x] = clip1(v, bit_depth);
}
}
}
BiPredMode::L1Only => {
let l1 = pred_l1.expect("L1 buffer required in L1Only mode");
for y in 0..h as usize {
for x in 0..w as usize {
let p = l1[y * stride + x];
let v = if log2_wd >= 1 {
((p * weight_l1.weight + (1i32 << (log2_wd - 1))) >> log2_wd)
+ weight_l1.offset
} else {
p * weight_l1.weight + weight_l1.offset
};
dst[y * dst_stride + x] = clip1(v, bit_depth);
}
}
}
BiPredMode::Bipred => {
let l0 = pred_l0.expect("L0 buffer required in Bipred mode");
let l1 = pred_l1.expect("L1 buffer required in Bipred mode");
for y in 0..h as usize {
for x in 0..w as usize {
let p0 = l0[y * stride + x];
let p1 = l1[y * stride + x];
let sum = p0 * weight_l0.weight + p1 * weight_l1.weight + (1i32 << log2_wd);
let v =
(sum >> (log2_wd + 1)) + ((weight_l0.offset + weight_l1.offset + 1) >> 1);
dst[y * dst_stride + x] = clip1(v, bit_depth);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn make_plane<F: FnMut(i32, i32) -> i32>(w: usize, h: usize, mut f: F) -> Vec<i32> {
let mut v = vec![0i32; w * h];
for y in 0..h {
for x in 0..w {
v[y * w + x] = f(x as i32, y as i32);
}
}
v
}
#[test]
fn luma_integer_4x4_copy() {
let src = make_plane(16, 16, |x, y| (x + y) & 0xff);
let mut dst = vec![0i32; 4 * 4];
interpolate_luma(&src, 16, 16, 16, 4, 4, 0, 0, 4, 4, 8, &mut dst, 4).unwrap();
for y in 0..4 {
for x in 0..4 {
assert_eq!(dst[y * 4 + x], ((x + 4 + y + 4) & 0xff) as i32);
}
}
}
#[test]
fn luma_integer_8x8_copy() {
let src = make_plane(32, 32, |x, y| (x * 3 + y * 5) & 0xff);
let mut dst = vec![0i32; 8 * 8];
interpolate_luma(&src, 32, 32, 32, 8, 10, 0, 0, 8, 8, 8, &mut dst, 8).unwrap();
for y in 0..8 {
for x in 0..8 {
let ex = ((8 + x as i32) * 3 + (10 + y as i32) * 5) & 0xff;
assert_eq!(dst[y * 8 + x], ex);
}
}
}
#[test]
fn luma_integer_16x16_copy() {
let src = make_plane(64, 64, |x, y| (x ^ y) & 0xff);
let mut dst = vec![0i32; 16 * 16];
interpolate_luma(&src, 64, 64, 64, 12, 20, 0, 0, 16, 16, 8, &mut dst, 16).unwrap();
for y in 0..16 {
for x in 0..16 {
let ex = ((12 + x as i32) ^ (20 + y as i32)) & 0xff;
assert_eq!(dst[y * 16 + x], ex);
}
}
}
#[test]
fn luma_half_pel_horizontal_constant_plane() {
let src = make_plane(32, 32, |_, _| 128);
let mut dst = vec![0i32; 4 * 4];
interpolate_luma(&src, 32, 32, 32, 10, 10, 2, 0, 4, 4, 8, &mut dst, 4).unwrap();
for v in &dst {
assert_eq!(*v, 128);
}
}
#[test]
fn luma_half_pel_horizontal_known_sample() {
let mut src = vec![0i32; 32 * 32];
let row_vals: [i32; 6] = [10, 20, 30, 40, 50, 60];
for (i, v) in row_vals.iter().enumerate() {
src[10 * 32 + 8 + i] = *v;
}
let mut dst = vec![0i32; 4];
interpolate_luma(&src, 32, 32, 32, 10, 10, 2, 0, 4, 4, 8, &mut [0i32; 16], 4).unwrap();
let mut dst_one = vec![0i32; 16];
interpolate_luma(&src, 32, 32, 32, 10, 10, 2, 0, 4, 4, 8, &mut dst_one, 4).unwrap();
let _ = &mut dst; assert_eq!(dst_one[0], 35);
}
#[test]
fn luma_half_pel_vertical_constant() {
let src = make_plane(32, 32, |_, _| 200);
let mut dst = vec![0i32; 4 * 4];
interpolate_luma(&src, 32, 32, 32, 10, 10, 0, 2, 4, 4, 8, &mut dst, 4).unwrap();
for v in &dst {
assert_eq!(*v, 200);
}
}
#[test]
fn luma_half_pel_vertical_known_sample() {
let mut src = vec![0i32; 32 * 32];
let col_vals: [i32; 6] = [10, 20, 30, 40, 50, 60];
for (i, v) in col_vals.iter().enumerate() {
src[(8 + i) * 32 + 10] = *v;
}
let mut dst = vec![0i32; 16];
interpolate_luma(&src, 32, 32, 32, 10, 10, 0, 2, 4, 4, 8, &mut dst, 4).unwrap();
assert_eq!(dst[0], 35);
}
#[test]
fn luma_half_pel_diagonal_j_constant() {
let src = make_plane(32, 32, |_, _| 100);
let mut dst = vec![0i32; 4 * 4];
interpolate_luma(&src, 32, 32, 32, 10, 10, 2, 2, 4, 4, 8, &mut dst, 4).unwrap();
for v in &dst {
assert_eq!(*v, 100);
}
}
#[test]
fn luma_half_pel_diagonal_j_known_sample() {
let mut src = vec![0i32; 32 * 32];
let row_vals: [i32; 6] = [10, 20, 30, 40, 50, 60];
for row in 8..=13 {
for (i, v) in row_vals.iter().enumerate() {
src[row * 32 + 8 + i] = *v;
}
}
let mut dst = vec![0i32; 16];
interpolate_luma(&src, 32, 32, 32, 10, 10, 2, 2, 4, 4, 8, &mut dst, 4).unwrap();
assert_eq!(dst[0], 35);
}
#[test]
fn luma_quarter_pel_position_a() {
let mut src = vec![0i32; 32 * 32];
let row_vals: [i32; 6] = [10, 20, 30, 40, 50, 60];
for (i, v) in row_vals.iter().enumerate() {
src[10 * 32 + 8 + i] = *v;
}
let mut dst = vec![0i32; 16];
interpolate_luma(&src, 32, 32, 32, 10, 10, 1, 0, 4, 4, 8, &mut dst, 4).unwrap();
assert_eq!(dst[0], 33);
}
#[test]
fn luma_edge_replication() {
let src = make_plane(8, 8, |_, _| 50);
let mut dst = vec![0i32; 4 * 4];
interpolate_luma(&src, 8, 8, 8, -5, -5, 2, 2, 4, 4, 8, &mut dst, 4).unwrap();
for v in &dst {
assert_eq!(*v, 50);
}
}
#[test]
fn luma_edge_replication_integer_past_right() {
let mut src = vec![100i32; 8 * 8];
for y in 0..8 {
src[y * 8 + 7] = 200;
}
let mut dst = vec![0i32; 16];
interpolate_luma(&src, 8, 8, 8, 8, 2, 0, 0, 4, 4, 8, &mut dst, 4).unwrap();
for v in &dst {
assert_eq!(*v, 200);
}
}
#[test]
fn chroma_integer_copy() {
let src = make_plane(16, 16, |x, y| (x * 7 + y) & 0xff);
let mut dst = vec![0i32; 4 * 4];
interpolate_chroma(&src, 16, 16, 16, 2, 3, 0, 0, 4, 4, 8, &mut dst, 4).unwrap();
for y in 0..4 {
for x in 0..4 {
let ex = ((2 + x as i32) * 7 + (3 + y as i32)) & 0xff;
assert_eq!(dst[y * 4 + x], ex);
}
}
}
#[test]
fn chroma_bilinear_center_known_corners() {
let mut src = vec![0i32; 8 * 8];
src[2 * 8 + 2] = 10;
src[2 * 8 + 3] = 20;
src[3 * 8 + 2] = 30;
src[3 * 8 + 3] = 40;
let mut dst = vec![0i32; 16];
interpolate_chroma(&src, 8, 8, 8, 2, 2, 4, 4, 4, 4, 8, &mut dst, 4).unwrap();
assert_eq!(dst[0], 25);
}
#[test]
fn chroma_bilinear_zero_frac_passthrough() {
let src = make_plane(16, 16, |x, y| (x * 11 + y * 13) & 0xff);
let mut dst = vec![0i32; 4 * 4];
interpolate_chroma(&src, 16, 16, 16, 1, 2, 0, 0, 4, 4, 8, &mut dst, 4).unwrap();
for y in 0..4 {
for x in 0..4 {
let ex = ((1 + x as i32) * 11 + (2 + y as i32) * 13) & 0xff;
assert_eq!(dst[y * 4 + x], ex);
}
}
}
#[test]
fn chroma_bilinear_full_right_frac() {
let src = make_plane(16, 16, |x, _| x);
let mut dst = vec![0i32; 4 * 4];
assert!(interpolate_chroma(&src, 16, 16, 16, 2, 2, 7, 0, 4, 4, 8, &mut dst, 4).is_ok());
assert!(matches!(
interpolate_chroma(&src, 16, 16, 16, 2, 2, 8, 0, 4, 4, 8, &mut dst, 4),
Err(InterPredError::ChromaFracOutOfRange(8))
));
}
#[test]
fn weighted_default_bipred_roundup() {
let l0 = vec![10i32; 16];
let l1 = vec![20i32; 16];
let mut dst = vec![0i32; 16];
weighted_pred_default(&l0, &l1, 4, 4, 4, 8, &mut dst, 4);
for v in &dst {
assert_eq!(*v, 15);
}
}
#[test]
fn weighted_default_bipred_zero_plus_one() {
let l0 = vec![0i32; 16];
let l1 = vec![1i32; 16];
let mut dst = vec![0i32; 16];
weighted_pred_default(&l0, &l1, 4, 4, 4, 8, &mut dst, 4);
for v in &dst {
assert_eq!(*v, 1);
}
}
#[test]
fn weighted_explicit_l0_only_logwd_nonzero() {
let l0 = vec![50i32; 16];
let mut dst = vec![0i32; 16];
weighted_pred_explicit(
Some(&l0),
None,
4,
4,
4,
BiPredMode::L0Only,
WeightedEntry {
weight: 64,
offset: 10,
},
WeightedEntry::default(),
6,
8,
&mut dst,
4,
);
for v in &dst {
assert_eq!(*v, 60);
}
}
#[test]
fn weighted_explicit_l0_only_logwd_zero() {
let l0 = vec![10i32; 16];
let mut dst = vec![0i32; 16];
weighted_pred_explicit(
Some(&l0),
None,
4,
4,
4,
BiPredMode::L0Only,
WeightedEntry {
weight: 2,
offset: 5,
},
WeightedEntry::default(),
0,
8,
&mut dst,
4,
);
for v in &dst {
assert_eq!(*v, 25);
}
}
#[test]
fn weighted_explicit_l1_only() {
let l1 = vec![30i32; 16];
let mut dst = vec![0i32; 16];
weighted_pred_explicit(
None,
Some(&l1),
4,
4,
4,
BiPredMode::L1Only,
WeightedEntry::default(),
WeightedEntry {
weight: 32,
offset: -5,
},
5,
8,
&mut dst,
4,
);
for v in &dst {
assert_eq!(*v, 25);
}
}
#[test]
fn weighted_explicit_bipred_known() {
let l0 = vec![100i32; 16];
let l1 = vec![200i32; 16];
let mut dst = vec![0i32; 16];
weighted_pred_explicit(
Some(&l0),
Some(&l1),
4,
4,
4,
BiPredMode::Bipred,
WeightedEntry {
weight: 32,
offset: 0,
},
WeightedEntry {
weight: 32,
offset: 0,
},
5,
8,
&mut dst,
4,
);
for v in &dst {
assert_eq!(*v, 150);
}
}
#[test]
fn weighted_explicit_bipred_with_offset() {
let l0 = vec![40i32; 16];
let l1 = vec![60i32; 16];
let mut dst = vec![0i32; 16];
weighted_pred_explicit(
Some(&l0),
Some(&l1),
4,
4,
4,
BiPredMode::Bipred,
WeightedEntry {
weight: 32,
offset: 5,
},
WeightedEntry {
weight: 32,
offset: 7,
},
5,
8,
&mut dst,
4,
);
for v in &dst {
assert_eq!(*v, 56);
}
}
#[test]
fn weighted_explicit_clips_to_max() {
let l0 = vec![255i32; 16];
let mut dst = vec![0i32; 16];
weighted_pred_explicit(
Some(&l0),
None,
4,
4,
4,
BiPredMode::L0Only,
WeightedEntry {
weight: 64,
offset: 100,
},
WeightedEntry::default(),
6,
8,
&mut dst,
4,
);
for v in &dst {
assert_eq!(*v, 255);
}
}
#[test]
fn weighted_explicit_clips_to_zero() {
let l0 = vec![10i32; 16];
let mut dst = vec![0i32; 16];
weighted_pred_explicit(
Some(&l0),
None,
4,
4,
4,
BiPredMode::L0Only,
WeightedEntry {
weight: 64,
offset: -100,
},
WeightedEntry::default(),
6,
8,
&mut dst,
4,
);
for v in &dst {
assert_eq!(*v, 0);
}
}
#[test]
fn clip1_bounds() {
assert_eq!(clip1(-5, 8), 0);
assert_eq!(clip1(0, 8), 0);
assert_eq!(clip1(255, 8), 255);
assert_eq!(clip1(1000, 8), 255);
assert_eq!(clip1(1023, 10), 1023);
assert_eq!(clip1(4095, 12), 4095);
}
#[test]
fn luma_rejects_bad_frac() {
let src = vec![0i32; 16 * 16];
let mut dst = vec![0i32; 16];
assert!(matches!(
interpolate_luma(&src, 16, 16, 16, 0, 0, 4, 0, 4, 4, 8, &mut dst, 4),
Err(InterPredError::LumaFracOutOfRange(4))
));
assert!(matches!(
interpolate_luma(&src, 16, 16, 16, 0, 0, 0, 5, 4, 4, 8, &mut dst, 4),
Err(InterPredError::LumaFracOutOfRange(5))
));
}
#[test]
fn luma_rejects_bad_block_size() {
let src = vec![0i32; 16 * 16];
let mut dst = vec![0i32; 16];
assert!(matches!(
interpolate_luma(&src, 16, 16, 16, 0, 0, 0, 0, 5, 4, 8, &mut dst, 5),
Err(InterPredError::InvalidBlockSize { w: 5, h: 4 })
));
}
#[test]
fn luma_half_pel_horizontal_10bit_clip() {
let mut src = vec![0i32; 32 * 32];
let row_vals: [i32; 6] = [200, 300, 400, 500, 600, 700];
for (i, v) in row_vals.iter().enumerate() {
src[10 * 32 + 8 + i] = *v;
}
let mut dst = vec![0i32; 16];
interpolate_luma(&src, 32, 32, 32, 10, 10, 2, 0, 4, 4, 10, &mut dst, 4).unwrap();
assert_eq!(dst[0], 450);
}
#[test]
fn chroma_2x2_block_4x4_luma_sub_partition() {
let src = make_plane(16, 16, |x, y| (x * 2 + y) & 0xff);
let mut dst = vec![0i32; 2 * 2];
assert!(
interpolate_chroma(&src, 16, 16, 16, 4, 4, 0, 0, 2, 2, 8, &mut dst, 2).is_ok(),
"2x2 chroma MC should be accepted (luma 4x4 sub-partitions \
imply 2x2 chroma for 4:2:0)"
);
assert_eq!(dst[0], ((4 * 2 + 4) & 0xff));
assert_eq!(dst[1], ((5 * 2 + 4) & 0xff));
assert_eq!(dst[2], ((4 * 2 + 5) & 0xff));
assert_eq!(dst[3], ((5 * 2 + 5) & 0xff));
}
#[test]
fn chroma_2x4_block_4x8_luma_sub_partition() {
let src = make_plane(16, 16, |x, y| x + y);
let mut dst = vec![0i32; 2 * 4];
assert!(interpolate_chroma(&src, 16, 16, 16, 2, 2, 0, 0, 2, 4, 8, &mut dst, 2).is_ok());
}
#[test]
fn chroma_full_offset_corner_d() {
let mut src = vec![0i32; 8 * 8];
src[3 * 8 + 3] = 64; let mut dst = vec![0i32; 16];
interpolate_chroma(&src, 8, 8, 8, 2, 2, 7, 7, 4, 4, 8, &mut dst, 4).unwrap();
assert_eq!(dst[0], 49);
}
#[test]
fn luma_zero_dim_ref_is_rejected() {
let src: [i32; 0] = [];
let mut dst = vec![0i32; 4 * 4];
let err = interpolate_luma(&src, 0, 0, 0, 0, 0, 0, 0, 4, 4, 8, &mut dst, 4).unwrap_err();
assert!(
matches!(
err,
InterPredError::ZeroDimRef {
src_width: 0,
src_height: 0
}
),
"wrong variant: {err:?}"
);
}
#[test]
fn chroma_zero_dim_ref_is_rejected() {
let src: [i32; 0] = [];
let mut dst = vec![0i32; 4 * 4];
let err = interpolate_chroma(&src, 0, 0, 0, 0, 0, 0, 0, 4, 4, 8, &mut dst, 4).unwrap_err();
assert!(
matches!(
err,
InterPredError::ZeroDimRef {
src_width: 0,
src_height: 0
}
),
"wrong variant: {err:?}"
);
}
}