use crate::cavlc::CoeffTokenContext;
use crate::encoder::bitstream::BitWriter;
use crate::encoder::cavlc::{encode_residual_block_cavlc, CavlcEncodeError};
use crate::encoder::transform::zigzag_scan_4x4;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ChromaWriteKind<'a> {
Mono,
Yuv420 {
chroma_dc_cb: &'a [i32; 4],
chroma_dc_cr: &'a [i32; 4],
chroma_ac_cb: &'a [[i32; 16]; 4],
chroma_ac_cr: &'a [[i32; 16]; 4],
cb_ac_nc: &'a [i32; 8],
cr_ac_nc: &'a [i32; 8],
},
Yuv422 {
chroma_dc_cb: &'a [i32; 8],
chroma_dc_cr: &'a [i32; 8],
chroma_ac_cb: &'a [[i32; 16]; 8],
chroma_ac_cr: &'a [[i32; 16]; 8],
cb_ac_nc: &'a [i32; 8],
cr_ac_nc: &'a [i32; 8],
},
Yuv444 {
cb_dc_raster: &'a [i32; 16],
cr_dc_raster: &'a [i32; 16],
cb_ac_levels: &'a [[i32; 16]; 16],
cr_ac_levels: &'a [[i32; 16]; 16],
cb_ac_nc: &'a [i32; 16],
cr_ac_nc: &'a [i32; 16],
cb_dc_nc: i32,
cr_dc_nc: i32,
cbp_luma_for_ac_gate: u8,
},
Yuv444Inter {
cb_levels: &'a [[i32; 16]; 16],
cr_levels: &'a [[i32; 16]; 16],
cb_nc: &'a [i32; 16],
cr_nc: &'a [i32; 16],
cbp_luma: u8,
},
}
impl<'a> ChromaWriteKind<'a> {
pub fn emit(self, w: &mut BitWriter, cbp_chroma: u8) -> Result<(), CavlcEncodeError> {
debug_assert!(cbp_chroma <= 2);
match self {
ChromaWriteKind::Mono => {
debug_assert_eq!(cbp_chroma, 0, "no chroma CBP at ChromaArrayType 0");
}
ChromaWriteKind::Yuv420 {
chroma_dc_cb,
chroma_dc_cr,
chroma_ac_cb,
chroma_ac_cr,
cb_ac_nc,
cr_ac_nc,
} => {
if cbp_chroma > 0 {
encode_residual_block_cavlc(
w,
CoeffTokenContext::ChromaDc420,
4,
chroma_dc_cb,
)?;
encode_residual_block_cavlc(
w,
CoeffTokenContext::ChromaDc420,
4,
chroma_dc_cr,
)?;
}
if cbp_chroma == 2 {
for (k, blk) in chroma_ac_cb.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cb_ac_nc[k]),
15,
&blk[..15],
)?;
}
for (k, blk) in chroma_ac_cr.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cr_ac_nc[k]),
15,
&blk[..15],
)?;
}
}
}
ChromaWriteKind::Yuv422 {
chroma_dc_cb,
chroma_dc_cr,
chroma_ac_cb,
chroma_ac_cr,
cb_ac_nc,
cr_ac_nc,
} => {
if cbp_chroma > 0 {
encode_residual_block_cavlc(
w,
CoeffTokenContext::ChromaDc422,
8,
chroma_dc_cb,
)?;
encode_residual_block_cavlc(
w,
CoeffTokenContext::ChromaDc422,
8,
chroma_dc_cr,
)?;
}
if cbp_chroma == 2 {
for (k, blk) in chroma_ac_cb.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cb_ac_nc[k]),
15,
&blk[..15],
)?;
}
for (k, blk) in chroma_ac_cr.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cr_ac_nc[k]),
15,
&blk[..15],
)?;
}
}
}
ChromaWriteKind::Yuv444 {
cb_dc_raster,
cr_dc_raster,
cb_ac_levels,
cr_ac_levels,
cb_ac_nc,
cr_ac_nc,
cb_dc_nc,
cr_dc_nc,
cbp_luma_for_ac_gate,
} => {
let _ = cbp_chroma;
let scan_cb = zigzag_scan_4x4(cb_dc_raster);
encode_residual_block_cavlc(w, CoeffTokenContext::Numeric(cb_dc_nc), 16, &scan_cb)?;
if cbp_luma_for_ac_gate == 15 {
for (blk_idx, blk) in cb_ac_levels.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cb_ac_nc[blk_idx]),
15,
&blk[..15],
)?;
}
}
let scan_cr = zigzag_scan_4x4(cr_dc_raster);
encode_residual_block_cavlc(w, CoeffTokenContext::Numeric(cr_dc_nc), 16, &scan_cr)?;
if cbp_luma_for_ac_gate == 15 {
for (blk_idx, blk) in cr_ac_levels.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cr_ac_nc[blk_idx]),
15,
&blk[..15],
)?;
}
}
}
ChromaWriteKind::Yuv444Inter {
cb_levels,
cr_levels,
cb_nc,
cr_nc,
cbp_luma,
} => {
let _ = cbp_chroma;
for (levels, ncs) in [(cb_levels, cb_nc), (cr_levels, cr_nc)] {
for blk8 in 0..4u8 {
if (cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(ncs[blk]),
16,
&levels[blk],
)?;
}
}
}
}
}
}
Ok(())
}
}
const INTRA_CBP_TO_CODENUM_420_422: [u8; 48] = {
const FWD: [u8; 48] = [
47, 31, 15, 0, 23, 27, 29, 30, 7, 11, 13, 14, 39, 43, 45, 46, 16, 3, 5, 10, 12, 19, 21, 26,
28, 35, 37, 42, 44, 1, 2, 4, 8, 17, 18, 20, 24, 6, 9, 22, 25, 32, 33, 34, 36, 40, 38, 41,
];
let mut inv = [0u8; 48];
let mut i = 0;
while i < 48 {
inv[FWD[i] as usize] = i as u8;
i += 1;
}
inv
};
pub fn intra_cbp_to_codenum_420_422(cbp_luma: u8, cbp_chroma: u8) -> u32 {
debug_assert!(cbp_luma <= 15);
debug_assert!(cbp_chroma <= 2);
let cbp = ((cbp_chroma as usize) << 4) | (cbp_luma as usize);
INTRA_CBP_TO_CODENUM_420_422[cbp] as u32
}
const INTRA_CBP_TO_CODENUM_0_3: [u8; 16] = {
const FWD: [u8; 16] = [15, 0, 7, 11, 13, 14, 3, 5, 10, 12, 1, 2, 4, 8, 6, 9];
let mut inv = [0u8; 16];
let mut i = 0;
while i < 16 {
inv[FWD[i] as usize] = i as u8;
i += 1;
}
inv
};
pub fn intra_cbp_to_codenum_0_3(cbp_luma: u8) -> u32 {
debug_assert!(cbp_luma <= 15);
INTRA_CBP_TO_CODENUM_0_3[cbp_luma as usize] as u32
}
#[allow(clippy::too_many_arguments)]
pub fn write_i8x8_mb_444(
w: &mut BitWriter,
prev_intra8x8_pred_mode_flag: &[bool; 4],
rem_intra8x8_pred_mode: &[u8; 4],
cbp_luma: u8,
mb_qp_delta: i32,
luma_4x4_levels: &[[i32; 16]; 16],
luma_4x4_nc: &[i32; 16],
cb_4x4_levels: &[[i32; 16]; 16],
cb_4x4_nc: &[i32; 16],
cr_4x4_levels: &[[i32; 16]; 16],
cr_4x4_nc: &[i32; 16],
) -> Result<(), CavlcEncodeError> {
debug_assert!(cbp_luma <= 15);
w.ue(0);
w.u(1, 1);
for blk8 in 0..4usize {
let flag = prev_intra8x8_pred_mode_flag[blk8];
w.u(1, if flag { 1 } else { 0 });
if !flag {
debug_assert!(rem_intra8x8_pred_mode[blk8] <= 7);
w.u(3, rem_intra8x8_pred_mode[blk8] as u32);
}
}
w.ue(intra_cbp_to_codenum_0_3(cbp_luma));
if cbp_luma > 0 {
w.se(mb_qp_delta);
}
for (levels, ncs) in [
(luma_4x4_levels, luma_4x4_nc),
(cb_4x4_levels, cb_4x4_nc),
(cr_4x4_levels, cr_4x4_nc),
] {
for blk8 in 0..4u8 {
if (cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(ncs[blk]),
16,
&levels[blk],
)?;
}
}
}
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn write_i4x4_mb_mono(
w: &mut BitWriter,
emit_transform_size_8x8_zero: bool,
prev_intra4x4_pred_mode_flag: &[bool; 16],
rem_intra4x4_pred_mode: &[u8; 16],
cbp_luma: u8,
mb_qp_delta: i32,
luma_4x4_levels: &[[i32; 16]; 16],
luma_4x4_nc: &[i32; 16],
) -> Result<(), CavlcEncodeError> {
debug_assert!(cbp_luma <= 15);
w.ue(0);
if emit_transform_size_8x8_zero {
w.u(1, 0);
}
for blk in 0..16usize {
let flag = prev_intra4x4_pred_mode_flag[blk];
w.u(1, if flag { 1 } else { 0 });
if !flag {
debug_assert!(rem_intra4x4_pred_mode[blk] <= 7);
w.u(3, rem_intra4x4_pred_mode[blk] as u32);
}
}
w.ue(intra_cbp_to_codenum_0_3(cbp_luma));
if cbp_luma > 0 {
w.se(mb_qp_delta);
}
for blk8 in 0..4u8 {
if (cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(luma_4x4_nc[blk]),
16,
&luma_4x4_levels[blk],
)?;
}
}
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn write_i4x4_mb_444(
w: &mut BitWriter,
emit_transform_size_8x8_zero: bool,
prev_intra4x4_pred_mode_flag: &[bool; 16],
rem_intra4x4_pred_mode: &[u8; 16],
cbp_luma: u8,
mb_qp_delta: i32,
luma_4x4_levels: &[[i32; 16]; 16],
luma_4x4_nc: &[i32; 16],
cb_4x4_levels: &[[i32; 16]; 16],
cb_4x4_nc: &[i32; 16],
cr_4x4_levels: &[[i32; 16]; 16],
cr_4x4_nc: &[i32; 16],
) -> Result<(), CavlcEncodeError> {
debug_assert!(cbp_luma <= 15);
w.ue(0);
if emit_transform_size_8x8_zero {
w.u(1, 0);
}
for blk in 0..16usize {
let flag = prev_intra4x4_pred_mode_flag[blk];
w.u(1, if flag { 1 } else { 0 });
if !flag {
debug_assert!(rem_intra4x4_pred_mode[blk] <= 7);
w.u(3, rem_intra4x4_pred_mode[blk] as u32);
}
}
w.ue(intra_cbp_to_codenum_0_3(cbp_luma));
if cbp_luma > 0 {
w.se(mb_qp_delta);
}
for (levels, ncs) in [
(luma_4x4_levels, luma_4x4_nc),
(cb_4x4_levels, cb_4x4_nc),
(cr_4x4_levels, cr_4x4_nc),
] {
for blk8 in 0..4u8 {
if (cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(ncs[blk]),
16,
&levels[blk],
)?;
}
}
}
}
Ok(())
}
pub struct I16x16McbConfig {
pub pred_mode: u8,
pub intra_chroma_pred_mode: u8,
pub cbp_luma: u8,
pub cbp_chroma: u8,
pub mb_qp_delta: i32,
pub luma_dc_levels_raster: [i32; 16],
pub luma_ac_levels: [[i32; 16]; 16],
pub luma_ac_nc: [i32; 16],
pub chroma_dc_cb: [i32; 4],
pub chroma_dc_cr: [i32; 4],
pub chroma_ac_cb: [[i32; 16]; 4],
pub chroma_ac_cr: [[i32; 16]; 4],
pub chroma_ac_nc_cb: [i32; 8],
pub chroma_ac_nc_cr: [i32; 8],
}
pub fn intra16x16_mb_type_value(pred_mode: u8, cbp_luma: u8, cbp_chroma: u8) -> u32 {
debug_assert!(pred_mode <= 3);
debug_assert!(cbp_luma == 0 || cbp_luma == 15);
debug_assert!(cbp_chroma <= 2);
let group = match (cbp_luma, cbp_chroma) {
(0, 0) => 0u32,
(0, 1) => 1,
(0, 2) => 2,
(15, 0) => 3,
(15, 1) => 4,
(15, 2) => 5,
_ => unreachable!("invalid (cbp_luma, cbp_chroma) for Intra_16x16"),
};
1 + group * 4 + pred_mode as u32
}
#[allow(clippy::too_many_arguments)]
pub fn write_intra16x16_mb_chroma(
w: &mut BitWriter,
pred_mode: u8,
intra_chroma_pred_mode: u8,
cbp_luma: u8,
cbp_chroma: u8,
mb_qp_delta: i32,
luma_dc_levels_raster: &[i32; 16],
luma_ac_levels: &[[i32; 16]; 16],
luma_ac_nc: &[i32; 16],
chroma: ChromaWriteKind<'_>,
nc_ctx: CoeffTokenContext,
) -> Result<(), CavlcEncodeError> {
let raw = intra16x16_mb_type_value(pred_mode, cbp_luma, cbp_chroma);
w.ue(raw);
if !matches!(
chroma,
ChromaWriteKind::Yuv444 { .. } | ChromaWriteKind::Mono
) {
w.ue(intra_chroma_pred_mode as u32);
}
w.se(mb_qp_delta);
let scan = zigzag_scan_4x4(luma_dc_levels_raster);
encode_residual_block_cavlc(w, nc_ctx, 16, &scan)?;
if cbp_luma == 15 {
for blk in 0..16usize {
let nc = luma_ac_nc[blk];
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(nc),
15,
&luma_ac_levels[blk][..15],
)?;
}
}
chroma.emit(w, cbp_chroma)?;
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn write_intra16x16_mb_in_inter_slice_chroma(
w: &mut BitWriter,
mb_type_offset: u32,
pred_mode: u8,
intra_chroma_pred_mode: u8,
cbp_luma: u8,
cbp_chroma: u8,
mb_qp_delta: i32,
luma_dc_levels_raster: &[i32; 16],
luma_ac_levels: &[[i32; 16]; 16],
luma_ac_nc: &[i32; 16],
chroma: ChromaWriteKind<'_>,
nc_ctx: CoeffTokenContext,
) -> Result<(), CavlcEncodeError> {
debug_assert!(
mb_type_offset == 5 || mb_type_offset == 23,
"mb_type_offset must be 5 (P-slice) or 23 (B-slice); got {mb_type_offset}",
);
let raw = mb_type_offset + intra16x16_mb_type_value(pred_mode, cbp_luma, cbp_chroma);
w.ue(raw);
if !matches!(
chroma,
ChromaWriteKind::Yuv444 { .. } | ChromaWriteKind::Mono
) {
w.ue(intra_chroma_pred_mode as u32);
}
w.se(mb_qp_delta);
let scan = zigzag_scan_4x4(luma_dc_levels_raster);
encode_residual_block_cavlc(w, nc_ctx, 16, &scan)?;
if cbp_luma == 15 {
for blk in 0..16usize {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(luma_ac_nc[blk]),
15,
&luma_ac_levels[blk][..15],
)?;
}
}
chroma.emit(w, cbp_chroma)?;
Ok(())
}
pub fn write_intra16x16_mb(
w: &mut BitWriter,
cfg: &I16x16McbConfig,
nc_ctx: CoeffTokenContext,
) -> Result<(), CavlcEncodeError> {
let raw = intra16x16_mb_type_value(cfg.pred_mode, cfg.cbp_luma, cfg.cbp_chroma);
w.ue(raw);
w.ue(cfg.intra_chroma_pred_mode as u32);
w.se(cfg.mb_qp_delta);
let scan = zigzag_scan_4x4(&cfg.luma_dc_levels_raster);
encode_residual_block_cavlc(w, nc_ctx, 16, &scan)?;
if cfg.cbp_luma == 15 {
for blk in 0..16usize {
let nc = cfg.luma_ac_nc[blk];
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(nc),
15,
&cfg.luma_ac_levels[blk][..15],
)?;
}
}
if cfg.cbp_chroma > 0 {
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cb)?;
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cr)?;
}
if cfg.cbp_chroma == 2 {
for (k, blk) in cfg.chroma_ac_cb.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cb[k]),
15,
&blk[..15],
)?;
}
for (k, blk) in cfg.chroma_ac_cr.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cr[k]),
15,
&blk[..15],
)?;
}
}
Ok(())
}
pub fn write_intra16x16_mb_in_inter_slice(
w: &mut BitWriter,
cfg: &I16x16McbConfig,
mb_type_offset: u32,
nc_ctx: CoeffTokenContext,
) -> Result<(), CavlcEncodeError> {
debug_assert!(
mb_type_offset == 5 || mb_type_offset == 23,
"mb_type_offset must be 5 (P-slice) or 23 (B-slice); got {mb_type_offset}",
);
let raw =
mb_type_offset + intra16x16_mb_type_value(cfg.pred_mode, cfg.cbp_luma, cfg.cbp_chroma);
w.ue(raw);
w.ue(cfg.intra_chroma_pred_mode as u32);
w.se(cfg.mb_qp_delta);
let scan = zigzag_scan_4x4(&cfg.luma_dc_levels_raster);
encode_residual_block_cavlc(w, nc_ctx, 16, &scan)?;
if cfg.cbp_luma == 15 {
for blk in 0..16usize {
let nc = cfg.luma_ac_nc[blk];
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(nc),
15,
&cfg.luma_ac_levels[blk][..15],
)?;
}
}
if cfg.cbp_chroma > 0 {
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cb)?;
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cr)?;
}
if cfg.cbp_chroma == 2 {
for (k, blk) in cfg.chroma_ac_cb.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cb[k]),
15,
&blk[..15],
)?;
}
for (k, blk) in cfg.chroma_ac_cr.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cr[k]),
15,
&blk[..15],
)?;
}
}
Ok(())
}
pub struct INxNMcbConfig {
pub emit_transform_size_8x8_zero: bool,
pub prev_intra4x4_pred_mode_flag: [bool; 16],
pub rem_intra4x4_pred_mode: [u8; 16],
pub intra_chroma_pred_mode: u8,
pub cbp_luma: u8,
pub cbp_chroma: u8,
pub mb_qp_delta: i32,
pub luma_4x4_levels: [[i32; 16]; 16],
pub luma_4x4_nc: [i32; 16],
pub chroma_dc_cb: [i32; 4],
pub chroma_dc_cr: [i32; 4],
pub chroma_ac_cb: [[i32; 16]; 4],
pub chroma_ac_cr: [[i32; 16]; 4],
pub chroma_ac_nc_cb: [i32; 8],
pub chroma_ac_nc_cr: [i32; 8],
}
#[allow(clippy::too_many_arguments)]
pub fn write_i_nxn_mb_chroma(
w: &mut BitWriter,
emit_transform_size_8x8_zero: bool,
prev_intra4x4_pred_mode_flag: &[bool; 16],
rem_intra4x4_pred_mode: &[u8; 16],
intra_chroma_pred_mode: u8,
cbp_luma: u8,
cbp_chroma: u8,
mb_qp_delta: i32,
luma_4x4_levels: &[[i32; 16]; 16],
luma_4x4_nc: &[i32; 16],
chroma: ChromaWriteKind<'_>,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cbp_luma <= 15);
debug_assert!(cbp_chroma <= 2);
debug_assert!(intra_chroma_pred_mode <= 3);
w.ue(0);
if emit_transform_size_8x8_zero {
w.u(1, 0);
}
for blk in 0..16usize {
let flag = prev_intra4x4_pred_mode_flag[blk];
w.u(1, if flag { 1 } else { 0 });
if !flag {
debug_assert!(rem_intra4x4_pred_mode[blk] <= 7);
w.u(3, rem_intra4x4_pred_mode[blk] as u32);
}
}
w.ue(intra_chroma_pred_mode as u32);
let codenum = intra_cbp_to_codenum_420_422(cbp_luma, cbp_chroma);
w.ue(codenum);
let needs_qp_delta = cbp_luma > 0 || cbp_chroma > 0;
if needs_qp_delta {
w.se(mb_qp_delta);
}
for blk8 in 0..4u8 {
if (cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
let nc = luma_4x4_nc[blk];
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(nc),
16,
&luma_4x4_levels[blk],
)?;
}
}
}
chroma.emit(w, cbp_chroma)?;
Ok(())
}
pub fn write_i_nxn_mb(w: &mut BitWriter, cfg: &INxNMcbConfig) -> Result<(), CavlcEncodeError> {
debug_assert!(cfg.cbp_luma <= 15);
debug_assert!(cfg.cbp_chroma <= 2);
debug_assert!(cfg.intra_chroma_pred_mode <= 3);
w.ue(0);
if cfg.emit_transform_size_8x8_zero {
w.u(1, 0);
}
for blk in 0..16usize {
let flag = cfg.prev_intra4x4_pred_mode_flag[blk];
w.u(1, if flag { 1 } else { 0 });
if !flag {
debug_assert!(cfg.rem_intra4x4_pred_mode[blk] <= 7);
w.u(3, cfg.rem_intra4x4_pred_mode[blk] as u32);
}
}
w.ue(cfg.intra_chroma_pred_mode as u32);
let codenum = intra_cbp_to_codenum_420_422(cfg.cbp_luma, cfg.cbp_chroma);
w.ue(codenum);
let needs_qp_delta = cfg.cbp_luma > 0 || cfg.cbp_chroma > 0;
if needs_qp_delta {
w.se(cfg.mb_qp_delta);
}
for blk8 in 0..4u8 {
if (cfg.cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
let nc = cfg.luma_4x4_nc[blk];
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(nc),
16,
&cfg.luma_4x4_levels[blk],
)?;
}
}
}
if cfg.cbp_chroma > 0 {
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cb)?;
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cr)?;
}
if cfg.cbp_chroma == 2 {
for (k, blk) in cfg.chroma_ac_cb.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cb[k]),
15,
&blk[..15],
)?;
}
for (k, blk) in cfg.chroma_ac_cr.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cr[k]),
15,
&blk[..15],
)?;
}
}
Ok(())
}
pub struct I8x8McbConfig {
pub prev_intra8x8_pred_mode_flag: [bool; 4],
pub rem_intra8x8_pred_mode: [u8; 4],
pub intra_chroma_pred_mode: u8,
pub cbp_luma: u8,
pub cbp_chroma: u8,
pub mb_qp_delta: i32,
pub luma_4x4_levels: [[i32; 16]; 16],
pub luma_4x4_nc: [i32; 16],
pub chroma_dc_cb: [i32; 4],
pub chroma_dc_cr: [i32; 4],
pub chroma_ac_cb: [[i32; 16]; 4],
pub chroma_ac_cr: [[i32; 16]; 4],
pub chroma_ac_nc_cb: [i32; 8],
pub chroma_ac_nc_cr: [i32; 8],
}
pub fn write_i8x8_mb(w: &mut BitWriter, cfg: &I8x8McbConfig) -> Result<(), CavlcEncodeError> {
write_i8x8_mb_chroma(
w,
&cfg.prev_intra8x8_pred_mode_flag,
&cfg.rem_intra8x8_pred_mode,
cfg.intra_chroma_pred_mode,
cfg.cbp_luma,
cfg.cbp_chroma,
cfg.mb_qp_delta,
&cfg.luma_4x4_levels,
&cfg.luma_4x4_nc,
ChromaWriteKind::Yuv420 {
chroma_dc_cb: &cfg.chroma_dc_cb,
chroma_dc_cr: &cfg.chroma_dc_cr,
chroma_ac_cb: &cfg.chroma_ac_cb,
chroma_ac_cr: &cfg.chroma_ac_cr,
cb_ac_nc: &cfg.chroma_ac_nc_cb,
cr_ac_nc: &cfg.chroma_ac_nc_cr,
},
)
}
#[allow(clippy::too_many_arguments)]
pub fn write_i8x8_mb_chroma(
w: &mut BitWriter,
prev_intra8x8_pred_mode_flag: &[bool; 4],
rem_intra8x8_pred_mode: &[u8; 4],
intra_chroma_pred_mode: u8,
cbp_luma: u8,
cbp_chroma: u8,
mb_qp_delta: i32,
luma_4x4_levels: &[[i32; 16]; 16],
luma_4x4_nc: &[i32; 16],
chroma: ChromaWriteKind<'_>,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cbp_luma <= 15);
debug_assert!(cbp_chroma <= 2);
debug_assert!(intra_chroma_pred_mode <= 3);
w.ue(0);
w.u(1, 1);
for blk8 in 0..4usize {
let flag = prev_intra8x8_pred_mode_flag[blk8];
w.u(1, if flag { 1 } else { 0 });
if !flag {
debug_assert!(rem_intra8x8_pred_mode[blk8] <= 7);
w.u(3, rem_intra8x8_pred_mode[blk8] as u32);
}
}
w.ue(intra_chroma_pred_mode as u32);
let codenum = intra_cbp_to_codenum_420_422(cbp_luma, cbp_chroma);
w.ue(codenum);
let needs_qp_delta = cbp_luma > 0 || cbp_chroma > 0;
if needs_qp_delta {
w.se(mb_qp_delta);
}
for blk8 in 0..4u8 {
if (cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
let nc = luma_4x4_nc[blk];
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(nc),
16,
&luma_4x4_levels[blk],
)?;
}
}
}
chroma.emit(w, cbp_chroma)?;
Ok(())
}
const INTER_CBP_TO_CODENUM_420_422: [u8; 48] = {
const FWD: [u8; 48] = [
0, 16, 1, 2, 4, 8, 32, 3, 5, 10, 12, 15, 47, 7, 11, 13, 14, 6, 9, 31, 35, 37, 42, 44, 33,
34, 36, 40, 39, 43, 45, 46, 17, 18, 20, 24, 19, 21, 26, 28, 23, 27, 29, 30, 22, 25, 38, 41,
];
let mut inv = [0u8; 48];
let mut i = 0;
while i < 48 {
inv[FWD[i] as usize] = i as u8;
i += 1;
}
inv
};
pub fn inter_cbp_to_codenum_420_422(cbp_luma: u8, cbp_chroma: u8) -> u32 {
debug_assert!(cbp_luma <= 15);
debug_assert!(cbp_chroma <= 2);
let cbp = ((cbp_chroma as usize) << 4) | (cbp_luma as usize);
INTER_CBP_TO_CODENUM_420_422[cbp] as u32
}
const INTER_CBP_TO_CODENUM_0_3: [u8; 16] = {
const FWD: [u8; 16] = [0, 1, 2, 4, 8, 3, 5, 10, 12, 15, 7, 11, 13, 14, 6, 9];
let mut inv = [0u8; 16];
let mut i = 0;
while i < 16 {
inv[FWD[i] as usize] = i as u8;
i += 1;
}
inv
};
pub fn inter_cbp_to_codenum_0_3(cbp_luma: u8) -> u32 {
debug_assert!(cbp_luma <= 15);
INTER_CBP_TO_CODENUM_0_3[cbp_luma as usize] as u32
}
#[allow(clippy::too_many_arguments)]
pub fn emit_inter_mb_tail(
w: &mut BitWriter,
chroma_array_type: u32,
cbp_luma: u8,
cbp_chroma: u8,
transform_size_8x8_flag: Option<bool>,
mb_qp_delta: i32,
luma_4x4_levels: &[[i32; 16]; 16],
luma_4x4_nc: &[i32; 16],
chroma: ChromaWriteKind<'_>,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cbp_luma <= 15);
debug_assert!(cbp_chroma <= 2);
let codenum = if chroma_array_type == 0 || chroma_array_type == 3 {
debug_assert_eq!(cbp_chroma, 0, "no chroma CBP at ChromaArrayType 0 / 3");
inter_cbp_to_codenum_0_3(cbp_luma)
} else {
inter_cbp_to_codenum_420_422(cbp_luma, cbp_chroma)
};
w.ue(codenum);
if let Some(flag) = transform_size_8x8_flag {
if cbp_luma > 0 {
w.u(1, if flag { 1 } else { 0 });
}
}
if cbp_luma > 0 || cbp_chroma > 0 {
w.se(mb_qp_delta);
}
for blk8 in 0..4u8 {
if (cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(luma_4x4_nc[blk]),
16,
&luma_4x4_levels[blk],
)?;
}
}
}
chroma.emit(w, cbp_chroma)
}
pub struct PL016x16McbConfig {
pub ref_idx_l0: u32,
pub transform_size_8x8_flag: Option<bool>,
pub mvd_l0_x: i32,
pub mvd_l0_y: i32,
pub cbp_luma: u8,
pub cbp_chroma: u8,
pub mb_qp_delta: i32,
pub luma_4x4_levels: [[i32; 16]; 16],
pub luma_4x4_nc: [i32; 16],
pub chroma_dc_cb: [i32; 4],
pub chroma_dc_cr: [i32; 4],
pub chroma_ac_cb: [[i32; 16]; 4],
pub chroma_ac_cr: [[i32; 16]; 4],
pub chroma_ac_nc_cb: [i32; 8],
pub chroma_ac_nc_cr: [i32; 8],
}
pub fn write_p_l0_16x16_mb(
w: &mut BitWriter,
cfg: &PL016x16McbConfig,
num_ref_idx_l0_active_minus1: u32,
) -> Result<(), CavlcEncodeError> {
write_p_l0_16x16_mb_chroma(
w,
cfg,
num_ref_idx_l0_active_minus1,
1,
ChromaWriteKind::Yuv420 {
chroma_dc_cb: &cfg.chroma_dc_cb,
chroma_dc_cr: &cfg.chroma_dc_cr,
chroma_ac_cb: &cfg.chroma_ac_cb,
chroma_ac_cr: &cfg.chroma_ac_cr,
cb_ac_nc: &cfg.chroma_ac_nc_cb,
cr_ac_nc: &cfg.chroma_ac_nc_cr,
},
)
}
pub fn write_p_l0_16x16_mb_chroma(
w: &mut BitWriter,
cfg: &PL016x16McbConfig,
num_ref_idx_l0_active_minus1: u32,
chroma_array_type: u32,
chroma: ChromaWriteKind<'_>,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cfg.cbp_luma <= 15);
debug_assert!(cfg.cbp_chroma <= 2);
w.ue(0);
if num_ref_idx_l0_active_minus1 > 0 {
debug_assert!(cfg.ref_idx_l0 <= num_ref_idx_l0_active_minus1);
w.te(num_ref_idx_l0_active_minus1, cfg.ref_idx_l0);
}
w.se(cfg.mvd_l0_x);
w.se(cfg.mvd_l0_y);
emit_inter_mb_tail(
w,
chroma_array_type,
cfg.cbp_luma,
cfg.cbp_chroma,
cfg.transform_size_8x8_flag,
cfg.mb_qp_delta,
&cfg.luma_4x4_levels,
&cfg.luma_4x4_nc,
chroma,
)
}
pub struct P8x8AllPL08x8McbConfig {
pub ref_idx_l0: u32,
pub emit_transform_size_8x8_zero: bool,
pub mvd_l0: [(i32, i32); 4],
pub cbp_luma: u8,
pub cbp_chroma: u8,
pub mb_qp_delta: i32,
pub luma_4x4_levels: [[i32; 16]; 16],
pub luma_4x4_nc: [i32; 16],
pub chroma_dc_cb: [i32; 4],
pub chroma_dc_cr: [i32; 4],
pub chroma_ac_cb: [[i32; 16]; 4],
pub chroma_ac_cr: [[i32; 16]; 4],
pub chroma_ac_nc_cb: [i32; 8],
pub chroma_ac_nc_cr: [i32; 8],
}
pub fn write_p_8x8_all_pl08x8_mb(
w: &mut BitWriter,
cfg: &P8x8AllPL08x8McbConfig,
num_ref_idx_l0_active_minus1: u32,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cfg.cbp_luma <= 15);
debug_assert!(cfg.cbp_chroma <= 2);
w.ue(3);
for _ in 0..4 {
w.ue(0);
}
if num_ref_idx_l0_active_minus1 > 0 {
debug_assert!(cfg.ref_idx_l0 <= num_ref_idx_l0_active_minus1);
for _ in 0..4 {
w.te(num_ref_idx_l0_active_minus1, cfg.ref_idx_l0);
}
}
for &(mvd_x, mvd_y) in cfg.mvd_l0.iter() {
w.se(mvd_x);
w.se(mvd_y);
}
let codenum = inter_cbp_to_codenum_420_422(cfg.cbp_luma, cfg.cbp_chroma);
w.ue(codenum);
if cfg.emit_transform_size_8x8_zero && cfg.cbp_luma > 0 {
w.u(1, 0);
}
let needs_qp_delta = cfg.cbp_luma > 0 || cfg.cbp_chroma > 0;
if needs_qp_delta {
w.se(cfg.mb_qp_delta);
}
for blk8 in 0..4u8 {
if (cfg.cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
let nc = cfg.luma_4x4_nc[blk];
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(nc),
16,
&cfg.luma_4x4_levels[blk],
)?;
}
}
}
if cfg.cbp_chroma > 0 {
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cb)?;
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cr)?;
}
if cfg.cbp_chroma == 2 {
for (k, blk) in cfg.chroma_ac_cb.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cb[k]),
15,
&blk[..15],
)?;
}
for (k, blk) in cfg.chroma_ac_cr.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cr[k]),
15,
&blk[..15],
)?;
}
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BPred16x16 {
L0,
L1,
Bi,
}
impl BPred16x16 {
pub fn mb_type_raw(self) -> u32 {
match self {
BPred16x16::L0 => 1,
BPred16x16::L1 => 2,
BPred16x16::Bi => 3,
}
}
pub fn uses_l0(self) -> bool {
matches!(self, BPred16x16::L0 | BPred16x16::Bi)
}
pub fn uses_l1(self) -> bool {
matches!(self, BPred16x16::L1 | BPred16x16::Bi)
}
}
pub struct B16x16McbConfig {
pub transform_size_8x8_flag: Option<bool>,
pub pred: BPred16x16,
pub mvd_l0_x: i32,
pub mvd_l0_y: i32,
pub mvd_l1_x: i32,
pub mvd_l1_y: i32,
pub cbp_luma: u8,
pub cbp_chroma: u8,
pub mb_qp_delta: i32,
pub luma_4x4_levels: [[i32; 16]; 16],
pub luma_4x4_nc: [i32; 16],
pub chroma_dc_cb: [i32; 4],
pub chroma_dc_cr: [i32; 4],
pub chroma_ac_cb: [[i32; 16]; 4],
pub chroma_ac_cr: [[i32; 16]; 4],
pub chroma_ac_nc_cb: [i32; 8],
pub chroma_ac_nc_cr: [i32; 8],
}
pub fn write_b_16x16_mb(
w: &mut BitWriter,
cfg: &B16x16McbConfig,
num_ref_idx_l0_active_minus1: u32,
num_ref_idx_l1_active_minus1: u32,
) -> Result<(), CavlcEncodeError> {
write_b_16x16_mb_chroma(
w,
cfg,
num_ref_idx_l0_active_minus1,
num_ref_idx_l1_active_minus1,
1,
ChromaWriteKind::Yuv420 {
chroma_dc_cb: &cfg.chroma_dc_cb,
chroma_dc_cr: &cfg.chroma_dc_cr,
chroma_ac_cb: &cfg.chroma_ac_cb,
chroma_ac_cr: &cfg.chroma_ac_cr,
cb_ac_nc: &cfg.chroma_ac_nc_cb,
cr_ac_nc: &cfg.chroma_ac_nc_cr,
},
)
}
pub fn write_b_16x16_mb_chroma(
w: &mut BitWriter,
cfg: &B16x16McbConfig,
num_ref_idx_l0_active_minus1: u32,
num_ref_idx_l1_active_minus1: u32,
chroma_array_type: u32,
chroma: ChromaWriteKind<'_>,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cfg.cbp_luma <= 15);
debug_assert!(cfg.cbp_chroma <= 2);
w.ue(cfg.pred.mb_type_raw());
if cfg.pred.uses_l0() && num_ref_idx_l0_active_minus1 > 0 {
w.te(num_ref_idx_l0_active_minus1, 0);
}
if cfg.pred.uses_l1() && num_ref_idx_l1_active_minus1 > 0 {
w.te(num_ref_idx_l1_active_minus1, 0);
}
if cfg.pred.uses_l0() {
w.se(cfg.mvd_l0_x);
w.se(cfg.mvd_l0_y);
}
if cfg.pred.uses_l1() {
w.se(cfg.mvd_l1_x);
w.se(cfg.mvd_l1_y);
}
emit_inter_mb_tail(
w,
chroma_array_type,
cfg.cbp_luma,
cfg.cbp_chroma,
cfg.transform_size_8x8_flag,
cfg.mb_qp_delta,
&cfg.luma_4x4_levels,
&cfg.luma_4x4_nc,
chroma,
)
}
pub struct BDirect16x16McbConfig {
pub transform_size_8x8_flag: Option<bool>,
pub cbp_luma: u8,
pub cbp_chroma: u8,
pub mb_qp_delta: i32,
pub luma_4x4_levels: [[i32; 16]; 16],
pub luma_4x4_nc: [i32; 16],
pub chroma_dc_cb: [i32; 4],
pub chroma_dc_cr: [i32; 4],
pub chroma_ac_cb: [[i32; 16]; 4],
pub chroma_ac_cr: [[i32; 16]; 4],
pub chroma_ac_nc_cb: [i32; 8],
pub chroma_ac_nc_cr: [i32; 8],
}
pub fn write_b_direct_16x16_mb(
w: &mut BitWriter,
cfg: &BDirect16x16McbConfig,
) -> Result<(), CavlcEncodeError> {
write_b_direct_16x16_mb_chroma(
w,
cfg,
1,
ChromaWriteKind::Yuv420 {
chroma_dc_cb: &cfg.chroma_dc_cb,
chroma_dc_cr: &cfg.chroma_dc_cr,
chroma_ac_cb: &cfg.chroma_ac_cb,
chroma_ac_cr: &cfg.chroma_ac_cr,
cb_ac_nc: &cfg.chroma_ac_nc_cb,
cr_ac_nc: &cfg.chroma_ac_nc_cr,
},
)
}
pub fn write_b_direct_16x16_mb_chroma(
w: &mut BitWriter,
cfg: &BDirect16x16McbConfig,
chroma_array_type: u32,
chroma: ChromaWriteKind<'_>,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cfg.cbp_luma <= 15);
debug_assert!(cfg.cbp_chroma <= 2);
w.ue(0);
emit_inter_mb_tail(
w,
chroma_array_type,
cfg.cbp_luma,
cfg.cbp_chroma,
cfg.transform_size_8x8_flag,
cfg.mb_qp_delta,
&cfg.luma_4x4_levels,
&cfg.luma_4x4_nc,
chroma,
)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BPartPred {
L0,
L1,
Bi,
}
impl BPartPred {
pub fn uses_l0(self) -> bool {
matches!(self, BPartPred::L0 | BPartPred::Bi)
}
pub fn uses_l1(self) -> bool {
matches!(self, BPartPred::L1 | BPartPred::Bi)
}
}
pub fn b_16x8_mb_type_raw(top: BPartPred, bottom: BPartPred) -> u32 {
use BPartPred::*;
match (top, bottom) {
(L0, L0) => 4,
(L1, L1) => 6,
(L0, L1) => 8,
(L1, L0) => 10,
(L0, Bi) => 12,
(L1, Bi) => 14,
(Bi, L0) => 16,
(Bi, L1) => 18,
(Bi, Bi) => 20,
}
}
pub fn b_8x16_mb_type_raw(left: BPartPred, right: BPartPred) -> u32 {
use BPartPred::*;
match (left, right) {
(L0, L0) => 5,
(L1, L1) => 7,
(L0, L1) => 9,
(L1, L0) => 11,
(L0, Bi) => 13,
(L1, Bi) => 15,
(Bi, L0) => 17,
(Bi, L1) => 19,
(Bi, Bi) => 21,
}
}
pub struct B16x8McbConfig {
pub transform_size_8x8_flag: Option<bool>,
pub top: BPartPred,
pub bottom: BPartPred,
pub mvd_l0: [(i32, i32); 2],
pub mvd_l1: [(i32, i32); 2],
pub cbp_luma: u8,
pub cbp_chroma: u8,
pub mb_qp_delta: i32,
pub luma_4x4_levels: [[i32; 16]; 16],
pub luma_4x4_nc: [i32; 16],
pub chroma_dc_cb: [i32; 4],
pub chroma_dc_cr: [i32; 4],
pub chroma_ac_cb: [[i32; 16]; 4],
pub chroma_ac_cr: [[i32; 16]; 4],
pub chroma_ac_nc_cb: [i32; 8],
pub chroma_ac_nc_cr: [i32; 8],
}
pub fn write_b_16x8_mb(
w: &mut BitWriter,
cfg: &B16x8McbConfig,
num_ref_idx_l0_active_minus1: u32,
num_ref_idx_l1_active_minus1: u32,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cfg.cbp_luma <= 15);
debug_assert!(cfg.cbp_chroma <= 2);
w.ue(b_16x8_mb_type_raw(cfg.top, cfg.bottom));
write_b_two_part_pred(
w,
[cfg.top, cfg.bottom],
cfg.mvd_l0,
cfg.mvd_l1,
num_ref_idx_l0_active_minus1,
num_ref_idx_l1_active_minus1,
);
write_b_two_part_residuals(
w,
cfg.transform_size_8x8_flag,
cfg.cbp_luma,
cfg.cbp_chroma,
cfg.mb_qp_delta,
&cfg.luma_4x4_levels,
&cfg.luma_4x4_nc,
&cfg.chroma_dc_cb,
&cfg.chroma_dc_cr,
&cfg.chroma_ac_cb,
&cfg.chroma_ac_cr,
&cfg.chroma_ac_nc_cb,
&cfg.chroma_ac_nc_cr,
)
}
pub struct B8x16McbConfig {
pub transform_size_8x8_flag: Option<bool>,
pub left: BPartPred,
pub right: BPartPred,
pub mvd_l0: [(i32, i32); 2],
pub mvd_l1: [(i32, i32); 2],
pub cbp_luma: u8,
pub cbp_chroma: u8,
pub mb_qp_delta: i32,
pub luma_4x4_levels: [[i32; 16]; 16],
pub luma_4x4_nc: [i32; 16],
pub chroma_dc_cb: [i32; 4],
pub chroma_dc_cr: [i32; 4],
pub chroma_ac_cb: [[i32; 16]; 4],
pub chroma_ac_cr: [[i32; 16]; 4],
pub chroma_ac_nc_cb: [i32; 8],
pub chroma_ac_nc_cr: [i32; 8],
}
pub fn write_b_8x16_mb(
w: &mut BitWriter,
cfg: &B8x16McbConfig,
num_ref_idx_l0_active_minus1: u32,
num_ref_idx_l1_active_minus1: u32,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cfg.cbp_luma <= 15);
debug_assert!(cfg.cbp_chroma <= 2);
w.ue(b_8x16_mb_type_raw(cfg.left, cfg.right));
write_b_two_part_pred(
w,
[cfg.left, cfg.right],
cfg.mvd_l0,
cfg.mvd_l1,
num_ref_idx_l0_active_minus1,
num_ref_idx_l1_active_minus1,
);
write_b_two_part_residuals(
w,
cfg.transform_size_8x8_flag,
cfg.cbp_luma,
cfg.cbp_chroma,
cfg.mb_qp_delta,
&cfg.luma_4x4_levels,
&cfg.luma_4x4_nc,
&cfg.chroma_dc_cb,
&cfg.chroma_dc_cr,
&cfg.chroma_ac_cb,
&cfg.chroma_ac_cr,
&cfg.chroma_ac_nc_cb,
&cfg.chroma_ac_nc_cr,
)
}
pub fn write_b_16x8_mb_chroma(
w: &mut BitWriter,
cfg: &B16x8McbConfig,
num_ref_idx_l0_active_minus1: u32,
num_ref_idx_l1_active_minus1: u32,
chroma_array_type: u32,
chroma: ChromaWriteKind<'_>,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cfg.cbp_luma <= 15);
debug_assert!(cfg.cbp_chroma <= 2);
w.ue(b_16x8_mb_type_raw(cfg.top, cfg.bottom));
write_b_two_part_pred(
w,
[cfg.top, cfg.bottom],
cfg.mvd_l0,
cfg.mvd_l1,
num_ref_idx_l0_active_minus1,
num_ref_idx_l1_active_minus1,
);
emit_inter_mb_tail(
w,
chroma_array_type,
cfg.cbp_luma,
cfg.cbp_chroma,
cfg.transform_size_8x8_flag,
cfg.mb_qp_delta,
&cfg.luma_4x4_levels,
&cfg.luma_4x4_nc,
chroma,
)
}
pub fn write_b_8x16_mb_chroma(
w: &mut BitWriter,
cfg: &B8x16McbConfig,
num_ref_idx_l0_active_minus1: u32,
num_ref_idx_l1_active_minus1: u32,
chroma_array_type: u32,
chroma: ChromaWriteKind<'_>,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cfg.cbp_luma <= 15);
debug_assert!(cfg.cbp_chroma <= 2);
w.ue(b_8x16_mb_type_raw(cfg.left, cfg.right));
write_b_two_part_pred(
w,
[cfg.left, cfg.right],
cfg.mvd_l0,
cfg.mvd_l1,
num_ref_idx_l0_active_minus1,
num_ref_idx_l1_active_minus1,
);
emit_inter_mb_tail(
w,
chroma_array_type,
cfg.cbp_luma,
cfg.cbp_chroma,
cfg.transform_size_8x8_flag,
cfg.mb_qp_delta,
&cfg.luma_4x4_levels,
&cfg.luma_4x4_nc,
chroma,
)
}
fn write_b_two_part_pred(
w: &mut BitWriter,
parts: [BPartPred; 2],
mvd_l0: [(i32, i32); 2],
mvd_l1: [(i32, i32); 2],
num_ref_idx_l0_active_minus1: u32,
num_ref_idx_l1_active_minus1: u32,
) {
for &p in &parts {
if p.uses_l0() && num_ref_idx_l0_active_minus1 > 0 {
w.te(num_ref_idx_l0_active_minus1, 0);
}
}
for &p in &parts {
if p.uses_l1() && num_ref_idx_l1_active_minus1 > 0 {
w.te(num_ref_idx_l1_active_minus1, 0);
}
}
for (i, &p) in parts.iter().enumerate() {
if p.uses_l0() {
w.se(mvd_l0[i].0);
w.se(mvd_l0[i].1);
}
}
for (i, &p) in parts.iter().enumerate() {
if p.uses_l1() {
w.se(mvd_l1[i].0);
w.se(mvd_l1[i].1);
}
}
}
#[allow(clippy::too_many_arguments)]
fn write_b_two_part_residuals(
w: &mut BitWriter,
transform_size_8x8_flag: Option<bool>,
cbp_luma: u8,
cbp_chroma: u8,
mb_qp_delta: i32,
luma_4x4_levels: &[[i32; 16]; 16],
luma_4x4_nc: &[i32; 16],
chroma_dc_cb: &[i32; 4],
chroma_dc_cr: &[i32; 4],
chroma_ac_cb: &[[i32; 16]; 4],
chroma_ac_cr: &[[i32; 16]; 4],
chroma_ac_nc_cb: &[i32; 8],
chroma_ac_nc_cr: &[i32; 8],
) -> Result<(), CavlcEncodeError> {
let codenum = inter_cbp_to_codenum_420_422(cbp_luma, cbp_chroma);
w.ue(codenum);
if let Some(flag) = transform_size_8x8_flag {
if cbp_luma > 0 {
w.u(1, if flag { 1 } else { 0 });
}
}
let needs_qp_delta = cbp_luma > 0 || cbp_chroma > 0;
if needs_qp_delta {
w.se(mb_qp_delta);
}
for blk8 in 0..4u8 {
if (cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
let nc = luma_4x4_nc[blk];
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(nc),
16,
&luma_4x4_levels[blk],
)?;
}
}
}
if cbp_chroma > 0 {
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, chroma_dc_cb)?;
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, chroma_dc_cr)?;
}
if cbp_chroma == 2 {
for (k, blk) in chroma_ac_cb.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(chroma_ac_nc_cb[k]),
15,
&blk[..15],
)?;
}
for (k, blk) in chroma_ac_cr.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(chroma_ac_nc_cr[k]),
15,
&blk[..15],
)?;
}
}
Ok(())
}
pub struct B8x8AllDirectMcbConfig {
pub transform_size_8x8_flag: Option<bool>,
pub cbp_luma: u8,
pub cbp_chroma: u8,
pub mb_qp_delta: i32,
pub luma_4x4_levels: [[i32; 16]; 16],
pub luma_4x4_nc: [i32; 16],
pub chroma_dc_cb: [i32; 4],
pub chroma_dc_cr: [i32; 4],
pub chroma_ac_cb: [[i32; 16]; 4],
pub chroma_ac_cr: [[i32; 16]; 4],
pub chroma_ac_nc_cb: [i32; 8],
pub chroma_ac_nc_cr: [i32; 8],
}
pub fn write_b_8x8_all_direct_mb(
w: &mut BitWriter,
cfg: &B8x8AllDirectMcbConfig,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cfg.cbp_luma <= 15);
debug_assert!(cfg.cbp_chroma <= 2);
w.ue(22);
for _ in 0..4 {
w.ue(0);
}
let codenum = inter_cbp_to_codenum_420_422(cfg.cbp_luma, cfg.cbp_chroma);
w.ue(codenum);
if let Some(flag) = cfg.transform_size_8x8_flag {
if cfg.cbp_luma > 0 {
w.u(1, if flag { 1 } else { 0 });
}
}
let needs_qp_delta = cfg.cbp_luma > 0 || cfg.cbp_chroma > 0;
if needs_qp_delta {
w.se(cfg.mb_qp_delta);
}
for blk8 in 0..4u8 {
if (cfg.cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
let nc = cfg.luma_4x4_nc[blk];
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(nc),
16,
&cfg.luma_4x4_levels[blk],
)?;
}
}
}
if cfg.cbp_chroma > 0 {
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cb)?;
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cr)?;
}
if cfg.cbp_chroma == 2 {
for (k, blk) in cfg.chroma_ac_cb.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cb[k]),
15,
&blk[..15],
)?;
}
for (k, blk) in cfg.chroma_ac_cr.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cr[k]),
15,
&blk[..15],
)?;
}
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BSubMbCell {
Direct,
L0,
L1,
Bi,
}
impl BSubMbCell {
pub fn sub_mb_type_raw(self) -> u32 {
match self {
BSubMbCell::Direct => 0,
BSubMbCell::L0 => 1,
BSubMbCell::L1 => 2,
BSubMbCell::Bi => 3,
}
}
pub fn writes_l0(self) -> bool {
matches!(self, BSubMbCell::L0 | BSubMbCell::Bi)
}
pub fn writes_l1(self) -> bool {
matches!(self, BSubMbCell::L1 | BSubMbCell::Bi)
}
}
pub struct B8x8MixedMcbConfig {
pub transform_size_8x8_flag: Option<bool>,
pub cells: [BSubMbCell; 4],
pub mvd_l0: [(i32, i32); 4],
pub mvd_l1: [(i32, i32); 4],
pub cbp_luma: u8,
pub cbp_chroma: u8,
pub mb_qp_delta: i32,
pub luma_4x4_levels: [[i32; 16]; 16],
pub luma_4x4_nc: [i32; 16],
pub chroma_dc_cb: [i32; 4],
pub chroma_dc_cr: [i32; 4],
pub chroma_ac_cb: [[i32; 16]; 4],
pub chroma_ac_cr: [[i32; 16]; 4],
pub chroma_ac_nc_cb: [i32; 8],
pub chroma_ac_nc_cr: [i32; 8],
}
pub fn write_b_8x8_mixed_mb(
w: &mut BitWriter,
cfg: &B8x8MixedMcbConfig,
num_ref_idx_l0_active_minus1: u32,
num_ref_idx_l1_active_minus1: u32,
) -> Result<(), CavlcEncodeError> {
debug_assert!(cfg.cbp_luma <= 15);
debug_assert!(cfg.cbp_chroma <= 2);
w.ue(22);
for &c in &cfg.cells {
w.ue(c.sub_mb_type_raw());
}
if num_ref_idx_l0_active_minus1 > 0 {
for &c in &cfg.cells {
if c.writes_l0() {
w.te(num_ref_idx_l0_active_minus1, 0);
}
}
}
if num_ref_idx_l1_active_minus1 > 0 {
for &c in &cfg.cells {
if c.writes_l1() {
w.te(num_ref_idx_l1_active_minus1, 0);
}
}
}
for (i, &c) in cfg.cells.iter().enumerate() {
if c.writes_l0() {
w.se(cfg.mvd_l0[i].0);
w.se(cfg.mvd_l0[i].1);
}
}
for (i, &c) in cfg.cells.iter().enumerate() {
if c.writes_l1() {
w.se(cfg.mvd_l1[i].0);
w.se(cfg.mvd_l1[i].1);
}
}
let codenum = inter_cbp_to_codenum_420_422(cfg.cbp_luma, cfg.cbp_chroma);
w.ue(codenum);
if let Some(flag) = cfg.transform_size_8x8_flag {
if cfg.cbp_luma > 0 {
w.u(1, if flag { 1 } else { 0 });
}
}
let needs_qp_delta = cfg.cbp_luma > 0 || cfg.cbp_chroma > 0;
if needs_qp_delta {
w.se(cfg.mb_qp_delta);
}
for blk8 in 0..4u8 {
if (cfg.cbp_luma >> blk8) & 1 == 1 {
for sub in 0..4u8 {
let blk = (blk8 * 4 + sub) as usize;
let nc = cfg.luma_4x4_nc[blk];
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(nc),
16,
&cfg.luma_4x4_levels[blk],
)?;
}
}
}
if cfg.cbp_chroma > 0 {
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cb)?;
encode_residual_block_cavlc(w, CoeffTokenContext::ChromaDc420, 4, &cfg.chroma_dc_cr)?;
}
if cfg.cbp_chroma == 2 {
for (k, blk) in cfg.chroma_ac_cb.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cb[k]),
15,
&blk[..15],
)?;
}
for (k, blk) in cfg.chroma_ac_cr.iter().enumerate() {
encode_residual_block_cavlc(
w,
CoeffTokenContext::Numeric(cfg.chroma_ac_nc_cr[k]),
15,
&blk[..15],
)?;
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn inter_cbp_inverse_round_trips_through_decoder_table() {
for cbp_luma in 0u8..=15 {
for cbp_chroma in 0u8..=2 {
let codenum = inter_cbp_to_codenum_420_422(cbp_luma, cbp_chroma);
assert!(codenum < 48);
static FWD: [u8; 48] = [
0, 16, 1, 2, 4, 8, 32, 3, 5, 10, 12, 15, 47, 7, 11, 13, 14, 6, 9, 31, 35, 37,
42, 44, 33, 34, 36, 40, 39, 43, 45, 46, 17, 18, 20, 24, 19, 21, 26, 28, 23, 27,
29, 30, 22, 25, 38, 41,
];
let cbp = FWD[codenum as usize];
assert_eq!(
cbp,
((cbp_chroma << 4) | cbp_luma),
"luma={cbp_luma} chroma={cbp_chroma} codeNum={codenum} got cbp={cbp}",
);
}
}
}
#[test]
fn intra_cbp_inverse_round_trips_through_decoder_table() {
for cbp_luma in 0u8..=15 {
for cbp_chroma in 0u8..=2 {
let codenum = intra_cbp_to_codenum_420_422(cbp_luma, cbp_chroma);
assert!(codenum < 48);
static FWD: [u8; 48] = [
47, 31, 15, 0, 23, 27, 29, 30, 7, 11, 13, 14, 39, 43, 45, 46, 16, 3, 5, 10, 12,
19, 21, 26, 28, 35, 37, 42, 44, 1, 2, 4, 8, 17, 18, 20, 24, 6, 9, 22, 25, 32,
33, 34, 36, 40, 38, 41,
];
let cbp = FWD[codenum as usize];
assert_eq!(
cbp,
((cbp_chroma << 4) | cbp_luma),
"luma={cbp_luma} chroma={cbp_chroma} codeNum={codenum} got cbp={cbp}",
);
}
}
}
#[test]
fn mb_type_value_matches_table_7_11_decode() {
use crate::macroblock_layer::{Intra16x16, MbType};
for pred_mode in 0u8..=3 {
for &cbp_luma in &[0u8, 15] {
for cbp_chroma in 0u8..=2 {
let raw = intra16x16_mb_type_value(pred_mode, cbp_luma, cbp_chroma);
let decoded = MbType::from_i_slice(raw).unwrap();
assert_eq!(
decoded,
MbType::Intra16x16(Intra16x16 {
pred_mode,
cbp_luma,
cbp_chroma,
}),
"raw={raw}",
);
}
}
}
}
#[test]
fn b16x16_mb_type_raw_matches_table_7_14_decode() {
use crate::macroblock_layer::MbType;
assert_eq!(BPred16x16::L0.mb_type_raw(), 1);
assert_eq!(BPred16x16::L1.mb_type_raw(), 2);
assert_eq!(BPred16x16::Bi.mb_type_raw(), 3);
assert_eq!(MbType::from_b_slice(1).unwrap(), MbType::BL016x16);
assert_eq!(MbType::from_b_slice(2).unwrap(), MbType::BL116x16);
assert_eq!(MbType::from_b_slice(3).unwrap(), MbType::BBi16x16);
assert!(BPred16x16::L0.uses_l0() && !BPred16x16::L0.uses_l1());
assert!(!BPred16x16::L1.uses_l0() && BPred16x16::L1.uses_l1());
assert!(BPred16x16::Bi.uses_l0() && BPred16x16::Bi.uses_l1());
}
#[test]
fn b_direct_16x16_mb_type_raw_matches_table_7_14_decode() {
use crate::macroblock_layer::MbType;
assert_eq!(MbType::from_b_slice(0).unwrap(), MbType::BDirect16x16);
}
#[test]
fn b_8x8_mb_type_raw_matches_table_7_14_decode() {
use crate::macroblock_layer::{MbType, SubMbType};
assert_eq!(MbType::from_b_slice(22).unwrap(), MbType::B8x8);
assert_eq!(SubMbType::from_b(0).unwrap(), SubMbType::BDirect8x8);
}
#[test]
fn b_8x8_all_direct_zero_residual_emits_expected_bit_count() {
let mut bw = BitWriter::new();
let cfg = B8x8AllDirectMcbConfig {
chroma_ac_nc_cb: [0; 8],
chroma_ac_nc_cr: [0; 8],
transform_size_8x8_flag: None,
cbp_luma: 0,
cbp_chroma: 0,
mb_qp_delta: 0,
luma_4x4_levels: [[0; 16]; 16],
luma_4x4_nc: [0; 16],
chroma_dc_cb: [0; 4],
chroma_dc_cr: [0; 4],
chroma_ac_cb: [[0; 16]; 4],
chroma_ac_cr: [[0; 16]; 4],
};
write_b_8x8_all_direct_mb(&mut bw, &cfg).expect("write");
assert_eq!(bw.bits_emitted(), 14);
}
#[test]
fn b_direct_16x16_zero_residual_emits_two_bits() {
let mut bw = BitWriter::new();
let cfg = BDirect16x16McbConfig {
chroma_ac_nc_cb: [0; 8],
chroma_ac_nc_cr: [0; 8],
transform_size_8x8_flag: None,
cbp_luma: 0,
cbp_chroma: 0,
mb_qp_delta: 0,
luma_4x4_levels: [[0; 16]; 16],
luma_4x4_nc: [0; 16],
chroma_dc_cb: [0; 4],
chroma_dc_cr: [0; 4],
chroma_ac_cb: [[0; 16]; 4],
chroma_ac_cr: [[0; 16]; 4],
};
write_b_direct_16x16_mb(&mut bw, &cfg).expect("write");
assert_eq!(bw.bits_emitted(), 2);
}
#[test]
fn b_16x8_mb_type_raw_matches_table_7_14_decode() {
use crate::macroblock_layer::MbType;
use BPartPred::*;
let cases: &[(BPartPred, BPartPred, u32, MbType)] = &[
(L0, L0, 4, MbType::BL0L016x8),
(L1, L1, 6, MbType::BL1L116x8),
(L0, L1, 8, MbType::BL0L116x8),
(L1, L0, 10, MbType::BL1L016x8),
(L0, Bi, 12, MbType::BL0Bi16x8),
(L1, Bi, 14, MbType::BL1Bi16x8),
(Bi, L0, 16, MbType::BBiL016x8),
(Bi, L1, 18, MbType::BBiL116x8),
(Bi, Bi, 20, MbType::BBiBi16x8),
];
for (top, bot, raw, expected) in cases.iter().cloned() {
assert_eq!(b_16x8_mb_type_raw(top, bot), raw);
assert_eq!(MbType::from_b_slice(raw).unwrap(), expected);
}
}
#[test]
fn b_8x16_mb_type_raw_matches_table_7_14_decode() {
use crate::macroblock_layer::MbType;
use BPartPred::*;
let cases: &[(BPartPred, BPartPred, u32, MbType)] = &[
(L0, L0, 5, MbType::BL0L08x16),
(L1, L1, 7, MbType::BL1L18x16),
(L0, L1, 9, MbType::BL0L18x16),
(L1, L0, 11, MbType::BL1L08x16),
(L0, Bi, 13, MbType::BL0Bi8x16),
(L1, Bi, 15, MbType::BL1Bi8x16),
(Bi, L0, 17, MbType::BBiL08x16),
(Bi, L1, 19, MbType::BBiL18x16),
(Bi, Bi, 21, MbType::BBiBi8x16),
];
for (left, right, raw, expected) in cases.iter().cloned() {
assert_eq!(b_8x16_mb_type_raw(left, right), raw);
assert_eq!(MbType::from_b_slice(raw).unwrap(), expected);
}
}
#[test]
fn b_16x8_writer_round_trip_smoke() {
let mut bw = BitWriter::new();
let cfg = B16x8McbConfig {
chroma_ac_nc_cb: [0; 8],
chroma_ac_nc_cr: [0; 8],
transform_size_8x8_flag: None,
top: BPartPred::Bi,
bottom: BPartPred::Bi,
mvd_l0: [(0, 0); 2],
mvd_l1: [(0, 0); 2],
cbp_luma: 0,
cbp_chroma: 0,
mb_qp_delta: 0,
luma_4x4_levels: [[0; 16]; 16],
luma_4x4_nc: [0; 16],
chroma_dc_cb: [0; 4],
chroma_dc_cr: [0; 4],
chroma_ac_cb: [[0; 16]; 4],
chroma_ac_cr: [[0; 16]; 4],
};
write_b_16x8_mb(&mut bw, &cfg, 0, 0).expect("write");
assert_eq!(bw.bits_emitted(), 18);
}
#[test]
fn b_8x16_writer_l0_l1_zero_residual_bit_count() {
let mut bw = BitWriter::new();
let cfg = B8x16McbConfig {
chroma_ac_nc_cb: [0; 8],
chroma_ac_nc_cr: [0; 8],
transform_size_8x8_flag: None,
left: BPartPred::L0,
right: BPartPred::L1,
mvd_l0: [(0, 0); 2],
mvd_l1: [(0, 0); 2],
cbp_luma: 0,
cbp_chroma: 0,
mb_qp_delta: 0,
luma_4x4_levels: [[0; 16]; 16],
luma_4x4_nc: [0; 16],
chroma_dc_cb: [0; 4],
chroma_dc_cr: [0; 4],
chroma_ac_cb: [[0; 16]; 4],
chroma_ac_cr: [[0; 16]; 4],
};
write_b_8x16_mb(&mut bw, &cfg, 0, 0).expect("write");
assert_eq!(bw.bits_emitted(), 12);
}
#[test]
fn b8x8_mixed_sub_mb_type_raws_match_table_7_18() {
use crate::macroblock_layer::SubMbType;
assert_eq!(BSubMbCell::Direct.sub_mb_type_raw(), 0);
assert_eq!(BSubMbCell::L0.sub_mb_type_raw(), 1);
assert_eq!(BSubMbCell::L1.sub_mb_type_raw(), 2);
assert_eq!(BSubMbCell::Bi.sub_mb_type_raw(), 3);
assert_eq!(SubMbType::from_b(0).unwrap(), SubMbType::BDirect8x8);
assert_eq!(SubMbType::from_b(1).unwrap(), SubMbType::BL08x8);
assert_eq!(SubMbType::from_b(2).unwrap(), SubMbType::BL18x8);
assert_eq!(SubMbType::from_b(3).unwrap(), SubMbType::BBi8x8);
assert!(!BSubMbCell::Direct.writes_l0() && !BSubMbCell::Direct.writes_l1());
assert!(BSubMbCell::L0.writes_l0() && !BSubMbCell::L0.writes_l1());
assert!(!BSubMbCell::L1.writes_l0() && BSubMbCell::L1.writes_l1());
assert!(BSubMbCell::Bi.writes_l0() && BSubMbCell::Bi.writes_l1());
}
#[test]
fn b_8x8_mixed_zero_residual_emits_expected_bit_count() {
let mut bw = BitWriter::new();
let cfg = B8x8MixedMcbConfig {
chroma_ac_nc_cb: [0; 8],
chroma_ac_nc_cr: [0; 8],
transform_size_8x8_flag: None,
cells: [
BSubMbCell::Direct,
BSubMbCell::L0,
BSubMbCell::L1,
BSubMbCell::Bi,
],
mvd_l0: [(0, 0); 4],
mvd_l1: [(0, 0); 4],
cbp_luma: 0,
cbp_chroma: 0,
mb_qp_delta: 0,
luma_4x4_levels: [[0; 16]; 16],
luma_4x4_nc: [0; 16],
chroma_dc_cb: [0; 4],
chroma_dc_cr: [0; 4],
chroma_ac_cb: [[0; 16]; 4],
chroma_ac_cr: [[0; 16]; 4],
};
write_b_8x8_mixed_mb(&mut bw, &cfg, 0, 0).expect("write");
assert_eq!(bw.bits_emitted(), 30);
}
#[test]
fn b_8x8_mixed_all_direct_matches_dedicated_all_direct_writer_bit_count() {
let mut bw = BitWriter::new();
let cfg = B8x8MixedMcbConfig {
chroma_ac_nc_cb: [0; 8],
chroma_ac_nc_cr: [0; 8],
transform_size_8x8_flag: None,
cells: [BSubMbCell::Direct; 4],
mvd_l0: [(0, 0); 4],
mvd_l1: [(0, 0); 4],
cbp_luma: 0,
cbp_chroma: 0,
mb_qp_delta: 0,
luma_4x4_levels: [[0; 16]; 16],
luma_4x4_nc: [0; 16],
chroma_dc_cb: [0; 4],
chroma_dc_cr: [0; 4],
chroma_ac_cb: [[0; 16]; 4],
chroma_ac_cr: [[0; 16]; 4],
};
write_b_8x8_mixed_mb(&mut bw, &cfg, 0, 0).expect("write");
assert_eq!(bw.bits_emitted(), 14);
let mut bw_dedicated = BitWriter::new();
let cfg_dedicated = B8x8AllDirectMcbConfig {
chroma_ac_nc_cb: [0; 8],
chroma_ac_nc_cr: [0; 8],
transform_size_8x8_flag: None,
cbp_luma: 0,
cbp_chroma: 0,
mb_qp_delta: 0,
luma_4x4_levels: [[0; 16]; 16],
luma_4x4_nc: [0; 16],
chroma_dc_cb: [0; 4],
chroma_dc_cr: [0; 4],
chroma_ac_cb: [[0; 16]; 4],
chroma_ac_cr: [[0; 16]; 4],
};
write_b_8x8_all_direct_mb(&mut bw_dedicated, &cfg_dedicated).expect("write");
assert_eq!(bw.bits_emitted(), bw_dedicated.bits_emitted());
}
}