pub(super) const COS_Q15: [i32; 9] = [
32768, 32138, 30274, 27246, 23170, 18205, 12540, 6393, 0, ];
pub(super) fn cos_q15_periodic(angle: usize) -> i32 {
let angle = angle % 32;
match angle {
0..=8 => COS_Q15[angle],
9..=15 => -COS_Q15[16 - angle],
16..=23 => -COS_Q15[angle - 16],
_ => COS_Q15[32 - angle],
}
}
fn idct_1d(input: &[i32; 8]) -> [i32; 8] {
let mut out = [0i32; 8];
for (n, slot) in out.iter_mut().enumerate() {
let mut acc: i64 = i64::from(input[0]) * i64::from(COS_Q15[4]);
for k in 1..8 {
let phase_index = ((2 * n + 1) * k) % 32;
let cos_val = cos_q15_periodic(phase_index);
acc += i64::from(input[k]) * i64::from(cos_val);
}
*slot = ((acc + (1 << 14)) >> 15) as i32;
}
out
}
#[must_use]
pub fn idct_8x8(coeffs: &[i32; 64]) -> [i32; 64] {
let mut intermediate = [0i32; 64];
for row in 0..8 {
let row_in: [i32; 8] = std::array::from_fn(|c| coeffs[row * 8 + c]);
let row_out = idct_1d(&row_in);
for (c, &v) in row_out.iter().enumerate() {
intermediate[row * 8 + c] = v;
}
}
let mut output = [0i32; 64];
for col in 0..8 {
let col_in: [i32; 8] = std::array::from_fn(|r| intermediate[r * 8 + col]);
let col_out = idct_1d(&col_in);
for (r, &v) in col_out.iter().enumerate() {
output[r * 8 + col] = v;
}
}
output
}
#[must_use]
pub fn finalize_8bit(val: i32, dc_offset: i32) -> u8 {
let centered = (val + dc_offset + 128).clamp(0, 255);
centered as u8
}
#[must_use]
pub fn finalize_10bit(val: i32, dc_offset: i32) -> u16 {
let centered = (val + dc_offset + 512).clamp(0, 1023);
centered as u16
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn idct_dc_only_is_uniform() {
let mut coeffs = [0i32; 64];
coeffs[0] = 128 * 8;
let out = idct_8x8(&coeffs);
let first = out[0];
for (i, &v) in out.iter().enumerate() {
assert!(
(v - first).abs() <= 2,
"DC-only IDCT sample[{i}]={v} vs first={first}"
);
}
}
#[test]
fn idct_zero_input_is_zero() {
let out = idct_8x8(&[0i32; 64]);
assert!(out.iter().all(|&v| v == 0));
}
#[test]
fn finalize_8bit_clamps() {
assert_eq!(finalize_8bit(i32::MAX / 4, 0), 255);
assert_eq!(finalize_8bit(i32::MIN / 4, 0), 0);
}
#[test]
fn finalize_10bit_clamps() {
assert_eq!(finalize_10bit(i32::MAX / 4, 0), 1023);
assert_eq!(finalize_10bit(i32::MIN / 4, 0), 0);
}
#[test]
fn cos_q15_periodic_quarter_periods() {
assert_eq!(cos_q15_periodic(0), 32768); assert_eq!(cos_q15_periodic(8), 0); assert_eq!(cos_q15_periodic(16), -32768); assert_eq!(cos_q15_periodic(24), 0); assert_eq!(cos_q15_periodic(32), cos_q15_periodic(0));
}
}