use crate::av2_frame::Plane;
use crate::av2_ipred::DIV_RECIP;
include!("av2_warp_filter.rs"); include!("av2_ext_warp_table.rs");
#[inline]
fn ulog2(d: u32) -> i32 {
31 - d.leading_zeros() as i32
}
#[inline]
fn apply_sign(a: i32, b: i32) -> i32 {
if b < 0 { -a } else { a }
}
#[inline]
fn apply_sign64(a: i32, b: i64) -> i32 {
if b < 0 { -a } else { a }
}
#[inline]
fn iclip_wmp(v: i32) -> i32 {
((v + 0x20 - (v < 0) as i32) & !0x3f).clamp(-0x8000, 0x7fc0)
}
#[inline]
pub fn resolve_divisor_32(d: u32) -> (i32, i32) {
let shift = ulog2(d);
let e = (d - (1u32 << shift)) as i32;
let f = if shift > 7 { (e + (1 << (shift - 8))) >> (shift - 7) } else { e << (7 - shift) };
(DIV_RECIP[f as usize] as i32, shift + 9)
}
pub fn get_shear_params(mat: &[i32; 6]) -> Option<[i16; 4]> {
if mat[2] <= 0 {
return None;
}
let alpha = iclip_wmp(mat[2] - 0x10000);
let beta = iclip_wmp(mat[3]);
let (y0, shift) = resolve_divisor_32(mat[2].unsigned_abs());
let y = apply_sign(y0, mat[2]);
let v1 = mat[4] as i64 * 0x10000 * y as i64;
let rnd = (1i64 << shift) >> 1;
let gamma = iclip_wmp(apply_sign64(((v1.abs() + rnd) >> shift) as i32, v1));
let v2 = mat[3] as i64 * mat[4] as i64 * y as i64;
let delta = iclip_wmp(mat[5] - apply_sign64(((v2.abs() + rnd) >> shift) as i32, v2) - 0x10000);
let affine =
4 * alpha.abs() + 7 * beta.abs() < 0x30000 && 4 * gamma.abs() + 4 * delta.abs() < 0x30000;
affine.then_some([alpha as i16, beta as i16, gamma as i16, delta as i16])
}
#[allow(clippy::too_many_arguments)]
fn warp8x8(
rf: &Plane, dst: &mut [i32], dstw: usize, ox: usize, oy: usize, dx: i32, dy: i32, abcd: &[i16; 4],
mx: i32, my: i32, bdmax: i32,
) {
let (rw, rh) = (rf.w as i32, rf.h as i32);
let get = |x: i32, y: i32| -> i32 { rf.at(x.clamp(0, rw - 1) as usize, y.clamp(0, rh - 1) as usize) };
const H_SH: i32 = 7 - 4; const V_SH: i32 = 7 + 4; let mut mid = [[0i32; 8]; 15];
let mut mxr = mx;
for yy in 0..15usize {
if !crate::av2_recon::work_tick("av2_warp:77") { break; }
let sy = dy - 3 + yy as i32;
let mut tmx = mxr;
for xx in 0..8usize {
if !crate::av2_recon::work_tick("av2_warp:80") { break; }
let f = &MC_WARP_FILTER[(3 * 64 + ((tmx + 512) >> 10)) as usize];
let s: i32 = (0..8).map(|k| f[k] as i32 * get(dx + xx as i32 + k as i32 - 3, sy)).sum();
mid[yy][xx] = (s + ((1 << H_SH) >> 1)) >> H_SH;
tmx += abcd[0] as i32; }
mxr += abcd[1] as i32; }
let mut myr = my;
for yy in 0..8usize {
if !crate::av2_recon::work_tick("av2_warp:89") { break; }
let mut tmy = myr;
for xx in 0..8usize {
if !crate::av2_recon::work_tick("av2_warp:91") { break; }
let f = &MC_WARP_FILTER[(3 * 64 + ((tmy + 512) >> 10)) as usize];
let s: i32 = (0..8).map(|k| f[k] as i32 * mid[yy + k][xx]).sum();
dst[(oy + yy) * dstw + ox + xx] = ((s + ((1 << V_SH) >> 1)) >> V_SH).clamp(0, bdmax);
tmy += abcd[2] as i32; }
myr += abcd[3] as i32; }
}
#[allow(clippy::too_many_arguments)]
pub fn warp_affine(
rf: &Plane, dst: &mut [i32], dstw: usize, mat: &[i32; 6], abcd: &[i16; 4], bx4: usize, by4: usize,
bw: usize, bh: usize, ss_hor: u32, ss_ver: u32, bdmax: i32,
) {
for y in (0..bh).step_by(8) {
if !crate::av2_recon::work_tick("av2_warp:109") { break; }
let src_y = by4 as i32 * 4 + ((y as i32 + 4) << ss_ver);
let mat3_y = mat[3] as i64 * src_y as i64 + mat[0] as i64;
let mat5_y = mat[5] as i64 * src_y as i64 + mat[1] as i64;
for x in (0..bw).step_by(8) {
if !crate::av2_recon::work_tick("av2_warp:113") { break; }
let src_x = bx4 as i32 * 4 + ((x as i32 + 4) << ss_hor);
let mvx = (mat[2] as i64 * src_x as i64 + mat3_y) >> ss_hor;
let mvy = (mat[4] as i64 * src_x as i64 + mat5_y) >> ss_ver;
let dx = (mvx >> 16) as i32 - 4;
let mx = (((mvx as i32) & 0xffff) - abcd[0] as i32 * 4 - abcd[1] as i32 * 7) & !0x3f;
let dy = (mvy >> 16) as i32 - 4;
let my = (((mvy as i32) & 0xffff) - abcd[2] as i32 * 4 - abcd[3] as i32 * 4) & !0x3f;
warp8x8(rf, dst, dstw, x, y, dx, dy, abcd, mx, my, bdmax);
if std::env::var("MWARP2").is_ok() && ss_hor == 0 && bx4 == 120 / 4 && by4 == 80 / 4 {
crate::dlog!("[MWARP2] sub=({x},{y}) dxy=({dx},{dy}) mxy=({mx},{my}) abcd={abcd:?} out0={} out57={}",
dst[y * dstw + x], dst[(y + 7) * dstw + x + 5]);
}
}
}
}
pub fn ext_warp(
rf: &Plane, dst: &mut [i32], dstw: usize, mat: &[i32; 6], bx4: usize, by4: usize,
bw: usize, bh: usize, ss_hor: u32, ss_ver: u32, bdmax: i32,
) {
let (rw, rh) = (rf.w as i32, rf.h as i32);
let sw = (bw as i32).min(8);
let sh = (bh as i32).min(8);
let (hsw, hsh) = (sw >> 1, sh >> 1);
let mut gy = 0i32;
while gy < bh as i32 {
if !crate::av2_recon::work_tick("av2_warp:149") { break; }
let src_y = by4 as i32 * 4 + ((gy + hsh) << ss_ver);
let g_mat3_y = mat[3] as i64 * src_y as i64 + mat[0] as i64;
let g_mat5_y = mat[5] as i64 * src_y as i64 + mat[1] as i64;
let mut gx = 0i32;
while gx < bw as i32 {
if !crate::av2_recon::work_tick("av2_warp:154") { break; }
let src_x = bx4 as i32 * 4 + ((gx + hsw) << ss_hor);
let g_mvx = (mat[2] as i64 * src_x as i64 + g_mat3_y) >> ss_hor;
let g_mvy = (mat[4] as i64 * src_x as i64 + g_mat5_y) >> ss_ver;
let left_window = (g_mvx >> 16) as i32 - hsw - 3;
let top_window = (g_mvy >> 16) as i32 - hsh - 3;
let left = left_window.clamp(0, rw - 1);
let right = (left_window + sw + 7).clamp(1, rw);
let top = top_window.clamp(0, rh - 1);
let bottom = (top_window + sh + 7).clamp(1, rh);
let get = |x: i32, y: i32| -> i32 {
rf.at(x.clamp(left, right - 1) as usize, y.clamp(top, bottom - 1) as usize)
};
let mut yy = gy;
while yy < gy + sh {
if !crate::av2_recon::work_tick("av2_warp:168") { break; }
let src_y = by4 as i32 * 4 + ((yy + 2) << ss_ver);
let mat3_y = mat[3] as i64 * src_y as i64 + mat[0] as i64;
let mat5_y = mat[5] as i64 * src_y as i64 + mat[1] as i64;
let mut xx = gx;
while xx < gx + sw {
if !crate::av2_recon::work_tick("av2_warp:173") { break; }
let src_x = bx4 as i32 * 4 + ((xx + 2) << ss_hor);
let mvx = ((mat[2] as i64 * src_x as i64 + mat3_y) >> ss_hor) + 0x200;
let mvy = ((mat[4] as i64 * src_x as i64 + mat5_y) >> ss_ver) + 0x200;
let dx = (mvx >> 16) as i32 - 2;
let mx = ((mvx >> 10) & 63) as i32;
let dy = (mvy >> 16) as i32 - 2;
let my = ((mvy >> 10) & 63) as i32;
let (ox, oy) = (xx as usize, yy as usize);
if mx != 0 && my != 0 {
let fh = &EXT_WARP_FILTER[(mx - 1) as usize];
let fv = &EXT_WARP_FILTER[(my - 1) as usize];
let mut mid = [[0i32; 4]; 11]; for (j, row) in mid.iter_mut().enumerate() {
let sy = dy - 3 + j as i32;
for (i, m) in row.iter_mut().enumerate() {
let s: i32 = (0..8).map(|k| fh[k] as i32 * get(dx + i as i32 + k as i32 - 3, sy)).sum();
*m = (s + 4) >> 3; }
}
for j in 0..4usize {
if !crate::av2_recon::work_tick("av2_warp:198") { break; }
for i in 0..4usize {
if !crate::av2_recon::work_tick("av2_warp:199") { break; }
let s: i32 = (0..8).map(|k| fv[k] as i32 * mid[j + k][i]).sum();
dst[(oy + j) * dstw + ox + i] = ((s + 1024) >> 11).clamp(0, bdmax); }
}
} else if mx != 0 {
let fh = &EXT_WARP_FILTER[(mx - 1) as usize];
for j in 0..4usize {
if !crate::av2_recon::work_tick("av2_warp:206") { break; }
let sy = dy + j as i32;
for i in 0..4usize {
if !crate::av2_recon::work_tick("av2_warp:208") { break; }
let s: i32 = (0..8).map(|k| fh[k] as i32 * get(dx + i as i32 + k as i32 - 3, sy)).sum();
dst[(oy + j) * dstw + ox + i] = ((s + 68) >> 7).clamp(0, bdmax); }
}
} else if my != 0 {
let fv = &EXT_WARP_FILTER[(my - 1) as usize];
for j in 0..4usize {
if !crate::av2_recon::work_tick("av2_warp:215") { break; }
for i in 0..4usize {
if !crate::av2_recon::work_tick("av2_warp:216") { break; }
let s: i32 = (0..8).map(|k| fv[k] as i32 * get(dx + i as i32, dy + j as i32 + k as i32 - 3)).sum();
dst[(oy + j) * dstw + ox + i] = ((s + 64) >> 7).clamp(0, bdmax); }
}
} else {
for j in 0..4usize {
if !crate::av2_recon::work_tick("av2_warp:222") { break; }
for i in 0..4usize {
if !crate::av2_recon::work_tick("av2_warp:223") { break; }
dst[(oy + j) * dstw + ox + i] = get(dx + i as i32, dy + j as i32);
}
}
}
xx += 4;
}
yy += 4;
}
gx += sw;
}
gy += sh;
}
}