const N: usize = 8;
static COS_TAB: once_cell_once_lock::CosTab = once_cell_once_lock::CosTab::new();
mod once_cell_once_lock {
use std::sync::OnceLock;
pub struct CosTab {
inner: OnceLock<[[f32; 8]; 8]>,
}
impl CosTab {
pub const fn new() -> Self {
Self {
inner: OnceLock::new(),
}
}
pub fn get(&self) -> &[[f32; 8]; 8] {
self.inner.get_or_init(|| {
let mut t = [[0.0f32; 8]; 8];
for k in 0..8 {
for n in 0..8 {
let theta = std::f64::consts::PI * (2.0 * n as f64 + 1.0) * k as f64 / 16.0;
t[k][n] = theta.cos() as f32;
}
}
t
})
}
}
}
fn idct_1d(in_row: &[f32; N], out_row: &mut [f32; N]) {
let ct = COS_TAB.get();
let c0 = 1.0 / std::f32::consts::SQRT_2;
for n in 0..N {
let mut sum = 0.0f32;
for k in 0..N {
let c = if k == 0 { c0 } else { 1.0 };
sum += c * in_row[k] * ct[k][n];
}
out_row[n] = sum;
}
}
pub fn idct8x8(block: &mut [f32; 64]) {
let mut tmp = [0.0f32; 64];
let mut row_in = [0.0f32; 8];
let mut row_out = [0.0f32; 8];
for y in 0..N {
for x in 0..N {
row_in[x] = block[y * N + x];
}
idct_1d(&row_in, &mut row_out);
for x in 0..N {
tmp[y * N + x] = row_out[x] * 0.5;
}
}
let mut col_in = [0.0f32; 8];
let mut col_out = [0.0f32; 8];
for x in 0..N {
for y in 0..N {
col_in[y] = tmp[y * N + x];
}
idct_1d(&col_in, &mut col_out);
for y in 0..N {
block[y * N + x] = col_out[y] * 0.5;
}
}
}
pub fn idct_signed(coeffs: &[i32; 64], out: &mut [i32; 64]) {
let mut f = [0.0f32; 64];
for i in 0..64 {
f[i] = coeffs[i] as f32;
}
idct8x8(&mut f);
for i in 0..64 {
let v = f[i].round() as i32;
out[i] = v.clamp(-256, 255);
}
}
pub fn idct_intra(coeffs: &[i32; 64], out: &mut [u8; 64]) {
let mut f = [0.0f32; 64];
for i in 0..64 {
f[i] = coeffs[i] as f32;
}
idct8x8(&mut f);
for i in 0..64 {
let v = f[i].round() as i32;
out[i] = v.clamp(0, 255) as u8;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn zeros_give_zeros() {
let mut b = [0.0f32; 64];
idct8x8(&mut b);
for v in b.iter() {
assert!(v.abs() < 1e-4);
}
}
#[test]
fn dc_only_flat_block() {
let mut b = [0.0f32; 64];
b[0] = 8.0;
idct8x8(&mut b);
for v in b.iter() {
assert!((*v - 1.0).abs() < 1e-3, "{v}");
}
}
#[test]
fn intra_dc_level_matches_128() {
let mut b = [0i32; 64];
b[0] = 1024;
let mut out = [0u8; 64];
idct_intra(&b, &mut out);
for v in out.iter() {
assert_eq!(*v, 128);
}
}
}