use super::idct::{cos_q15_periodic, COS_Q15};
fn fdct_1d(input: &[i32; 8]) -> [i64; 8] {
let mut out = [0i64; 8];
for k in 0..8usize {
let mut acc: i64 = 0;
for n in 0..8usize {
let phase_index = ((2 * n + 1) * k) % 32;
let cos_val = if k == 0 {
COS_Q15[4]
} else {
cos_q15_periodic(phase_index)
};
acc += i64::from(input[n]) * i64::from(cos_val);
}
out[k] = acc;
}
out
}
#[must_use]
pub fn fdct_8x8(block: &[i32; 64]) -> [i32; 64] {
let mut intermediate = [0i64; 64];
for row in 0..8 {
let row_in: [i32; 8] = std::array::from_fn(|c| block[row * 8 + c]);
let row_out = fdct_1d(&row_in);
for (h_freq, &v) in row_out.iter().enumerate() {
intermediate[row * 8 + h_freq] = v;
}
}
let round: i64 = 1 << 33;
let mut output = [0i32; 64];
for h_freq in 0..8usize {
for v_freq in 0..8usize {
let mut acc: i64 = 0;
for r in 0..8usize {
let phase_index = ((2 * r + 1) * v_freq) % 32;
let cos_val = if v_freq == 0 {
COS_Q15[4]
} else {
cos_q15_periodic(phase_index)
};
acc += intermediate[r * 8 + h_freq] * i64::from(cos_val);
}
let shifted = (acc + round) >> 34;
output[v_freq * 8 + h_freq] = shifted as i32;
}
}
output
}
#[cfg(test)]
mod tests {
use super::*;
use crate::prores::idct::idct_8x8;
#[test]
fn fdct_idct_round_trip_dc_only() {
let block: [i32; 64] = [100; 64];
let freq = fdct_8x8(&block);
let spatial = idct_8x8(&freq);
for (i, &v) in spatial.iter().enumerate() {
assert!(
(v - 100).abs() <= 3,
"round-trip failed at [{i}]: input=100, got={v}"
);
}
}
#[test]
fn fdct_zero_block_is_zero() {
let block = [0i32; 64];
let freq = fdct_8x8(&block);
assert!(
freq.iter().all(|&v| v == 0),
"fdct(0) should be 0, got {freq:?}"
);
}
#[test]
fn fdct_idct_round_trip_ramp() {
let block: [i32; 64] = std::array::from_fn(|i| (i as i32) - 32);
let freq = fdct_8x8(&block);
let spatial = idct_8x8(&freq);
for (i, &v) in spatial.iter().enumerate() {
let expected = block[i];
assert!(
(v - expected).abs() <= 16,
"ramp round-trip failed at [{i}]: expected={expected}, got={v}"
);
}
}
#[test]
fn fdct_energy_concentrates_at_low_frequencies() {
let block: [i32; 64] = std::array::from_fn(|i| {
let r = (i / 8) as i32;
let c = (i % 8) as i32;
(r * 10 + c * 5) - 100
});
let freq = fdct_8x8(&block);
assert!(
freq[0].abs() >= freq[63].abs(),
"DC energy should dominate: DC={}, AC63={}",
freq[0],
freq[63]
);
}
}