#![allow(clippy::needless_range_loop, clippy::too_many_arguments)]
use crate::inter_pred::{InterPredError, InterPredResult};
#[inline(always)]
fn clip3(x: i32, y: i32, z: i32) -> i32 {
if z < x {
x
} else if z > y {
y
} else {
z
}
}
#[inline(always)]
fn clip1(x: i32, max_v: i32) -> i32 {
if x < 0 {
0
} else if x > max_v {
max_v
} else {
x
}
}
#[inline(always)]
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(always)]
fn fill_row(
out: &mut [i32],
src: &[i32],
src_stride: usize,
src_w: usize,
iy: usize,
int_x: i32,
n: usize,
) {
let row_base = iy * src_stride;
let xmax = src_w as i32 - 1;
let x0 = int_x - 2;
let xn = x0 + n as i32 - 1;
if x0 >= 0 && xn <= xmax {
let start = (row_base as i32 + x0) as usize;
out[..n].copy_from_slice(&src[start..start + n]);
} else {
for (k, slot) in out.iter_mut().take(n).enumerate() {
let cx = clip3(0, xmax, x0 + k as i32) as usize;
*slot = src[row_base + cx];
}
}
}
const MAX_H: usize = 16 + 5;
const MAX_W: usize = 16 + 5;
struct LumaWs {
h_strip: [[i32; MAX_W]; MAX_H],
int_row: [i32; MAX_W + 8],
}
impl LumaWs {
#[inline(always)]
fn new() -> Self {
Self {
h_strip: [[0i32; MAX_W]; MAX_H],
int_row: [0i32; MAX_W + 8],
}
}
}
#[inline(always)]
fn integer_row_at(
src: &[i32],
src_stride: usize,
src_w: usize,
src_h: usize,
int_x: i32,
int_y: i32,
iy_rel: i32,
w: usize,
out: &mut [i32; MAX_W],
) {
let iy = clip3(0, src_h as i32 - 1, int_y + iy_rel) as usize;
let xmax = src_w as i32 - 1;
let row_base = iy * src_stride;
if int_x >= 0 && int_x + w as i32 - 1 <= xmax {
out[..w].copy_from_slice(&src[row_base + int_x as usize..row_base + int_x as usize + w]);
} else {
for (k, slot) in out.iter_mut().take(w).enumerate() {
let cx = clip3(0, xmax, int_x + k as i32) as usize;
*slot = src[row_base + cx];
}
}
}
#[inline(always)]
fn build_h_strip(
src: &[i32],
src_stride: usize,
src_w: usize,
src_h: usize,
int_x: i32,
int_y: i32,
w: usize,
h: usize,
ws: &mut LumaWs,
) {
let n_int = w + 5; let nrows = h + 5;
for r in 0..nrows {
let iy_abs = int_y + r as i32 - 2;
let iy = clip3(0, src_h as i32 - 1, iy_abs) as usize;
fill_row(&mut ws.int_row, src, src_stride, src_w, iy, int_x, n_int);
let strip_row = &mut ws.h_strip[r];
for i in 0..w {
strip_row[i] = tap6(
ws.int_row[i],
ws.int_row[i + 1],
ws.int_row[i + 2],
ws.int_row[i + 3],
ws.int_row[i + 4],
ws.int_row[i + 5],
);
}
}
}
#[inline(always)]
fn fill_b(
src: &[i32],
src_stride: usize,
src_w: usize,
src_h: usize,
int_x: i32,
int_y: i32,
w: usize,
h: usize,
max_v: i32,
dst: &mut [i32],
dst_stride: usize,
ws: &mut LumaWs,
) {
let n_int = w + 5;
for yl in 0..h {
let iy = clip3(0, src_h as i32 - 1, int_y + yl as i32) as usize;
fill_row(&mut ws.int_row, src, src_stride, src_w, iy, int_x, n_int);
let drow = &mut dst[yl * dst_stride..yl * dst_stride + w];
for i in 0..w {
let b1 = tap6(
ws.int_row[i],
ws.int_row[i + 1],
ws.int_row[i + 2],
ws.int_row[i + 3],
ws.int_row[i + 4],
ws.int_row[i + 5],
);
drow[i] = clip1((b1 + 16) >> 5, max_v);
}
}
}
#[inline(always)]
fn fill_h_vert(
src: &[i32],
src_stride: usize,
src_w: usize,
src_h: usize,
int_x: i32,
int_y: i32,
w: usize,
h: usize,
max_v: i32,
dst: &mut [i32],
dst_stride: usize,
) {
let mut col_strip: [[i32; MAX_W]; MAX_H] = [[0; MAX_W]; MAX_H];
let nrows = h + 5;
for r in 0..nrows {
let iy = clip3(0, src_h as i32 - 1, int_y + r as i32 - 2) as usize;
let row_base = iy * src_stride;
let xmax = src_w as i32 - 1;
if int_x >= 0 && int_x + w as i32 - 1 <= xmax {
col_strip[r][..w]
.copy_from_slice(&src[row_base + int_x as usize..row_base + int_x as usize + w]);
} else {
for i in 0..w {
let cx = clip3(0, xmax, int_x + i as i32) as usize;
col_strip[r][i] = src[row_base + cx];
}
}
}
for yl in 0..h {
let drow = &mut dst[yl * dst_stride..yl * dst_stride + w];
for i in 0..w {
let h1 = tap6(
col_strip[yl][i],
col_strip[yl + 1][i],
col_strip[yl + 2][i],
col_strip[yl + 3][i],
col_strip[yl + 4][i],
col_strip[yl + 5][i],
);
drow[i] = clip1((h1 + 16) >> 5, max_v);
}
}
}
#[allow(clippy::too_many_arguments)]
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,
});
}
let wu = w as usize;
let hu = h as usize;
if wu > MAX_W || hu > MAX_H {
return super::scalar::interpolate_luma(
src, src_stride, src_width, src_height, int_x, int_y, x_frac, y_frac, w, h, bit_depth,
dst, dst_stride,
);
}
let max_v = (1i32 << bit_depth) - 1;
if x_frac == 0 && y_frac == 0 {
for yl in 0..hu {
let iy = clip3(0, src_height as i32 - 1, int_y + yl as i32) as usize;
let row_base = iy * src_stride;
let xmax = src_width as i32 - 1;
let drow = &mut dst[yl * dst_stride..yl * dst_stride + wu];
if int_x >= 0 && int_x + wu as i32 - 1 <= xmax {
drow.copy_from_slice(
&src[row_base + int_x as usize..row_base + int_x as usize + wu],
);
} else {
for (i, slot) in drow.iter_mut().enumerate() {
let cx = clip3(0, xmax, int_x + i as i32) as usize;
*slot = src[row_base + cx];
}
}
}
return Ok(());
}
if x_frac == 2 && y_frac == 0 {
let mut ws = LumaWs::new();
fill_b(
src, src_stride, src_width, src_height, int_x, int_y, wu, hu, max_v, dst, dst_stride,
&mut ws,
);
return Ok(());
}
if x_frac == 0 && y_frac == 2 {
fill_h_vert(
src, src_stride, src_width, src_height, int_x, int_y, wu, hu, max_v, dst, dst_stride,
);
return Ok(());
}
let mut ws = LumaWs::new();
build_h_strip(
src, src_stride, src_width, src_height, int_x, int_y, wu, hu, &mut ws,
);
match (x_frac, y_frac) {
(2, 2) => {
for yl in 0..hu {
let r0 = &ws.h_strip[yl];
let r1 = &ws.h_strip[yl + 1];
let r2 = &ws.h_strip[yl + 2];
let r3 = &ws.h_strip[yl + 3];
let r4 = &ws.h_strip[yl + 4];
let r5 = &ws.h_strip[yl + 5];
let drow = &mut dst[yl * dst_stride..yl * dst_stride + wu];
for i in 0..wu {
let j1 = tap6(r0[i], r1[i], r2[i], r3[i], r4[i], r5[i]);
drow[i] = clip1((j1 + 512) >> 10, max_v);
}
}
}
(1, 0) => {
for yl in 0..hu {
let b_row = &ws.h_strip[yl + 2]; let drow = &mut dst[yl * dst_stride..yl * dst_stride + wu];
let iy = clip3(0, src_height as i32 - 1, int_y + yl as i32) as usize;
let row_base = iy * src_stride;
let xmax = src_width as i32 - 1;
for i in 0..wu {
let cx = if int_x >= 0 && int_x + i as i32 <= xmax {
(int_x + i as i32) as usize
} else {
clip3(0, xmax, int_x + i as i32) as usize
};
let g = src[row_base + cx];
let b_s = clip1((b_row[i] + 16) >> 5, max_v);
drow[i] = (g + b_s + 1) >> 1;
}
}
}
(3, 0) => {
for yl in 0..hu {
let b_row = &ws.h_strip[yl + 2];
let drow = &mut dst[yl * dst_stride..yl * dst_stride + wu];
let iy = clip3(0, src_height as i32 - 1, int_y + yl as i32) as usize;
let row_base = iy * src_stride;
let xmax = src_width as i32 - 1;
for i in 0..wu {
let xi = int_x + i as i32 + 1;
let cx = clip3(0, xmax, xi) as usize;
let h_int = src[row_base + cx];
let b_s = clip1((b_row[i] + 16) >> 5, max_v);
drow[i] = (h_int + b_s + 1) >> 1;
}
}
}
(0, 1) | (0, 3) => {
let mut col_strip: [[i32; MAX_W]; MAX_H] = [[0; MAX_W]; MAX_H];
let nrows = hu + 5;
for r in 0..nrows {
let iy = clip3(0, src_height as i32 - 1, int_y + r as i32 - 2) as usize;
integer_row_at(
src,
src_stride,
src_width,
src_height,
int_x,
iy as i32,
0,
wu,
&mut col_strip[r],
);
}
let g_offset = if y_frac == 1 { 0 } else { 1 };
for yl in 0..hu {
let drow = &mut dst[yl * dst_stride..yl * dst_stride + wu];
for i in 0..wu {
let h1 = tap6(
col_strip[yl][i],
col_strip[yl + 1][i],
col_strip[yl + 2][i],
col_strip[yl + 3][i],
col_strip[yl + 4][i],
col_strip[yl + 5][i],
);
let h_s = clip1((h1 + 16) >> 5, max_v);
let g = col_strip[yl + 2 + g_offset][i];
drow[i] = (g + h_s + 1) >> 1;
}
}
}
_ => {
let need_h = matches!(
(x_frac, y_frac),
(1, 1) | (1, 2) | (1, 3) | (3, 1) | (3, 2) | (3, 3)
);
let need_j = matches!((x_frac, y_frac), (1, 2) | (3, 2) | (2, 1) | (2, 3));
let need_b = matches!(
(x_frac, y_frac),
(1, 1) | (3, 1) | (2, 1) | (1, 3) | (3, 3) | (2, 3)
);
let mut col_strip: [[i32; MAX_W]; MAX_H] = [[0; MAX_W]; MAX_H];
let mut col_strip_p1: [[i32; MAX_W]; MAX_H] = [[0; MAX_W]; MAX_H];
let nrows = hu + 5;
if need_h {
for r in 0..nrows {
let iy = clip3(0, src_height as i32 - 1, int_y + r as i32 - 2) as usize;
integer_row_at(
src,
src_stride,
src_width,
src_height,
int_x,
iy as i32,
0,
wu,
&mut col_strip[r],
);
}
}
let need_m = matches!((x_frac, y_frac), (3, 1) | (3, 2) | (3, 3));
if need_m {
for r in 0..nrows {
let iy = clip3(0, src_height as i32 - 1, int_y + r as i32 - 2) as usize;
integer_row_at(
src,
src_stride,
src_width,
src_height,
int_x + 1,
iy as i32,
0,
wu,
&mut col_strip_p1[r],
);
}
}
for yl in 0..hu {
let drow = &mut dst[yl * dst_stride..yl * dst_stride + wu];
for i in 0..wu {
let b_s = if need_b {
clip1((ws.h_strip[yl + 2][i] + 16) >> 5, max_v)
} else {
0
};
let j_s = if need_j {
let j1 = tap6(
ws.h_strip[yl][i],
ws.h_strip[yl + 1][i],
ws.h_strip[yl + 2][i],
ws.h_strip[yl + 3][i],
ws.h_strip[yl + 4][i],
ws.h_strip[yl + 5][i],
);
clip1((j1 + 512) >> 10, max_v)
} else {
0
};
let h_s = if need_h {
let h1 = tap6(
col_strip[yl][i],
col_strip[yl + 1][i],
col_strip[yl + 2][i],
col_strip[yl + 3][i],
col_strip[yl + 4][i],
col_strip[yl + 5][i],
);
clip1((h1 + 16) >> 5, max_v)
} else {
0
};
let m_s = if need_m {
let m1 = tap6(
col_strip_p1[yl][i],
col_strip_p1[yl + 1][i],
col_strip_p1[yl + 2][i],
col_strip_p1[yl + 3][i],
col_strip_p1[yl + 4][i],
col_strip_p1[yl + 5][i],
);
clip1((m1 + 16) >> 5, max_v)
} else {
0
};
let need_s = matches!((x_frac, y_frac), (1, 3) | (3, 3) | (2, 3));
let s_s = if need_s {
clip1((ws.h_strip[yl + 3][i] + 16) >> 5, max_v)
} else {
0
};
drow[i] = match (x_frac, y_frac) {
(1, 1) => (b_s + h_s + 1) >> 1, (1, 2) => (h_s + j_s + 1) >> 1, (1, 3) => (h_s + s_s + 1) >> 1, (2, 1) => (b_s + j_s + 1) >> 1, (2, 3) => (j_s + s_s + 1) >> 1, (3, 1) => (b_s + m_s + 1) >> 1, (3, 2) => (j_s + m_s + 1) >> 1, (3, 3) => (m_s + s_s + 1) >> 1, _ => unreachable!(),
};
}
}
}
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
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 wu = w as usize;
let hu = h as usize;
let xf = x_frac as i32;
let yf = y_frac as i32;
let w8mxf = 8 - xf;
let w8myf = 8 - yf;
let xmax = src_width as i32 - 1;
let ymax = src_height as i32 - 1;
let inside =
int_x >= 0 && int_y >= 0 && (int_x + wu as i32) <= xmax && (int_y + hu as i32) <= ymax;
if inside && xf == 0 && yf == 0 {
for yl in 0..hu {
let iy = (int_y + yl as i32) as usize;
let row_base = iy * src_stride + int_x as usize;
dst[yl * dst_stride..yl * dst_stride + wu]
.copy_from_slice(&src[row_base..row_base + wu]);
}
return Ok(());
}
if inside {
let c00 = w8mxf * w8myf;
let c10 = xf * w8myf;
let c01 = w8mxf * yf;
let c11 = xf * yf;
for yl in 0..hu {
let iy = (int_y + yl as i32) as usize;
let row_a = iy * src_stride + int_x as usize;
let row_c = (iy + 1) * src_stride + int_x as usize;
let drow = &mut dst[yl * dst_stride..yl * dst_stride + wu];
for i in 0..wu {
let sa = src[row_a + i];
let sb = src[row_a + i + 1];
let sc = src[row_c + i];
let sd = src[row_c + i + 1];
drow[i] = (c00 * sa + c10 * sb + c01 * sc + c11 * sd + 32) >> 6;
}
}
return Ok(());
}
for yl in 0..hu {
for xl in 0..wu {
let ix = int_x + xl as i32;
let iy = int_y + yl as i32;
let cxa = clip3(0, xmax, ix) as usize;
let cxb = clip3(0, xmax, ix + 1) as usize;
let cya = clip3(0, ymax, iy) as usize;
let cyc = clip3(0, ymax, iy + 1) as usize;
let sa = src[cya * src_stride + cxa];
let sb = src[cya * src_stride + cxb];
let sc = src[cyc * src_stride + cxa];
let sd = src[cyc * src_stride + cxb];
let v =
(w8mxf * w8myf * sa + xf * w8myf * sb + w8mxf * yf * sc + xf * yf * sd + 32) >> 6;
dst[yl * dst_stride + xl] = v;
}
}
Ok(())
}