#[inline]
fn coeff_class(i: usize, j: usize) -> usize {
match (i % 2, j % 2) {
(0, 0) => 0,
(1, 1) => 1,
_ => 2,
}
}
#[inline]
fn a_ij(i: usize, j: usize) -> i64 {
const A: [i64; 3] = [16, 25, 20];
A[coeff_class(i, j)]
}
const LEVEL_SCALE2_W: [[i64; 3]; 6] = [
[13107, 5243, 8066],
[11916, 4660, 7490],
[10082, 4194, 6554],
[9362, 3647, 5825],
[8192, 3355, 5243],
[7282, 2893, 4559],
];
#[inline]
fn level_scale2(m: usize, i: usize, j: usize) -> i64 {
LEVEL_SCALE2_W[m][coeff_class(i, j)]
}
#[inline]
fn level_scale4x4(weight_scale: &[i32; 16], m: usize, i: usize, j: usize) -> i64 {
const NORM_ADJUST: [[i64; 3]; 6] = [
[10, 16, 13],
[11, 18, 14],
[13, 20, 16],
[14, 23, 18],
[16, 25, 20],
[18, 29, 23],
];
weight_scale[i * 4 + j] as i64 * NORM_ADJUST[m][coeff_class(i, j)]
}
pub fn forward_core_4x4(p: &[i32; 16]) -> [i32; 16] {
let mut t = [0i32; 16];
for j in 0..4 {
let (a, b, c, d) = (p[j], p[4 + j], p[8 + j], p[12 + j]);
t[j] = a + b + c + d;
t[4 + j] = 2 * a + b - c - 2 * d;
t[8 + j] = a - b - c + d;
t[12 + j] = a - 2 * b + 2 * c - d;
}
let mut cp = [0i32; 16];
for i in 0..4 {
let (a, b, c, d) = (t[i * 4], t[i * 4 + 1], t[i * 4 + 2], t[i * 4 + 3]);
cp[i * 4] = a + b + c + d;
cp[i * 4 + 1] = 2 * a + b - c - 2 * d;
cp[i * 4 + 2] = a - b - c + d;
cp[i * 4 + 3] = a - 2 * b + 2 * c - d;
}
cp
}
#[inline]
pub fn hadamard_2x2(x: &[i64; 4]) -> [i64; 4] {
let (a, b, c, d) = (x[0], x[1], x[2], x[3]);
[a + b + c + d, a - b + c - d, a + b - c - d, a - b - c + d]
}
#[inline]
fn dequant_8_416(cr: i32, qp: i32, weight_scale: &[i32; 16], i: usize, j: usize) -> i64 {
let m = (qp % 6) as usize;
((cr as i64 * level_scale4x4(weight_scale, m, i, j) * a_ij(i, j)) << (qp / 6)) >> 10
}
#[inline]
fn quant_qs_luma(cs: i64, qs: i32, i: usize, j: usize) -> i32 {
let m = (qs % 6) as usize;
let shift = 15 + qs / 6;
let mag = (cs.abs() * level_scale2(m, i, j) + (1i64 << (14 + qs / 6))) >> shift;
(cs.signum() * mag) as i32
}
#[inline]
fn quant_qs_chroma(cs: i64, qs: i32, i: usize, j: usize) -> i32 {
let m = (qs % 6) as usize;
let shift = 15 + qs / 6;
((cs.signum() * (cs.abs() * level_scale2(m, i, j) + (1i64 << (14 + qs / 6)))) >> shift) as i32
}
#[inline]
pub(crate) fn quant_qs_dc(x: i64, qs: i32) -> i64 {
let m = (qs % 6) as usize;
let shift = 16 + qs / 6;
(x.signum() * (x.abs() * level_scale2(m, 0, 0) + (1i64 << (15 + qs / 6)))) >> shift
}
pub fn sp_luma_non_switching(
pred: &[i32; 16],
cr: &[i32; 16],
qp_y: i32,
qs_y: i32,
weight_scale: &[i32; 16],
) -> [i32; 16] {
let cp = forward_core_4x4(pred);
let mut c = [0i32; 16];
for i in 0..4 {
for j in 0..4 {
let idx = i * 4 + j;
let cs = cp[idx] as i64 + dequant_8_416(cr[idx], qp_y, weight_scale, i, j);
c[idx] = quant_qs_luma(cs, qs_y, i, j);
}
}
c
}
pub fn sp_luma_switching(pred: &[i32; 16], cr: &[i32; 16], qs_y: i32) -> [i32; 16] {
let cp = forward_core_4x4(pred);
let mut c = [0i32; 16];
for i in 0..4 {
for j in 0..4 {
let idx = i * 4 + j;
let cs = quant_qs_luma(cp[idx] as i64, qs_y, i, j);
c[idx] = cr[idx] + cs;
}
}
c
}
pub fn sp_chroma_non_switching(
pred: &[i32; 64],
dc_levels: &[i32; 4],
ac: &[[i32; 16]; 4],
qp_c: i32,
qs_c: i32,
weight_scale: &[i32; 16],
) -> [[i32; 16]; 4] {
let cp = chroma_pred_transform(pred);
let mut out = [[0i32; 16]; 4];
for blk in 0..4 {
for i in 0..4 {
for j in 0..4 {
if i == 0 && j == 0 {
continue; }
let idx = i * 4 + j;
let cs =
cp[blk][idx] as i64 + dequant_8_416(ac[blk][idx], qp_c, weight_scale, i, j);
out[blk][idx] = quant_qs_chroma(cs, qs_c, i, j);
}
}
}
let dcp = hadamard_2x2(&[
cp[0][0] as i64,
cp[1][0] as i64,
cp[2][0] as i64,
cp[3][0] as i64,
]);
let m = (qp_c % 6) as usize;
let ls_dc_qp = level_scale4x4(weight_scale, m, 0, 0);
let mut dcr = [0i64; 4];
for pos in 0..4 {
let level = dc_levels[pos] as i64;
let dcs = dcp[pos] + (((level * ls_dc_qp * a_ij(0, 0)) << (qp_c / 6)) >> 9);
dcr[pos] = quant_qs_dc(dcs, qs_c);
}
let f = hadamard_2x2(&dcr);
let ls_dc_qs = level_scale4x4(weight_scale, (qs_c % 6) as usize, 0, 0);
for (blk, fv) in f.iter().enumerate() {
out[blk][0] = (((fv * ls_dc_qs) << (qs_c / 6)) >> 5) as i32;
}
out
}
pub fn sp_chroma_switching(
pred: &[i32; 64],
dc_levels: &[i32; 4],
ac: &[[i32; 16]; 4],
qs_c: i32,
) -> [[i32; 16]; 4] {
let cp = chroma_pred_transform(pred);
let mut out = [[0i32; 16]; 4];
for blk in 0..4 {
for i in 0..4 {
for j in 0..4 {
if i == 0 && j == 0 {
continue;
}
let idx = i * 4 + j;
let cs = quant_qs_chroma(cp[blk][idx] as i64, qs_c, i, j);
out[blk][idx] = ac[blk][idx] + cs;
}
}
}
let dcp = hadamard_2x2(&[
cp[0][0] as i64,
cp[1][0] as i64,
cp[2][0] as i64,
cp[3][0] as i64,
]);
let mut dcr = [0i64; 4];
for pos in 0..4 {
let dcs = quant_qs_dc(dcp[pos], qs_c);
dcr[pos] = dcs + dc_levels[pos] as i64;
}
let f = hadamard_2x2(&dcr);
for (blk, fv) in f.iter().enumerate() {
out[blk][0] = *fv as i32;
}
out
}
pub(crate) fn chroma_pred_transform(pred: &[i32; 64]) -> [[i32; 16]; 4] {
let mut cp = [[0i32; 16]; 4];
for (blk, dst) in cp.iter_mut().enumerate() {
let (bx, by) = ((blk % 2) * 4, (blk / 2) * 4);
let mut p = [0i32; 16];
for y in 0..4 {
for x in 0..4 {
p[y * 4 + x] = pred[(by + y) * 8 + bx + x];
}
}
*dst = forward_core_4x4(&p);
}
cp
}
pub fn encoder_quant_8_416(
target: i64,
qp: i32,
weight_scale: &[i32; 16],
i: usize,
j: usize,
) -> i32 {
let m = (qp % 6) as usize;
let den = (level_scale4x4(weight_scale, m, i, j) * a_ij(i, j)) << (qp / 6);
let num = target << 10;
let r = if num >= 0 {
(num + den / 2) / den
} else {
(num - den / 2) / den
};
r as i32
}
pub fn encoder_quant_8_428(target: i64, qp_c: i32, weight_scale: &[i32; 16]) -> i32 {
let m = (qp_c % 6) as usize;
let den = (level_scale4x4(weight_scale, m, 0, 0) * a_ij(0, 0)) << (qp_c / 6);
let num = target << 9;
let r = if num >= 0 {
(num + den / 2) / den
} else {
(num - den / 2) / den
};
r as i32
}
pub fn chroma_dc_switch_scale(qs_c: i32, weight_scale: &[i32; 16]) -> Option<i64> {
let ls = level_scale4x4(weight_scale, (qs_c % 6) as usize, 0, 0);
let num = ls << (qs_c / 6);
if num % 32 == 0 {
Some(num / 32)
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
const FLAT: [i32; 16] = [16; 16];
#[test]
fn forward_core_dc_of_constant_block() {
let p = [7i32; 16];
let cp = forward_core_4x4(&p);
assert_eq!(cp[0], 16 * 7);
assert!(cp[1..].iter().all(|&v| v == 0));
}
#[test]
fn sp_switching_luma_roundtrip_close_to_prediction() {
let mut pred = [0i32; 16];
for (k, v) in pred.iter_mut().enumerate() {
*v = 60 + (k as i32) * 3;
}
let qs_y = 24;
let c = sp_luma_switching(&pred, &[0i32; 16], qs_y);
let r = crate::transform::inverse_transform_4x4(&c, qs_y, &FLAT, 8).unwrap();
for k in 0..16 {
let err = (r[k] - pred[k]).abs();
assert!(
err <= 6,
"blk[{k}]: recon {} vs pred {} (err {err})",
r[k],
pred[k]
);
}
}
#[test]
fn sp_non_switching_zero_residual_requantises() {
let mut pred = [0i32; 16];
for (k, v) in pred.iter_mut().enumerate() {
*v = 100 + ((k as i32 * 37) % 23);
}
let qp_y = 28;
let qs_y = 28;
let c = sp_luma_non_switching(&pred, &[0i32; 16], qp_y, qs_y, &FLAT);
let r = crate::transform::inverse_transform_4x4(&c, qs_y, &FLAT, 8).unwrap();
let max_err = (0..16).map(|k| (r[k] - pred[k]).abs()).max().unwrap();
assert!(max_err > 0, "QS=28 must not be lossless on this block");
assert!(max_err <= 12, "but must stay within quantiser range");
}
#[test]
fn switching_identity_luma_exact() {
let mut pred_a = [0i32; 16];
let mut pred_b = [0i32; 16];
for k in 0..16 {
pred_a[k] = 90 + ((k as i32 * 17) % 31);
pred_b[k] = 15 + ((k as i32 * 29) % 61);
}
let qp_y = 27;
let qs_y = 25;
let mut cr_a = [0i32; 16];
cr_a[0] = 3;
cr_a[5] = -2;
let c_target = sp_luma_non_switching(&pred_a, &cr_a, qp_y, qs_y, &FLAT);
let cs_b = sp_luma_switching(&pred_b, &[0i32; 16], qs_y);
let mut cr_sw = [0i32; 16];
for k in 0..16 {
cr_sw[k] = c_target[k] - cs_b[k];
}
let c_sw = sp_luma_switching(&pred_b, &cr_sw, qs_y);
assert_eq!(c_sw, c_target);
}
#[test]
fn switching_identity_chroma_dc_exact() {
let mut pred_a = [0i32; 64];
let mut pred_b = [0i32; 64];
for k in 0..64 {
pred_a[k] = 120 + ((k as i32 * 13) % 17);
pred_b[k] = 40 + ((k as i32 * 7) % 29);
}
let (qp_c, qs_c) = (28, 26);
let dc_a = [5, -3, 2, 0];
let ac_zero = [[0i32; 16]; 4];
let c_a = sp_chroma_non_switching(&pred_a, &dc_a, &ac_zero, qp_c, qs_c, &FLAT);
let k = chroma_dc_switch_scale(qs_c, &FLAT).expect("QSC >= 6 scale is integral");
let kf = [
c_a[0][0] as i64,
c_a[1][0] as i64,
c_a[2][0] as i64,
c_a[3][0] as i64,
];
let h_kf = hadamard_2x2(&kf); assert!(h_kf.iter().all(|v| v % 4 == 0));
let target_dcr = [h_kf[0] / 4, h_kf[1] / 4, h_kf[2] / 4, h_kf[3] / 4];
assert!(target_dcr.iter().all(|v| v % k == 0 || k == 1));
let cp_b = chroma_pred_transform(&pred_b);
let dcp_b = hadamard_2x2(&[
cp_b[0][0] as i64,
cp_b[1][0] as i64,
cp_b[2][0] as i64,
cp_b[3][0] as i64,
]);
let mut dc_levels_sw = [0i32; 4];
for pos in 0..4 {
let dcs = quant_qs_dc(dcp_b[pos], qs_c);
dc_levels_sw[pos] = (target_dcr[pos] - dcs) as i32;
}
let c_sw = sp_chroma_switching(&pred_b, &dc_levels_sw, &ac_zero, qs_c);
for blk in 0..4 {
assert_eq!(c_sw[blk][0], c_a[blk][0], "blk {blk} DC");
}
}
#[test]
fn encoder_quant_8_416_inverts_dequant() {
for qp in [10, 22, 30, 40] {
for target in [-2000i64, -37, 0, 5, 419, 3000] {
for (i, j) in [(0, 0), (1, 1), (2, 3)] {
let level = encoder_quant_8_416(target, qp, &FLAT, i, j);
let back = dequant_8_416(level, qp, &FLAT, i, j);
let den =
(level_scale4x4(&FLAT, (qp % 6) as usize, i, j) * a_ij(i, j)) << (qp / 6);
let step = den >> 10;
assert!(
(back - target).abs() <= step.max(1),
"qp {qp} target {target} ({i},{j}): back {back} step {step}"
);
}
}
}
}
}