use oxideav_core::bits::BitReader;
use oxideav_core::{Error, Result};
use crate::idct::{idct_intra, idct_signed};
use crate::tables::{decode_tcoeff, TcoeffSym, ZIGZAG};
pub fn decode_intra_dc(br: &mut BitReader<'_>) -> Result<i32> {
let v = br.read_u32(8)? as u8;
if v == 0x00 || v == 0x80 {
return Err(Error::invalid(format!(
"h261 INTRA DC: forbidden bitstream value 0x{v:02x}"
)));
}
if v == 0xFF {
return Ok(1024);
}
Ok((v as i32) * 8)
}
pub fn dequant_ac(level: i32, quant: u32) -> i32 {
if level == 0 {
return 0;
}
let q = quant as i32;
let (sign, abs) = if level > 0 { (1, level) } else { (-1, -level) };
let mut mag = q * (2 * abs + 1);
if quant & 1 == 0 {
mag -= 1;
}
let rec = sign * mag;
rec.clamp(-2048, 2047)
}
pub fn decode_intra_block(br: &mut BitReader<'_>, quant: u32, out: &mut [u8; 64]) -> Result<()> {
let dc_level = decode_intra_dc(br)?;
let mut coeffs = [0i32; 64];
coeffs[0] = dc_level;
decode_ac_coeffs(br, &mut coeffs, 1, quant, false)?;
idct_intra(&coeffs, out);
Ok(())
}
pub fn decode_inter_block(br: &mut BitReader<'_>, quant: u32, out: &mut [i32; 64]) -> Result<()> {
let mut coeffs = [0i32; 64];
decode_ac_coeffs(br, &mut coeffs, 0, quant, true)?;
idct_signed(&coeffs, out);
Ok(())
}
fn decode_ac_coeffs(
br: &mut BitReader<'_>,
coeffs: &mut [i32; 64],
start: usize,
quant: u32,
is_first_inter: bool,
) -> Result<()> {
let mut idx = start;
let mut first = is_first_inter;
loop {
let sym = decode_tcoeff(br, first)?;
first = false;
match sym {
TcoeffSym::Eob => return Ok(()),
TcoeffSym::RunLevel { run, level_abs } => {
let sign = br.read_u1()?; let level_signed = if sign == 1 {
-(level_abs as i32)
} else {
level_abs as i32
};
idx = idx.saturating_add(run as usize);
if idx > 63 {
return Err(Error::invalid(format!(
"h261 block: AC run overflow (idx={idx}, run={run})"
)));
}
coeffs[ZIGZAG[idx]] = dequant_ac(level_signed, quant);
idx += 1;
}
TcoeffSym::Escape => {
let run = br.read_u32(6)? as u8;
let raw = br.read_u32(8)?;
let level: i32 = if raw == 0 {
return Err(Error::invalid("h261 escape: level == 0 forbidden"));
} else if raw == 0x80 {
return Err(Error::invalid(
"h261 escape: level == -128 forbidden (per Table 5 level FLC)",
));
} else if raw & 0x80 != 0 {
raw as i32 - 256
} else {
raw as i32
};
idx = idx.saturating_add(run as usize);
if idx > 63 {
return Err(Error::invalid(format!(
"h261 escape: run overflow (idx={idx}, run={run}, level={level})"
)));
}
coeffs[ZIGZAG[idx]] = dequant_ac(level, quant);
idx += 1;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intra_dc_basic() {
let data = [0x10u8];
let mut br = BitReader::new(&data);
assert_eq!(decode_intra_dc(&mut br).unwrap(), 0x10 * 8);
}
#[test]
fn intra_dc_special_ff() {
let data = [0xFFu8];
let mut br = BitReader::new(&data);
assert_eq!(decode_intra_dc(&mut br).unwrap(), 1024);
}
#[test]
fn intra_dc_zero_is_forbidden() {
let data = [0x00u8];
let mut br = BitReader::new(&data);
assert!(decode_intra_dc(&mut br).is_err());
}
#[test]
fn intra_dc_0x80_is_forbidden() {
let data = [0x80u8];
let mut br = BitReader::new(&data);
assert!(decode_intra_dc(&mut br).is_err());
}
#[test]
fn dequant_level_1_odd_q() {
assert_eq!(dequant_ac(1, 1), 3);
assert_eq!(dequant_ac(-1, 1), -3);
}
#[test]
fn dequant_level_1_even_q() {
assert_eq!(dequant_ac(1, 2), 5);
assert_eq!(dequant_ac(-1, 2), -5);
}
#[test]
fn dequant_matches_spec_row_for_q8_l1() {
assert_eq!(dequant_ac(1, 8), 23);
assert_eq!(dequant_ac(2, 8), 39);
assert_eq!(dequant_ac(-1, 8), -23);
}
}