pub const ZIGZAG_4X4: [(usize, usize); 16] = [
(0, 0),
(0, 1),
(1, 0),
(2, 0),
(1, 1),
(0, 2),
(0, 3),
(1, 2),
(2, 1),
(3, 0),
(3, 1),
(2, 2),
(1, 3),
(2, 3),
(3, 2),
(3, 3),
];
pub fn inverse_hadamard_4x4(dc: &mut [i32; 16]) {
let mut tmp = [0i32; 16];
for i in 0..4 {
let a = dc[i * 4];
let b = dc[i * 4 + 1];
let c = dc[i * 4 + 2];
let d = dc[i * 4 + 3];
tmp[i * 4] = a + b + c + d;
tmp[i * 4 + 1] = a + b - c - d;
tmp[i * 4 + 2] = a - b - c + d;
tmp[i * 4 + 3] = a - b + c - d;
}
for j in 0..4 {
let a = tmp[j];
let b = tmp[4 + j];
let c = tmp[8 + j];
let d = tmp[12 + j];
dc[j] = a + b + c + d;
dc[4 + j] = a + b - c - d;
dc[8 + j] = a - b - c + d;
dc[12 + j] = a - b + c - d;
}
}
pub fn inverse_hadamard_2x2(dc: &mut [i32; 4]) {
let a = dc[0] + dc[1] + dc[2] + dc[3];
let b = dc[0] - dc[1] + dc[2] - dc[3];
let c = dc[0] + dc[1] - dc[2] - dc[3];
let d = dc[0] - dc[1] - dc[2] + dc[3];
dc[0] = a;
dc[1] = b;
dc[2] = c;
dc[3] = d;
}
pub fn inverse_dct_4x4(block: &mut [i32; 16]) {
block[0] += 32;
for i in 0..4 {
let z0 = block[i * 4] + block[i * 4 + 2];
let z1 = block[i * 4] - block[i * 4 + 2];
let z2 = (block[i * 4 + 1] >> 1) - block[i * 4 + 3];
let z3 = block[i * 4 + 1] + (block[i * 4 + 3] >> 1);
block[i * 4] = z0 + z3;
block[i * 4 + 1] = z1 + z2;
block[i * 4 + 2] = z1 - z2;
block[i * 4 + 3] = z0 - z3;
}
for j in 0..4 {
let z0 = block[j] + block[8 + j];
let z1 = block[j] - block[8 + j];
let z2 = (block[4 + j] >> 1) - block[12 + j];
let z3 = block[4 + j] + (block[12 + j] >> 1);
block[j] = (z0 + z3) >> 6;
block[4 + j] = (z1 + z2) >> 6;
block[8 + j] = (z1 - z2) >> 6;
block[12 + j] = (z0 - z3) >> 6;
}
}
const LEVEL_SCALE: [[i32; 3]; 6] = [
[10, 13, 16],
[11, 14, 18],
[13, 16, 20],
[14, 18, 23],
[16, 20, 25],
[18, 23, 29],
];
fn position_category(row: usize, col: usize) -> usize {
(row & 1) + (col & 1)
}
pub fn dequant_4x4(block: &mut [i32; 16], qp: i32, scale: &[u8; 16]) {
let qp_per = qp / 6;
let qp_rem = (qp % 6) as usize;
for idx in 0..16 {
if block[idx] != 0 {
let (r, c) = ZIGZAG_4X4[idx];
let v = LEVEL_SCALE[qp_rem][position_category(r, c)] * scale[idx] as i32;
if qp_per >= 4 {
block[idx] = (block[idx] * v) << (qp_per - 4);
} else {
block[idx] = (block[idx] * v + (1 << (3 - qp_per))) >> (4 - qp_per);
}
}
}
}
pub fn dequant_luma_dc_i16x16(dc: &mut [i32; 16], qp: i32, scale_dc: u8) {
let qp_per = qp / 6;
let qp_rem = (qp % 6) as usize;
let v = LEVEL_SCALE[qp_rem][0] * scale_dc as i32;
if qp_per >= 6 {
for d in dc.iter_mut() {
*d = (*d * v) << (qp_per - 6);
}
} else {
let round = 1 << (5 - qp_per);
for d in dc.iter_mut() {
*d = (*d * v + round) >> (6 - qp_per);
}
}
}
pub fn dequant_chroma_dc(dc: &mut [i32; 4], qp: i32, scale_dc: u8) {
let qp_per = qp / 6;
let qp_rem = (qp % 6) as usize;
let v = LEVEL_SCALE[qp_rem][0] * scale_dc as i32;
if qp_per >= 5 {
for d in dc.iter_mut() {
*d = (*d * v) << (qp_per - 5);
}
} else {
let round = 1 << (4 - qp_per);
for d in dc.iter_mut() {
*d = (*d * v + round) >> (5 - qp_per);
}
}
}
pub const QPC_TABLE: [i32; 52] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
26, 27, 28, 29, 29, 30, 31, 32, 32, 33, 34, 34, 35, 35, 36, 36, 37, 37, 37, 38, 38, 38, 39, 39,
39, 39,
];
pub fn chroma_qp(qp_y: i32, chroma_qp_index_offset: i32) -> i32 {
let qpi = (qp_y + chroma_qp_index_offset).clamp(0, 51);
QPC_TABLE[qpi as usize]
}
#[rustfmt::skip]
pub const ZIGZAG_8X8_CAVLC: [usize; 64] = [
0, 9, 17, 18, 12, 40, 27, 7, 35, 57, 29, 30, 58, 38, 53, 47,
1, 2, 24, 11, 19, 48, 20, 14, 42, 50, 22, 37, 59, 31, 60, 55,
8, 3, 32, 4, 26, 41, 13, 21, 49, 43, 15, 44, 52, 39, 61, 62,
16, 10, 25, 5, 33, 34, 6, 28, 56, 36, 23, 51, 45, 46, 54, 63,
];
#[rustfmt::skip]
pub const ZIGZAG_8X8_CABAC: [usize; 64] = [
0, 1, 8, 16, 9, 2, 3, 10,
17, 24, 32, 25, 18, 11, 4, 5,
12, 19, 26, 33, 40, 48, 41, 34,
27, 20, 13, 6, 7, 14, 21, 28,
35, 42, 49, 56, 57, 50, 43, 36,
29, 22, 15, 23, 30, 37, 44, 51,
58, 59, 52, 45, 38, 31, 39, 46,
53, 60, 61, 54, 47, 55, 62, 63,
];
const LEVEL_SCALE_8X8: [[i32; 6]; 6] = [
[20, 18, 32, 19, 25, 24],
[22, 19, 35, 21, 28, 26],
[26, 23, 42, 24, 33, 31],
[28, 25, 45, 26, 35, 33],
[32, 28, 51, 30, 40, 38],
[36, 32, 58, 34, 46, 43],
];
const DEQUANT_8X8_POS_CAT: [usize; 16] = [0, 3, 4, 3, 3, 1, 5, 1, 4, 5, 2, 5, 3, 1, 5, 1];
pub fn dequant_8x8(block: &mut [i32; 64], qp: i32, scale: &[u8; 64]) {
let qp_per = qp / 6;
let qp_rem = (qp % 6) as usize;
for (idx, coeff) in block.iter_mut().enumerate() {
if *coeff != 0 {
let r = idx / 8;
let c = idx % 8;
let cat = DEQUANT_8X8_POS_CAT[(r % 4) * 4 + (c % 4)];
let v = LEVEL_SCALE_8X8[qp_rem][cat] * scale[r * 8 + c] as i32;
if qp_per >= 6 {
*coeff = (*coeff * v) << (qp_per - 6);
} else {
*coeff = (*coeff * v + (1 << (5 - qp_per))) >> (6 - qp_per);
}
}
}
}
pub fn inverse_dct_8x8(block: &mut [i32; 64]) {
block[0] += 32;
for i in 0..8 {
let s = i * 8;
let a0 = block[s] + block[s + 4];
let a2 = block[s] - block[s + 4];
let a4 = (block[s + 2] >> 1) - block[s + 6];
let a6 = (block[s + 6] >> 1) + block[s + 2];
let b0 = a0 + a6;
let b2 = a2 + a4;
let b4 = a2 - a4;
let b6 = a0 - a6;
let a1 = -block[s + 3] + block[s + 5] - block[s + 7] - (block[s + 7] >> 1);
let a3 = block[s + 1] + block[s + 7] - block[s + 3] - (block[s + 3] >> 1);
let a5 = -block[s + 1] + block[s + 7] + block[s + 5] + (block[s + 5] >> 1);
let a7 = block[s + 3] + block[s + 5] + block[s + 1] + (block[s + 1] >> 1);
let b1 = (a7 >> 2) + a1;
let b3 = a3 + (a5 >> 2);
let b5 = (a3 >> 2) - a5;
let b7 = a7 - (a1 >> 2);
block[s] = b0 + b7;
block[s + 1] = b2 + b5;
block[s + 2] = b4 + b3;
block[s + 3] = b6 + b1;
block[s + 4] = b6 - b1;
block[s + 5] = b4 - b3;
block[s + 6] = b2 - b5;
block[s + 7] = b0 - b7;
}
for i in 0..8 {
let a0 = block[i] + block[i + 4 * 8];
let a2 = block[i] - block[i + 4 * 8];
let a4 = (block[i + 2 * 8] >> 1) - block[i + 6 * 8];
let a6 = (block[i + 6 * 8] >> 1) + block[i + 2 * 8];
let b0 = a0 + a6;
let b2 = a2 + a4;
let b4 = a2 - a4;
let b6 = a0 - a6;
let a1 = -block[i + 3 * 8] + block[i + 5 * 8] - block[i + 7 * 8] - (block[i + 7 * 8] >> 1);
let a3 = block[i + 8] + block[i + 7 * 8] - block[i + 3 * 8] - (block[i + 3 * 8] >> 1);
let a5 = -block[i + 8] + block[i + 7 * 8] + block[i + 5 * 8] + (block[i + 5 * 8] >> 1);
let a7 = block[i + 3 * 8] + block[i + 5 * 8] + block[i + 8] + (block[i + 8] >> 1);
let b1 = (a7 >> 2) + a1;
let b3 = a3 + (a5 >> 2);
let b5 = (a3 >> 2) - a5;
let b7 = a7 - (a1 >> 2);
block[i] = (b0 + b7) >> 6;
block[i + 8] = (b2 + b5) >> 6;
block[i + 2 * 8] = (b4 + b3) >> 6;
block[i + 3 * 8] = (b6 + b1) >> 6;
block[i + 4 * 8] = (b6 - b1) >> 6;
block[i + 5 * 8] = (b4 - b3) >> 6;
block[i + 6 * 8] = (b2 - b5) >> 6;
block[i + 7 * 8] = (b0 - b7) >> 6;
}
}
pub const BLOCK_INDEX_TO_OFFSET: [(usize, usize); 16] = [
(0, 0),
(0, 4),
(4, 0),
(4, 4), (0, 8),
(0, 12),
(4, 8),
(4, 12), (8, 0),
(8, 4),
(12, 0),
(12, 4), (8, 8),
(8, 12),
(12, 8),
(12, 12), ];
pub const OFFSET_TO_BLOCK: [[usize; 4]; 4] = [
[0, 1, 4, 5], [2, 3, 6, 7], [8, 9, 12, 13], [10, 11, 14, 15], ];
#[rustfmt::skip]
pub const CBP_INTRA_TABLE: [u8; 48] = [
47, 31, 15, 0, 23, 27, 29, 30, 7, 11, 13, 14, 39, 43, 45, 46,
16, 3, 5, 10, 12, 19, 21, 26, 28, 35, 37, 42, 44, 1, 2, 4,
8, 17, 18, 20, 24, 6, 9, 22, 25, 32, 33, 34, 36, 40, 38, 41,
];
#[rustfmt::skip]
pub const CBP_INTER_TABLE: [u8; 48] = [
0, 16, 1, 2, 4, 8, 32, 3, 5, 10, 12, 15, 47, 7, 11, 13,
14, 6, 9, 31, 35, 37, 42, 44, 33, 34, 36, 40, 39, 43, 45, 46,
17, 18, 20, 24, 19, 21, 26, 28, 23, 27, 29, 30, 22, 25, 38, 41,
];
pub fn dequant_4x4_full(block: &mut [i32; 16], qp: i32, scale: &[u8; 16]) {
let qp_per = qp / 6;
let qp_rem = (qp % 6) as usize;
for r in 0..4 {
for c in 0..4 {
let idx = r * 4 + c;
if block[idx] != 0 {
let pc = position_category(r, c);
let scan_idx = ZIGZAG_4X4
.iter()
.position(|&(zr, zc)| zr == r && zc == c)
.unwrap();
let v = LEVEL_SCALE[qp_rem][pc] * scale[scan_idx] as i32;
if qp_per >= 4 {
block[idx] = (block[idx] * v) << (qp_per - 4);
} else {
block[idx] = (block[idx] * v + (1 << (3 - qp_per))) >> (4 - qp_per);
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_inverse_hadamard_4x4_all_same() {
let mut dc = [5i32; 16];
inverse_hadamard_4x4(&mut dc);
assert_eq!(dc[0], 80); for &v in &dc[1..] {
assert_eq!(v, 0);
}
}
#[test]
fn test_inverse_hadamard_2x2() {
let mut dc = [1, 0, 0, 0];
inverse_hadamard_2x2(&mut dc);
assert_eq!(dc, [1, 1, 1, 1]);
}
}