//! Forward transforms (encoder side, any integer approximation is allowed), quantisation, and the //! normative scaling (8.6.3) and inverse transforms (8.6.4.2) the decoder applies. The inverse path //! is exact integer arithmetic: the even/odd decomposition computes the same sums as the matrix //! products of the specification. use crate::tables::{DST_MATRIX, LEVEL_SCALE, TRANS_MATRIX}; #[inline] fn m(n: usize, k: usize, i: usize) -> i32 { // k-th basis function of the n-point DCT, sample i TRANS_MATRIX[k * (32 / n)][i] as i32 } /// 1-D forward DCT of `x` (length n) into `out`. fn fdct1d(x: &[i32], out: &mut [i32], n: usize) { if n <= 4 { for k in 0..n { let mut s = 0; for i in 0..n { s += m(n, k, i) * x[i]; } out[k] = s; } return; } let h = n / 2; let mut e = [0i32; 16]; let mut o = [0i32; 16]; for i in 0..h { e[i] = x[i] + x[n - 1 - i]; o[i] = x[i] - x[n - 1 - i]; } let mut ev = [0i32; 16]; fdct1d(&e[..h], &mut ev[..h], h); for k in 0..h { out[2 * k] = ev[k]; let kk = 2 * k + 1; let mut s = 0; for i in 0..h { s += m(n, kk, i) * o[i]; } out[kk] = s; } } /// 1-D inverse DCT: x[i] = sum_k M[k][i] * c[k]. fn idct1d(c: &[i32], out: &mut [i32], n: usize) { if n <= 4 { for i in 0..n { let mut s = 0; for k in 0..n { s += m(n, k, i) * c[k]; } out[i] = s; } return; } let h = n / 2; let mut ce = [0i32; 16]; for k in 0..h { ce[k] = c[2 * k]; } let mut e = [0i32; 16]; idct1d(&ce[..h], &mut e[..h], h); for i in 0..h { let mut o = 0; let mut k = 1; while k < n { let v = c[k]; if v != 0 { o += m(n, k, i) * v; } k += 2; } out[i] = e[i] + o; out[n - 1 - i] = e[i] - o; } } fn fdst1d(x: &[i32], out: &mut [i32]) { for k in 0..4 { out[k] = (0..4).map(|i| DST_MATRIX[k][i] as i32 * x[i]).sum(); } } fn idst1d(c: &[i32], out: &mut [i32]) { for i in 0..4 { out[i] = (0..4).map(|k| DST_MATRIX[k][i] as i32 * c[k]).sum(); } } /// Forward 2-D transform of the n x n residual `r` (row-major) into `c`. pub fn forward(r: &[i32], c: &mut [i32], n: usize, dst: bool, bit_depth: u32) { let log2 = n.trailing_zeros(); let shift1 = log2 + bit_depth - 9; let shift2 = log2 + 6; let (r1, r2) = (1i32 << (shift1 - 1), 1i32 << (shift2 - 1)); let mut tmp = vec![0i32; n * n]; let mut out = [0i32; 32]; // rows (horizontal frequencies) for y in 0..n { let row = &r[y * n..y * n + n]; if dst { fdst1d(row, &mut out); } else { fdct1d(row, &mut out, n); } for k in 0..n { tmp[k * n + y] = (out[k] + r1) >> shift1; // transposed: tmp[kx][y] } } // columns let mut col = [0i32; 32]; for kx in 0..n { col[..n].copy_from_slice(&tmp[kx * n..kx * n + n]); if dst { fdst1d(&col, &mut out); } else { fdct1d(&col, &mut out, n); } for ky in 0..n { c[ky * n + kx] = (out[ky] + r2) >> shift2; } } } /// Inverse 2-D transform (8.6.4.2) of scaled coefficients `d` in place, producing residuals. pub fn inverse(d: &mut [i32], n: usize, dst: bool, bit_depth: u32) { let bd_shift = 20 - bit_depth; let mut tmp = vec![0i32; n * n]; let mut col = [0i32; 32]; let mut out = [0i32; 32]; // max non-zero row, to skip empty work let mut nz_cols = [false; 32]; for y in 0..n { for x in 0..n { if d[y * n + x] != 0 { nz_cols[x] = true; } } } // 1. columns for x in 0..n { if !nz_cols[x] { continue; // tmp column stays zero } for j in 0..n { col[j] = d[j * n + x]; } if dst { idst1d(&col, &mut out); } else { idct1d(&col, &mut out, n); } for y in 0..n { tmp[y * n + x] = ((out[y] + 64) >> 7).clamp(-32768, 32767); } } // 2. rows let round = 1 << (bd_shift - 1); for y in 0..n { let row = &tmp[y * n..y * n + n]; if dst { idst1d(row, &mut out); } else { idct1d(row, &mut out, n); } for x in 0..n { d[y * n + x] = (out[x] + round) >> bd_shift; } } } const QUANT_SCALE: [i64; 6] = [26214, 23302, 20560, 18396, 16384, 14564]; /// Quantise transform coefficients `c` into levels `l` with a dead-zone rounding offset /// (`intra`: 1/3, otherwise 1/6). `qp` includes QpBdOffset. Returns the number of non-zero levels. pub fn quantize(c: &[i32], l: &mut [i32], n: usize, qp: i32, intra: bool, bit_depth: u32) -> usize { let log2 = n.trailing_zeros() as i32; let tshift = 15 - bit_depth as i32 - log2; let qbits = 14 + qp / 6 + tshift; let scale = QUANT_SCALE[(qp % 6) as usize]; let offset = (if intra { 171i64 } else { 85 }) << (qbits - 9); let mut nz = 0; for i in 0..n * n { let v = c[i] as i64; let a = ((v.abs() * scale + offset) >> qbits).min(32767); if a != 0 { nz += 1; } l[i] = if v < 0 { -(a as i32) } else { a as i32 }; } nz } /// Sign data hiding (7.3.8.11 signHidden): in every 4x4 sub-block whose first and last non-zero /// coefficients (in scan order) are more than 3 positions apart, the sign of the first one is not /// coded but inferred from the parity of the sum of absolute levels. Fix the parity where needed by /// the +-1 level change that adds the least quantisation error, without moving the first / last /// non-zero positions. pub fn sign_hiding(c: &[i32], l: &mut [i32], log2: u32, scan_idx: usize, qp: i32, bit_depth: u32) { use crate::tables::{SCAN_4, scan_order}; let n = 1usize << log2; let tshift = 15 - bit_depth as i32 - log2 as i32; let qbits = 14 + qp / 6 + tshift; let scale = QUANT_SCALE[(qp % 6) as usize]; let sb_scan = scan_order(log2 - 2, scan_idx); let pos = &SCAN_4[scan_idx]; for &(xs, ys) in sb_scan { let idx = |p: usize| (ys as usize * 4 + pos[p].1 as usize) * n + xs as usize * 4 + pos[p].0 as usize; let (Some(first), Some(last)) = ((0..16).find(|&p| l[idx(p)] != 0), (0..16).rev().find(|&p| l[idx(p)] != 0)) else { continue }; if last - first <= 3 { continue; } let sum: i32 = (0..16).map(|p| l[idx(p)].abs()).sum(); let negative = l[idx(first)] < 0; if negative == (sum & 1 == 1) { continue; } // cost of +-1 changes in 1/256 units of quantisation error let mut best: Option<(i64, usize, i32)> = None; for p in first..=last { let i = idx(p); let q = ((c[i] as i64).abs() * scale) >> (qbits - 8); let a = l[i].abs() as i64; let err = |lv: i64| (q - (lv << 8)).abs(); let mut consider = |cost: i64, d: i32| { if best.is_none_or(|b| cost < b.0) { best = Some((cost, i, d)); } }; if a < 32767 && (a > 0 || (p > first && p < last)) { consider(err(a + 1) - err(a), 1); } if a > 1 || (a == 1 && p > first && p < last) { consider(err(a - 1) - err(a), -1); } } if let Some((_, i, d)) = best { let a = l[i].abs() + d; l[i] = if c[i] < 0 { -a } else { a }; } } } /// Scaling process (8.6.2 / 8.6.3, flat scaling matrix): levels to scaled coefficients. pub fn dequantize(l: &[i32], d: &mut [i32], n: usize, qp: i32, bit_depth: u32) { let log2 = n.trailing_zeros(); let bd_shift = bit_depth + log2 - 5; let round = 1i64 << (bd_shift - 1); let scale = (LEVEL_SCALE[(qp % 6) as usize] as i64) << (qp / 6); for i in 0..n * n { let v = l[i]; d[i] = if v == 0 { 0 } else { ((v as i64 * 16 * scale + round) >> bd_shift).clamp(-32768, 32767) as i32 }; } } #[cfg(test)] mod tests { use super::*; /// Direct matrix form of the inverse transform, as written in the specification. fn inverse_ref(c: &mut [i32], n: usize, dst: bool, bd: u32) { let mut tmp = vec![0i32; n * n]; let step = 32 / n; for x in 0..n { for y in 0..n { let mut s = 0i32; for j in 0..n { let m = if dst { DST_MATRIX[j][y] as i32 } else { TRANS_MATRIX[j * step][y] as i32 }; s += m * c[j * n + x]; } tmp[y * n + x] = ((s + 64) >> 7).clamp(-32768, 32767); } } let bd_shift = 20 - bd; for y in 0..n { for x in 0..n { let mut s = 0i32; for j in 0..n { let m = if dst { DST_MATRIX[j][x] as i32 } else { TRANS_MATRIX[j * step][x] as i32 }; s += m * tmp[y * n + j]; } c[y * n + x] = (s + (1 << (bd_shift - 1))) >> bd_shift; } } } #[test] fn inverse_matches_matrix_form() { let mut seed = 7u32; for &(n, dst) in &[(4usize, true), (4, false), (8, false), (16, false), (32, false)] { for _ in 0..20 { let mut c = vec![0i32; n * n]; for v in c.iter_mut() { seed = seed.wrapping_mul(1664525).wrapping_add(1013904223); if (seed >> 24).is_multiple_of(3) { *v = ((seed >> 8) % 2001) as i32 - 1000; } } let mut a = c.clone(); let mut b = c.clone(); inverse(&mut a, n, dst, 8); inverse_ref(&mut b, n, dst, 8); assert_eq!(a, b, "n {n} dst {dst}"); } } } #[test] fn round_trip_is_close() { for &(n, dst) in &[(4usize, true), (8, false), (32, false)] { for bd in [8u32, 10] { let r: Vec = (0..n * n).map(|i| ((i * 37 + i / n * 11) % 61) as i32 - 30).collect(); let mut c = vec![0; n * n]; forward(&r, &mut c, n, dst, bd); let mut l = vec![0; n * n]; let qp = 4; quantize(&c, &mut l, n, qp, true, bd); let mut d = vec![0; n * n]; dequantize(&l, &mut d, n, qp, bd); inverse(&mut d, n, dst, bd); let err: i32 = r.iter().zip(&d).map(|(a, b)| (a - b).abs()).max().unwrap(); assert!(err <= 2, "n {n} bd {bd} err {err}"); } } } }