#![allow(dead_code)]
#![allow(clippy::cast_possible_truncation)]
#![allow(clippy::cast_sign_loss)]
const DCT_PRECISION: i32 = 14;
const DCT_ROUND: i32 = 1 << (DCT_PRECISION - 1);
const COS_PI_8: i32 = 11585; const COS_3PI_8: i32 = 6270; const SIN_PI_8: i32 = 15137; const SIN_3PI_8: i32 = 11585;
const SQRT2: i32 = 11585;
pub type Block4x4 = [[i16; 4]; 4];
pub type PixelBlock4x4 = [[u8; 4]; 4];
#[allow(clippy::similar_names)]
fn idct4_1d(input: &[i32; 4], output: &mut [i32; 4]) {
let a1 = input[0] + input[2];
let b1 = input[0] - input[2];
let c1 = (input[1] * COS_3PI_8 - input[3] * SIN_PI_8 + DCT_ROUND) >> DCT_PRECISION;
let d1 = (input[1] * SIN_PI_8 + input[3] * COS_3PI_8 + DCT_ROUND) >> DCT_PRECISION;
output[0] = a1 + d1;
output[1] = b1 + c1;
output[2] = b1 - c1;
output[3] = a1 - d1;
}
#[allow(clippy::cast_sign_loss)]
pub fn idct4x4(coeffs: &Block4x4, output: &mut [u8], stride: usize, pred: &[u8]) {
let mut temp = [[0i32; 4]; 4];
let mut residual = [[0i32; 4]; 4];
for row in 0..4 {
let input = [
i32::from(coeffs[row][0]),
i32::from(coeffs[row][1]),
i32::from(coeffs[row][2]),
i32::from(coeffs[row][3]),
];
let mut row_out = [0i32; 4];
idct4_1d(&input, &mut row_out);
temp[row] = row_out;
}
for col in 0..4 {
let input = [temp[0][col], temp[1][col], temp[2][col], temp[3][col]];
let mut col_out = [0i32; 4];
idct4_1d(&input, &mut col_out);
for row in 0..4 {
residual[row][col] = col_out[row];
}
}
for row in 0..4 {
for col in 0..4 {
let res = (residual[row][col] + 8) >> 4; let pred_val = i32::from(pred[row * stride + col]);
let pixel = (pred_val + res).clamp(0, 255) as u8;
output[row * stride + col] = pixel;
}
}
}
fn iwht4_1d(input: &[i32; 4], output: &mut [i32; 4]) {
let a = input[0] + input[2];
let b = input[1] + input[3];
let c = input[1] - input[3];
let d = input[0] - input[2];
output[0] = a + b;
output[1] = d + c;
output[2] = a - b;
output[3] = d - c;
}
#[allow(clippy::cast_possible_truncation)]
pub fn iwht4x4(coeffs: &Block4x4, dc_out: &mut [i16; 16]) {
let mut temp = [[0i32; 4]; 4];
let mut output = [[0i32; 4]; 4];
for row in 0..4 {
let input = [
i32::from(coeffs[row][0]),
i32::from(coeffs[row][1]),
i32::from(coeffs[row][2]),
i32::from(coeffs[row][3]),
];
let mut row_out = [0i32; 4];
iwht4_1d(&input, &mut row_out);
temp[row] = row_out;
}
for col in 0..4 {
let input = [temp[0][col], temp[1][col], temp[2][col], temp[3][col]];
let mut col_out = [0i32; 4];
iwht4_1d(&input, &mut col_out);
for row in 0..4 {
output[row][col] = col_out[row];
}
}
for row in 0..4 {
for col in 0..4 {
let val = (output[row][col] + 1) >> 1; dc_out[row * 4 + col] = val.clamp(i32::from(i16::MIN), i32::from(i16::MAX)) as i16;
}
}
}
#[must_use]
#[inline]
pub fn dequantize_coeff(coeff: i16, quant: i32) -> i16 {
let dequant = i32::from(coeff) * quant;
dequant.clamp(i32::from(i16::MIN), i32::from(i16::MAX)) as i16
}
pub fn dequantize_block(quantized: &Block4x4, dc_quant: i32, ac_quant: i32, output: &mut Block4x4) {
for row in 0..4 {
for col in 0..4 {
let quant = if row == 0 && col == 0 {
dc_quant
} else {
ac_quant
};
output[row][col] = dequantize_coeff(quantized[row][col], quant);
}
}
}
#[allow(dead_code)]
pub static ZIGZAG_4X4: [usize; 16] = [0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15];
#[allow(dead_code)]
pub fn zigzag_to_raster(zigzag: &[i16; 16], raster: &mut Block4x4) {
for (i, &pos) in ZIGZAG_4X4.iter().enumerate() {
let row = pos / 4;
let col = pos % 4;
raster[row][col] = zigzag[i];
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_idct4_1d() {
let input = [1000i32, 0, 0, 0]; let mut output = [0i32; 4];
idct4_1d(&input, &mut output);
for val in &output {
assert!(*val > 0);
}
}
#[test]
fn test_iwht4_1d() {
let input = [16i32, 0, 0, 0]; let mut output = [0i32; 4];
iwht4_1d(&input, &mut output);
assert_eq!(output[0], 16);
assert_eq!(output[1], 16);
assert_eq!(output[2], 16);
assert_eq!(output[3], 16);
}
#[test]
fn test_idct4x4() {
let mut coeffs = [[0i16; 4]; 4];
coeffs[0][0] = 100;
let pred = [128u8; 16];
let mut output = [0u8; 16];
idct4x4(&coeffs, &mut output, 4, &pred);
assert!(output.iter().any(|&v| v != 128));
}
#[test]
fn test_iwht4x4() {
let mut coeffs = [[0i16; 4]; 4];
coeffs[0][0] = 32;
let mut dc_out = [0i16; 16];
iwht4x4(&coeffs, &mut dc_out);
for val in &dc_out {
assert_eq!(*val, 16); }
}
#[test]
fn test_dequantize_coeff() {
assert_eq!(dequantize_coeff(10, 5), 50);
assert_eq!(dequantize_coeff(-10, 5), -50);
assert_eq!(dequantize_coeff(0, 100), 0);
}
#[test]
fn test_dequantize_block() {
let mut quantized = [[0i16; 4]; 4];
quantized[0][0] = 10; quantized[0][1] = 5; quantized[1][0] = 3;
let mut output = [[0i16; 4]; 4];
dequantize_block(&quantized, 4, 2, &mut output);
assert_eq!(output[0][0], 40); assert_eq!(output[0][1], 10); assert_eq!(output[1][0], 6); }
#[test]
fn test_zigzag_order() {
assert_eq!(ZIGZAG_4X4[0], 0); assert_eq!(ZIGZAG_4X4[1], 1); assert_eq!(ZIGZAG_4X4[2], 4); assert_eq!(ZIGZAG_4X4[3], 8); }
#[test]
fn test_zigzag_to_raster() {
let zigzag = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
let mut raster = [[0i16; 4]; 4];
zigzag_to_raster(&zigzag, &mut raster);
assert_eq!(raster[0][0], 1); assert_eq!(raster[0][1], 2); assert_eq!(raster[1][0], 3); }
}