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]) {
use std::num::Wrapping as W;
block[0] = block[0].wrapping_add(32);
for i in 0..4 {
let s = i * 4;
let z0 = (W(block[s]) + W(block[s + 2])).0;
let z1 = (W(block[s]) - W(block[s + 2])).0;
let z2 = (W(block[s + 1] >> 1) - W(block[s + 3])).0;
let z3 = (W(block[s + 1]) + W(block[s + 3] >> 1)).0;
block[s] = (W(z0) + W(z3)).0;
block[s + 1] = (W(z1) + W(z2)).0;
block[s + 2] = (W(z1) - W(z2)).0;
block[s + 3] = (W(z0) - W(z3)).0;
}
for j in 0..4 {
let z0 = (W(block[j]) + W(block[8 + j])).0;
let z1 = (W(block[j]) - W(block[8 + j])).0;
let z2 = (W(block[4 + j] >> 1) - W(block[12 + j])).0;
let z3 = (W(block[4 + j]) + W(block[12 + j] >> 1)).0;
block[j] = (W(z0) + W(z3)).0 >> 6;
block[4 + j] = (W(z1) + W(z2)).0 >> 6;
block[8 + j] = (W(z1) - W(z2)).0 >> 6;
block[12 + j] = (W(z0) - W(z3)).0 >> 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].wrapping_mul(v).wrapping_shl((qp_per - 4) as u32);
} else {
block[idx] =
(block[idx].wrapping_mul(v).wrapping_add(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.wrapping_mul(v).wrapping_shl((qp_per - 6) as u32);
}
} else {
let round = 1 << (5 - qp_per);
for d in dc.iter_mut() {
*d = (d.wrapping_mul(v).wrapping_add(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.wrapping_mul(v).wrapping_shl((qp_per - 5) as u32);
}
} else {
let round = 1 << (4 - qp_per);
for d in dc.iter_mut() {
*d = (d.wrapping_mul(v).wrapping_add(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.wrapping_mul(v).wrapping_shl((qp_per - 6) as u32);
} else {
*coeff = (coeff.wrapping_mul(v).wrapping_add(1 << (5 - qp_per))) >> (6 - qp_per);
}
}
}
}
pub fn inverse_dct_8x8(block: &mut [i32; 64]) {
use std::num::Wrapping as W;
block[0] = block[0].wrapping_add(32);
for i in 0..8 {
let s = i * 8;
let (b0, b2, b4, b6, b1, b3, b5, b7) = idct8_butterfly(
block[s],
block[s + 1],
block[s + 2],
block[s + 3],
block[s + 4],
block[s + 5],
block[s + 6],
block[s + 7],
);
block[s] = (W(b0) + W(b7)).0;
block[s + 1] = (W(b2) + W(b5)).0;
block[s + 2] = (W(b4) + W(b3)).0;
block[s + 3] = (W(b6) + W(b1)).0;
block[s + 4] = (W(b6) - W(b1)).0;
block[s + 5] = (W(b4) - W(b3)).0;
block[s + 6] = (W(b2) - W(b5)).0;
block[s + 7] = (W(b0) - W(b7)).0;
}
for i in 0..8 {
let (b0, b2, b4, b6, b1, b3, b5, b7) = idct8_butterfly(
block[i],
block[i + 8],
block[i + 16],
block[i + 24],
block[i + 32],
block[i + 40],
block[i + 48],
block[i + 56],
);
block[i] = (W(b0) + W(b7)).0 >> 6;
block[i + 8] = (W(b2) + W(b5)).0 >> 6;
block[i + 16] = (W(b4) + W(b3)).0 >> 6;
block[i + 24] = (W(b6) + W(b1)).0 >> 6;
block[i + 32] = (W(b6) - W(b1)).0 >> 6;
block[i + 40] = (W(b4) - W(b3)).0 >> 6;
block[i + 48] = (W(b2) - W(b5)).0 >> 6;
block[i + 56] = (W(b0) - W(b7)).0 >> 6;
}
}
#[allow(clippy::too_many_arguments)]
#[inline(always)]
fn idct8_butterfly(
x0: i32,
x1: i32,
x2: i32,
x3: i32,
x4: i32,
x5: i32,
x6: i32,
x7: i32,
) -> (i32, i32, i32, i32, i32, i32, i32, i32) {
use std::num::Wrapping as W;
let a0 = (W(x0) + W(x4)).0;
let a2 = (W(x0) - W(x4)).0;
let a4 = (W(x2 >> 1) - W(x6)).0;
let a6 = (W(x6 >> 1) + W(x2)).0;
let b0 = (W(a0) + W(a6)).0;
let b2 = (W(a2) + W(a4)).0;
let b4 = (W(a2) - W(a4)).0;
let b6 = (W(a0) - W(a6)).0;
let a1 = (W(0) - W(x3) + W(x5) - W(x7) - W(x7 >> 1)).0;
let a3 = (W(x1) + W(x7) - W(x3) - W(x3 >> 1)).0;
let a5 = (W(0) - W(x1) + W(x7) + W(x5) + W(x5 >> 1)).0;
let a7 = (W(x3) + W(x5) + W(x1) + W(x1 >> 1)).0;
let b1 = (W(a7 >> 2) + W(a1)).0;
let b3 = (W(a3) + W(a5 >> 2)).0;
let b5 = (W(a3 >> 2) - W(a5)).0;
let b7 = (W(a7) - W(a1 >> 2)).0;
(b0, b2, b4, b6, b1, b3, b5, b7)
}
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].wrapping_mul(v).wrapping_shl((qp_per - 4) as u32);
} else {
block[idx] = (block[idx].wrapping_mul(v).wrapping_add(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]);
}
}