pub const DEFAULT_INTRA_MATRIX: [u8; 64] = [
8, 16, 19, 22, 26, 27, 29, 34, 16, 16, 22, 24, 27, 29, 34, 37, 19, 22, 26, 27, 29, 34, 34, 38, 22, 22, 26, 27, 29, 34, 37, 40, 22, 26, 27, 29, 32, 35, 40, 48, 26, 27, 29, 32, 35, 40, 48, 58, 26, 27, 29, 34, 38, 46, 56, 69, 27, 29, 35, 38, 46, 56, 69, 83,
];
pub const DEFAULT_NON_INTRA_MATRIX: [u8; 64] = [16u8; 64];
pub const F_MIN: i32 = -2048;
pub const F_MAX: i32 = 2047;
#[must_use]
pub fn intra_dc_mult(intra_dc_precision: u8) -> i32 {
match intra_dc_precision & 0x03 {
0 => 8,
1 => 4,
2 => 2,
_ => 1,
}
}
#[must_use]
pub fn quantiser_scale(quantiser_scale_code: u8, q_scale_type: bool) -> i32 {
let code = quantiser_scale_code as usize;
if !q_scale_type {
2 * code as i32
} else {
const NONLINEAR: [i32; 32] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 44, 48, 52, 56,
64, 72, 80, 88, 96, 104, 112,
];
NONLINEAR[code.min(31)]
}
}
#[must_use]
pub fn dequantize_intra(
quantised: &[i32; 64],
intra_matrix: &[u8; 64],
intra_dc_precision: u8,
q_scale: i32,
) -> [i32; 64] {
let mut f = [0i32; 64];
f[0] = intra_dc_mult(intra_dc_precision)
.saturating_mul(quantised[0])
.clamp(F_MIN, F_MAX);
for i in 1..64 {
let qf = quantised[i];
if qf == 0 {
f[i] = 0;
continue;
}
let w = i64::from(intra_matrix[i]);
let recon = (2 * i64::from(qf) * w * i64::from(q_scale)) / 32;
f[i] = recon.clamp(i64::from(F_MIN), i64::from(F_MAX)) as i32;
}
let sum: i64 = f.iter().map(|&v| i64::from(v)).sum();
if sum & 1 == 0 {
if f[63] & 1 != 0 {
f[63] -= 1;
} else {
f[63] += 1;
}
f[63] = f[63].clamp(F_MIN, F_MAX);
}
f
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intra_dc_mult_table() {
assert_eq!(intra_dc_mult(0), 8);
assert_eq!(intra_dc_mult(1), 4);
assert_eq!(intra_dc_mult(2), 2);
assert_eq!(intra_dc_mult(3), 1);
}
#[test]
fn dc_scaling_per_precision() {
for (prec, mult) in [(0u8, 8i32), (1, 4), (2, 2), (3, 1)] {
let mut q = [0i32; 64];
q[0] = 10;
let f = dequantize_intra(&q, &DEFAULT_INTRA_MATRIX, prec, 2);
assert_eq!(f[0], 10 * mult, "precision {prec}");
}
}
#[test]
fn dc_saturates_high() {
let mut q = [0i32; 64];
q[0] = 1000; let f = dequantize_intra(&q, &DEFAULT_INTRA_MATRIX, 0, 2);
assert_eq!(f[0], F_MAX);
}
#[test]
fn quantiser_scale_linear() {
assert_eq!(quantiser_scale(1, false), 2);
assert_eq!(quantiser_scale(8, false), 16);
assert_eq!(quantiser_scale(31, false), 62);
}
#[test]
fn quantiser_scale_nonlinear() {
assert_eq!(quantiser_scale(1, true), 1);
assert_eq!(quantiser_scale(9, true), 10);
assert_eq!(quantiser_scale(31, true), 112);
}
#[test]
fn ac_reconstruction_formula() {
let mut q = [0i32; 64];
q[1] = 3;
let mut m = [16u8; 64];
m[1] = 16;
let f = dequantize_intra(&q, &m, 0, 8);
assert_eq!(f[1], (2 * 3 * 16 * 8) / 32);
}
#[test]
fn ac_saturates() {
let mut q = [0i32; 64];
q[1] = 1000;
let mut m = [255u8; 64];
m[1] = 255;
let f = dequantize_intra(&q, &m, 0, 31 * 2);
assert_eq!(f[1], F_MAX);
}
#[test]
fn mismatch_control_makes_sum_odd() {
let mut q = [0i32; 64];
q[0] = 2; let f = dequantize_intra(&q, &DEFAULT_INTRA_MATRIX, 0, 2);
let sum: i64 = f.iter().map(|&v| i64::from(v)).sum();
assert_eq!(sum & 1, 1, "sum must be odd after mismatch control");
assert_eq!(f[63], 1);
}
#[test]
fn mismatch_control_leaves_odd_sum_alone() {
let mut q = [0i32; 64];
q[0] = 1; q[1] = 1; let mut m = DEFAULT_INTRA_MATRIX;
m[2] = 8; q[2] = 1;
let f = dequantize_intra(&q, &m, 0, 2);
let sum: i64 = f.iter().map(|&v| i64::from(v)).sum();
assert_eq!(sum & 1, 1);
}
}