#![allow(dead_code)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub struct Mv {
pub x: i32,
pub y: i32,
}
impl Mv {
pub const ZERO: Mv = Mv { x: 0, y: 0 };
pub const fn new(x: i32, y: i32) -> Self {
Mv { x, y }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum RefList {
L0,
L1,
}
#[derive(Debug, Copy, Clone, Default)]
pub struct NeighbourMv {
pub available: bool,
pub mb_available: bool,
pub partition_available: bool,
pub ref_idx: i32,
pub mv: Mv,
}
impl NeighbourMv {
pub const UNAVAILABLE: NeighbourMv = NeighbourMv {
available: false,
mb_available: false,
partition_available: false,
ref_idx: -1,
mv: Mv::ZERO,
};
pub const fn new(ref_idx: i32, mv: Mv) -> Self {
NeighbourMv {
available: ref_idx >= 0,
mb_available: ref_idx >= 0,
partition_available: ref_idx >= 0,
ref_idx,
mv,
}
}
pub const fn intra_but_mb_available() -> Self {
NeighbourMv {
available: false,
mb_available: true,
partition_available: true,
ref_idx: -1,
mv: Mv::ZERO,
}
}
pub const fn partition_not_yet_decoded_same_mb() -> Self {
NeighbourMv {
available: false,
mb_available: true,
partition_available: false,
ref_idx: -1,
mv: Mv::ZERO,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
pub enum MvpredShape {
#[default]
Default,
Partition16x8Top,
Partition16x8Bottom,
Partition8x16Left,
Partition8x16Right,
}
#[derive(Debug, Copy, Clone, Default)]
pub struct MvpredInputs {
pub neighbour_a: NeighbourMv,
pub neighbour_b: NeighbourMv,
pub neighbour_c: NeighbourMv,
pub current_ref_idx: i32,
pub shape: MvpredShape,
}
impl MvpredInputs {
pub fn from_abc(
neighbour_a: NeighbourMv,
neighbour_b: NeighbourMv,
neighbour_c: NeighbourMv,
current_ref_idx: i32,
shape: MvpredShape,
) -> Self {
Self {
neighbour_a,
neighbour_b,
neighbour_c,
current_ref_idx,
shape,
}
}
}
pub fn derive_mvpred(inputs: &MvpredInputs) -> Mv {
derive_mvpred_with_d(inputs, NeighbourMv::UNAVAILABLE)
}
pub fn derive_mvpred_with_d(inputs: &MvpredInputs, neighbour_d: NeighbourMv) -> Mv {
let MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c_raw,
current_ref_idx,
shape,
} = *inputs;
let c = if !c_raw.partition_available && neighbour_d.partition_available {
neighbour_d
} else {
c_raw
};
match shape {
MvpredShape::Partition16x8Top if b.ref_idx == current_ref_idx && b.available => {
return b.mv;
}
MvpredShape::Partition16x8Bottom if a.ref_idx == current_ref_idx && a.available => {
return a.mv;
}
MvpredShape::Partition8x16Left if a.ref_idx == current_ref_idx && a.available => {
return a.mv;
}
MvpredShape::Partition8x16Right if c.ref_idx == current_ref_idx && c.available => {
return c.mv;
}
_ => {}
}
derive_median_mvpred(a, b, c, current_ref_idx)
}
pub fn derive_median_mvpred(
a: NeighbourMv,
b: NeighbourMv,
c: NeighbourMv,
current_ref_idx: i32,
) -> Mv {
let (a_eff, b_eff, c_eff) =
if !b.partition_available && !c.partition_available && a.partition_available {
(a, a, a)
} else {
(a, b, c)
};
let a_match = a_eff.available && a_eff.ref_idx == current_ref_idx;
let b_match = b_eff.available && b_eff.ref_idx == current_ref_idx;
let c_match = c_eff.available && c_eff.ref_idx == current_ref_idx;
let match_count = (a_match as u32) + (b_match as u32) + (c_match as u32);
if match_count == 1 {
if a_match {
return a_eff.mv;
}
if b_match {
return b_eff.mv;
}
return c_eff.mv;
}
let x = median3(a_eff.mv.x, b_eff.mv.x, c_eff.mv.x);
let y = median3(a_eff.mv.y, b_eff.mv.y, c_eff.mv.y);
Mv::new(x, y)
}
#[inline]
fn median3(a: i32, b: i32, c: i32) -> i32 {
let lo = a.min(b.min(c));
let hi = a.max(b.max(c));
a + b + c - lo - hi
}
pub fn derive_p_skip_mv(a: NeighbourMv, b: NeighbourMv, c: NeighbourMv) -> (i32, Mv) {
derive_p_skip_mv_with_d(a, b, c, NeighbourMv::UNAVAILABLE)
}
pub fn derive_p_skip_mv_with_d(
a: NeighbourMv,
b: NeighbourMv,
c: NeighbourMv,
d: NeighbourMv,
) -> (i32, Mv) {
let ref_idx_l0 = 0;
let force_zero = !a.mb_available
|| !b.mb_available
|| (a.ref_idx == 0 && a.mv == Mv::ZERO)
|| (b.ref_idx == 0 && b.mv == Mv::ZERO);
if force_zero {
return (ref_idx_l0, Mv::ZERO);
}
let c_eff = if !c.partition_available && d.partition_available {
d
} else {
c
};
let mv = derive_median_mvpred(a, b, c_eff, ref_idx_l0);
(ref_idx_l0, mv)
}
pub fn derive_b_spatial_direct(
a_l0: NeighbourMv,
a_l1: NeighbourMv,
b_l0: NeighbourMv,
b_l1: NeighbourMv,
c_l0: NeighbourMv,
c_l1: NeighbourMv,
) -> (i32, Mv, i32, Mv) {
derive_b_spatial_direct_with_d(
a_l0,
a_l1,
b_l0,
b_l1,
c_l0,
c_l1,
NeighbourMv::UNAVAILABLE,
NeighbourMv::UNAVAILABLE,
)
}
#[allow(clippy::too_many_arguments)]
pub fn derive_b_spatial_direct_with_d(
a_l0: NeighbourMv,
a_l1: NeighbourMv,
b_l0: NeighbourMv,
b_l1: NeighbourMv,
c_l0: NeighbourMv,
c_l1: NeighbourMv,
d_l0: NeighbourMv,
d_l1: NeighbourMv,
) -> (i32, Mv, i32, Mv) {
let c_l0_eff = if !c_l0.partition_available && d_l0.partition_available {
d_l0
} else {
c_l0
};
let c_l1_eff = if !c_l1.partition_available && d_l1.partition_available {
d_l1
} else {
c_l1
};
let mut ref_idx_l0 = min_positive(a_l0.ref_idx, min_positive(b_l0.ref_idx, c_l0_eff.ref_idx));
let mut ref_idx_l1 = min_positive(a_l1.ref_idx, min_positive(b_l1.ref_idx, c_l1_eff.ref_idx));
let direct_zero = ref_idx_l0 < 0 && ref_idx_l1 < 0;
if direct_zero {
ref_idx_l0 = 0;
ref_idx_l1 = 0;
}
let mv_l0 = if direct_zero || ref_idx_l0 < 0 {
Mv::ZERO
} else {
derive_median_mvpred(a_l0, b_l0, c_l0_eff, ref_idx_l0)
};
let mv_l1 = if direct_zero || ref_idx_l1 < 0 {
Mv::ZERO
} else {
derive_median_mvpred(a_l1, b_l1, c_l1_eff, ref_idx_l1)
};
(ref_idx_l0, mv_l0, ref_idx_l1, mv_l1)
}
#[inline]
fn min_positive(x: i32, y: i32) -> i32 {
if x >= 0 && y >= 0 {
x.min(y)
} else {
x.max(y)
}
}
pub fn derive_b_temporal_direct(
colocated_mv: Mv,
col_pic_poc: i32,
curr_pic_poc: i32,
list0_ref_poc: i32,
) -> (Mv, Mv) {
let tb = clip3(-128, 127, curr_pic_poc - list0_ref_poc);
let td_raw = col_pic_poc - list0_ref_poc;
let td = clip3(-128, 127, td_raw);
if td == 0 {
return (colocated_mv, Mv::ZERO);
}
let tx = (16384 + (td / 2).abs()) / td;
let dist_scale_factor = clip3(-1024, 1023, (tb * tx + 32) >> 6);
let mv_l0_x = (dist_scale_factor * colocated_mv.x + 128) >> 8;
let mv_l0_y = (dist_scale_factor * colocated_mv.y + 128) >> 8;
let mv_l0 = Mv::new(mv_l0_x, mv_l0_y);
let mv_l1 = Mv::new(mv_l0.x - colocated_mv.x, mv_l0.y - colocated_mv.y);
(mv_l0, mv_l1)
}
#[inline]
fn clip3(a: i32, b: i32, x: i32) -> i32 {
if x < a {
a
} else if x > b {
b
} else {
x
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum NeighbourSource {
InternalBlock(u8),
ExternalLeft(u8),
ExternalAbove(u8),
ExternalAboveRight(u8),
ExternalAboveLeft(u8),
}
#[inline]
fn inverse_4x4_luma_scan(idx: u8) -> (i32, i32) {
let hi = (idx / 4) as i32; let lo = (idx % 4) as i32;
let x_hi = (hi % 2) * 8;
let y_hi = (hi / 2) * 8;
let x_lo = (lo % 2) * 4;
let y_lo = (lo / 2) * 4;
(x_hi + x_lo, y_hi + y_lo)
}
#[inline]
fn luma_4x4_blk_idx(x: i32, y: i32) -> u8 {
(8 * (y / 8) + 4 * (x / 8) + 2 * ((y % 8) / 4) + ((x % 8) / 4)) as u8
}
pub fn neighbour_4x4_map(current_block_idx: u8) -> [NeighbourSource; 4] {
const PRED_PART_WIDTH: i32 = 4; const MAX_W: i32 = 16;
const MAX_H: i32 = 16;
let (x, y) = inverse_4x4_luma_scan(current_block_idx);
let offsets: [(i32, i32); 4] = [
(-1, 0), (0, -1), (PRED_PART_WIDTH, -1), (-1, -1), ];
let mut out = [NeighbourSource::InternalBlock(0); 4];
for (i, (xd, yd)) in offsets.iter().copied().enumerate() {
let xn = x + xd;
let yn = y + yd;
let source = if xn < 0 && yn < 0 {
let xw = (xn + MAX_W).rem_euclid(MAX_W);
let yw = (yn + MAX_H).rem_euclid(MAX_H);
NeighbourSource::ExternalAboveLeft(luma_4x4_blk_idx(xw, yw))
} else if xn < 0 && (0..MAX_H).contains(&yn) {
let xw = (xn + MAX_W).rem_euclid(MAX_W);
let yw = yn;
NeighbourSource::ExternalLeft(luma_4x4_blk_idx(xw, yw))
} else if (0..MAX_W).contains(&xn) && yn < 0 {
let xw = xn;
let yw = (yn + MAX_H).rem_euclid(MAX_H);
NeighbourSource::ExternalAbove(luma_4x4_blk_idx(xw, yw))
} else if (0..MAX_W).contains(&xn) && (0..MAX_H).contains(&yn) {
NeighbourSource::InternalBlock(luma_4x4_blk_idx(xn, yn))
} else if xn > MAX_W - 1 && yn < 0 {
let xw = (xn + MAX_W).rem_euclid(MAX_W);
let yw = (yn + MAX_H).rem_euclid(MAX_H);
NeighbourSource::ExternalAboveRight(luma_4x4_blk_idx(xw, yw))
} else {
NeighbourSource::ExternalAboveRight(0)
};
out[i] = source;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn median3_basic() {
assert_eq!(median3(1, 2, 3), 2);
assert_eq!(median3(3, 1, 2), 2);
assert_eq!(median3(-1, 0, 1), 0);
assert_eq!(median3(5, 5, 5), 5);
assert_eq!(median3(7, 7, 3), 7);
assert_eq!(median3(-5, -10, -3), -5);
}
#[test]
fn mvpred_all_available_same_refidx_median() {
let a = NeighbourMv::new(0, Mv::new(10, 20));
let b = NeighbourMv::new(0, Mv::new(30, 10));
let c = NeighbourMv::new(0, Mv::new(20, 40));
let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c,
current_ref_idx: 0,
shape: MvpredShape::Default,
};
assert_eq!(derive_mvpred(&inputs), Mv::new(20, 20));
}
#[test]
fn mvpred_only_a_matching() {
let a = NeighbourMv::new(0, Mv::new(7, -3));
let b = NeighbourMv::new(1, Mv::new(100, 100));
let c = NeighbourMv::new(1, Mv::new(-50, 50));
let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c,
current_ref_idx: 0,
shape: MvpredShape::Default,
};
assert_eq!(derive_mvpred(&inputs), Mv::new(7, -3));
}
#[test]
fn mvpred_no_neighbours_available() {
let inputs = MvpredInputs {
neighbour_a: NeighbourMv::UNAVAILABLE,
neighbour_b: NeighbourMv::UNAVAILABLE,
neighbour_c: NeighbourMv::UNAVAILABLE,
current_ref_idx: 0,
shape: MvpredShape::Default,
};
assert_eq!(derive_mvpred(&inputs), Mv::ZERO);
}
#[test]
fn mvpred_16x8_top_uses_b_when_match() {
let a = NeighbourMv::new(1, Mv::new(100, 200));
let b = NeighbourMv::new(0, Mv::new(5, 5));
let c = NeighbourMv::new(1, Mv::new(-1, -1));
let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c,
current_ref_idx: 0,
shape: MvpredShape::Partition16x8Top,
};
assert_eq!(derive_mvpred(&inputs), Mv::new(5, 5));
}
#[test]
fn mvpred_16x8_top_falls_through_to_median_when_b_mismatches() {
let a = NeighbourMv::new(0, Mv::new(10, 10));
let b = NeighbourMv::new(1, Mv::new(999, 999));
let c = NeighbourMv::new(0, Mv::new(20, 20));
let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c,
current_ref_idx: 0,
shape: MvpredShape::Partition16x8Top,
};
assert_eq!(derive_mvpred(&inputs), Mv::new(20, 20));
}
#[test]
fn mvpred_16x8_bottom_uses_a_when_match() {
let a = NeighbourMv::new(2, Mv::new(9, 11));
let b = NeighbourMv::new(1, Mv::new(7, 7));
let c = NeighbourMv::new(2, Mv::new(-1, -1));
let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c,
current_ref_idx: 2,
shape: MvpredShape::Partition16x8Bottom,
};
assert_eq!(derive_mvpred(&inputs), Mv::new(9, 11));
}
#[test]
fn mvpred_8x16_left_uses_a_and_right_uses_c() {
let a = NeighbourMv::new(0, Mv::new(1, 2));
let b = NeighbourMv::new(0, Mv::new(3, 4));
let c = NeighbourMv::new(0, Mv::new(5, 6));
let inputs_left = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c,
current_ref_idx: 0,
shape: MvpredShape::Partition8x16Left,
};
assert_eq!(derive_mvpred(&inputs_left), Mv::new(1, 2));
let inputs_right = MvpredInputs {
shape: MvpredShape::Partition8x16Right,
..inputs_left
};
assert_eq!(derive_mvpred(&inputs_right), Mv::new(5, 6));
}
#[test]
fn mvpred_with_d_no_substitution_when_c_available() {
let a = NeighbourMv::new(0, Mv::new(10, 20));
let b = NeighbourMv::new(0, Mv::new(30, 10));
let c = NeighbourMv::new(0, Mv::new(20, 40));
let d = NeighbourMv::new(0, Mv::new(999, 999)); let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c,
current_ref_idx: 0,
shape: MvpredShape::Default,
};
assert_eq!(derive_mvpred_with_d(&inputs, d), Mv::new(20, 20));
assert_eq!(derive_mvpred(&inputs), Mv::new(20, 20));
}
#[test]
fn mvpred_with_d_substitutes_when_c_unavailable() {
let a = NeighbourMv::new(0, Mv::new(10, 20));
let b = NeighbourMv::new(0, Mv::new(30, 10));
let c = NeighbourMv::UNAVAILABLE;
let d = NeighbourMv::new(0, Mv::new(20, 40));
let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c,
current_ref_idx: 0,
shape: MvpredShape::Default,
};
assert_eq!(derive_mvpred_with_d(&inputs, d), Mv::new(20, 20));
}
#[test]
fn mvpred_with_d_falls_through_when_both_c_and_d_unavailable() {
let a = NeighbourMv::new(0, Mv::new(7, -3));
let b = NeighbourMv::UNAVAILABLE;
let c = NeighbourMv::UNAVAILABLE;
let d = NeighbourMv::UNAVAILABLE;
let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c,
current_ref_idx: 0,
shape: MvpredShape::Default,
};
assert_eq!(derive_mvpred_with_d(&inputs, d), Mv::new(7, -3));
let inputs_all_unavail = MvpredInputs {
neighbour_a: NeighbourMv::UNAVAILABLE,
neighbour_b: NeighbourMv::UNAVAILABLE,
neighbour_c: NeighbourMv::UNAVAILABLE,
current_ref_idx: 0,
shape: MvpredShape::Default,
};
assert_eq!(
derive_mvpred_with_d(&inputs_all_unavail, NeighbourMv::UNAVAILABLE),
Mv::ZERO
);
}
#[test]
fn mvpred_with_d_no_substitution_when_c_is_intra_but_mb_available() {
let a = NeighbourMv::new(0, Mv::new(11, 7)); let b = NeighbourMv::new(0, Mv::new(7, 8)); let c_intra = NeighbourMv::intra_but_mb_available();
let d = NeighbourMv::new(0, Mv::new(7, 8)); let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c_intra,
current_ref_idx: 0,
shape: MvpredShape::Partition8x16Right,
};
assert_eq!(derive_mvpred_with_d(&inputs, d), Mv::new(7, 7));
}
#[test]
fn mvpred_with_d_substitutes_when_c_partition_not_yet_decoded() {
let a = NeighbourMv::new(0, Mv::new(14, 6));
let b = NeighbourMv::new(0, Mv::new(13, 3));
let c_not_yet_decoded = NeighbourMv::partition_not_yet_decoded_same_mb();
let d = NeighbourMv::new(0, Mv::new(14, 4));
let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c_not_yet_decoded,
current_ref_idx: 0,
shape: MvpredShape::Default,
};
assert_eq!(derive_mvpred_with_d(&inputs, d), Mv::new(14, 4));
let (_r, mv_no_d) = (0, derive_mvpred(&inputs));
let _ = _r;
assert_eq!(mv_no_d, Mv::new(13, 3));
}
#[test]
fn mvpred_with_d_substitution_not_applied_for_16x8_top() {
let a = NeighbourMv::new(1, Mv::new(100, 200));
let b = NeighbourMv::new(0, Mv::new(5, 5));
let c = NeighbourMv::UNAVAILABLE;
let d = NeighbourMv::new(0, Mv::new(-999, -999)); let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: b,
neighbour_c: c,
current_ref_idx: 0,
shape: MvpredShape::Partition16x8Top,
};
assert_eq!(derive_mvpred_with_d(&inputs, d), Mv::new(5, 5));
}
#[test]
fn p_skip_with_d_substitutes_when_c_unavailable() {
let a = NeighbourMv::new(0, Mv::new(10, 20));
let b = NeighbourMv::new(0, Mv::new(30, 40));
let c = NeighbourMv::UNAVAILABLE;
let d = NeighbourMv::new(0, Mv::new(50, 60));
let (ref_idx, mv) = derive_p_skip_mv_with_d(a, b, c, d);
assert_eq!(ref_idx, 0);
assert_eq!(mv, Mv::new(30, 40));
let (_, mv_no_sub) = derive_p_skip_mv(a, b, c);
assert_eq!(mv_no_sub, Mv::new(10, 20));
}
#[test]
fn mvpred_only_a_available_b_and_c_copied() {
let a = NeighbourMv::new(0, Mv::new(42, -42));
let inputs = MvpredInputs {
neighbour_a: a,
neighbour_b: NeighbourMv::UNAVAILABLE,
neighbour_c: NeighbourMv::UNAVAILABLE,
current_ref_idx: 0,
shape: MvpredShape::Default,
};
assert_eq!(derive_mvpred(&inputs), Mv::new(42, -42));
}
#[test]
fn p_skip_all_available_median() {
let a = NeighbourMv::new(0, Mv::new(10, 20));
let b = NeighbourMv::new(0, Mv::new(30, 40));
let c = NeighbourMv::new(0, Mv::new(50, 60));
let (ref_idx, mv) = derive_p_skip_mv(a, b, c);
assert_eq!(ref_idx, 0);
assert_eq!(mv, Mv::new(30, 40));
}
#[test]
fn p_skip_a_unavailable_forces_zero() {
let a = NeighbourMv::UNAVAILABLE;
let b = NeighbourMv::new(0, Mv::new(10, 10));
let c = NeighbourMv::new(0, Mv::new(20, 20));
let (ref_idx, mv) = derive_p_skip_mv(a, b, c);
assert_eq!(ref_idx, 0);
assert_eq!(mv, Mv::ZERO);
}
#[test]
fn p_skip_b_unavailable_forces_zero() {
let a = NeighbourMv::new(0, Mv::new(10, 10));
let b = NeighbourMv::UNAVAILABLE;
let c = NeighbourMv::new(0, Mv::new(20, 20));
let (ref_idx, mv) = derive_p_skip_mv(a, b, c);
assert_eq!(ref_idx, 0);
assert_eq!(mv, Mv::ZERO);
}
#[test]
fn p_skip_a_zero_mv_refidx0_forces_zero() {
let a = NeighbourMv::new(0, Mv::ZERO);
let b = NeighbourMv::new(0, Mv::new(20, 20));
let c = NeighbourMv::new(0, Mv::new(30, 30));
let (ref_idx, mv) = derive_p_skip_mv(a, b, c);
assert_eq!(ref_idx, 0);
assert_eq!(mv, Mv::ZERO);
}
#[test]
fn p_skip_a_refidx_nonzero_does_not_force_zero() {
let a = NeighbourMv::new(1, Mv::new(5, 5));
let b = NeighbourMv::new(0, Mv::new(10, 10));
let c = NeighbourMv::new(0, Mv::new(20, 20));
let (ref_idx, mv) = derive_p_skip_mv(a, b, c);
assert_eq!(ref_idx, 0);
assert_eq!(mv, Mv::new(10, 10));
}
#[test]
fn p_skip_a_intra_does_not_force_zero() {
let a_intra = NeighbourMv::intra_but_mb_available();
let b = NeighbourMv::new(0, Mv::new(10, 10));
let c = NeighbourMv::new(0, Mv::new(20, 20));
let (ref_idx, mv) = derive_p_skip_mv(a_intra, b, c);
assert_eq!(ref_idx, 0);
assert_eq!(mv, Mv::new(10, 10));
}
#[test]
fn p_skip_b_intra_does_not_force_zero() {
let a = NeighbourMv::new(0, Mv::new(10, 10));
let b_intra = NeighbourMv::intra_but_mb_available();
let c = NeighbourMv::new(0, Mv::new(20, 20));
let (ref_idx, mv) = derive_p_skip_mv(a, b_intra, c);
assert_eq!(ref_idx, 0);
assert_eq!(mv, Mv::new(10, 10));
}
#[test]
fn p_skip_a_unavailable_still_forces_zero() {
let a = NeighbourMv::UNAVAILABLE; let b = NeighbourMv::new(0, Mv::new(10, 10));
let c = NeighbourMv::new(0, Mv::new(20, 20));
let (ref_idx, mv) = derive_p_skip_mv(a, b, c);
assert_eq!(ref_idx, 0);
assert_eq!(mv, Mv::ZERO);
}
#[test]
fn b_spatial_direct_min_positive_basic() {
let a_l0 = NeighbourMv::new(2, Mv::new(1, 2));
let b_l0 = NeighbourMv::new(5, Mv::new(3, 4));
let c_l0 = NeighbourMv::new(3, Mv::new(5, 6));
let a_l1 = NeighbourMv::UNAVAILABLE;
let b_l1 = NeighbourMv::UNAVAILABLE;
let c_l1 = NeighbourMv::UNAVAILABLE;
let (r0, mv0, r1, mv1) = derive_b_spatial_direct(a_l0, a_l1, b_l0, b_l1, c_l0, c_l1);
assert_eq!(r0, 2);
assert_eq!(r1, -1);
assert_eq!(mv1, Mv::ZERO);
assert_eq!(mv0, Mv::new(1, 2));
}
#[test]
fn b_spatial_direct_both_negative_triggers_zero_fallback() {
let (r0, mv0, r1, mv1) = derive_b_spatial_direct(
NeighbourMv::UNAVAILABLE,
NeighbourMv::UNAVAILABLE,
NeighbourMv::UNAVAILABLE,
NeighbourMv::UNAVAILABLE,
NeighbourMv::UNAVAILABLE,
NeighbourMv::UNAVAILABLE,
);
assert_eq!(r0, 0);
assert_eq!(r1, 0);
assert_eq!(mv0, Mv::ZERO);
assert_eq!(mv1, Mv::ZERO);
}
#[test]
fn b_spatial_direct_both_lists_present() {
let a_l0 = NeighbourMv::new(0, Mv::new(4, 8));
let b_l0 = NeighbourMv::new(0, Mv::new(12, 16));
let c_l0 = NeighbourMv::new(0, Mv::new(8, 4));
let a_l1 = NeighbourMv::new(1, Mv::new(-4, -8));
let b_l1 = NeighbourMv::new(1, Mv::new(-12, -16));
let c_l1 = NeighbourMv::new(1, Mv::new(-8, -4));
let (r0, mv0, r1, mv1) = derive_b_spatial_direct(a_l0, a_l1, b_l0, b_l1, c_l0, c_l1);
assert_eq!(r0, 0);
assert_eq!(r1, 1);
assert_eq!(mv0, Mv::new(8, 8));
assert_eq!(mv1, Mv::new(-8, -8));
}
#[test]
fn b_temporal_direct_forward_scaling() {
let (mv_l0, mv_l1) = derive_b_temporal_direct(Mv::new(16, 32), 20, 10, 0);
assert_eq!(mv_l0, Mv::new(8, 16));
assert_eq!(mv_l1, Mv::new(-8, -16));
}
#[test]
fn b_temporal_direct_td_zero_uses_colocated_as_is() {
let col = Mv::new(3, 7);
let (mv_l0, mv_l1) = derive_b_temporal_direct(col, 5, 10, 5);
assert_eq!(mv_l0, col);
assert_eq!(mv_l1, Mv::ZERO);
}
#[test]
fn b_temporal_direct_symmetric_half() {
let (mv_l0, mv_l1) = derive_b_temporal_direct(Mv::new(8, 8), 10, 5, 0);
assert_eq!(mv_l0, Mv::new(4, 4));
assert_eq!(mv_l1, Mv::new(-4, -4));
}
#[test]
fn inverse_4x4_luma_scan_figure_6_10() {
assert_eq!(inverse_4x4_luma_scan(0), (0, 0));
assert_eq!(inverse_4x4_luma_scan(1), (4, 0));
assert_eq!(inverse_4x4_luma_scan(2), (0, 4));
assert_eq!(inverse_4x4_luma_scan(3), (4, 4));
assert_eq!(inverse_4x4_luma_scan(4), (8, 0));
assert_eq!(inverse_4x4_luma_scan(5), (12, 0));
assert_eq!(inverse_4x4_luma_scan(6), (8, 4));
assert_eq!(inverse_4x4_luma_scan(7), (12, 4));
assert_eq!(inverse_4x4_luma_scan(8), (0, 8));
assert_eq!(inverse_4x4_luma_scan(9), (4, 8));
assert_eq!(inverse_4x4_luma_scan(10), (0, 12));
assert_eq!(inverse_4x4_luma_scan(11), (4, 12));
assert_eq!(inverse_4x4_luma_scan(12), (8, 8));
assert_eq!(inverse_4x4_luma_scan(13), (12, 8));
assert_eq!(inverse_4x4_luma_scan(14), (8, 12));
assert_eq!(inverse_4x4_luma_scan(15), (12, 12));
}
#[test]
fn luma_4x4_blk_idx_inverse_of_scan() {
for idx in 0u8..=15 {
let (x, y) = inverse_4x4_luma_scan(idx);
assert_eq!(luma_4x4_blk_idx(x, y), idx);
}
}
#[test]
fn neighbour_map_block_0_top_left_of_mb() {
let [a, b, c, d] = neighbour_4x4_map(0);
assert_eq!(a, NeighbourSource::ExternalLeft(5));
assert_eq!(b, NeighbourSource::ExternalAbove(10));
assert_eq!(c, NeighbourSource::ExternalAbove(11));
assert_eq!(d, NeighbourSource::ExternalAboveLeft(15));
}
#[test]
fn neighbour_map_block_5_top_right_of_mb() {
let [a, b, c, d] = neighbour_4x4_map(5);
assert_eq!(a, NeighbourSource::InternalBlock(4));
assert_eq!(b, NeighbourSource::ExternalAbove(15));
assert_eq!(c, NeighbourSource::ExternalAboveRight(10));
assert_eq!(d, NeighbourSource::ExternalAbove(14));
}
#[test]
fn neighbour_map_block_6_interior() {
let [a, b, c, d] = neighbour_4x4_map(6);
assert_eq!(a, NeighbourSource::InternalBlock(3));
assert_eq!(b, NeighbourSource::InternalBlock(4));
assert_eq!(c, NeighbourSource::InternalBlock(5));
assert_eq!(d, NeighbourSource::InternalBlock(1));
}
#[test]
fn neighbour_map_block_15_bottom_right_of_mb() {
let [a, b, _c, d] = neighbour_4x4_map(15);
assert_eq!(a, NeighbourSource::InternalBlock(14));
assert_eq!(b, NeighbourSource::InternalBlock(13));
assert_eq!(d, NeighbourSource::InternalBlock(12));
}
}