use crate::cdf::{
get_br_ctx, get_coeff_base_ctx, get_coeff_base_eob_ctx, TileCdfContext, BR_CDF_SIZE,
COEFF_BASE_RANGE, DC_SIGN_CONTEXTS, EOB_COEF_CONTEXTS, LEVEL_CONTEXTS, NUM_BASE_LEVELS,
PLANE_TYPES, SIG_COEF_CONTEXTS, SIG_COEF_CONTEXTS_EOB, TXB_SKIP_CONTEXTS, TX_SIZES,
TX_SIZES_ALL, TX_SIZE_SQR_UP, TX_WIDTH, TX_WIDTH_LOG2,
};
use crate::encoder::symbol_writer::SymbolWriter;
use crate::Error;
pub const GOLOMB_MAX_LENGTH: u32 = 20;
pub fn write_txb_skip(
writer: &mut SymbolWriter,
cdfs: &mut TileCdfContext,
all_zero: u8,
tx_sz_ctx: usize,
ctx: usize,
) -> Result<(), Error> {
if all_zero > 1 {
return Err(Error::PartitionWalkOutOfRange);
}
if tx_sz_ctx >= TX_SIZES {
return Err(Error::PartitionWalkOutOfRange);
}
if ctx >= TXB_SKIP_CONTEXTS {
return Err(Error::PartitionWalkOutOfRange);
}
let cdf = cdfs
.txb_skip_cdf(tx_sz_ctx, ctx)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(all_zero as u32, cdf)
}
#[allow(clippy::too_many_arguments)]
pub fn write_eob_pt(
writer: &mut SymbolWriter,
cdfs: &mut TileCdfContext,
eob: u32,
tx_size: usize,
tx_class: usize,
plane: u8,
is_inter: u8,
) -> Result<(), Error> {
if tx_size >= TX_SIZES_ALL {
return Err(Error::PartitionWalkOutOfRange);
}
if tx_class > 2 {
return Err(Error::PartitionWalkOutOfRange);
}
if plane > 2 {
return Err(Error::PartitionWalkOutOfRange);
}
if is_inter > 1 {
return Err(Error::PartitionWalkOutOfRange);
}
let tx_w = TX_WIDTH[tx_size];
let tx_h = crate::cdf::TX_HEIGHT[tx_size];
let tx_sz_sqr = {
let side = core::cmp::min(tx_w, tx_h);
(side.trailing_zeros() as usize) - 2
};
let tx_sz_sqr_up = TX_SIZE_SQR_UP[tx_size];
let tx_sz_ctx = (tx_sz_sqr + tx_sz_sqr_up + 1) >> 1;
let ptype = (plane > 0) as usize;
let tx_height_log2_local = (tx_h.trailing_zeros() as usize) & 0xFF;
let eob_multisize: i32 = (core::cmp::min(TX_WIDTH_LOG2[tx_size], 5)
+ core::cmp::min(tx_height_log2_local, 5)) as i32
- 4;
debug_assert!(
(0..=6).contains(&eob_multisize),
"eobMultisize ∈ 0..=6 over every TX_SIZES_ALL ordinal"
);
if eob == 0 {
return Err(Error::PartitionWalkOutOfRange);
}
let eob_pt: u32 = if eob <= 2 {
eob
} else {
let v = eob - 1;
(31 - v.leading_zeros()) + 2
};
let base_eob: u32 = if eob_pt < 2 {
eob_pt
} else {
(1u32 << (eob_pt - 2)) + 1
};
let refine_bits: u32 = eob_pt.saturating_sub(2);
let residue = eob - base_eob;
debug_assert!(
residue < (1u32 << refine_bits.max(1)) || refine_bits == 0,
"residue {residue} exceeds {refine_bits}-bit refinement window for eobPt {eob_pt}"
);
let eob_pt_sym: u32 = eob_pt - 1;
let alphabet: usize = match eob_multisize {
0 => 6,
1 => 7,
2 => 8,
3 => 9,
4 => 10,
5 => 11,
_ => 12,
};
if (eob_pt_sym as usize) >= alphabet {
return Err(Error::PartitionWalkOutOfRange);
}
let eob_pt_ctx = if tx_class == crate::cdf::TX_CLASS_2D {
0
} else {
1
};
match eob_multisize {
0 => {
let cdf = cdfs
.eob_pt_16_cdf(ptype, eob_pt_ctx)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(eob_pt_sym, cdf)?;
}
1 => {
let cdf = cdfs
.eob_pt_32_cdf(ptype, eob_pt_ctx)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(eob_pt_sym, cdf)?;
}
2 => {
let cdf = cdfs
.eob_pt_64_cdf(ptype, eob_pt_ctx)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(eob_pt_sym, cdf)?;
}
3 => {
let cdf = cdfs
.eob_pt_128_cdf(ptype, eob_pt_ctx)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(eob_pt_sym, cdf)?;
}
4 => {
let cdf = cdfs
.eob_pt_256_cdf(ptype, eob_pt_ctx)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(eob_pt_sym, cdf)?;
}
5 => {
let cdf = cdfs
.eob_pt_512_cdf(ptype)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(eob_pt_sym, cdf)?;
}
_ => {
let cdf = cdfs
.eob_pt_1024_cdf(ptype)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(eob_pt_sym, cdf)?;
}
}
if eob_pt >= 3 {
let extra_ctx = (eob_pt - 3) as usize;
if extra_ctx >= EOB_COEF_CONTEXTS {
return Err(Error::PartitionWalkOutOfRange);
}
let msb_shift = refine_bits - 1;
let msb_bit = (residue >> msb_shift) & 0x1;
let cdf = cdfs
.eob_extra_cdf(tx_sz_ctx, ptype, extra_ctx)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(msb_bit, cdf)?;
for i in 1..refine_bits {
let shift = refine_bits - 1 - i;
let bit = (residue >> shift) & 0x1;
writer.write_literal(1, bit)?;
}
}
Ok(())
}
pub fn write_dc_sign(
writer: &mut SymbolWriter,
cdfs: &mut TileCdfContext,
dc_sign: u8,
plane: u8,
ctx: usize,
) -> Result<(), Error> {
if dc_sign > 1 {
return Err(Error::PartitionWalkOutOfRange);
}
if plane > 2 {
return Err(Error::PartitionWalkOutOfRange);
}
if ctx >= DC_SIGN_CONTEXTS {
return Err(Error::PartitionWalkOutOfRange);
}
let ptype = (plane > 0) as usize;
let cdf = cdfs
.dc_sign_cdf(ptype, ctx)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(dc_sign as u32, cdf)
}
pub fn write_coeff_base_eob(
writer: &mut SymbolWriter,
cdfs: &mut TileCdfContext,
sym: u8,
tx_sz_ctx: usize,
ptype: usize,
ctx: usize,
) -> Result<(), Error> {
if sym > 2 {
return Err(Error::PartitionWalkOutOfRange);
}
if tx_sz_ctx >= TX_SIZES {
return Err(Error::PartitionWalkOutOfRange);
}
if ptype >= PLANE_TYPES {
return Err(Error::PartitionWalkOutOfRange);
}
if ctx >= SIG_COEF_CONTEXTS_EOB {
return Err(Error::PartitionWalkOutOfRange);
}
let cdf = cdfs
.coeff_base_eob_cdf(tx_sz_ctx, ptype, ctx)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(sym as u32, cdf)
}
pub fn write_coeff_base(
writer: &mut SymbolWriter,
cdfs: &mut TileCdfContext,
sym: u8,
tx_sz_ctx: usize,
ptype: usize,
ctx: usize,
) -> Result<(), Error> {
if sym > 3 {
return Err(Error::PartitionWalkOutOfRange);
}
if tx_sz_ctx >= TX_SIZES {
return Err(Error::PartitionWalkOutOfRange);
}
if ptype >= PLANE_TYPES {
return Err(Error::PartitionWalkOutOfRange);
}
if ctx >= SIG_COEF_CONTEXTS {
return Err(Error::PartitionWalkOutOfRange);
}
let cdf = cdfs
.coeff_base_cdf(tx_sz_ctx, ptype, ctx)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(sym as u32, cdf)
}
pub fn write_coeff_br(
writer: &mut SymbolWriter,
cdfs: &mut TileCdfContext,
sym: u8,
tx_sz_ctx: usize,
ptype: usize,
ctx: usize,
) -> Result<(), Error> {
if (sym as usize) >= BR_CDF_SIZE {
return Err(Error::PartitionWalkOutOfRange);
}
if tx_sz_ctx >= TX_SIZES {
return Err(Error::PartitionWalkOutOfRange);
}
if ptype >= PLANE_TYPES {
return Err(Error::PartitionWalkOutOfRange);
}
if ctx >= LEVEL_CONTEXTS {
return Err(Error::PartitionWalkOutOfRange);
}
let cdf = cdfs
.coeff_br_cdf(tx_sz_ctx, ptype, ctx)
.ok_or(Error::PartitionWalkOutOfRange)?;
writer.write_symbol(sym as u32, cdf)
}
pub fn write_golomb(writer: &mut SymbolWriter, value: u32) -> Result<(), Error> {
let threshold = (NUM_BASE_LEVELS + COEFF_BASE_RANGE) as u32;
if value <= threshold {
return Err(Error::PartitionWalkOutOfRange);
}
let x: u32 = value - threshold;
debug_assert!(x >= 1, "value > threshold guarantees x >= 1");
let length: u32 = 32 - x.leading_zeros();
if length > GOLOMB_MAX_LENGTH {
return Err(Error::PartitionWalkOutOfRange);
}
for _ in 0..(length - 1) {
writer.write_literal(1, 0)?;
}
writer.write_literal(1, 1)?;
if length >= 2 {
let payload_bits = length - 1;
for i in (0..payload_bits).rev() {
let bit = (x >> i) & 0x1;
writer.write_literal(1, bit)?;
}
}
Ok(())
}
#[allow(clippy::too_many_arguments)]
pub fn write_coefficients(
writer: &mut SymbolWriter,
cdfs: &mut TileCdfContext,
plane: u8,
is_inter: u8,
tx_size: usize,
tx_class: usize,
txb_skip_ctx: usize,
dc_sign_ctx: usize,
scan: &[u16],
quant_in: &[i32],
) -> Result<crate::cdf::CoefficientsReadout, Error> {
if tx_size >= TX_SIZES_ALL {
return Err(Error::PartitionWalkOutOfRange);
}
if tx_class > crate::cdf::TX_CLASS_VERT {
return Err(Error::PartitionWalkOutOfRange);
}
if plane > 2 {
return Err(Error::PartitionWalkOutOfRange);
}
if is_inter > 1 {
return Err(Error::PartitionWalkOutOfRange);
}
if txb_skip_ctx >= TXB_SKIP_CONTEXTS {
return Err(Error::PartitionWalkOutOfRange);
}
if dc_sign_ctx >= DC_SIGN_CONTEXTS {
return Err(Error::PartitionWalkOutOfRange);
}
let tx_w = TX_WIDTH[tx_size];
let tx_h = crate::cdf::TX_HEIGHT[tx_size];
let tx_sz_sqr = {
let side = core::cmp::min(tx_w, tx_h);
(side.trailing_zeros() as usize) - 2
};
let tx_sz_sqr_up = TX_SIZE_SQR_UP[tx_size];
let tx_sz_ctx = (tx_sz_sqr + tx_sz_sqr_up + 1) >> 1;
let seg_eob = if tx_size == crate::cdf::TX_16X64 || tx_size == crate::cdf::TX_64X16 {
512usize
} else {
core::cmp::min(1024, tx_w * tx_h)
};
if scan.len() < seg_eob {
return Err(Error::PartitionWalkOutOfRange);
}
if quant_in.len() < tx_w * tx_h {
return Err(Error::PartitionWalkOutOfRange);
}
let mut eob: u32 = 0;
for c in (0..seg_eob).rev() {
let pos = scan[c] as usize;
if pos >= quant_in.len() {
return Err(Error::PartitionWalkOutOfRange);
}
if quant_in[pos] != 0 {
eob = (c as u32) + 1;
break;
}
}
let all_zero: u8 = if eob == 0 { 1 } else { 0 };
write_txb_skip(writer, cdfs, all_zero, tx_sz_ctx, txb_skip_ctx)?;
if eob == 0 {
return Ok(crate::cdf::CoefficientsReadout {
all_zero: true,
eob: 0,
cul_level: 0,
dc_category: 0,
});
}
write_coefficients_gate_open(
writer,
cdfs,
plane,
is_inter,
tx_size,
tx_class,
dc_sign_ctx,
scan,
quant_in,
)
}
#[allow(clippy::too_many_arguments)]
pub fn write_coefficients_gate_open(
writer: &mut SymbolWriter,
cdfs: &mut TileCdfContext,
plane: u8,
is_inter: u8,
tx_size: usize,
tx_class: usize,
dc_sign_ctx: usize,
scan: &[u16],
quant_in: &[i32],
) -> Result<crate::cdf::CoefficientsReadout, Error> {
if tx_size >= TX_SIZES_ALL {
return Err(Error::PartitionWalkOutOfRange);
}
if tx_class > crate::cdf::TX_CLASS_VERT {
return Err(Error::PartitionWalkOutOfRange);
}
if plane > 2 {
return Err(Error::PartitionWalkOutOfRange);
}
if is_inter > 1 {
return Err(Error::PartitionWalkOutOfRange);
}
if dc_sign_ctx >= DC_SIGN_CONTEXTS {
return Err(Error::PartitionWalkOutOfRange);
}
let tx_w = TX_WIDTH[tx_size];
let tx_h = crate::cdf::TX_HEIGHT[tx_size];
let tx_sz_sqr = {
let side = core::cmp::min(tx_w, tx_h);
(side.trailing_zeros() as usize) - 2
};
let tx_sz_sqr_up = TX_SIZE_SQR_UP[tx_size];
let tx_sz_ctx = (tx_sz_sqr + tx_sz_sqr_up + 1) >> 1;
let ptype = (plane > 0) as usize;
let seg_eob = if tx_size == crate::cdf::TX_16X64 || tx_size == crate::cdf::TX_64X16 {
512usize
} else {
core::cmp::min(1024, tx_w * tx_h)
};
if scan.len() < seg_eob {
return Err(Error::PartitionWalkOutOfRange);
}
if quant_in.len() < tx_w * tx_h {
return Err(Error::PartitionWalkOutOfRange);
}
let mut eob: u32 = 0;
for c in (0..seg_eob).rev() {
let pos = scan[c] as usize;
if pos >= quant_in.len() {
return Err(Error::PartitionWalkOutOfRange);
}
if quant_in[pos] != 0 {
eob = (c as u32) + 1;
break;
}
}
if eob == 0 {
return Err(Error::PartitionWalkOutOfRange);
}
write_eob_pt(writer, cdfs, eob, tx_size, tx_class, plane, is_inter)?;
let mut running: Vec<i32> = vec![0i32; tx_w * tx_h];
let threshold_post_br: u32 = (NUM_BASE_LEVELS + COEFF_BASE_RANGE + 1) as u32; let threshold_golomb: u32 = (NUM_BASE_LEVELS + COEFF_BASE_RANGE) as u32; let max_base_eob: u8 = 2; let max_base: u8 = 3;
{
let mut c: i32 = eob as i32 - 1;
while c >= 0 {
let pos = scan[c as usize] as usize;
let target_abs: u32 = quant_in[pos].unsigned_abs();
let coded_mag: u32 = core::cmp::min(target_abs, threshold_post_br);
let is_eob = c == (eob as i32 - 1);
if is_eob && coded_mag == 0 {
return Err(Error::PartitionWalkOutOfRange);
}
let base_sym: u8 = if is_eob {
core::cmp::min((coded_mag - 1) as u8, max_base_eob)
} else {
core::cmp::min(coded_mag as u8, max_base)
};
if is_eob {
let ctx = get_coeff_base_eob_ctx(&running, tx_size, tx_class, pos, c as usize);
write_coeff_base_eob(writer, cdfs, base_sym, tx_sz_ctx, ptype, ctx)?;
} else {
let ctx = get_coeff_base_ctx(&running, tx_size, tx_class, pos, c as usize, false);
write_coeff_base(writer, cdfs, base_sym, tx_sz_ctx, ptype, ctx)?;
}
let mut level: u32 = if is_eob {
(base_sym as u32) + 1
} else {
base_sym as u32
};
if level as usize > NUM_BASE_LEVELS {
let br_iters: u32 = (COEFF_BASE_RANGE / (BR_CDF_SIZE - 1)) as u32;
let br_step: u32 = (BR_CDF_SIZE - 1) as u32; for _ in 0..br_iters {
let residue = coded_mag - level;
let sym: u8 = core::cmp::min(residue, br_step) as u8;
let ctx = get_br_ctx(&running, tx_size, tx_class, pos);
write_coeff_br(writer, cdfs, sym, tx_sz_ctx, ptype, ctx)?;
level += sym as u32;
if (sym as usize) < BR_CDF_SIZE - 1 {
break;
}
}
}
running[pos] = level as i32;
c -= 1;
}
}
let mut cul_level: u32 = 0;
let mut dc_category: u8 = 0;
for (c, &pos_u16) in scan.iter().enumerate().take(eob as usize) {
let pos = pos_u16 as usize;
let target = quant_in[pos];
let target_abs: u32 = target.unsigned_abs();
let sign: u8 = if target < 0 { 1 } else { 0 };
if target_abs != 0 {
if c == 0 {
write_dc_sign(writer, cdfs, sign, plane, dc_sign_ctx)?;
} else {
writer.write_literal(1, sign as u32)?;
}
}
if target_abs > threshold_golomb {
write_golomb(writer, target_abs)?;
}
if pos == 0 && target_abs > 0 {
dc_category = if sign != 0 { 1 } else { 2 };
}
cul_level = cul_level.saturating_add(target_abs & 0xFFFFF);
}
let cul_level: u8 = core::cmp::min(63, cul_level) as u8;
Ok(crate::cdf::CoefficientsReadout {
all_zero: false,
eob,
cul_level,
dc_category,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cdf::{
get_br_ctx, get_coeff_base_ctx, get_coeff_base_eob_ctx, TileCdfContext, TX_16X16, TX_32X32,
TX_4X4, TX_8X8, TX_CLASS_2D, TX_CLASS_HORIZ,
};
use crate::symbol_decoder::SymbolDecoder;
fn read_all_zero(
dec: &mut SymbolDecoder<'_>,
cdfs: &mut TileCdfContext,
tx_sz_ctx: usize,
ctx: usize,
) -> u32 {
let cdf = cdfs.txb_skip_cdf(tx_sz_ctx, ctx).unwrap();
dec.read_symbol(cdf).unwrap()
}
#[test]
fn write_txb_skip_one_round_trip() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_txb_skip(&mut writer, &mut enc_cdfs, 1, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let all_zero = read_all_zero(&mut dec, &mut dec_cdfs, 0, 0);
assert_eq!(all_zero, 1);
}
#[test]
fn write_txb_skip_zero_round_trip() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_txb_skip(&mut writer, &mut enc_cdfs, 0, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let all_zero = read_all_zero(&mut dec, &mut dec_cdfs, 0, 0);
assert_eq!(all_zero, 0);
}
#[test]
fn write_txb_skip_upper_grid_round_trip() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_txb_skip(&mut writer, &mut enc_cdfs, 1, 4, 12).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let all_zero = read_all_zero(&mut dec, &mut dec_cdfs, 4, 12);
assert_eq!(all_zero, 1);
}
#[test]
fn write_txb_skip_rejects_out_of_range_symbol() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_txb_skip(&mut writer, &mut cdfs, 2, 0, 0).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn write_txb_skip_rejects_out_of_range_tx_sz_ctx() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_txb_skip(&mut writer, &mut cdfs, 0, TX_SIZES, 0).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn write_txb_skip_rejects_out_of_range_ctx() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_txb_skip(&mut writer, &mut cdfs, 0, 0, TXB_SKIP_CONTEXTS).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[allow(clippy::too_many_arguments)]
fn read_eob(
dec: &mut SymbolDecoder<'_>,
cdfs: &mut TileCdfContext,
tx_size: usize,
tx_class: usize,
plane: u8,
is_inter: u8,
) -> u32 {
let tx_w = TX_WIDTH[tx_size];
let tx_h = crate::cdf::TX_HEIGHT[tx_size];
let tx_sz_sqr = (core::cmp::min(tx_w, tx_h).trailing_zeros() as usize) - 2;
let tx_sz_sqr_up = TX_SIZE_SQR_UP[tx_size];
let tx_sz_ctx = (tx_sz_sqr + tx_sz_sqr_up + 1) >> 1;
let ptype = (plane > 0) as usize;
let tx_height_log2_local = (tx_h.trailing_zeros() as usize) & 0xFF;
let eob_multisize: i32 = (core::cmp::min(TX_WIDTH_LOG2[tx_size], 5)
+ core::cmp::min(tx_height_log2_local, 5)) as i32
- 4;
let eob_pt_ctx = if tx_class == TX_CLASS_2D { 0 } else { 1 };
let eob_pt: u32 = match eob_multisize {
0 => {
let cdf = cdfs.eob_pt_16_cdf(ptype, eob_pt_ctx).unwrap();
dec.read_symbol(cdf).unwrap() + 1
}
1 => {
let cdf = cdfs.eob_pt_32_cdf(ptype, eob_pt_ctx).unwrap();
dec.read_symbol(cdf).unwrap() + 1
}
2 => {
let cdf = cdfs.eob_pt_64_cdf(ptype, eob_pt_ctx).unwrap();
dec.read_symbol(cdf).unwrap() + 1
}
3 => {
let cdf = cdfs.eob_pt_128_cdf(ptype, eob_pt_ctx).unwrap();
dec.read_symbol(cdf).unwrap() + 1
}
4 => {
let cdf = cdfs.eob_pt_256_cdf(ptype, eob_pt_ctx).unwrap();
dec.read_symbol(cdf).unwrap() + 1
}
5 => {
let cdf = cdfs.eob_pt_512_cdf(ptype).unwrap();
dec.read_symbol(cdf).unwrap() + 1
}
_ => {
let cdf = cdfs.eob_pt_1024_cdf(ptype).unwrap();
dec.read_symbol(cdf).unwrap() + 1
}
};
let mut eob: u32 = if eob_pt < 2 {
eob_pt
} else {
(1u32 << (eob_pt - 2)) + 1
};
let mut eob_shift: i32 = core::cmp::max(-1, eob_pt as i32 - 3);
if eob_shift >= 0 {
let extra_ctx = (eob_pt - 3) as usize;
let cdf = cdfs.eob_extra_cdf(tx_sz_ctx, ptype, extra_ctx).unwrap();
let eob_extra = dec.read_symbol(cdf).unwrap();
if eob_extra != 0 {
eob += 1u32 << eob_shift;
}
let upper = core::cmp::max(0, eob_pt as i32 - 2);
let mut i: i32 = 1;
while i < upper {
eob_shift = upper - 1 - i;
let bit = dec.read_literal(1).unwrap();
if bit != 0 {
eob += 1u32 << eob_shift;
}
i += 1;
}
}
let _ = is_inter;
eob
}
#[test]
fn write_eob_pt_eob_1_round_trip_tx4x4() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_eob_pt(&mut writer, &mut enc_cdfs, 1, TX_4X4, TX_CLASS_2D, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let eob = read_eob(&mut dec, &mut dec_cdfs, TX_4X4, TX_CLASS_2D, 0, 0);
assert_eq!(eob, 1);
}
#[test]
fn write_eob_pt_eob_2_round_trip_tx4x4() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_eob_pt(&mut writer, &mut enc_cdfs, 2, TX_4X4, TX_CLASS_2D, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let eob = read_eob(&mut dec, &mut dec_cdfs, TX_4X4, TX_CLASS_2D, 0, 0);
assert_eq!(eob, 2);
}
#[test]
fn write_eob_pt_eob_3_round_trip_tx4x4() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_eob_pt(&mut writer, &mut enc_cdfs, 3, TX_4X4, TX_CLASS_2D, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let eob = read_eob(&mut dec, &mut dec_cdfs, TX_4X4, TX_CLASS_2D, 0, 0);
assert_eq!(eob, 3);
}
#[test]
fn write_eob_pt_eob_8_round_trip_tx4x4() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_eob_pt(&mut writer, &mut enc_cdfs, 8, TX_4X4, TX_CLASS_2D, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let eob = read_eob(&mut dec, &mut dec_cdfs, TX_4X4, TX_CLASS_2D, 0, 0);
assert_eq!(eob, 8);
}
#[test]
fn write_eob_pt_eob_16_round_trip_tx8x8() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_eob_pt(&mut writer, &mut enc_cdfs, 16, TX_8X8, TX_CLASS_2D, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let eob = read_eob(&mut dec, &mut dec_cdfs, TX_8X8, TX_CLASS_2D, 0, 0);
assert_eq!(eob, 16);
}
#[test]
fn write_eob_pt_eob_5_round_trip_tx16x16_chroma_horiz() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_eob_pt(
&mut writer,
&mut enc_cdfs,
5,
TX_16X16,
TX_CLASS_HORIZ,
1,
0,
)
.unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let eob = read_eob(&mut dec, &mut dec_cdfs, TX_16X16, TX_CLASS_HORIZ, 1, 0);
assert_eq!(eob, 5);
}
#[test]
fn write_eob_pt_eob_17_round_trip_tx32x32_inter() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_eob_pt(&mut writer, &mut enc_cdfs, 17, TX_32X32, TX_CLASS_2D, 0, 1).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let eob = read_eob(&mut dec, &mut dec_cdfs, TX_32X32, TX_CLASS_2D, 0, 1);
assert_eq!(eob, 17);
}
#[test]
fn write_eob_pt_rejects_eob_zero() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_eob_pt(&mut writer, &mut cdfs, 0, TX_4X4, TX_CLASS_2D, 0, 0).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn write_eob_pt_rejects_out_of_range_tx_size() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err =
write_eob_pt(&mut writer, &mut cdfs, 1, TX_SIZES_ALL, TX_CLASS_2D, 0, 0).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn write_eob_pt_rejects_eob_overflow_for_tx_size() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_eob_pt(&mut writer, &mut cdfs, 64, TX_4X4, TX_CLASS_2D, 0, 0).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
fn read_dc_sign(
dec: &mut SymbolDecoder<'_>,
cdfs: &mut TileCdfContext,
ptype: usize,
ctx: usize,
) -> u32 {
let cdf = cdfs.dc_sign_cdf(ptype, ctx).unwrap();
dec.read_symbol(cdf).unwrap()
}
#[test]
fn write_dc_sign_positive_round_trip_luma() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_dc_sign(&mut writer, &mut enc_cdfs, 0, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let s = read_dc_sign(&mut dec, &mut dec_cdfs, 0, 0);
assert_eq!(s, 0);
}
#[test]
fn write_dc_sign_negative_round_trip_chroma_ctx2() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_dc_sign(&mut writer, &mut enc_cdfs, 1, 1, 2).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let s = read_dc_sign(&mut dec, &mut dec_cdfs, 1, 2);
assert_eq!(s, 1);
}
#[test]
fn write_dc_sign_rejects_out_of_range_symbol() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_dc_sign(&mut writer, &mut cdfs, 2, 0, 0).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn write_dc_sign_rejects_out_of_range_ctx() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_dc_sign(&mut writer, &mut cdfs, 0, 0, DC_SIGN_CONTEXTS).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn sequence_txb_skip_then_eob_pt_round_trip() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_txb_skip(&mut writer, &mut enc_cdfs, 0, 0, 0).unwrap();
write_eob_pt(&mut writer, &mut enc_cdfs, 4, TX_4X4, TX_CLASS_2D, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let all_zero = read_all_zero(&mut dec, &mut dec_cdfs, 0, 0);
assert_eq!(all_zero, 0);
let eob = read_eob(&mut dec, &mut dec_cdfs, TX_4X4, TX_CLASS_2D, 0, 0);
assert_eq!(eob, 4);
}
fn read_coeff_base_eob(
dec: &mut SymbolDecoder<'_>,
cdfs: &mut TileCdfContext,
tx_sz_ctx: usize,
ptype: usize,
ctx: usize,
) -> u32 {
let cdf = cdfs.coeff_base_eob_cdf(tx_sz_ctx, ptype, ctx).unwrap();
dec.read_symbol(cdf).unwrap()
}
#[test]
fn write_coeff_base_eob_zero_round_trip() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_coeff_base_eob(&mut writer, &mut enc_cdfs, 0, 0, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let sym = read_coeff_base_eob(&mut dec, &mut dec_cdfs, 0, 0, 0);
assert_eq!(sym, 0);
}
#[test]
fn write_coeff_base_eob_max_sym_round_trip_chroma() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_coeff_base_eob(&mut writer, &mut enc_cdfs, 2, 2, 1, 3).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let sym = read_coeff_base_eob(&mut dec, &mut dec_cdfs, 2, 1, 3);
assert_eq!(sym, 2);
}
#[test]
fn write_coeff_base_eob_rejects_out_of_range_symbol() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_coeff_base_eob(&mut writer, &mut cdfs, 3, 0, 0, 0).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn write_coeff_base_eob_rejects_out_of_range_ctx() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_coeff_base_eob(&mut writer, &mut cdfs, 0, 0, 0, SIG_COEF_CONTEXTS_EOB)
.unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
fn read_coeff_base(
dec: &mut SymbolDecoder<'_>,
cdfs: &mut TileCdfContext,
tx_sz_ctx: usize,
ptype: usize,
ctx: usize,
) -> u32 {
let cdf = cdfs.coeff_base_cdf(tx_sz_ctx, ptype, ctx).unwrap();
dec.read_symbol(cdf).unwrap()
}
#[test]
fn write_coeff_base_zero_round_trip() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_coeff_base(&mut writer, &mut enc_cdfs, 0, 0, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let sym = read_coeff_base(&mut dec, &mut dec_cdfs, 0, 0, 0);
assert_eq!(sym, 0);
}
#[test]
fn write_coeff_base_continues_round_trip_luma_upper_ctx() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_coeff_base(&mut writer, &mut enc_cdfs, 3, 3, 0, SIG_COEF_CONTEXTS - 1).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let sym = read_coeff_base(&mut dec, &mut dec_cdfs, 3, 0, SIG_COEF_CONTEXTS - 1);
assert_eq!(sym, 3);
}
#[test]
fn write_coeff_base_rejects_out_of_range_symbol() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_coeff_base(&mut writer, &mut cdfs, 4, 0, 0, 0).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn write_coeff_base_rejects_out_of_range_ctx() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_coeff_base(&mut writer, &mut cdfs, 0, 0, 0, SIG_COEF_CONTEXTS).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
fn read_coeff_br(
dec: &mut SymbolDecoder<'_>,
cdfs: &mut TileCdfContext,
tx_sz_ctx: usize,
ptype: usize,
ctx: usize,
) -> u32 {
let cdf = cdfs.coeff_br_cdf(tx_sz_ctx, ptype, ctx).unwrap();
dec.read_symbol(cdf).unwrap()
}
#[test]
fn write_coeff_br_zero_round_trip() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_coeff_br(&mut writer, &mut enc_cdfs, 0, 0, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let sym = read_coeff_br(&mut dec, &mut dec_cdfs, 0, 0, 0);
assert_eq!(sym, 0);
}
#[test]
fn write_coeff_br_continues_round_trip_chroma_upper_ctx() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_coeff_br(
&mut writer,
&mut enc_cdfs,
(BR_CDF_SIZE - 1) as u8,
1, 1, LEVEL_CONTEXTS - 1,
)
.unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let sym = read_coeff_br(&mut dec, &mut dec_cdfs, 1, 1, LEVEL_CONTEXTS - 1);
assert_eq!(sym, (BR_CDF_SIZE - 1) as u32);
}
#[test]
fn write_coeff_br_tx_size_clamp_round_trip() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_coeff_br(&mut writer, &mut enc_cdfs, 1, 4, 0, 0).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let sym = read_coeff_br(&mut dec, &mut dec_cdfs, 4, 0, 0);
assert_eq!(sym, 1);
}
#[test]
fn write_coeff_br_rejects_out_of_range_symbol() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_coeff_br(&mut writer, &mut cdfs, BR_CDF_SIZE as u8, 0, 0, 0).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn write_coeff_br_rejects_out_of_range_ctx() {
let mut cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let err = write_coeff_br(&mut writer, &mut cdfs, 0, 0, 0, LEVEL_CONTEXTS).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn driver_shape_eob2_round_trip_tx4x4() {
let scan_pos_eob = 1usize; let scan_pos_dc = 0usize;
let mut quant_after_eob = [0i32; 16];
quant_after_eob[scan_pos_eob] = 1;
let ctx_eob = get_coeff_base_eob_ctx(&[0i32; 16], TX_4X4, TX_CLASS_2D, scan_pos_eob, 1);
let ctx_base =
get_coeff_base_ctx(&quant_after_eob, TX_4X4, TX_CLASS_2D, scan_pos_dc, 0, false);
let ctx_br = get_br_ctx(&quant_after_eob, TX_4X4, TX_CLASS_2D, scan_pos_dc);
let tx_sz_ctx = 0usize;
let ptype = 0usize;
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_coeff_base_eob(&mut writer, &mut enc_cdfs, 0, tx_sz_ctx, ptype, ctx_eob).unwrap();
write_coeff_base(&mut writer, &mut enc_cdfs, 3, tx_sz_ctx, ptype, ctx_base).unwrap();
write_coeff_br(&mut writer, &mut enc_cdfs, 0, tx_sz_ctx, ptype, ctx_br).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let sym_eob = {
let cdf = dec_cdfs
.coeff_base_eob_cdf(tx_sz_ctx, ptype, ctx_eob)
.unwrap();
dec.read_symbol(cdf).unwrap()
};
assert_eq!(sym_eob, 0, "coeff_base_eob round-trips");
let sym_base = {
let cdf = dec_cdfs.coeff_base_cdf(tx_sz_ctx, ptype, ctx_base).unwrap();
dec.read_symbol(cdf).unwrap()
};
assert_eq!(sym_base, 3, "coeff_base round-trips");
let sym_br = {
let cdf = dec_cdfs.coeff_br_cdf(tx_sz_ctx, ptype, ctx_br).unwrap();
dec.read_symbol(cdf).unwrap()
};
assert_eq!(sym_br, 0, "coeff_br round-trips");
}
fn read_golomb(dec: &mut SymbolDecoder<'_>) -> u32 {
let mut length: u32 = 0;
loop {
length += 1;
let bit = dec.read_literal(1).unwrap();
if bit != 0 {
break;
}
assert!(length <= GOLOMB_MAX_LENGTH, "length must terminate by 20");
}
let mut x: u32 = 1;
if length >= 2 {
let payload_bits = length - 1;
for _ in 0..payload_bits {
let bit = dec.read_literal(1).unwrap();
x = (x << 1) | bit;
}
}
x + (COEFF_BASE_RANGE as u32) + (NUM_BASE_LEVELS as u32)
}
#[test]
fn write_golomb_min_magnitude_round_trip() {
let mut writer = SymbolWriter::new(false);
write_golomb(&mut writer, 15).unwrap();
let bytes = writer.finish();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let value = read_golomb(&mut dec);
assert_eq!(value, 15);
}
#[test]
fn write_golomb_value_16_round_trip() {
let mut writer = SymbolWriter::new(false);
write_golomb(&mut writer, 16).unwrap();
let bytes = writer.finish();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let value = read_golomb(&mut dec);
assert_eq!(value, 16);
}
#[test]
fn write_golomb_value_21_round_trip() {
let mut writer = SymbolWriter::new(false);
write_golomb(&mut writer, 21).unwrap();
let bytes = writer.finish();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let value = read_golomb(&mut dec);
assert_eq!(value, 21);
}
#[test]
fn write_golomb_value_270_round_trip() {
let mut writer = SymbolWriter::new(false);
write_golomb(&mut writer, 270).unwrap();
let bytes = writer.finish();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let value = read_golomb(&mut dec);
assert_eq!(value, 270);
}
#[test]
fn write_golomb_small_magnitudes_round_trip_exhaustive() {
for value in 15u32..=29 {
let mut writer = SymbolWriter::new(false);
write_golomb(&mut writer, value).unwrap();
let bytes = writer.finish();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let got = read_golomb(&mut dec);
assert_eq!(got, value, "value {value} did not round-trip");
}
}
#[test]
fn write_golomb_max_magnitude_round_trip() {
let value: u32 = (0xFFFFFu32) + (COEFF_BASE_RANGE as u32) + (NUM_BASE_LEVELS as u32);
let mut writer = SymbolWriter::new(false);
write_golomb(&mut writer, value).unwrap();
let bytes = writer.finish();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let got = read_golomb(&mut dec);
assert_eq!(got, value);
}
#[test]
fn write_golomb_rejects_value_at_threshold() {
let mut writer = SymbolWriter::new(false);
let err = write_golomb(&mut writer, 14).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn write_golomb_rejects_zero() {
let mut writer = SymbolWriter::new(false);
let err = write_golomb(&mut writer, 0).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn write_golomb_rejects_value_past_conformance_limit() {
let value: u32 = (0xFFFFFu32) + (COEFF_BASE_RANGE as u32) + (NUM_BASE_LEVELS as u32) + 1;
let mut writer = SymbolWriter::new(false);
let err = write_golomb(&mut writer, value).unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
#[test]
fn sequence_dc_sign_then_golomb_round_trip() {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
write_dc_sign(&mut writer, &mut enc_cdfs, 1, 0, 0).unwrap();
write_golomb(&mut writer, 18).unwrap();
let bytes = writer.finish();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let sign = {
let cdf = dec_cdfs.dc_sign_cdf(0, 0).unwrap();
dec.read_symbol(cdf).unwrap()
};
assert_eq!(sign, 1);
let magnitude = read_golomb(&mut dec);
assert_eq!(magnitude, 18);
}
use crate::cdf::{PartitionWalker, TileGeometry, TX_4X4 as TX_4X4_C, TX_8X8 as TX_8X8_C};
fn make_walker() -> PartitionWalker {
let geom = TileGeometry {
mi_row_start: 0,
mi_row_end: 8,
mi_col_start: 0,
mi_col_end: 8,
};
PartitionWalker::new(8, 8, geom).expect("walker construction")
}
#[allow(clippy::too_many_arguments)]
fn drive_round_trip(
plane: u8,
is_inter: u8,
tx_size: usize,
tx_class: usize,
txb_skip_ctx: usize,
dc_sign_ctx: usize,
scan: &[u16],
quant_in: &[i32],
) -> (crate::cdf::CoefficientsReadout, Vec<i32>) {
let mut enc_cdfs = TileCdfContext::new_from_defaults();
let mut writer = SymbolWriter::new(false);
let wout = write_coefficients(
&mut writer,
&mut enc_cdfs,
plane,
is_inter,
tx_size,
tx_class,
txb_skip_ctx,
dc_sign_ctx,
scan,
quant_in,
)
.expect("driver encode succeeded");
let bytes = writer.finish();
let mut walker = make_walker();
let mut dec_cdfs = TileCdfContext::new_from_defaults();
let mut dec = SymbolDecoder::init_symbol(&bytes, bytes.len(), false).unwrap();
let mut quant_out = vec![0i32; quant_in.len()];
let readout = walker
.coefficients(
&mut dec,
&mut dec_cdfs,
plane,
is_inter,
tx_size,
tx_class,
txb_skip_ctx,
dc_sign_ctx,
scan,
&mut quant_out,
)
.expect("reader decode succeeded");
if quant_in.iter().all(|&q| q == 0) {
assert_eq!(wout.eob, 0, "encoder eob == 0 for all-zero input");
assert!(readout.all_zero, "decoder all_zero == true");
assert_eq!(readout.eob, 0);
} else {
assert!(!readout.all_zero, "non-empty block must clear all_zero");
}
assert_eq!(
wout, readout,
"writer readout mirror must equal reader readout"
);
(readout, quant_out)
}
#[test]
fn driver_round_trip_all_zero_tx4x4_luma() {
let scan: Vec<u16> = (0..16u16).collect();
let quant_in = vec![0i32; 16];
let (_readout, quant_out) =
drive_round_trip(0, 0, TX_4X4_C, TX_CLASS_2D, 0, 0, &scan, &quant_in);
assert!(quant_out.iter().all(|&q| q == 0));
}
#[test]
fn driver_round_trip_single_dc_one_tx4x4_luma() {
let scan: Vec<u16> = (0..16u16).collect();
let mut quant_in = vec![0i32; 16];
quant_in[0] = 1;
let (_readout, quant_out) =
drive_round_trip(0, 0, TX_4X4_C, TX_CLASS_2D, 0, 0, &scan, &quant_in);
assert_eq!(quant_out[0], 1);
assert!(quant_out[1..].iter().all(|&q| q == 0));
}
#[test]
fn driver_round_trip_two_coeffs_signed_tx4x4_luma() {
let scan: Vec<u16> = (0..16u16).collect();
let mut quant_in = vec![0i32; 16];
quant_in[0] = -1;
quant_in[1] = 2;
let (_readout, quant_out) =
drive_round_trip(0, 0, TX_4X4_C, TX_CLASS_2D, 0, 0, &scan, &quant_in);
assert_eq!(quant_out[0], -1);
assert_eq!(quant_out[1], 2);
assert!(quant_out[2..].iter().all(|&q| q == 0));
}
#[test]
fn driver_round_trip_level3_no_br_extension_tx4x4_luma() {
let scan: Vec<u16> = (0..16u16).collect();
let mut quant_in = vec![0i32; 16];
quant_in[0] = 3;
let (_readout, quant_out) =
drive_round_trip(0, 0, TX_4X4_C, TX_CLASS_2D, 0, 0, &scan, &quant_in);
assert_eq!(quant_out[0], 3);
}
#[test]
fn driver_round_trip_level8_br_chain_terminate_tx4x4_luma() {
let scan: Vec<u16> = (0..16u16).collect();
let mut quant_in = vec![0i32; 16];
quant_in[0] = 8;
let (_readout, quant_out) =
drive_round_trip(0, 0, TX_4X4_C, TX_CLASS_2D, 0, 0, &scan, &quant_in);
assert_eq!(quant_out[0], 8);
}
#[test]
fn driver_round_trip_level14_boundary_tx4x4_luma() {
let scan: Vec<u16> = (0..16u16).collect();
let mut quant_in = vec![0i32; 16];
quant_in[0] = 14;
let (_readout, quant_out) =
drive_round_trip(0, 0, TX_4X4_C, TX_CLASS_2D, 0, 0, &scan, &quant_in);
assert_eq!(quant_out[0], 14);
}
#[test]
fn driver_round_trip_level15_min_golomb_tx4x4_luma() {
let scan: Vec<u16> = (0..16u16).collect();
let mut quant_in = vec![0i32; 16];
quant_in[0] = 15;
let (_readout, quant_out) =
drive_round_trip(0, 0, TX_4X4_C, TX_CLASS_2D, 0, 0, &scan, &quant_in);
assert_eq!(quant_out[0], 15);
}
#[test]
fn driver_round_trip_ac_golomb_with_sign_bit_tx4x4_luma() {
let scan: Vec<u16> = (0..16u16).collect();
let mut quant_in = vec![0i32; 16];
quant_in[1] = -30;
let (readout, quant_out) =
drive_round_trip(0, 0, TX_4X4_C, TX_CLASS_2D, 0, 0, &scan, &quant_in);
assert_eq!(quant_out[1], -30);
assert_eq!(quant_out[0], 0);
assert_eq!(readout.eob, 2, "eob counts past the highest non-zero");
}
#[test]
fn driver_round_trip_dense_small_pattern_tx8x8_luma() {
let scan: Vec<u16> = (0..64u16).collect();
let mut quant_in = vec![0i32; 64];
quant_in[0] = 2;
quant_in[1] = -1;
quant_in[2] = 3;
quant_in[3] = -2;
quant_in[4] = 1;
quant_in[5] = -1;
let (readout, quant_out) =
drive_round_trip(0, 0, TX_8X8_C, TX_CLASS_2D, 0, 0, &scan, &quant_in);
assert_eq!(readout.eob, 6, "eob = 6 (highest non-zero at scan[5])");
for (i, (&expect, &got)) in quant_in.iter().zip(quant_out.iter()).enumerate() {
assert_eq!(
got, expect,
"Quant[{i}] mismatch: expected {expect}, got {got}"
);
}
}
#[test]
fn driver_round_trip_chroma_ptype_axis_tx4x4() {
let scan: Vec<u16> = (0..16u16).collect();
let mut quant_in = vec![0i32; 16];
quant_in[0] = 4;
quant_in[2] = -1;
let (_readout, quant_out) =
drive_round_trip(1, 0, TX_4X4_C, TX_CLASS_2D, 5, 1, &scan, &quant_in);
assert_eq!(quant_out[0], 4);
assert_eq!(quant_out[2], -1);
}
#[test]
fn driver_round_trip_large_golomb_tx4x4_luma() {
let scan: Vec<u16> = (0..16u16).collect();
let mut quant_in = vec![0i32; 16];
quant_in[0] = 1;
quant_in[1] = -2;
quant_in[2] = 200;
let (readout, quant_out) =
drive_round_trip(0, 0, TX_4X4_C, TX_CLASS_2D, 0, 0, &scan, &quant_in);
assert_eq!(readout.eob, 3);
assert_eq!(quant_out[0], 1);
assert_eq!(quant_out[1], -2);
assert_eq!(quant_out[2], 200);
}
#[test]
fn driver_rejects_out_of_range_arguments() {
let scan: Vec<u16> = (0..16u16).collect();
let quant_in = vec![0i32; 16];
let mut cdfs = TileCdfContext::new_from_defaults();
let mut w = SymbolWriter::new(false);
let err = write_coefficients(
&mut w,
&mut cdfs,
0,
0,
TX_SIZES_ALL,
TX_CLASS_2D,
0,
0,
&scan,
&quant_in,
)
.unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
let mut w = SymbolWriter::new(false);
let err = write_coefficients(&mut w, &mut cdfs, 0, 0, TX_4X4_C, 3, 0, 0, &scan, &quant_in)
.unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
let mut w = SymbolWriter::new(false);
let err = write_coefficients(
&mut w,
&mut cdfs,
3,
0,
TX_4X4_C,
TX_CLASS_2D,
0,
0,
&scan,
&quant_in,
)
.unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
let mut w = SymbolWriter::new(false);
let err = write_coefficients(
&mut w,
&mut cdfs,
0,
2,
TX_4X4_C,
TX_CLASS_2D,
0,
0,
&scan,
&quant_in,
)
.unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
let mut w = SymbolWriter::new(false);
let err = write_coefficients(
&mut w,
&mut cdfs,
0,
0,
TX_4X4_C,
TX_CLASS_2D,
TXB_SKIP_CONTEXTS,
0,
&scan,
&quant_in,
)
.unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
let mut w = SymbolWriter::new(false);
let err = write_coefficients(
&mut w,
&mut cdfs,
0,
0,
TX_4X4_C,
TX_CLASS_2D,
0,
DC_SIGN_CONTEXTS,
&scan,
&quant_in,
)
.unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
let short_scan: Vec<u16> = (0..4u16).collect();
let mut w = SymbolWriter::new(false);
let err = write_coefficients(
&mut w,
&mut cdfs,
0,
0,
TX_4X4_C,
TX_CLASS_2D,
0,
0,
&short_scan,
&quant_in,
)
.unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
let tiny_quant: Vec<i32> = vec![0i32; 4];
let mut w = SymbolWriter::new(false);
let err = write_coefficients(
&mut w,
&mut cdfs,
0,
0,
TX_4X4_C,
TX_CLASS_2D,
0,
0,
&scan,
&tiny_quant,
)
.unwrap_err();
assert!(matches!(err, Error::PartitionWalkOutOfRange));
}
}