#![allow(dead_code)]
static DC_Q_TABLE: [u16; 64] = [
4, 8, 10, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 35, 37, 39, 41, 43, 46, 49,
52, 56, 60, 64, 68, 73, 78, 83, 88, 94, 100, 107, 114, 122, 131, 140,
150, 161, 172, 185, 199, 214, 230, 247, 266, 286, 308, 331, 355, 381, 409, 440,
];
static AC_Q_TABLE: [u16; 64] = [
4, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
39, 40, 41, 42, 43, 44, 45, 47, 49, 51, 53, 55, 58, 61, 64, 67,
70, 74, 78, 82, 87, 92, 97, 102, 108, 114, 120, 127, 134, 142, 150, 158,
];
pub fn dc_q(base_q_idx: u8) -> u16 {
let q = base_q_idx as usize;
if q == 0 {
return 1;
}
if q < 64 {
DC_Q_TABLE[q]
} else {
440
}
}
pub fn ac_q(base_q_idx: u8) -> u16 {
let q = base_q_idx as usize;
if q == 0 {
return 1;
}
if q < 64 {
AC_Q_TABLE[q]
} else {
158
}
}
pub fn quantize_coeff(coeff: i32, q: u16) -> i32 {
if q <= 1 {
return coeff;
}
let q_i32 = q as i32;
let half = q_i32 / 2;
if coeff >= 0 {
(coeff + half) / q_i32
} else {
-(((-coeff) + half) / q_i32)
}
}
pub fn dequantize_coeff(level: i32, q: u16) -> i32 {
level * (q as i32)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_dc_q_zero_is_one() {
assert_eq!(dc_q(0), 1, "dc_q(0) must be 1 (lossless identity)");
}
#[test]
fn test_ac_q_zero_is_one() {
assert_eq!(ac_q(0), 1, "ac_q(0) must be 1 (lossless identity)");
}
#[test]
fn test_dc_q_1_is_8() {
assert_eq!(dc_q(1), 8, "dc_q(1) must be 8 per AV1 spec Table 7.10");
}
#[test]
fn test_dc_q_63_is_440() {
assert_eq!(dc_q(63), 440);
}
#[test]
fn test_dc_q_clamping() {
assert_eq!(dc_q(64), 440);
assert_eq!(dc_q(128), 440);
assert_eq!(dc_q(255), 440);
}
#[test]
fn test_ac_q_clamping() {
assert_eq!(ac_q(64), 158);
assert_eq!(ac_q(255), 158);
}
#[test]
fn test_dc_q_table_spot_checks() {
assert_eq!(dc_q(2), 10);
assert_eq!(dc_q(5), 14);
assert_eq!(dc_q(35), 64);
assert_eq!(dc_q(36), 68);
assert_eq!(dc_q(63), 440);
}
#[test]
fn test_quantize_dequantize_round_trip() {
let q_values: &[u8] = &[0, 1, 8, 16, 32, 63, 128, 255];
let coeffs: &[i32] = &[0, 1, -1, 100, -100, 255, -255, 1000, -1000];
for &q_idx in q_values {
let q = dc_q(q_idx);
for &coeff in coeffs {
let level = quantize_coeff(coeff, q);
let recon = dequantize_coeff(level, q);
let error = (coeff - recon).unsigned_abs();
assert!(
error < q as u32,
"round-trip error {error} >= q={q} for coeff={coeff}, q_idx={q_idx}"
);
}
}
}
#[test]
fn test_lossless_identity() {
for coeff in [-1000i32, -1, 0, 1, 255, 1000] {
assert_eq!(quantize_coeff(coeff, 1), coeff);
assert_eq!(dequantize_coeff(coeff, 1), coeff);
}
}
#[test]
fn test_quantize_rounding() {
assert_eq!(quantize_coeff(5, 10), 1);
assert_eq!(quantize_coeff(4, 10), 0);
assert_eq!(quantize_coeff(-5, 10), -1);
assert_eq!(quantize_coeff(-4, 10), 0);
}
#[test]
fn test_dequantize_sign_preservation() {
assert_eq!(dequantize_coeff(3, 10), 30);
assert_eq!(dequantize_coeff(-3, 10), -30);
assert_eq!(dequantize_coeff(0, 100), 0);
}
}