use super::motion_compensation::{
apply_chroma_mv_block, apply_chroma_mv_block_bipred, apply_luma_mv_block,
apply_luma_mv_block_bipred,
};
use super::motion_estimation::MotionVector;
use super::reference_buffer::ReconFrame;
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy)]
pub enum BInterMode {
L0_16x16 { mv: MotionVector },
L1_16x16 { mv: MotionVector },
Bi_16x16 { mv_l0: MotionVector, mv_l1: MotionVector },
}
pub fn build_b_luma_prediction(
mode: BInterMode,
l0_ref: &ReconFrame,
l1_ref: &ReconFrame,
mb_x: usize,
mb_y: usize,
) -> [[u8; 16]; 16] {
let mut out = [[0u8; 16]; 16];
let mb_px_x = (mb_x * 16) as u32;
let mb_px_y = (mb_y * 16) as u32;
let flat = out.as_flattened_mut();
match mode {
BInterMode::L0_16x16 { mv } => {
apply_luma_mv_block(l0_ref, mb_px_x, mb_px_y, 16, 16, mv, flat, 16);
}
BInterMode::L1_16x16 { mv } => {
apply_luma_mv_block(l1_ref, mb_px_x, mb_px_y, 16, 16, mv, flat, 16);
}
BInterMode::Bi_16x16 { mv_l0, mv_l1 } => {
apply_luma_mv_block_bipred(
l0_ref, mv_l0, l1_ref, mv_l1,
mb_px_x, mb_px_y, 16, 16, flat, 16,
);
}
}
out
}
pub fn build_b_chroma_prediction(
mode: BInterMode,
l0_ref: &ReconFrame,
l1_ref: &ReconFrame,
component: u8,
mb_x: usize,
mb_y: usize,
) -> [[u8; 8]; 8] {
let mut out = [[0u8; 8]; 8];
let mb_chroma_px_x = (mb_x * 8) as u32;
let mb_chroma_px_y = (mb_y * 8) as u32;
let flat = out.as_flattened_mut();
match mode {
BInterMode::L0_16x16 { mv } => {
apply_chroma_mv_block(
l0_ref, component, mb_chroma_px_x, mb_chroma_px_y,
8, 8, mv, flat, 8,
);
}
BInterMode::L1_16x16 { mv } => {
apply_chroma_mv_block(
l1_ref, component, mb_chroma_px_x, mb_chroma_px_y,
8, 8, mv, flat, 8,
);
}
BInterMode::Bi_16x16 { mv_l0, mv_l1 } => {
apply_chroma_mv_block_bipred(
l0_ref, mv_l0, l1_ref, mv_l1, component,
mb_chroma_px_x, mb_chroma_px_y, 8, 8, flat, 8,
);
}
}
out
}
#[cfg(test)]
mod tests {
use super::*;
use crate::codec::h264::encoder::reconstruction::ReconBuffer;
fn make_recon(width: u32, height: u32, y_fill: u8, c_fill: u8) -> ReconFrame {
let mut buf = ReconBuffer::new(width, height).unwrap();
for v in buf.y.iter_mut() { *v = y_fill; }
for v in buf.cb.iter_mut() { *v = c_fill; }
for v in buf.cr.iter_mut() { *v = c_fill; }
ReconFrame::snapshot(&buf)
}
#[test]
fn b_l0_prediction_zero_mv_matches_l0_ref() {
let l0 = make_recon(32, 32, 100, 128);
let l1 = make_recon(32, 32, 200, 128);
let mode = BInterMode::L0_16x16 { mv: MotionVector::ZERO };
let pred = build_b_luma_prediction(mode, &l0, &l1, 0, 0);
for row in &pred {
for &px in row {
assert_eq!(px, 100, "L0 mode with zero MV should be all-100");
}
}
}
#[test]
fn b_l1_prediction_zero_mv_matches_l1_ref() {
let l0 = make_recon(32, 32, 100, 128);
let l1 = make_recon(32, 32, 200, 128);
let mode = BInterMode::L1_16x16 { mv: MotionVector::ZERO };
let pred = build_b_luma_prediction(mode, &l0, &l1, 0, 0);
for row in &pred {
for &px in row {
assert_eq!(px, 200, "L1 mode with zero MV should be all-200");
}
}
}
#[test]
fn b_bi_prediction_zero_mv_averages_l0_l1() {
let l0 = make_recon(32, 32, 100, 128);
let l1 = make_recon(32, 32, 200, 128);
let mode = BInterMode::Bi_16x16 {
mv_l0: MotionVector::ZERO,
mv_l1: MotionVector::ZERO,
};
let pred = build_b_luma_prediction(mode, &l0, &l1, 0, 0);
for row in &pred {
for &px in row {
assert_eq!(px, 150, "Bi mode should average L0+L1: (100+200+1)>>1 = 150");
}
}
}
#[test]
fn b_chroma_bi_prediction_averages_components() {
let mut l0 = ReconBuffer::new(32, 32).unwrap();
let mut l1 = ReconBuffer::new(32, 32).unwrap();
for v in l0.cb.iter_mut() { *v = 80; }
for v in l0.cr.iter_mut() { *v = 80; }
for v in l1.cb.iter_mut() { *v = 120; }
for v in l1.cr.iter_mut() { *v = 120; }
let l0 = ReconFrame::snapshot(&l0);
let l1 = ReconFrame::snapshot(&l1);
let mode = BInterMode::Bi_16x16 {
mv_l0: MotionVector::ZERO,
mv_l1: MotionVector::ZERO,
};
let pred_cb = build_b_chroma_prediction(mode, &l0, &l1, 0, 0, 0);
let pred_cr = build_b_chroma_prediction(mode, &l0, &l1, 1, 0, 0);
for row in &pred_cb {
for &px in row {
assert_eq!(px, 100, "Cb bipred should be (80+120+1)>>1 = 100");
}
}
for row in &pred_cr {
for &px in row {
assert_eq!(px, 100, "Cr bipred should match");
}
}
}
}