use super::MbRowEntry;
use super::context::PreviousMacroBlock;
use super::tables::{DequantPair, FrameTables};
use crate::common::prediction::{MB_COEFF_SIZE, coeff_block};
use crate::common::transform;
use crate::common::types::*;
use crate::decoder::bit_reader::{ActivePartitionReader, VP8HeaderBitReader};
use crate::decoder::internal_error::InternalDecodeError;
const ZIGZAG: [u8; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
const PROB_DCT_CAT: [[u8; 12]; 6] = [
[159, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[165, 145, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[173, 148, 140, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[176, 155, 140, 135, 0, 0, 0, 0, 0, 0, 0, 0],
[180, 157, 141, 134, 130, 0, 0, 0, 0, 0, 0, 0],
[254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0],
];
const CAT_LENGTHS: [usize; 4] = [3, 4, 5, 11];
#[inline(never)]
fn get_large_value(reader: &mut ActivePartitionReader<'_>, prob: &[u8; 11]) -> i32 {
if reader.get_bit(prob[6]) == 0 {
if reader.get_bit(prob[7]) == 0 {
5 + reader.get_bit(159)
} else {
7 + 2 * reader.get_bit(165) + reader.get_bit(145)
}
} else {
let bit1 = reader.get_bit(prob[8]);
let bit0 = reader.get_bit(prob[9 + bit1 as usize]);
let cat = (2 * bit1 + bit0) as usize;
let cat_probs = &PROB_DCT_CAT[2 + cat];
let cat_len = CAT_LENGTHS[cat];
let mut extra = 0i32;
for &prob in cat_probs.iter().take(cat_len) {
extra = extra + extra + reader.get_bit(prob);
}
3 + (8 << cat) + extra
}
}
#[inline(never)]
fn read_coefficients(
reader: &mut ActivePartitionReader<'_>,
output: &mut [i32; 16],
probs: &[[[u8; 11]; 3]; 17],
first: usize,
complexity: usize,
dq: DequantPair,
) -> bool {
debug_assert!(complexity <= 2);
let dq_pair = [i32::from(dq.dc), i32::from(dq.ac)];
let mut n = first;
let mut prob = &probs[n][complexity];
while n < 16 {
if reader.get_bit(prob[0]) == 0 {
break;
}
while reader.get_bit(prob[1]) == 0 {
n += 1;
if n >= 16 {
return true;
}
prob = &probs[n][0]; }
let v: i32;
let next_ctx: usize;
if reader.get_bit(prob[2]) == 0 {
v = 1;
next_ctx = 1;
} else {
if reader.get_bit(prob[3]) == 0 {
if reader.get_bit(prob[4]) == 0 {
v = 2;
} else {
v = 3 + reader.get_bit(prob[5]);
}
} else {
v = get_large_value(reader, prob);
}
next_ctx = 2;
}
output[ZIGZAG[n] as usize] = reader.get_signed(v) * dq_pair[(n > 0) as usize];
n += 1;
if n < 16 {
prob = &probs[n][next_ctx];
}
}
n > first
}
pub(super) fn read_residual_data(
reader: &mut ActivePartitionReader<'_>,
mb: &mut MbRowEntry,
coeff_blocks: &mut [i32; MB_COEFF_SIZE],
probs: &[[[[u8; 11]; 3]; 17]; 4],
dequant: &[DequantPair; 3],
top: &mut PreviousMacroBlock,
left: &mut PreviousMacroBlock,
) -> Result<(), InternalDecodeError> {
const PLANE_Y1: usize = 0; const PLANE_Y2: usize = 1; const PLANE_CHROMA: usize = 2; const PLANE_Y0: usize = 3;
let has_y2 = mb.luma_mode != LumaMode::B;
let (y_plane, first_y) = if has_y2 {
let complexity = top.complexity[0] + left.complexity[0];
let mut block = [0i32; 16];
let n = read_coefficients(
reader,
&mut block,
&probs[PLANE_Y2],
0,
complexity as usize,
dequant[1], );
let ctx = u8::from(n);
left.complexity[0] = ctx;
top.complexity[0] = ctx;
let has_ac = block[1..].iter().any(|&c| c != 0);
if has_ac {
transform::iwht4x4(&mut block);
for (k, &val) in block.iter().enumerate() {
coeff_blocks[16 * k] = val;
if val != 0 {
mb.non_zero_blocks |= 1u32 << k;
}
}
} else if block[0] != 0 {
let dc0 = (block[0] + 3) >> 3;
for k in 0..16 {
coeff_blocks[16 * k] = dc0;
}
mb.non_zero_blocks |= 0xFFFF;
}
(PLANE_Y1, 1usize)
} else {
(PLANE_Y0, 0usize)
};
for y in 0usize..4 {
let mut left_ctx = left.complexity[y + 1];
for x in 0usize..4 {
let i = x + y * 4;
let complexity = top.complexity[x + 1] + left_ctx;
let block = coeff_block(coeff_blocks, i);
let n = read_coefficients(
reader,
block,
&probs[y_plane],
first_y,
complexity as usize,
dequant[0],
);
if block[0] != 0 || n {
mb.non_zero_dct = true;
mb.non_zero_blocks |= 1u32 << i;
}
left_ctx = u8::from(n);
top.complexity[x + 1] = u8::from(n);
}
left.complexity[y + 1] = left_ctx;
}
let chroma_probs = &probs[PLANE_CHROMA];
for &j in &[5usize, 7usize] {
for y in 0usize..2 {
let mut left_ctx = left.complexity[y + j];
for x in 0usize..2 {
let i = x + y * 2 + if j == 5 { 16 } else { 20 };
let complexity = top.complexity[x + j] + left_ctx;
let block = coeff_block(coeff_blocks, i);
let n = read_coefficients(
reader,
block,
chroma_probs,
0,
complexity as usize,
dequant[2], );
if block[0] != 0 || n {
mb.non_zero_dct = true;
mb.non_zero_blocks |= 1u32 << i;
}
if !mb.has_nonzero_uv_ac {
for &coeff in block[1..].iter() {
if coeff != 0 {
mb.has_nonzero_uv_ac = true;
break;
}
}
}
left_ctx = u8::from(n);
top.complexity[x + j] = u8::from(n);
}
left.complexity[y + j] = left_ctx;
}
}
if reader.is_eof() {
return Err(InternalDecodeError::BitStreamError);
}
Ok(())
}
#[inline]
fn read_segment_id(b: &mut VP8HeaderBitReader, probs: &[u8; 3]) -> u8 {
if !b.read_bool(probs[0]) {
if !b.read_bool(probs[1]) { 0 } else { 1 }
} else {
if !b.read_bool(probs[2]) { 2 } else { 3 }
}
}
#[inline]
fn read_ymode(b: &mut VP8HeaderBitReader) -> Result<LumaMode, InternalDecodeError> {
if !b.read_bool(145) {
Ok(LumaMode::B)
} else if !b.read_bool(156) {
if !b.read_bool(163) {
Ok(LumaMode::DC)
} else {
Ok(LumaMode::V)
}
} else if !b.read_bool(128) {
Ok(LumaMode::H)
} else {
Ok(LumaMode::TM)
}
}
#[inline]
fn read_uvmode(b: &mut VP8HeaderBitReader) -> Result<ChromaMode, InternalDecodeError> {
if !b.read_bool(142) {
Ok(ChromaMode::DC)
} else if !b.read_bool(114) {
Ok(ChromaMode::V)
} else if !b.read_bool(183) {
Ok(ChromaMode::H)
} else {
Ok(ChromaMode::TM)
}
}
#[inline]
fn read_bmode(
b: &mut VP8HeaderBitReader,
probs: &[Prob; 9],
) -> Result<IntraMode, InternalDecodeError> {
let mode = if !b.read_bool(probs[0]) {
IntraMode::DC
} else if !b.read_bool(probs[1]) {
IntraMode::TM
} else if !b.read_bool(probs[2]) {
IntraMode::VE
} else if !b.read_bool(probs[3]) {
if !b.read_bool(probs[4]) {
IntraMode::HE
} else if !b.read_bool(probs[5]) {
IntraMode::RD
} else {
IntraMode::VR
}
} else {
if !b.read_bool(probs[6]) {
IntraMode::LD
} else if !b.read_bool(probs[7]) {
IntraMode::VL
} else if !b.read_bool(probs[8]) {
IntraMode::HD
} else {
IntraMode::HU
}
};
Ok(mode)
}
pub(super) fn read_macroblock_header(
b: &mut VP8HeaderBitReader,
tables: &FrameTables,
top: &mut PreviousMacroBlock,
left: &mut PreviousMacroBlock,
mb: &mut MbRowEntry,
) -> Result<(), InternalDecodeError> {
if tables.segments_enabled && tables.segments_update_map {
mb.segmentid = read_segment_id(b, &tables.segment_tree_probs);
}
mb.coeffs_skipped = if let Some(prob) = tables.prob_skip_false {
b.read_bool(prob)
} else {
false
};
mb.luma_mode = read_ymode(b)?;
match mb.luma_mode.into_intra() {
None => {
for y in 0usize..4 {
for x in 0usize..4 {
let top_mode = top.bpred[x];
let left_mode = left.bpred[y];
let bmode = read_bmode(
b,
&KEYFRAME_BPRED_MODE_PROBS[top_mode as usize][left_mode as usize],
)?;
mb.bpred[x + y * 4] = bmode;
top.bpred[x] = bmode;
left.bpred[y] = bmode;
}
}
}
Some(mode) => {
for i in 0usize..4 {
mb.bpred[12 + i] = mode;
left.bpred[i] = mode;
}
}
}
mb.chroma_mode = read_uvmode(b)?;
top.bpred = [mb.bpred[12], mb.bpred[13], mb.bpred[14], mb.bpred[15]];
b.check(())?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::decoder::bit_reader::VP8Partitions;
use alloc::vec;
fn make_reader_parts(data: &[u8]) -> VP8Partitions {
let mut parts = VP8Partitions::new();
let boundaries = [(0, data.len())];
parts.init(data.to_vec(), &boundaries);
parts
}
#[test]
fn zigzag_matches_common() {
assert_eq!(ZIGZAG, crate::common::types::ZIGZAG);
}
#[test]
fn prob_dct_cat_matches_common() {
assert_eq!(PROB_DCT_CAT, crate::common::types::PROB_DCT_CAT);
}
#[test]
fn read_coefficients_all_zero() {
let data = vec![0u8; 64];
let mut parts = make_reader_parts(&data);
let mut reader = parts.active_reader(0);
let mut output = [0i32; 16];
let probs = [[[0u8; 11]; 3]; 17];
let dq = DequantPair { dc: 10, ac: 20 };
let n = read_coefficients(&mut reader, &mut output, &probs, 0, 0, dq);
assert!(!n, "should return false for all-zero block");
assert_eq!(output, [0i32; 16], "output should remain zero");
}
#[test]
fn get_large_value_valid() {
let data: alloc::vec::Vec<u8> = (0..64).map(|i| ((i * 37 + 13) & 0xFF) as u8).collect();
let mut parts = make_reader_parts(&data);
let mut reader = parts.active_reader(0);
let prob = [128u8; 11];
let v = get_large_value(&mut reader, &prob);
assert!(v >= 5, "large value must be >= 5, got {v}");
}
#[test]
fn wht_dc_only_broadcast() {
let mut coeff_blocks = [0i32; MB_COEFF_SIZE];
let dc = 80i32;
let dc0 = (dc + 3) >> 3; for k in 0..16 {
coeff_blocks[16 * k] = dc0;
}
for k in 0..16 {
assert_eq!(coeff_blocks[16 * k], dc0, "Y block {k} DC should be {dc0}");
for c in 1..16 {
assert_eq!(
coeff_blocks[16 * k + c],
0,
"Y block {k} coeff {c} should be 0"
);
}
}
}
#[test]
#[allow(clippy::absurd_extreme_comparisons, unused_comparisons)]
fn dequant_pair_dc_ac_indexing() {
let dq = DequantPair { dc: 7, ac: 13 };
let pair = [i32::from(dq.dc), i32::from(dq.ac)];
assert_eq!(pair[(0usize > 0) as usize], 7);
assert_eq!(pair[(1usize > 0) as usize], 13);
assert_eq!(pair[(15usize > 0) as usize], 13);
}
#[test]
fn mb_row_entry_default() {
let mb = MbRowEntry::default();
assert_eq!(mb.segmentid, 0);
assert!(!mb.coeffs_skipped);
assert_eq!(mb.non_zero_blocks, 0);
assert!(!mb.non_zero_dct);
assert!(!mb.has_nonzero_uv_ac);
assert_eq!(mb.luma_mode, LumaMode::DC);
assert_eq!(mb.chroma_mode, ChromaMode::DC);
}
#[test]
fn non_zero_blocks_y2_dc_only() {
let mut mb = MbRowEntry::default();
mb.non_zero_blocks |= 0xFFFF;
assert_eq!(mb.non_zero_blocks & 0xFFFF, 0xFFFF);
assert_eq!(mb.non_zero_blocks >> 16, 0);
}
#[test]
fn complexity_layout() {
let pmb = PreviousMacroBlock::default();
assert_eq!(pmb.complexity.len(), 9);
assert_eq!(pmb.complexity[0], 0); assert_eq!(pmb.complexity[5], 0); assert_eq!(pmb.complexity[7], 0); }
#[test]
fn read_coefficients_first_one() {
let data = vec![0u8; 64];
let mut parts = make_reader_parts(&data);
let mut reader = parts.active_reader(0);
let mut output = [0i32; 16];
let probs = [[[0u8; 11]; 3]; 17];
let dq = DequantPair { dc: 10, ac: 20 };
let n = read_coefficients(&mut reader, &mut output, &probs, 1, 0, dq);
assert!(!n, "should return false for all-zero block at first=1");
assert_eq!(output, [0i32; 16]);
}
#[test]
fn read_coefficients_nonzero() {
let data = vec![0xFFu8; 128];
let mut parts = make_reader_parts(&data);
let mut reader = parts.active_reader(0);
let mut output = [0i32; 16];
let probs = [[[128u8; 11]; 3]; 17];
let dq = DequantPair { dc: 4, ac: 8 };
let _n = read_coefficients(&mut reader, &mut output, &probs, 0, 0, dq);
}
}