#![allow(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
reason = "the small 0..=16 coefficient positions and category selectors cast \
between usize and i32; every value is provably in range"
)]
use crate::lossy::bool_enc::BoolEncoder;
use crate::lossy::constants::{
B_DC_PRED, B_HD_PRED, B_HE_PRED, B_LD_PRED, B_RD_PRED, B_TM_PRED, B_VE_PRED, B_VL_PRED,
B_VR_PRED, BANDS, CAT_3456, CoeffProbas, CoeffStats, H_PRED, NUM_BMODES, NUM_PROBAS, Prob,
TM_PRED, V_PRED,
};
use crate::lossy::prelude::*;
#[derive(Clone, Copy)]
pub(crate) struct Block {
pub(crate) levels: [i16; 16],
pub(crate) last: i32,
}
impl Default for Block {
fn default() -> Self {
Self {
levels: [0; 16],
last: -1,
}
}
}
pub(crate) struct MbTokens {
pub(crate) is_i4x4: bool,
pub(crate) y2: Block,
pub(crate) luma: [Block; 16],
pub(crate) chroma: [Block; 8],
}
pub(crate) struct NzContext {
top: Vec<u32>,
top_dc: Vec<u32>,
left: u32,
left_dc: u32,
}
impl NzContext {
#[must_use]
pub(crate) fn new(mb_w: usize) -> Self {
Self {
top: vec![0; mb_w],
top_dc: vec![0; mb_w],
left: 0,
left_dc: 0,
}
}
pub(crate) const fn init_scanline(&mut self) {
self.left = 0;
self.left_dc = 0;
}
pub(crate) fn skip_mb(&mut self, mb_x: usize) {
self.top[mb_x] = 0;
self.top_dc[mb_x] = 0;
self.left = 0;
self.left_dc = 0;
}
}
pub(crate) fn emit_mb_residuals(
enc: &mut BoolEncoder,
bands: &CoeffProbas,
mb: &MbTokens,
ctx: &mut NzContext,
mb_x: usize,
) {
let mb_nz = ctx.top[mb_x];
let left_nz = ctx.left;
let mut mb_nz_dc = ctx.top_dc[mb_x];
let mut left_nz_dc = ctx.left_dc;
let (first, ac_type) = if mb.is_i4x4 {
(0usize, 3usize)
} else {
let c = (mb_nz_dc + left_nz_dc) as usize;
let nz = put_coeffs(enc, bands, 1, c, mb.y2.levels, 0, mb.y2.last);
let dc_nz = u32::from(nz > 0);
mb_nz_dc = dc_nz;
left_nz_dc = dc_nz;
(1usize, 0usize)
};
let first_i32 = first as i32;
let mut tnz = mb_nz & 0x0f;
let mut lnz = left_nz & 0x0f;
let mut off = 0usize;
for _y in 0..4 {
let mut l = lnz & 1;
for _x in 0..4 {
let c = (l + (tnz & 1)) as usize;
let nz = put_coeffs(
enc,
bands,
ac_type,
c,
mb.luma[off].levels,
first,
mb.luma[off].last,
);
l = u32::from(nz > first_i32);
tnz = (tnz >> 1) | (l << 7);
off += 1;
}
tnz >>= 4;
lnz = (lnz >> 1) | (l << 7);
}
let mut out_top_nz = tnz;
let mut out_left_nz = lnz >> 4;
for ch in (0..4).step_by(2) {
let mut tnz = mb_nz >> (4 + ch);
let mut lnz = left_nz >> (4 + ch);
for y in 0..2 {
let mut l = lnz & 1;
for x in 0..2 {
let c = (l + (tnz & 1)) as usize;
let idx = ch * 2 + y * 2 + x;
let nz = put_coeffs(
enc,
bands,
2,
c,
mb.chroma[idx].levels,
0,
mb.chroma[idx].last,
);
l = u32::from(nz > 0);
tnz = (tnz >> 1) | (l << 3);
}
tnz >>= 2;
lnz = (lnz >> 1) | (l << 5);
}
out_top_nz |= (tnz << 4) << ch;
out_left_nz |= (lnz & 0xf0) << ch;
}
ctx.top[mb_x] = out_top_nz & 0xff;
ctx.left = out_left_nz & 0xff;
ctx.top_dc[mb_x] = mb_nz_dc;
ctx.left_dc = left_nz_dc;
}
fn put_coeffs(
enc: &mut BoolEncoder,
bands: &CoeffProbas,
plane: usize,
ctx: usize,
levels: [i16; 16],
first: usize,
last: i32,
) -> i32 {
let plane_bands = &bands[plane];
let mut n = first;
let mut p: &[Prob; NUM_PROBAS] = &plane_bands[BANDS[n]][ctx];
loop {
if n as i32 > last {
enc.put_bool(p[0], false); return last + 1;
}
enc.put_bool(p[0], true);
while levels[n] == 0 {
enc.put_bool(p[1], false);
n += 1;
p = &plane_bands[BANDS[n]][0];
}
enc.put_bool(p[1], true);
let v = i32::from(levels[n]).abs();
let p_next = if v == 1 {
enc.put_bool(p[2], false);
&plane_bands[BANDS[n + 1]][1]
} else {
enc.put_bool(p[2], true);
put_large_value(enc, *p, v);
&plane_bands[BANDS[n + 1]][2]
};
enc.put_flag(levels[n] < 0);
n += 1;
if n == 16 {
return 16;
}
p = p_next;
}
}
fn put_large_value(enc: &mut BoolEncoder, p: [Prob; NUM_PROBAS], v: i32) {
if v <= 4 {
enc.put_bool(p[3], false);
if v == 2 {
enc.put_bool(p[4], false);
} else {
enc.put_bool(p[4], true);
enc.put_bool(p[5], v == 4); }
} else if v <= 10 {
enc.put_bool(p[3], true);
enc.put_bool(p[6], false);
if v <= 6 {
enc.put_bool(p[7], false);
enc.put_bool(159, v == 6); } else {
enc.put_bool(p[7], true);
let hi = v - 7; enc.put_bool(165, (hi >> 1) & 1 == 1);
enc.put_bool(145, hi & 1 == 1);
}
} else {
enc.put_bool(p[3], true);
enc.put_bool(p[6], true);
let cat = match v {
11..=18 => 0usize,
19..=34 => 1,
35..=66 => 2,
_ => 3,
};
enc.put_bool(p[8], (cat >> 1) & 1 == 1);
enc.put_bool(p[9 + (cat >> 1)], cat & 1 == 1);
let extra = v - 3 - (8 << cat);
let probs = CAT_3456[cat];
let nbits = probs.len();
for (i, &prob) in probs.iter().enumerate() {
enc.put_bool(prob, (extra >> (nbits - 1 - i)) & 1 == 1);
}
}
}
pub(crate) fn count_mb_residuals(
stats: &mut CoeffStats,
mb: &MbTokens,
ctx: &mut NzContext,
mb_x: usize,
) {
let mb_nz = ctx.top[mb_x];
let left_nz = ctx.left;
let mut mb_nz_dc = ctx.top_dc[mb_x];
let mut left_nz_dc = ctx.left_dc;
let (first, ac_type) = if mb.is_i4x4 {
(0usize, 3usize)
} else {
let c = (mb_nz_dc + left_nz_dc) as usize;
let nz = count_coeffs(stats, 1, c, mb.y2.levels, 0, mb.y2.last);
let dc_nz = u32::from(nz > 0);
mb_nz_dc = dc_nz;
left_nz_dc = dc_nz;
(1usize, 0usize)
};
let first_i32 = first as i32;
let mut tnz = mb_nz & 0x0f;
let mut lnz = left_nz & 0x0f;
let mut off = 0usize;
for _y in 0..4 {
let mut l = lnz & 1;
for _x in 0..4 {
let c = (l + (tnz & 1)) as usize;
let nz = count_coeffs(
stats,
ac_type,
c,
mb.luma[off].levels,
first,
mb.luma[off].last,
);
l = u32::from(nz > first_i32);
tnz = (tnz >> 1) | (l << 7);
off += 1;
}
tnz >>= 4;
lnz = (lnz >> 1) | (l << 7);
}
let mut out_top_nz = tnz;
let mut out_left_nz = lnz >> 4;
for ch in (0..4).step_by(2) {
let mut tnz = mb_nz >> (4 + ch);
let mut lnz = left_nz >> (4 + ch);
for y in 0..2 {
let mut l = lnz & 1;
for x in 0..2 {
let c = (l + (tnz & 1)) as usize;
let idx = ch * 2 + y * 2 + x;
let nz = count_coeffs(stats, 2, c, mb.chroma[idx].levels, 0, mb.chroma[idx].last);
l = u32::from(nz > 0);
tnz = (tnz >> 1) | (l << 3);
}
tnz >>= 2;
lnz = (lnz >> 1) | (l << 5);
}
out_top_nz |= (tnz << 4) << ch;
out_left_nz |= (lnz & 0xf0) << ch;
}
ctx.top[mb_x] = out_top_nz & 0xff;
ctx.left = out_left_nz & 0xff;
ctx.top_dc[mb_x] = mb_nz_dc;
ctx.left_dc = left_nz_dc;
}
fn count_coeffs(
stats: &mut CoeffStats,
plane: usize,
ctx0: usize,
levels: [i16; 16],
first: usize,
last: i32,
) -> i32 {
let mut n = first;
let mut band = BANDS[n];
let mut ctx = ctx0;
loop {
if n as i32 > last {
stats[plane][band][ctx][0][0] += 1; return last + 1;
}
stats[plane][band][ctx][0][1] += 1; while levels[n] == 0 {
stats[plane][band][ctx][1][0] += 1; n += 1;
band = BANDS[n];
ctx = 0;
}
stats[plane][band][ctx][1][1] += 1; if i32::from(levels[n]).abs() == 1 {
stats[plane][band][ctx][2][0] += 1; n += 1;
if n == 16 {
return 16;
}
band = BANDS[n];
ctx = 1;
} else {
stats[plane][band][ctx][2][1] += 1; n += 1;
if n == 16 {
return 16;
}
band = BANDS[n];
ctx = 2;
}
}
}
pub(crate) fn put_segment_id(enc: &mut BoolEncoder, probs: [Prob; 3], seg: u8) {
if seg >= 2 {
enc.put_bool(probs[0], true);
enc.put_bool(probs[2], seg == 3);
} else {
enc.put_bool(probs[0], false);
enc.put_bool(probs[1], seg == 1);
}
}
pub(crate) fn put_is_i4x4(enc: &mut BoolEncoder, is_i4x4: bool) {
enc.put_bool(145, !is_i4x4);
}
pub(crate) fn put_ymode16(enc: &mut BoolEncoder, mode: u8) {
match mode {
H_PRED => {
enc.put_bool(156, true);
enc.put_bool(128, false);
},
TM_PRED => {
enc.put_bool(156, true);
enc.put_bool(128, true);
},
V_PRED => {
enc.put_bool(156, false);
enc.put_bool(163, true);
},
_ => {
enc.put_bool(156, false);
enc.put_bool(163, false);
},
}
}
pub(crate) fn put_uvmode(enc: &mut BoolEncoder, mode: u8) {
match mode {
V_PRED => {
enc.put_bool(142, true);
enc.put_bool(114, false);
},
TM_PRED => {
enc.put_bool(142, true);
enc.put_bool(114, true);
enc.put_bool(183, true);
},
H_PRED => {
enc.put_bool(142, true);
enc.put_bool(114, true);
enc.put_bool(183, false);
},
_ => {
enc.put_bool(142, false);
},
}
}
pub(crate) fn put_bmode(enc: &mut BoolEncoder, prob: [Prob; NUM_BMODES - 1], mode: u8) {
if mode == B_DC_PRED {
enc.put_bool(prob[0], false);
return;
}
enc.put_bool(prob[0], true);
if mode == B_TM_PRED {
enc.put_bool(prob[1], false);
return;
}
enc.put_bool(prob[1], true);
if mode == B_VE_PRED {
enc.put_bool(prob[2], false);
return;
}
enc.put_bool(prob[2], true);
match mode {
B_HE_PRED => {
enc.put_bool(prob[3], false);
enc.put_bool(prob[4], false);
},
B_RD_PRED => {
enc.put_bool(prob[3], false);
enc.put_bool(prob[4], true);
enc.put_bool(prob[5], false);
},
B_VR_PRED => {
enc.put_bool(prob[3], false);
enc.put_bool(prob[4], true);
enc.put_bool(prob[5], true);
},
B_LD_PRED => {
enc.put_bool(prob[3], true);
enc.put_bool(prob[6], false);
},
B_VL_PRED => {
enc.put_bool(prob[3], true);
enc.put_bool(prob[6], true);
enc.put_bool(prob[7], false);
},
B_HD_PRED => {
enc.put_bool(prob[3], true);
enc.put_bool(prob[6], true);
enc.put_bool(prob[7], true);
enc.put_bool(prob[8], false);
},
_ => {
enc.put_bool(prob[3], true);
enc.put_bool(prob[6], true);
enc.put_bool(prob[7], true);
enc.put_bool(prob[8], true);
},
}
}
#[cfg(test)]
mod tests {
use super::{
Block, MbTokens, NzContext, count_mb_residuals, emit_mb_residuals, put_bmode, put_is_i4x4,
put_segment_id, put_uvmode, put_ymode16,
};
use crate::lossy::bool_dec::BoolDecoder;
use crate::lossy::bool_enc::BoolEncoder;
use crate::lossy::constants::{
B_DC_PRED, B_HD_PRED, B_HE_PRED, B_HU_PRED, B_LD_PRED, B_RD_PRED, B_TM_PRED, B_VE_PRED,
B_VL_PRED, B_VR_PRED, BMODES_PROBA, COEFFS_PROBA_0, CoeffStats, DC_PRED, H_PRED, TM_PRED,
V_PRED, ZIGZAG,
};
use crate::lossy::decode::Frame;
use crate::lossy::idct::transform_wht;
use crate::lossy::mb::read_bmode;
fn block_from_natural(natural: [i16; 16], first: usize) -> Block {
let mut levels = [0i16; 16];
let mut last = first as i32 - 1;
for n in first..16 {
let v = natural[ZIGZAG[n]];
levels[n] = v;
if v != 0 {
last = n as i32;
}
}
Block { levels, last }
}
fn primed_frame(y1: [i32; 2], y2: [i32; 2], uv: [i32; 2], is_i4x4: bool) -> Frame {
let mut frame = Frame::test_frame(1, 1);
frame.proba.bands = COEFFS_PROBA_0;
frame.dqm[0].y1 = y1;
frame.dqm[0].y2 = y2;
frame.dqm[0].uv = uv;
frame.mb_data[0].is_i4x4 = is_i4x4;
frame
}
#[test]
fn i4x4_residuals_round_trip_through_the_decoder() {
let y1 = [7, 13];
let uv = [5, 9];
let mut luma = [Block::default(); 16];
luma[0] = block_from_natural([-1, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
luma[5] = block_from_natural([0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
let mut chroma = [Block::default(); 8];
chroma[0] = block_from_natural([3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
chroma[6] = block_from_natural([-2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
let mb = MbTokens {
is_i4x4: true,
y2: Block::default(),
luma,
chroma,
};
let mut enc = BoolEncoder::new();
let mut ctx = NzContext::new(1);
emit_mb_residuals(&mut enc, &COEFFS_PROBA_0, &mb, &mut ctx, 0);
let bytes = enc.finish();
let mut frame = primed_frame(y1, y1, uv, true);
let mut br = BoolDecoder::new(&bytes);
frame.parse_residuals(&mut br, 0);
let coeffs = &frame.mb_data[0].coeffs;
assert_eq!(coeffs[0], -7);
assert_eq!(coeffs[1], 26);
assert_eq!(coeffs[3], 13);
assert_eq!(coeffs[80 + 1], 221);
assert_eq!(coeffs[256], 15);
assert_eq!(coeffs[352], -10);
}
#[test]
fn y2_dc_only_round_trips_to_broadcast_dc() {
let mut mb = MbTokens {
is_i4x4: false,
y2: block_from_natural([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0),
luma: [Block::default(); 16],
chroma: [Block::default(); 8],
};
for b in &mut mb.luma {
b.last = 0; }
let mut enc = BoolEncoder::new();
let mut ctx = NzContext::new(1);
emit_mb_residuals(&mut enc, &COEFFS_PROBA_0, &mb, &mut ctx, 0);
let bytes = enc.finish();
let mut frame = primed_frame([10, 20], [50, 20], [10, 20], false);
let mut br = BoolDecoder::new(&bytes);
frame.parse_residuals(&mut br, 0);
let coeffs = &frame.mb_data[0].coeffs;
for (i, &c) in coeffs.iter().enumerate() {
let want = if i < 256 && i % 16 == 0 { 6 } else { 0 };
assert_eq!(c, want, "coeff[{i}]");
}
}
#[test]
fn y2_wht_scatter_round_trips() {
let y2_dq = [10, 4];
let y2 = block_from_natural([1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
let mut luma = [Block::default(); 16];
for b in &mut luma {
b.last = 0; }
let mb = MbTokens {
is_i4x4: false,
y2,
luma,
chroma: [Block::default(); 8],
};
let mut enc = BoolEncoder::new();
let mut ctx = NzContext::new(1);
emit_mb_residuals(&mut enc, &COEFFS_PROBA_0, &mb, &mut ctx, 0);
let bytes = enc.finish();
let mut frame = primed_frame([10, 20], y2_dq, [10, 20], false);
let mut br = BoolDecoder::new(&bytes);
frame.parse_residuals(&mut br, 0);
let mut expected = [0i16; 384];
let mut dc = [0i16; 16];
dc[0] = 10;
dc[1] = 4;
transform_wht(dc, &mut expected);
assert_eq!(frame.mb_data[0].coeffs, expected);
}
#[test]
fn intra_modes_round_trip_through_parse_intra_mode() {
for &(ymode, uvmode) in &[
(DC_PRED, DC_PRED),
(V_PRED, H_PRED),
(H_PRED, V_PRED),
(TM_PRED, TM_PRED),
] {
let mut enc = BoolEncoder::new();
put_is_i4x4(&mut enc, false);
put_ymode16(&mut enc, ymode);
put_uvmode(&mut enc, uvmode);
let bytes = enc.finish();
let mut frame = Frame::test_frame(1, 1);
let mut br = BoolDecoder::new(&bytes);
frame.parse_intra_mode_row(&mut br);
assert!(!frame.mb_data[0].is_i4x4, "16x16 luma-type");
assert_eq!(frame.mb_data[0].imodes[0], ymode, "ymode");
assert_eq!(frame.mb_data[0].uvmode, uvmode, "uvmode");
}
}
#[test]
fn segment_ids_round_trip_through_the_decoder_tree() {
let probs = [200u8, 60, 140];
for seg in 0..4u8 {
let mut enc = BoolEncoder::new();
put_segment_id(&mut enc, probs, seg);
let bytes = enc.finish();
let mut br = BoolDecoder::new(&bytes);
let got = if br.read_bool(probs[0]) {
2 + u8::from(br.read_bool(probs[2]))
} else {
u8::from(br.read_bool(probs[1]))
};
assert_eq!(got, seg, "segment id {seg}");
}
}
#[test]
fn bmodes_round_trip_through_read_bmode() {
let modes = [
B_DC_PRED, B_TM_PRED, B_VE_PRED, B_HE_PRED, B_RD_PRED, B_VR_PRED, B_LD_PRED, B_VL_PRED,
B_HD_PRED, B_HU_PRED,
];
for &(top, left) in &[(0usize, 0usize), (2, 3), (9, 5), (4, 8)] {
let prob = BMODES_PROBA[top][left];
for &mode in &modes {
let mut enc = BoolEncoder::new();
put_bmode(&mut enc, prob, mode);
let bytes = enc.finish();
let mut br = BoolDecoder::new(&bytes);
let got = read_bmode(&mut br, prob);
assert_eq!(got, mode, "mode {mode} at prob[{top}][{left}]");
}
}
}
fn node012_events(levels: [i16; 16], first: usize, last: i32) -> u64 {
let mut n = first;
let mut count = 0u64;
loop {
if n as i32 > last {
count += 1; break;
}
count += 1; while levels[n] == 0 {
count += 1; n += 1;
}
count += 1; count += 1; n += 1;
if n == 16 {
break;
}
}
count
}
#[test]
fn count_records_exactly_the_main_tree_events_emit_would_code() {
let y2 = block_from_natural([1, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
let mut luma = [Block::default(); 16];
for b in &mut luma {
b.last = 0; }
luma[0] = block_from_natural([0, 5, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 1);
luma[7] = block_from_natural([0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 1);
let mut chroma = [Block::default(); 8];
chroma[0] = block_from_natural([3, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
chroma[5] = block_from_natural([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40], 0);
let mb = MbTokens {
is_i4x4: false,
y2,
luma,
chroma,
};
let mut stats = Box::<CoeffStats>::default();
let mut ctx = NzContext::new(2);
count_mb_residuals(&mut stats, &mb, &mut ctx, 0);
let mut recorded = 0u64;
for plane in stats.iter() {
for band in plane {
for c in band {
for node in c {
recorded += node[0] + node[1];
}
}
}
}
let mut expected = node012_events(mb.y2.levels, 0, mb.y2.last);
for b in &mb.luma {
expected += node012_events(b.levels, 1, b.last);
}
for b in &mb.chroma {
expected += node012_events(b.levels, 0, b.last);
}
assert_eq!(
recorded, expected,
"count must record every main-tree event"
);
assert!(
expected > 25,
"the crafted MB should code more than the EOB minimum"
);
}
fn sample_token_mbs() -> Vec<MbTokens> {
let empty_ac = block_from_natural([0; 16], 1); let nz_ac = block_from_natural([0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 1);
let dc_chroma = block_from_natural([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
let mut luma0 = [empty_ac; 16];
for &i in &[0usize, 1, 4, 6, 9, 11, 14] {
luma0[i] = nz_ac;
}
let mut chroma0 = [Block::default(); 8];
for &i in &[0usize, 3, 5, 6] {
chroma0[i] = dc_chroma;
}
let mb0 = MbTokens {
is_i4x4: false,
y2: block_from_natural([1, 2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0),
luma: luma0,
chroma: chroma0,
};
let mut luma1 = [empty_ac; 16];
for &i in &[2usize, 3, 5, 8, 12, 15] {
luma1[i] = nz_ac;
}
let mut chroma1 = [Block::default(); 8];
for &i in &[1usize, 2, 4, 7] {
chroma1[i] = dc_chroma;
}
let mb1 = MbTokens {
is_i4x4: false,
y2: block_from_natural([0; 16], 0), luma: luma1,
chroma: chroma1,
};
let dc_luma = block_from_natural([2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
let mut luma2 = [Block::default(); 16];
for &i in &[0usize, 5, 7, 10, 13] {
luma2[i] = dc_luma;
}
let mut chroma2 = [Block::default(); 8];
for &i in &[0usize, 2, 5] {
chroma2[i] = dc_chroma;
}
let mb2 = MbTokens {
is_i4x4: true,
y2: Block::default(),
luma: luma2,
chroma: chroma2,
};
vec![mb0, mb1, mb2]
}
#[test]
fn count_threads_the_nz_context_exactly_like_emit() {
let mbs = sample_token_mbs();
let mut ctx_e = NzContext::new(1);
let mut ctx_c = NzContext::new(1);
let mut stats = Box::<CoeffStats>::default();
let mut saw_dc_nz = false;
for mb in &mbs {
let mut enc = BoolEncoder::new();
emit_mb_residuals(&mut enc, &COEFFS_PROBA_0, mb, &mut ctx_e, 0);
count_mb_residuals(&mut stats, mb, &mut ctx_c, 0);
assert_eq!(ctx_e.top, ctx_c.top, "top nz word");
assert_eq!(ctx_e.top_dc, ctx_c.top_dc, "top dc-nz word");
assert_eq!(ctx_e.left, ctx_c.left, "left nz word");
assert_eq!(ctx_e.left_dc, ctx_c.left_dc, "left dc-nz flag");
saw_dc_nz |= ctx_c.top_dc[0] != 0;
}
assert!(saw_dc_nz, "a Y2 dc-nz flag should have been set");
assert!(
ctx_c.top[0] != 0 && ctx_c.left != 0,
"threaded context should carry non-zero bits"
);
}
#[test]
fn count_attributes_node0_events_to_the_correct_nz_context_bins() {
let dc1 = block_from_natural([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], 0);
let mb = MbTokens {
is_i4x4: true,
y2: Block::default(),
luma: [dc1; 16],
chroma: [dc1; 8],
};
let mb_nz_in = 0xCAu32;
let left_nz_in = 0x6Cu32;
let mut stats = Box::<CoeffStats>::default();
let mut ctx = NzContext::new(1);
ctx.top[0] = mb_nz_in;
ctx.left = left_nz_in;
count_mb_residuals(&mut stats, &mb, &mut ctx, 0);
let bit = |word: u32, i: u32| (word >> i) & 1 == 1;
let mut expected = [[0u64; 3]; 4]; for r in 0..4u32 {
for col in 0..4u32 {
let top = if r == 0 { bit(mb_nz_in, col) } else { true };
let left = if col == 0 { bit(left_nz_in, r) } else { true };
expected[3][usize::from(top) + usize::from(left)] += 1;
}
}
for ch in [0u32, 2] {
for r in 0..2u32 {
for col in 0..2u32 {
let top = if r == 0 {
bit(mb_nz_in, 4 + ch + col)
} else {
true
};
let left = if col == 0 {
bit(left_nz_in, 4 + ch + r)
} else {
true
};
expected[2][usize::from(top) + usize::from(left)] += 1;
}
}
}
for plane in [2usize, 3] {
for c in 0..3usize {
assert_eq!(
stats[plane][0][c][0][1], expected[plane][c],
"plane {plane} ctx {c} node-0 events"
);
}
}
assert!(
expected[3][2] > 0 && expected[2][2] > 0,
"interior blocks should reach context 2"
);
}
}