#![allow(dead_code)]
#![allow(clippy::too_many_arguments)]
use crate::cabac_ctx::{
BlockType, CabacContexts, CbpMbaffProbe, MvdComponent, NeighbourCtx, SliceKind,
};
use crate::encoder::cabac_engine::CabacEncoder;
fn mb_skip_flag_ctx_offset(slice_kind: SliceKind) -> Option<u32> {
match slice_kind {
SliceKind::P | SliceKind::SP => Some(11),
SliceKind::B => Some(24),
_ => None,
}
}
pub fn encode_mb_skip_flag(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
slice_kind: SliceKind,
neighbours: &NeighbourCtx,
skip: bool,
) {
let offset =
mb_skip_flag_ctx_offset(slice_kind).expect("mb_skip_flag is not coded on I/SI slices");
let cond_a = u32::from(neighbours.available_left && !neighbours.mb_skip_flag_left);
let cond_b = u32::from(neighbours.available_above && !neighbours.mb_skip_flag_above);
let ctx_idx = (offset + cond_a + cond_b) as usize;
enc.encode_decision(ctxs.at_mut(ctx_idx), if skip { 1 } else { 0 });
}
fn mb_type_cond_term_flag(offset: u32, neighbours: &NeighbourCtx, is_left: bool) -> u32 {
let available = if is_left {
neighbours.available_left
} else {
neighbours.available_above
};
if !available {
return 0;
}
let filtered = match offset {
0 => {
if is_left {
neighbours.left_is_si
} else {
neighbours.above_is_si
}
}
3 => {
if is_left {
neighbours.left_is_i_nxn
} else {
neighbours.above_is_i_nxn
}
}
27 => {
if is_left {
neighbours.left_is_b_skip_or_direct
} else {
neighbours.above_is_b_skip_or_direct
}
}
_ => false,
};
u32::from(!filtered)
}
fn mb_type_ctx_inc_first3(offset: u32, bin_idx: u32, neighbours: &NeighbourCtx) -> u32 {
if bin_idx == 0 {
mb_type_cond_term_flag(offset, neighbours, true)
+ mb_type_cond_term_flag(offset, neighbours, false)
} else {
bin_idx
}
}
fn encode_mb_type_intra_suffix(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
offset: u32,
suffix: u32,
bin0_already_emitted: bool,
) {
debug_assert!(suffix <= 25);
if suffix == 0 {
if !bin0_already_emitted {
enc.encode_decision(ctxs.at_mut(offset as usize), 0);
}
return;
}
if !bin0_already_emitted {
enc.encode_decision(ctxs.at_mut(offset as usize), 1);
}
if suffix == 25 {
enc.encode_terminate(1);
return;
}
enc.encode_terminate(0);
let (inc_b2, inc_b3, inc_b4_b3ne0, inc_b4_b3eq0, inc_b5_b3ne0, inc_b5_b3eq0, inc_b6) =
if offset == 3 {
(3u32, 4u32, 5u32, 6u32, 6u32, 7u32, 7u32)
} else {
(1u32, 2u32, 2u32, 3u32, 3u32, 3u32, 3u32)
};
let b2: u8 = if (1..=12).contains(&suffix) { 0 } else { 1 };
let base = 12u32 * (b2 as u32);
let off = suffix - base;
let (b3, b4, b5, b6_opt) = if off <= 4 {
let v = off - 1;
let b4 = ((v >> 1) & 1) as u8;
let b5 = (v & 1) as u8;
(0u8, b4, b5, None)
} else {
let b4 = if off >= 9 { 1u8 } else { 0u8 };
let v = off - 5 - 4 * (b4 as u32);
let b5 = ((v >> 1) & 1) as u8;
let b6 = (v & 1) as u8;
(1u8, b4, b5, Some(b6))
};
enc.encode_decision(ctxs.at_mut((offset + inc_b2) as usize), b2);
enc.encode_decision(ctxs.at_mut((offset + inc_b3) as usize), b3);
let inc_b4 = if b3 != 0 { inc_b4_b3ne0 } else { inc_b4_b3eq0 };
enc.encode_decision(ctxs.at_mut((offset + inc_b4) as usize), b4);
let inc_b5 = if b3 != 0 { inc_b5_b3ne0 } else { inc_b5_b3eq0 };
enc.encode_decision(ctxs.at_mut((offset + inc_b5) as usize), b5);
if let Some(b6) = b6_opt {
enc.encode_decision(ctxs.at_mut((offset + inc_b6) as usize), b6);
}
}
pub fn encode_mb_type_i(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
neighbours: &NeighbourCtx,
value: u32,
) {
const OFFSET: u32 = 3;
debug_assert!(value <= 25);
let inc0 = mb_type_ctx_inc_first3(OFFSET, 0, neighbours);
if value == 0 {
enc.encode_decision(ctxs.at_mut((OFFSET + inc0) as usize), 0);
return;
}
enc.encode_decision(ctxs.at_mut((OFFSET + inc0) as usize), 1);
encode_mb_type_intra_suffix(enc, ctxs, OFFSET, value, true);
}
pub fn encode_mb_type_p(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
_neighbours: &NeighbourCtx,
value: u32,
) {
const OFFSET: u32 = 14;
debug_assert!(
value <= 4 || (5..=30).contains(&value),
"invalid P mb_type {value}"
);
if value >= 5 {
enc.encode_decision(ctxs.at_mut(OFFSET as usize), 1);
encode_mb_type_intra_suffix(enc, ctxs, 17, value - 5, false);
return;
}
enc.encode_decision(ctxs.at_mut(OFFSET as usize), 0);
let (b1, b2) = match value {
0 => (0u8, 0u8),
1 => (1, 1),
2 => (1, 0),
3 => (0, 1),
_ => unreachable!(),
};
enc.encode_decision(ctxs.at_mut((OFFSET + 1) as usize), b1);
let inc_b2 = if b1 != 1 { 2 } else { 3 };
enc.encode_decision(ctxs.at_mut((OFFSET + inc_b2) as usize), b2);
}
pub fn encode_mb_type_b(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
neighbours: &NeighbourCtx,
value: u32,
) {
const OFFSET: u32 = 27;
debug_assert!(value <= 48, "invalid B mb_type {value}");
let inc0 = mb_type_ctx_inc_first3(OFFSET, 0, neighbours);
if value == 0 {
enc.encode_decision(ctxs.at_mut((OFFSET + inc0) as usize), 0);
return;
}
enc.encode_decision(ctxs.at_mut((OFFSET + inc0) as usize), 1);
if value == 1 {
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), 0);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 0);
return;
}
if value == 2 {
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), 0);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 1);
return;
}
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), 1);
if (3..=10).contains(&value) {
enc.encode_decision(ctxs.at_mut((OFFSET + 4) as usize), 0);
let v = value - 3;
let b3 = ((v >> 2) & 1) as u8;
let b4 = ((v >> 1) & 1) as u8;
let b5 = (v & 1) as u8;
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), b3);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), b4);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), b5);
return;
}
enc.encode_decision(ctxs.at_mut((OFFSET + 4) as usize), 1);
if value == 22 {
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 1);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 1);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 1);
return;
}
if value == 11 {
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 1);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 1);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 0);
return;
}
if (23..=48).contains(&value) {
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 1);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 0);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 1);
encode_mb_type_intra_suffix(enc, ctxs, 32, value - 23, false);
return;
}
let v = value - 12;
let b3 = ((v >> 3) & 1) as u8;
let b4 = ((v >> 2) & 1) as u8;
let b5 = ((v >> 1) & 1) as u8;
let b6 = (v & 1) as u8;
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), b3);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), b4);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), b5);
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), b6);
}
pub fn encode_sub_mb_type_p(enc: &mut CabacEncoder, ctxs: &mut CabacContexts, value: u32) {
const OFFSET: u32 = 21;
debug_assert!(value <= 3, "invalid P sub_mb_type {value}");
if value == 0 {
enc.encode_decision(ctxs.at_mut(OFFSET as usize), 1);
return;
}
enc.encode_decision(ctxs.at_mut(OFFSET as usize), 0);
if value == 1 {
enc.encode_decision(ctxs.at_mut((OFFSET + 1) as usize), 0);
return;
}
enc.encode_decision(ctxs.at_mut((OFFSET + 1) as usize), 1);
if value == 2 {
enc.encode_decision(ctxs.at_mut((OFFSET + 2) as usize), 1);
} else {
enc.encode_decision(ctxs.at_mut((OFFSET + 2) as usize), 0);
}
}
pub fn encode_sub_mb_type_b(enc: &mut CabacEncoder, ctxs: &mut CabacContexts, value: u32) {
const OFFSET: u32 = 36;
debug_assert!(value <= 12, "invalid B sub_mb_type {value}");
if value == 0 {
enc.encode_decision(ctxs.at_mut(OFFSET as usize), 0);
return;
}
enc.encode_decision(ctxs.at_mut(OFFSET as usize), 1);
let b1: u8 = if (1..=2).contains(&value) { 0 } else { 1 };
enc.encode_decision(ctxs.at_mut((OFFSET + 1) as usize), b1);
let inc_b2 = if b1 != 0 { 2 } else { 3 };
if value == 1 {
enc.encode_decision(ctxs.at_mut((OFFSET + inc_b2) as usize), 0);
return;
}
if value == 2 {
enc.encode_decision(ctxs.at_mut((OFFSET + inc_b2) as usize), 1);
return;
}
let b2: u8 = if (3..=6).contains(&value) { 0 } else { 1 };
enc.encode_decision(ctxs.at_mut((OFFSET + inc_b2) as usize), b2);
if b2 == 0 {
let v = value - 3;
let b3 = ((v >> 1) & 1) as u8;
let b4 = (v & 1) as u8;
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), b3);
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), b4);
return;
}
let b3: u8 = if (11..=12).contains(&value) { 1 } else { 0 };
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), b3);
if b3 == 1 {
let b4: u8 = if value == 11 { 0 } else { 1 };
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), b4);
return;
}
let v = value - 7;
let b4 = ((v >> 1) & 1) as u8;
let b5 = (v & 1) as u8;
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), b4);
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), b5);
}
pub fn encode_mb_qp_delta(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
prev_mb_qp_delta_nonzero: bool,
value: i32,
) {
const OFFSET: u32 = 60;
let inc0 = u32::from(prev_mb_qp_delta_nonzero);
if value == 0 {
enc.encode_decision(ctxs.at_mut((OFFSET + inc0) as usize), 0);
return;
}
enc.encode_decision(ctxs.at_mut((OFFSET + inc0) as usize), 1);
let mapped: u32 = if value > 0 {
(2 * value - 1) as u32
} else {
(-2 * value) as u32
};
debug_assert!(mapped >= 1);
if mapped == 1 {
enc.encode_decision(ctxs.at_mut((OFFSET + 2) as usize), 0);
return;
}
enc.encode_decision(ctxs.at_mut((OFFSET + 2) as usize), 1);
for _ in 0..(mapped - 2) {
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), 1);
}
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), 0);
}
pub fn encode_ref_idx_lx(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
neighbour_left_gt_0: bool,
neighbour_above_gt_0: bool,
value: u32,
) {
const OFFSET: u32 = 54;
let inc0 = u32::from(neighbour_left_gt_0) + 2 * u32::from(neighbour_above_gt_0);
if value == 0 {
enc.encode_decision(ctxs.at_mut((OFFSET + inc0) as usize), 0);
return;
}
enc.encode_decision(ctxs.at_mut((OFFSET + inc0) as usize), 1);
if value == 1 {
enc.encode_decision(ctxs.at_mut((OFFSET + 4) as usize), 0);
return;
}
enc.encode_decision(ctxs.at_mut((OFFSET + 4) as usize), 1);
for _ in 0..(value - 2) {
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 1);
}
enc.encode_decision(ctxs.at_mut((OFFSET + 5) as usize), 0);
}
pub fn encode_mvd_lx(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
component: MvdComponent,
neighbour_abs_mvd_sum: u32,
value: i32,
) {
let offset = match component {
MvdComponent::X => 40,
MvdComponent::Y => 47,
};
let inc0 = if neighbour_abs_mvd_sum > 32 {
2
} else if neighbour_abs_mvd_sum > 2 {
1
} else {
0
};
let abs_val = value.unsigned_abs();
let prefix_val = abs_val.min(9);
let bins: [u32; 9] = [inc0, 3, 4, 5, 6, 6, 6, 6, 6];
for (i, inc) in bins.iter().enumerate() {
let ctx_idx = (offset + inc) as usize;
if (i as u32) < prefix_val {
enc.encode_decision(ctxs.at_mut(ctx_idx), 1);
} else {
enc.encode_decision(ctxs.at_mut(ctx_idx), 0);
break;
}
}
if abs_val >= 9 {
let mut suf = abs_val - 9;
let mut k: u32 = 3;
while suf >= (1u32 << k) {
enc.encode_bypass(1);
suf -= 1u32 << k;
k += 1;
}
enc.encode_bypass(0);
for i in (0..k).rev() {
let b = ((suf >> i) & 1) as u8;
enc.encode_bypass(b);
}
}
if abs_val != 0 {
let sign = if value < 0 { 1 } else { 0 };
enc.encode_bypass(sign);
}
}
pub fn encode_intra_chroma_pred_mode(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
neighbours: &NeighbourCtx,
value: u32,
) {
const OFFSET: u32 = 64;
debug_assert!(value <= 3);
let cond_a = u32::from(
neighbours.available_left
&& !neighbours.left_inter
&& !neighbours.left_is_i_pcm
&& neighbours.left_intra_chroma_pred_mode_nonzero,
);
let cond_b = u32::from(
neighbours.available_above
&& !neighbours.above_inter
&& !neighbours.above_is_i_pcm
&& neighbours.above_intra_chroma_pred_mode_nonzero,
);
let inc0 = cond_a + cond_b;
if value == 0 {
enc.encode_decision(ctxs.at_mut((OFFSET + inc0) as usize), 0);
return;
}
enc.encode_decision(ctxs.at_mut((OFFSET + inc0) as usize), 1);
if value == 1 {
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), 0);
return;
}
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), 1);
let bin2 = if value == 3 { 1u8 } else { 0 };
enc.encode_decision(ctxs.at_mut((OFFSET + 3) as usize), bin2);
}
pub fn encode_transform_size_8x8_flag(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
neighbours: &NeighbourCtx,
flag: bool,
) {
const OFFSET: u32 = 399;
let cond_a = u32::from(neighbours.available_left && neighbours.left_transform_8x8);
let cond_b = u32::from(neighbours.available_above && neighbours.above_transform_8x8);
let ctx_idx = (OFFSET + cond_a + cond_b) as usize;
enc.encode_decision(ctxs.at_mut(ctx_idx), if flag { 1 } else { 0 });
}
fn cbf_ctx_offset(bt: BlockType) -> u32 {
let cat = bt as u32;
match cat {
0..=4 => 85,
6..=8 => 460,
10..=12 => 472,
5 | 9 | 13 => 1012,
_ => 85,
}
}
fn cbf_block_cat_offset(bt: BlockType) -> u32 {
match bt {
BlockType::Luma16x16Dc => 0,
BlockType::Luma16x16Ac => 4,
BlockType::Luma4x4 => 8,
BlockType::ChromaDc => 12,
BlockType::ChromaAc => 16,
BlockType::Luma8x8 => 0,
BlockType::CbIntra16x16Dc => 0,
BlockType::CbIntra16x16Ac => 4,
BlockType::CbLuma4x4 => 8,
BlockType::Cb8x8 => 4,
BlockType::CrIntra16x16Dc => 0,
BlockType::CrIntra16x16Ac => 4,
BlockType::CrLuma4x4 => 8,
BlockType::Cr8x8 => 8,
}
}
fn sig_block_cat_offset(bt: BlockType) -> u32 {
match bt {
BlockType::Luma16x16Dc => 0,
BlockType::Luma16x16Ac => 15,
BlockType::Luma4x4 => 29,
BlockType::ChromaDc => 44,
BlockType::ChromaAc => 47,
BlockType::Luma8x8 => 0,
BlockType::CbIntra16x16Dc => 0,
BlockType::CbIntra16x16Ac => 15,
BlockType::CbLuma4x4 => 29,
BlockType::Cb8x8 => 0,
BlockType::CrIntra16x16Dc => 0,
BlockType::CrIntra16x16Ac => 15,
BlockType::CrLuma4x4 => 29,
BlockType::Cr8x8 => 0,
}
}
fn lvl_block_cat_offset(bt: BlockType) -> u32 {
match bt {
BlockType::Luma16x16Dc => 0,
BlockType::Luma16x16Ac => 10,
BlockType::Luma4x4 => 20,
BlockType::ChromaDc => 30,
BlockType::ChromaAc => 39,
BlockType::Luma8x8 => 0,
BlockType::CbIntra16x16Dc => 0,
BlockType::CbIntra16x16Ac => 10,
BlockType::CbLuma4x4 => 20,
BlockType::Cb8x8 => 0,
BlockType::CrIntra16x16Dc => 0,
BlockType::CrIntra16x16Ac => 10,
BlockType::CrLuma4x4 => 20,
BlockType::Cr8x8 => 0,
}
}
fn sig_coef_ctx_offset(bt: BlockType, field: bool) -> u32 {
let cat = bt as u32;
if field {
match cat {
0..=4 => 277,
5 => 436,
6..=8 => 776,
9 => 675,
10..=12 => 820,
13 => 733,
_ => 277,
}
} else {
match cat {
0..=4 => 105,
5 => 402,
6..=8 => 484,
9 => 660,
10..=12 => 528,
13 => 718,
_ => 105,
}
}
}
fn last_coef_ctx_offset(bt: BlockType, field: bool) -> u32 {
let cat = bt as u32;
if field {
match cat {
0..=4 => 338,
5 => 451,
6..=8 => 864,
9 => 699,
10..=12 => 908,
13 => 757,
_ => 338,
}
} else {
match cat {
0..=4 => 166,
5 => 417,
6..=8 => 572,
9 => 690,
10..=12 => 616,
13 => 748,
_ => 166,
}
}
}
fn abs_level_ctx_offset(bt: BlockType) -> u32 {
let cat = bt as u32;
match cat {
0..=4 => 227,
5 => 426,
6..=8 => 952,
9 => 708,
10..=12 => 982,
13 => 766,
_ => 227,
}
}
pub fn encode_coded_block_flag(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
block_type: BlockType,
neighbour_cbf_left: Option<bool>,
neighbour_cbf_above: Option<bool>,
flag: bool,
) {
let base = cbf_ctx_offset(block_type);
let cat_offset = cbf_block_cat_offset(block_type);
let cond_a = u32::from(neighbour_cbf_left.unwrap_or(false));
let cond_b = u32::from(neighbour_cbf_above.unwrap_or(false));
let inc = cond_a + 2 * cond_b;
let ctx_idx = (base + cat_offset + inc) as usize;
enc.encode_decision(ctxs.at_mut(ctx_idx), if flag { 1 } else { 0 });
}
pub fn encode_coded_block_pattern(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
neighbours: &NeighbourCtx,
chroma_array_type: u32,
cbp_luma: u8,
cbp_chroma: u8,
) {
debug_assert!(cbp_luma <= 15);
debug_assert!(cbp_chroma <= 2);
const LUMA_OFFSET: u32 = 73;
const CHROMA_OFFSET: u32 = 77;
let cond_ext =
|avail: bool, is_i_pcm: bool, is_skip: bool, cbp_luma: u8, bit_idx_n: u32| -> u32 {
if !avail {
return 0;
}
if is_i_pcm {
return 0;
}
if is_skip {
return 1;
}
let bit_set = ((cbp_luma >> bit_idx_n) & 1) != 0;
if bit_set {
0
} else {
1
}
};
let cond_probe = |p: &CbpMbaffProbe| -> u32 {
if !p.available || p.is_i_pcm {
return 0;
}
if p.is_skip {
return 1;
}
u32::from(!p.cbp_bit_set)
};
let mut prev_bins = [0u32; 4];
for bin_idx in 0..4u32 {
let bin = (cbp_luma >> bin_idx) & 1;
let cond_a = match bin_idx {
0 | 2 => {
if let Some(probes) = &neighbours.cbp_luma_mbaff {
cond_probe(&probes[0][bin_idx as usize])
} else {
cond_ext(
neighbours.available_left,
neighbours.left_is_i_pcm,
neighbours.left_is_p_or_b_skip,
neighbours.left_cbp_luma,
if bin_idx == 0 { 1 } else { 3 },
)
}
}
1 => {
let b_k = prev_bins[0];
if b_k != 0 {
0
} else {
1
}
}
3 => {
let b_k = prev_bins[2];
if b_k != 0 {
0
} else {
1
}
}
_ => 0,
};
let cond_b = match bin_idx {
0 | 1 => {
if let Some(probes) = &neighbours.cbp_luma_mbaff {
cond_probe(&probes[1][bin_idx as usize])
} else {
cond_ext(
neighbours.available_above,
neighbours.above_is_i_pcm,
neighbours.above_is_p_or_b_skip,
neighbours.above_cbp_luma,
if bin_idx == 0 { 2 } else { 3 },
)
}
}
2 => {
let b_k = prev_bins[0];
if b_k != 0 {
0
} else {
1
}
}
3 => {
let b_k = prev_bins[1];
if b_k != 0 {
0
} else {
1
}
}
_ => 0,
};
let inc = cond_a + 2 * cond_b;
let ctx_idx = (LUMA_OFFSET + inc) as usize;
enc.encode_decision(ctxs.at_mut(ctx_idx), bin);
prev_bins[bin_idx as usize] = bin as u32;
}
if chroma_array_type == 1 || chroma_array_type == 2 {
let chroma_cond =
|avail: bool, is_i_pcm: bool, is_skip: bool, cbp_chroma: u8, bin_idx: u32| -> u32 {
if !avail {
return 0;
}
if is_i_pcm {
return 1;
}
if is_skip {
return 0;
}
let zero_branch = match bin_idx {
0 => cbp_chroma == 0,
1 => cbp_chroma != 2,
_ => false,
};
if zero_branch {
0
} else {
1
}
};
let cond_a0 = chroma_cond(
neighbours.available_left,
neighbours.left_is_i_pcm,
neighbours.left_is_p_or_b_skip,
neighbours.left_cbp_chroma,
0,
);
let cond_b0 = chroma_cond(
neighbours.available_above,
neighbours.above_is_i_pcm,
neighbours.above_is_p_or_b_skip,
neighbours.above_cbp_chroma,
0,
);
let inc0 = cond_a0 + 2 * cond_b0;
let b0 = if cbp_chroma > 0 { 1 } else { 0 };
enc.encode_decision(ctxs.at_mut((CHROMA_OFFSET + inc0) as usize), b0);
if cbp_chroma > 0 {
let cond_a1 = chroma_cond(
neighbours.available_left,
neighbours.left_is_i_pcm,
neighbours.left_is_p_or_b_skip,
neighbours.left_cbp_chroma,
1,
);
let cond_b1 = chroma_cond(
neighbours.available_above,
neighbours.above_is_i_pcm,
neighbours.above_is_p_or_b_skip,
neighbours.above_cbp_chroma,
1,
);
let inc1 = cond_a1 + 2 * cond_b1 + 4;
let b1 = if cbp_chroma == 2 { 1 } else { 0 };
enc.encode_decision(ctxs.at_mut((CHROMA_OFFSET + inc1) as usize), b1);
}
}
}
static TBL_9_43_ENC: [(u8, u8, u8); 63] = [
(0, 0, 0),
(1, 1, 1),
(2, 1, 1),
(3, 2, 1),
(4, 2, 1),
(5, 3, 1),
(5, 3, 1),
(4, 4, 1),
(4, 5, 1),
(3, 6, 1),
(3, 7, 1),
(4, 7, 1),
(4, 7, 1),
(4, 8, 1),
(5, 4, 1),
(5, 5, 1),
(4, 6, 2),
(4, 9, 2),
(4, 10, 2),
(4, 10, 2),
(3, 8, 2),
(3, 11, 2),
(6, 12, 2),
(7, 11, 2),
(7, 9, 2),
(7, 9, 2),
(8, 10, 2),
(9, 10, 2),
(10, 8, 2),
(9, 11, 2),
(8, 12, 2),
(7, 11, 2),
(7, 9, 3),
(6, 9, 3),
(11, 10, 3),
(12, 10, 3),
(13, 8, 3),
(11, 11, 3),
(6, 12, 3),
(7, 11, 3),
(8, 9, 4),
(9, 9, 4),
(14, 10, 4),
(10, 10, 4),
(9, 8, 4),
(8, 13, 4),
(6, 13, 4),
(11, 9, 4),
(12, 9, 5),
(13, 10, 5),
(11, 10, 5),
(6, 8, 5),
(9, 13, 6),
(14, 13, 6),
(10, 9, 6),
(9, 9, 6),
(11, 10, 7),
(12, 10, 7),
(13, 14, 7),
(11, 14, 7),
(14, 14, 8),
(10, 14, 8),
(12, 14, 8),
];
fn sig_coeff_inc_enc(
block_type: BlockType,
level_list_idx: u32,
field: bool,
num_c8x8: u32,
) -> u32 {
let cat = block_type as u32;
match cat {
3 => core::cmp::min(level_list_idx / num_c8x8.max(1), 2),
5 | 9 | 13 => {
let row = TBL_9_43_ENC[level_list_idx as usize];
if field {
row.1 as u32
} else {
row.0 as u32
}
}
_ => level_list_idx,
}
}
fn last_coeff_inc_enc(block_type: BlockType, level_list_idx: u32, num_c8x8: u32) -> u32 {
let cat = block_type as u32;
match cat {
3 => core::cmp::min(level_list_idx / num_c8x8.max(1), 2),
5 | 9 | 13 => TBL_9_43_ENC[level_list_idx as usize].2 as u32,
_ => level_list_idx,
}
}
#[allow(clippy::too_many_arguments)]
pub fn encode_significant_coeff_flag(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
block_type: BlockType,
coeff_idx: u32,
field: bool,
num_c8x8: u32,
flag: bool,
) {
let base = sig_coef_ctx_offset(block_type, field);
let cat_offset = sig_block_cat_offset(block_type);
let inc = sig_coeff_inc_enc(block_type, coeff_idx, field, num_c8x8);
let ctx_idx = (base + cat_offset + inc) as usize;
enc.encode_decision(ctxs.at_mut(ctx_idx), if flag { 1 } else { 0 });
}
#[allow(clippy::too_many_arguments)]
pub fn encode_last_significant_coeff_flag(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
block_type: BlockType,
coeff_idx: u32,
field: bool,
num_c8x8: u32,
flag: bool,
) {
let base = last_coef_ctx_offset(block_type, field);
let cat_offset = sig_block_cat_offset(block_type);
let inc = last_coeff_inc_enc(block_type, coeff_idx, num_c8x8);
let ctx_idx = (base + cat_offset + inc) as usize;
enc.encode_decision(ctxs.at_mut(ctx_idx), if flag { 1 } else { 0 });
}
pub fn encode_coeff_abs_level_minus1(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
block_type: BlockType,
num_decoded_eq_1: u32,
num_decoded_gt_1: u32,
value: u32,
) {
let base = abs_level_ctx_offset(block_type);
let cat_offset = lvl_block_cat_offset(block_type);
let u_coff: u32 = 14;
let inc0 = if num_decoded_gt_1 != 0 {
0
} else {
core::cmp::min(4, 1 + num_decoded_eq_1)
};
let ctx0 = (base + cat_offset + inc0) as usize;
if value == 0 {
enc.encode_decision(ctxs.at_mut(ctx0), 0);
return;
}
enc.encode_decision(ctxs.at_mut(ctx0), 1);
let cat_minus = if (block_type as u32) == 3 { 1 } else { 0 };
let inc_rest = 5 + core::cmp::min(4u32 - cat_minus, num_decoded_gt_1);
let ctx_rest = (base + cat_offset + inc_rest) as usize;
if value < u_coff {
for _ in 0..(value - 1) {
enc.encode_decision(ctxs.at_mut(ctx_rest), 1);
}
enc.encode_decision(ctxs.at_mut(ctx_rest), 0);
return;
}
for _ in 0..(u_coff - 1) {
enc.encode_decision(ctxs.at_mut(ctx_rest), 1);
}
let mut suf = value - u_coff;
let mut k: u32 = 0;
while suf >= (1u32 << k) {
enc.encode_bypass(1);
suf -= 1u32 << k;
k += 1;
}
enc.encode_bypass(0);
for i in (0..k).rev() {
let b = ((suf >> i) & 1) as u8;
enc.encode_bypass(b);
}
}
pub fn encode_coeff_sign_flag(enc: &mut CabacEncoder, neg: bool) {
enc.encode_bypass(if neg { 1 } else { 0 });
}
pub fn encode_end_of_slice_flag(enc: &mut CabacEncoder, end_of_slice: bool) {
enc.encode_terminate(if end_of_slice { 1 } else { 0 });
}
pub fn encode_prev_intra_pred_mode_flag(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
flag: bool,
) {
enc.encode_decision(ctxs.at_mut(68), if flag { 1 } else { 0 });
}
pub fn encode_rem_intra_pred_mode(enc: &mut CabacEncoder, ctxs: &mut CabacContexts, value: u32) {
debug_assert!(value <= 7);
for i in 0..3 {
let bin = ((value >> i) & 1) as u8;
enc.encode_decision(ctxs.at_mut(69), bin);
}
}
#[allow(clippy::too_many_arguments)]
pub fn encode_residual_block_cabac(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
block_type: BlockType,
coeffs: &[i32],
max_num_coeff: u32,
cbf_neighbour_left: Option<bool>,
cbf_neighbour_above: Option<bool>,
skip_cbf: bool,
num_c8x8: u32,
) -> bool {
encode_residual_block_cabac_field(
enc,
ctxs,
block_type,
coeffs,
max_num_coeff,
cbf_neighbour_left,
cbf_neighbour_above,
skip_cbf,
num_c8x8,
false,
)
}
#[allow(clippy::too_many_arguments)]
pub fn encode_residual_block_cabac_field(
enc: &mut CabacEncoder,
ctxs: &mut CabacContexts,
block_type: BlockType,
coeffs: &[i32],
max_num_coeff: u32,
cbf_neighbour_left: Option<bool>,
cbf_neighbour_above: Option<bool>,
skip_cbf: bool,
num_c8x8: u32,
field: bool,
) -> bool {
debug_assert_eq!(coeffs.len(), max_num_coeff as usize);
let bin_before = enc.bin_count();
let any_nz = coeffs.iter().any(|&c| c != 0);
if !skip_cbf {
encode_coded_block_flag(
enc,
ctxs,
block_type,
cbf_neighbour_left,
cbf_neighbour_above,
any_nz,
);
}
if !any_nz {
if std::env::var_os("OXIDEAV_H264_CABAC_DEBUG").is_some() {
let bin_after = enc.bin_count();
eprintln!(
"[ENC-CABAC] block_type={:?} max={} bins={} cbf=false",
block_type,
max_num_coeff,
bin_after - bin_before,
);
}
return false;
}
let last_idx = coeffs
.iter()
.enumerate()
.rev()
.find(|(_, c)| **c != 0)
.map(|(i, _)| i as u32)
.expect("any_nz implies a last non-zero exists");
let last_position_exclusive = max_num_coeff - 1;
for i in 0..last_idx {
let sig = coeffs[i as usize] != 0;
encode_significant_coeff_flag(enc, ctxs, block_type, i, field, num_c8x8, sig);
if sig {
encode_last_significant_coeff_flag(enc, ctxs, block_type, i, field, num_c8x8, false);
}
}
if last_idx < last_position_exclusive {
encode_significant_coeff_flag(enc, ctxs, block_type, last_idx, field, num_c8x8, true);
encode_last_significant_coeff_flag(enc, ctxs, block_type, last_idx, field, num_c8x8, true);
} else {
}
let mut num_decoded_eq_1: u32 = 0;
let mut num_decoded_gt_1: u32 = 0;
for i in (0..=last_idx).rev() {
let level = coeffs[i as usize];
if level == 0 {
continue;
}
let abs = level.unsigned_abs();
let abs_minus1 = abs - 1;
encode_coeff_abs_level_minus1(
enc,
ctxs,
block_type,
num_decoded_eq_1,
num_decoded_gt_1,
abs_minus1,
);
encode_coeff_sign_flag(enc, level < 0);
if abs == 1 {
num_decoded_eq_1 += 1;
} else {
num_decoded_gt_1 += 1;
}
}
let bin_after = enc.bin_count();
if std::env::var_os("OXIDEAV_H264_CABAC_DEBUG").is_some() {
eprintln!(
"[ENC-CABAC] block_type={:?} max={} bins={} cbf=true coeffs={:?}",
block_type,
max_num_coeff,
bin_after - bin_before,
coeffs,
);
}
true
}
#[cfg(test)]
mod tests {
use super::*;
use crate::bitstream::BitReader;
use crate::cabac::CabacDecoder;
use crate::cabac_ctx::{
decode_coded_block_flag, decode_coded_block_pattern, decode_coeff_abs_level_minus1,
decode_coeff_sign_flag, decode_end_of_slice_flag, decode_intra_chroma_pred_mode,
decode_last_significant_coeff_flag, decode_mb_qp_delta, decode_mb_skip_flag,
decode_mb_type_b, decode_mb_type_i, decode_mb_type_p, decode_mvd_lx,
decode_prev_intra_pred_mode_flag, decode_ref_idx_lx, decode_rem_intra_pred_mode,
decode_significant_coeff_flag, decode_sub_mb_type_b, decode_sub_mb_type_p,
};
fn roundtrip<F1, F2>(encode_side: F1, decode_side: F2) -> Vec<u8>
where
F1: FnOnce(&mut CabacEncoder, &mut CabacContexts),
F2: FnOnce(&mut CabacDecoder<'_>, &mut CabacContexts),
{
let mut ctxs_enc = CabacContexts::init(SliceKind::I, None, 26).unwrap();
let mut enc = CabacEncoder::new();
encode_side(&mut enc, &mut ctxs_enc);
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut ctxs_dec = CabacContexts::init(SliceKind::I, None, 26).unwrap();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
decode_side(&mut dec, &mut ctxs_dec);
assert_eq!(dec.decode_terminate().unwrap(), 1);
bytes
}
#[test]
fn rt_mb_skip_flag_p_slice() {
let mut ctxs_enc = CabacContexts::init(SliceKind::P, Some(0), 26).unwrap();
let mut enc = CabacEncoder::new();
encode_mb_skip_flag(
&mut enc,
&mut ctxs_enc,
SliceKind::P,
&NeighbourCtx::default(),
true,
);
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut ctxs_dec = CabacContexts::init(SliceKind::P, Some(0), 26).unwrap();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
assert!(decode_mb_skip_flag(
&mut dec,
&mut ctxs_dec,
SliceKind::P,
&NeighbourCtx::default()
)
.unwrap());
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
#[test]
fn rt_mb_type_i_all_values() {
for v in 0u32..=25 {
let nb = NeighbourCtx::default();
let bytes = roundtrip(
|enc, ctxs| encode_mb_type_i(enc, ctxs, &nb, v),
|dec, ctxs| {
let got = decode_mb_type_i(dec, ctxs, &nb).unwrap();
assert_eq!(got, v, "mb_type_i value {v} mismatch");
},
);
assert!(!bytes.is_empty(), "empty encode for mb_type {v}");
}
}
#[test]
fn rt_mb_type_p_inter_values() {
for v in [0u32, 1, 2, 3] {
let mut ctxs_enc = CabacContexts::init(SliceKind::P, Some(0), 26).unwrap();
let mut enc = CabacEncoder::new();
let nb = NeighbourCtx::default();
encode_mb_type_p(&mut enc, &mut ctxs_enc, &nb, v);
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut ctxs_dec = CabacContexts::init(SliceKind::P, Some(0), 26).unwrap();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
let got = decode_mb_type_p(&mut dec, &mut ctxs_dec, &nb).unwrap();
assert_eq!(got, v, "P mb_type {v} round-trip");
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
}
#[test]
fn rt_mb_type_b_inter_values() {
let test_values: &[u32] = &[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23,
30, 48,
];
for &v in test_values {
let mut ctxs_enc = CabacContexts::init(SliceKind::B, Some(0), 26).unwrap();
let mut enc = CabacEncoder::new();
let nb = NeighbourCtx::default();
encode_mb_type_b(&mut enc, &mut ctxs_enc, &nb, v);
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut ctxs_dec = CabacContexts::init(SliceKind::B, Some(0), 26).unwrap();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
let got = decode_mb_type_b(&mut dec, &mut ctxs_dec, &nb).unwrap();
assert_eq!(got, v, "B mb_type {v} round-trip");
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
}
#[test]
fn rt_mb_type_p_intra_values() {
for v in [5u32, 6, 12, 25, 30] {
let mut ctxs_enc = CabacContexts::init(SliceKind::P, Some(0), 26).unwrap();
let mut enc = CabacEncoder::new();
let nb = NeighbourCtx::default();
encode_mb_type_p(&mut enc, &mut ctxs_enc, &nb, v);
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut ctxs_dec = CabacContexts::init(SliceKind::P, Some(0), 26).unwrap();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
let got = decode_mb_type_p(&mut dec, &mut ctxs_dec, &nb).unwrap();
assert_eq!(got, v, "P intra mb_type {v} round-trip");
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
}
#[test]
fn rt_sub_mb_type_p_all_values() {
for v in 0u32..=3 {
let mut ctxs_enc = CabacContexts::init(SliceKind::P, Some(0), 26).unwrap();
let mut enc = CabacEncoder::new();
encode_sub_mb_type_p(&mut enc, &mut ctxs_enc, v);
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut ctxs_dec = CabacContexts::init(SliceKind::P, Some(0), 26).unwrap();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
let got = decode_sub_mb_type_p(&mut dec, &mut ctxs_dec).unwrap();
assert_eq!(got, v, "P sub_mb_type {v} round-trip");
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
}
#[test]
fn rt_sub_mb_type_b_all_values() {
for v in 0u32..=12 {
let mut ctxs_enc = CabacContexts::init(SliceKind::B, Some(0), 26).unwrap();
let mut enc = CabacEncoder::new();
encode_sub_mb_type_b(&mut enc, &mut ctxs_enc, v);
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut ctxs_dec = CabacContexts::init(SliceKind::B, Some(0), 26).unwrap();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
let got = decode_sub_mb_type_b(&mut dec, &mut ctxs_dec).unwrap();
assert_eq!(got, v, "B sub_mb_type {v} round-trip");
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
}
#[test]
fn rt_mb_qp_delta_values() {
for v in [-5, -1, 0, 1, 7, 12] {
roundtrip(
|enc, ctxs| encode_mb_qp_delta(enc, ctxs, false, v),
|dec, ctxs| {
let got = decode_mb_qp_delta(dec, ctxs, false).unwrap();
assert_eq!(got, v);
},
);
}
}
#[test]
fn rt_ref_idx_lx_unary() {
for v in [0u32, 1, 2, 3, 7] {
roundtrip(
|enc, ctxs| encode_ref_idx_lx(enc, ctxs, false, false, v),
|dec, ctxs| {
let got = decode_ref_idx_lx(dec, ctxs, false, false).unwrap();
assert_eq!(got, v);
},
);
}
}
#[test]
fn rt_mvd_lx_uneg3() {
for v in [-15, -1, 0, 1, 8, 9, 16, 100] {
roundtrip(
|enc, ctxs| encode_mvd_lx(enc, ctxs, MvdComponent::X, 0, v),
|dec, ctxs| {
let got = decode_mvd_lx(dec, ctxs, MvdComponent::X, 0).unwrap();
assert_eq!(got, v);
},
);
}
}
#[test]
fn rt_intra_chroma_pred_mode() {
for v in 0u32..=3 {
let nb = NeighbourCtx::default();
roundtrip(
|enc, ctxs| encode_intra_chroma_pred_mode(enc, ctxs, &nb, v),
|dec, ctxs| {
let got = decode_intra_chroma_pred_mode(dec, ctxs, &nb).unwrap();
assert_eq!(got, v);
},
);
}
}
#[test]
fn rt_coded_block_pattern_intra_420() {
for cbp_l in [0u8, 1, 5, 15] {
for cbp_c in [0u8, 1, 2] {
let nb = NeighbourCtx::default();
let mut ctxs_enc = CabacContexts::init(SliceKind::I, None, 26).unwrap();
let mut enc = CabacEncoder::new();
encode_coded_block_pattern(&mut enc, &mut ctxs_enc, &nb, 1, cbp_l, cbp_c);
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut ctxs_dec = CabacContexts::init(SliceKind::I, None, 26).unwrap();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
let got = decode_coded_block_pattern(&mut dec, &mut ctxs_dec, &nb, 1).unwrap();
let expected = (cbp_l as u32) | ((cbp_c as u32) << 4);
assert_eq!(got, expected, "cbp luma={cbp_l} chroma={cbp_c}");
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
}
}
#[test]
fn rt_coded_block_flag() {
for &flag in &[false, true] {
roundtrip(
|enc, ctxs| {
encode_coded_block_flag(
enc,
ctxs,
BlockType::Luma4x4,
Some(false),
Some(false),
flag,
)
},
|dec, ctxs| {
let got = decode_coded_block_flag(
dec,
ctxs,
BlockType::Luma4x4,
Some(false),
Some(false),
)
.unwrap();
assert_eq!(got, flag);
},
);
}
}
#[test]
fn rt_significant_and_last_coeff_flag() {
for &sig in &[false, true] {
for &last in &[false, true] {
roundtrip(
|enc, ctxs| {
encode_significant_coeff_flag(
enc,
ctxs,
BlockType::Luma4x4,
3,
false,
1,
sig,
);
encode_last_significant_coeff_flag(
enc,
ctxs,
BlockType::Luma4x4,
3,
false,
1,
last,
);
},
|dec, ctxs| {
let s = decode_significant_coeff_flag(
dec,
ctxs,
BlockType::Luma4x4,
3,
false,
1,
)
.unwrap();
assert_eq!(s, sig);
let l = decode_last_significant_coeff_flag(
dec,
ctxs,
BlockType::Luma4x4,
3,
false,
1,
)
.unwrap();
assert_eq!(l, last);
},
);
}
}
}
#[test]
fn rt_coeff_abs_level_minus1_small() {
for v in [0u32, 1, 5, 13, 14, 15, 30, 100] {
roundtrip(
|enc, ctxs| {
encode_coeff_abs_level_minus1(enc, ctxs, BlockType::Luma4x4, 0, 0, v);
},
|dec, ctxs| {
let got =
decode_coeff_abs_level_minus1(dec, ctxs, BlockType::Luma4x4, 0, 0).unwrap();
assert_eq!(got, v, "value {v}: decoder returned {got}");
},
);
}
}
#[test]
fn rt_coeff_sign_flag_bypass() {
for &neg in &[false, true] {
roundtrip(
|enc, _ctxs| encode_coeff_sign_flag(enc, neg),
|dec, _ctxs| {
let got = decode_coeff_sign_flag(dec).unwrap();
assert_eq!(got, neg);
},
);
}
}
#[test]
fn rt_end_of_slice_flag() {
let mut ctxs_enc = CabacContexts::init(SliceKind::I, None, 26).unwrap();
let mut enc = CabacEncoder::new();
encode_end_of_slice_flag(&mut enc, false);
encode_mb_type_i(&mut enc, &mut ctxs_enc, &NeighbourCtx::default(), 0);
encode_end_of_slice_flag(&mut enc, true);
let bytes = enc.finish_no_trailing();
let mut ctxs_dec = CabacContexts::init(SliceKind::I, None, 26).unwrap();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
assert!(!decode_end_of_slice_flag(&mut dec).unwrap());
let mb_type = decode_mb_type_i(&mut dec, &mut ctxs_dec, &NeighbourCtx::default()).unwrap();
assert_eq!(mb_type, 0);
assert!(decode_end_of_slice_flag(&mut dec).unwrap());
}
fn rt_residual_block(coeffs: &[i32], block_type: BlockType, max_num_coeff: u32) {
rt_residual_block_opts(coeffs, block_type, max_num_coeff, false, 1);
}
fn rt_residual_block_opts(
coeffs: &[i32],
block_type: BlockType,
max_num_coeff: u32,
skip_cbf: bool,
num_c8x8: u32,
) {
let mut ctxs_enc = CabacContexts::init(SliceKind::I, None, 26).unwrap();
let mut enc = CabacEncoder::new();
encode_residual_block_cabac(
&mut enc,
&mut ctxs_enc,
block_type,
coeffs,
max_num_coeff,
Some(false),
Some(false),
skip_cbf,
num_c8x8,
);
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut ctxs_dec = CabacContexts::init(SliceKind::I, None, 26).unwrap();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
let any_nz = coeffs.iter().any(|&c| c != 0);
let coded = if skip_cbf {
assert!(any_nz, "skip_cbf blocks must carry a non-zero level");
true
} else {
decode_coded_block_flag(
&mut dec,
&mut ctxs_dec,
block_type,
Some(false),
Some(false),
)
.unwrap()
};
assert_eq!(coded, any_nz);
if !any_nz {
assert_eq!(dec.decode_terminate().unwrap(), 1);
return;
}
let span = max_num_coeff;
let mut significant = vec![false; max_num_coeff as usize];
let mut num_coeff_in_scan = span;
let mut i: u32 = 0;
while i + 1 < num_coeff_in_scan {
let sig = decode_significant_coeff_flag(
&mut dec,
&mut ctxs_dec,
block_type,
i,
false,
num_c8x8,
)
.unwrap();
significant[i as usize] = sig;
if sig {
let last = decode_last_significant_coeff_flag(
&mut dec,
&mut ctxs_dec,
block_type,
i,
false,
num_c8x8,
)
.unwrap();
if last {
num_coeff_in_scan = i + 1;
break;
}
}
i += 1;
}
significant[(num_coeff_in_scan - 1) as usize] = true;
let mut decoded = vec![0i32; max_num_coeff as usize];
let mut num_eq1 = 0u32;
let mut num_gt1 = 0u32;
for pos in (0..num_coeff_in_scan).rev() {
if !significant[pos as usize] {
continue;
}
let abs_m1 = decode_coeff_abs_level_minus1(
&mut dec,
&mut ctxs_dec,
block_type,
num_eq1,
num_gt1,
)
.unwrap();
let sign = decode_coeff_sign_flag(&mut dec).unwrap();
decoded[pos as usize] = ((abs_m1 + 1) as i32) * if sign { -1 } else { 1 };
if abs_m1 == 0 {
num_eq1 += 1;
} else {
num_gt1 += 1;
}
}
assert_eq!(decoded, coeffs);
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
#[test]
fn rt_residual_block_all_zero() {
rt_residual_block(&[0i32; 16], BlockType::Luma4x4, 16);
}
#[test]
fn rt_residual_block_single_dc() {
let mut c = [0i32; 16];
c[0] = 5;
rt_residual_block(&c, BlockType::Luma4x4, 16);
}
#[test]
fn rt_residual_block_dc_plus_ac() {
let mut c = [0i32; 16];
c[0] = 12;
c[5] = -3;
c[10] = 2;
rt_residual_block(&c, BlockType::Luma4x4, 16);
}
#[test]
fn rt_residual_block_last_pos_set() {
let mut c = [0i32; 16];
c[0] = 1;
c[15] = -1;
rt_residual_block(&c, BlockType::Luma4x4, 16);
}
#[test]
fn rt_residual_block_large_levels() {
let mut c = [0i32; 16];
c[0] = 100;
c[3] = -50;
rt_residual_block(&c, BlockType::Luma4x4, 16);
}
#[test]
fn rt_residual_block_chroma_dc_422() {
let mut c = [0i32; 8];
c[0] = 3;
c[2] = -1;
c[5] = 2;
rt_residual_block_opts(&c, BlockType::ChromaDc, 8, false, 2);
let mut c2 = [0i32; 8];
c2[0] = 1;
c2[7] = -1;
rt_residual_block_opts(&c2, BlockType::ChromaDc, 8, false, 2);
rt_residual_block_opts(&[0i32; 8], BlockType::ChromaDc, 8, false, 2);
}
#[test]
fn rt_residual_block_8x8_single_dc() {
let mut c = [0i32; 64];
c[0] = 7;
rt_residual_block_opts(&c, BlockType::Luma8x8, 64, true, 1);
}
#[test]
fn rt_residual_block_8x8_sparse() {
let mut c = [0i32; 64];
c[0] = 12;
c[5] = -3;
c[17] = 2;
c[30] = -1;
c[44] = 1;
rt_residual_block_opts(&c, BlockType::Luma8x8, 64, true, 1);
}
#[test]
fn rt_residual_block_8x8_last_pos_63() {
let mut c = [0i32; 64];
c[0] = 1;
c[63] = -1;
rt_residual_block_opts(&c, BlockType::Luma8x8, 64, true, 1);
}
#[test]
fn rt_residual_block_8x8_dense_large_levels() {
let mut c = [0i32; 64];
for (i, v) in c.iter_mut().enumerate() {
*v = match i % 4 {
0 => 1,
1 => -2,
2 => 20,
_ => -1,
};
}
c[0] = 200;
rt_residual_block_opts(&c, BlockType::Luma8x8, 64, true, 1);
}
#[test]
fn rt_transform_size_8x8_flag_all_neighbour_combos() {
for &flag in &[false, true] {
for &(la, lt) in &[(false, false), (true, false), (true, true)] {
for &(aa, at) in &[(false, false), (true, false), (true, true)] {
let nb = NeighbourCtx {
available_left: la,
left_transform_8x8: lt,
available_above: aa,
above_transform_8x8: at,
..NeighbourCtx::default()
};
let mut ctxs_enc = CabacContexts::init(SliceKind::P, Some(0), 26).unwrap();
let mut enc = CabacEncoder::new();
encode_transform_size_8x8_flag(&mut enc, &mut ctxs_enc, &nb, flag);
enc.encode_terminate(1);
let bytes = enc.finish_no_trailing();
let mut ctxs_dec = CabacContexts::init(SliceKind::P, Some(0), 26).unwrap();
let mut dec = CabacDecoder::new(BitReader::new(&bytes)).unwrap();
let got = crate::cabac_ctx::decode_transform_size_8x8_flag(
&mut dec,
&mut ctxs_dec,
&nb,
)
.unwrap();
assert_eq!(got, flag, "flag={flag} la={la}/{lt} aa={aa}/{at}");
assert_eq!(dec.decode_terminate().unwrap(), 1);
}
}
}
}
#[test]
fn rt_intra4x4_pred_mode_flags() {
roundtrip(
|enc, ctxs| {
encode_prev_intra_pred_mode_flag(enc, ctxs, true);
encode_prev_intra_pred_mode_flag(enc, ctxs, false);
encode_rem_intra_pred_mode(enc, ctxs, 5);
},
|dec, ctxs| {
assert!(decode_prev_intra_pred_mode_flag(dec, ctxs).unwrap());
assert!(!decode_prev_intra_pred_mode_flag(dec, ctxs).unwrap());
let v = decode_rem_intra_pred_mode(dec, ctxs).unwrap();
assert_eq!(v, 5);
},
);
}
}