#[allow(clippy::too_many_arguments)]
pub fn quantize(
coeffs: &[i32],
scan: &[i16],
dc_step: i32,
ac_step: i32,
ac_round: i64,
dq_shift: u32,
levels: &mut [i32],
dqcoeff: &mut [i32],
) -> usize {
let n = coeffs.len();
levels[..n].fill(0);
dqcoeff[..n].fill(0);
let ac_thresh = ((ac_step as i64 - ac_round + ((1i64 << dq_shift) - 1)) >> dq_shift).max(0);
#[cfg(target_arch = "x86_64")]
if n >= 16 && std::is_x86_feature_detected!("avx2") {
return unsafe {
quantize_masked_avx2(
coeffs, scan, dc_step, ac_step, ac_round, dq_shift, ac_thresh, levels, dqcoeff,
)
};
}
quantize_scan_loop(
coeffs, scan, dc_step, ac_step, ac_round, dq_shift, ac_thresh, levels, dqcoeff,
)
}
#[allow(clippy::too_many_arguments)]
fn quantize_scan_loop(
coeffs: &[i32],
scan: &[i16],
dc_step: i32,
ac_step: i32,
ac_round: i64,
dq_shift: u32,
ac_thresh: i64,
levels: &mut [i32],
dqcoeff: &mut [i32],
) -> usize {
let mut eob = 0usize;
for (idx, &p) in scan.iter().enumerate() {
let pos = p as usize;
let coeff = coeffs[pos] as i64;
let (step, round) = if idx == 0 {
(dc_step as i64, dc_step as i64 / 2)
} else {
if (coeff.unsigned_abs() as i64) < ac_thresh {
continue;
}
(ac_step as i64, ac_round)
};
let acoef = (coeff.unsigned_abs() << dq_shift) as i64;
let level = ((acoef + round) / step) as i32;
if level != 0 {
let mag = ((level as i64 * step) >> dq_shift) as i32;
levels[pos] = if coeff < 0 { -level } else { level };
dqcoeff[pos] = if coeff < 0 { -mag } else { mag };
eob = idx + 1;
}
}
eob
}
fn iscan_for(scan: &[i16]) -> std::rc::Rc<[u16]> {
use std::cell::RefCell;
use std::rc::Rc;
thread_local! {
static CACHE: RefCell<Vec<(usize, Rc<[u16]>)>> = const { RefCell::new(Vec::new()) };
}
let key = scan.as_ptr() as usize;
CACHE.with(|c| {
if let Some((_, v)) = c.borrow().iter().find(|(k, _)| *k == key) {
return v.clone();
}
let mut inv = vec![0u16; scan.len()];
for (idx, &p) in scan.iter().enumerate() {
inv[p as usize] = idx as u16;
}
let rc: Rc<[u16]> = inv.into();
c.borrow_mut().push((key, rc.clone()));
rc
})
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
#[allow(clippy::too_many_arguments)]
unsafe fn quantize_masked_avx2(
coeffs: &[i32],
scan: &[i16],
dc_step: i32,
ac_step: i32,
ac_round: i64,
dq_shift: u32,
ac_thresh: i64,
levels: &mut [i32],
dqcoeff: &mut [i32],
) -> usize {
use std::arch::x86_64::*;
let n = coeffs.len();
let iscan = iscan_for(scan);
let mut eob = 0usize;
let dc = coeffs[0] as i64;
if dc != 0 {
let level =
(((dc.unsigned_abs() << dq_shift) as i64 + dc_step as i64 / 2) / dc_step as i64) as i32;
if level != 0 {
let mag = ((level as i64 * dc_step as i64) >> dq_shift) as i32;
levels[0] = if dc < 0 { -level } else { level };
dqcoeff[0] = if dc < 0 { -mag } else { mag };
eob = 1;
}
}
let cmp_bound = _mm256_set1_epi32((ac_thresh as i32).saturating_sub(1));
let mut base = 0usize;
while base < n {
let mut mask = 0u64;
let lanes = (n - base).min(64);
let mut off = 0usize;
while off < lanes {
let v = _mm256_loadu_si256(coeffs.as_ptr().add(base + off) as *const __m256i);
let a = _mm256_abs_epi32(v);
let m = _mm256_movemask_ps(_mm256_castsi256_ps(_mm256_cmpgt_epi32(a, cmp_bound)));
mask |= (m as u32 as u64) << off;
off += 8;
}
if base == 0 {
mask &= !1u64; }
while mask != 0 {
let bit = mask.trailing_zeros() as usize;
mask &= mask - 1;
let pos = base + bit;
let coeff = coeffs[pos] as i64;
let acoef = (coeff.unsigned_abs() << dq_shift) as i64;
let level = ((acoef + ac_round) / ac_step as i64) as i32;
if level != 0 {
let mag = ((level as i64 * ac_step as i64) >> dq_shift) as i32;
levels[pos] = if coeff < 0 { -level } else { level };
dqcoeff[pos] = if coeff < 0 { -mag } else { mag };
eob = eob.max(iscan[pos] as usize + 1);
}
}
base += 64;
}
eob
}
#[cfg(test)]
mod tests {
use super::*;
use crate::quant::{ac_quant, dc_quant};
use crate::token::get_scan;
use crate::transform::{inverse_transform_add, TxType};
fn xs(s: &mut u64) -> u64 {
*s ^= *s << 13;
*s ^= *s >> 7;
*s ^= *s << 17;
*s
}
#[cfg(target_arch = "x86_64")]
#[test]
fn quantize_masked_matches_scan_loop() {
if !std::is_x86_feature_detected!("avx2") {
return;
}
let mut s = 0x5151_a3a3_7777_0202u64;
for &(n, tx) in &[
(4usize, TxType::DctDct),
(4, TxType::AdstAdst),
(8, TxType::DctDct),
(8, TxType::AdstDct),
(16, TxType::DctDct),
(32, TxType::DctDct),
] {
let tx_size = (n.trailing_zeros() - 2) as usize;
let dq_shift = if n == 32 { 1u32 } else { 0 };
let (scan, _) = get_scan(tx_size, tx);
for &qindex in &[0i32, 32, 96, 200, 255] {
let dc = dc_quant(qindex, 8);
let ac = ac_quant(qindex, 8);
for &round_div in &[2i64, 3] {
let ac_round = ac as i64 / round_div; let ac_thresh =
((ac as i64 - ac_round + ((1i64 << dq_shift) - 1)) >> dq_shift).max(0);
for density in [1u64, 4, 16] {
let coeffs: Vec<i32> = (0..n * n)
.map(|_| {
if xs(&mut s) % density != 0 {
0
} else {
(xs(&mut s) % 4001) as i32 - 2000
}
})
.collect();
let mut l1 = vec![0i32; n * n];
let mut d1 = vec![0i32; n * n];
let mut l2 = vec![0i32; n * n];
let mut d2 = vec![0i32; n * n];
let e1 = quantize_scan_loop(
&coeffs, scan, dc, ac, ac_round, dq_shift, ac_thresh, &mut l1, &mut d1,
);
let e2 = unsafe {
quantize_masked_avx2(
&coeffs, scan, dc, ac, ac_round, dq_shift, ac_thresh, &mut l2,
&mut d2,
)
};
assert_eq!(e1, e2, "eob {n}x{n} q{qindex} rd{round_div}");
assert_eq!(l1, l2, "levels {n}x{n} q{qindex}");
assert_eq!(d1, d2, "dqcoeff {n}x{n} q{qindex}");
}
}
}
}
}
#[test]
fn pixel_coeff_roundtrip_through_quant() {
let sizes = [
(4usize, TxType::DctDct),
(8, TxType::DctDct),
(16, TxType::DctDct),
(32, TxType::DctDct),
(4, TxType::AdstAdst),
(8, TxType::AdstDct),
(16, TxType::DctAdst),
];
let mut s = 0xc0ff_ee00_1234_5678u64;
let base = 512i32;
for &(n, tx) in &sizes {
let tx_size = (n.trailing_zeros() - 2) as usize;
let dq_shift = if n == 32 { 1 } else { 0 };
let (scan, _) = get_scan(tx_size, tx);
for &qindex in &[0i32, 32, 96, 200] {
let dc = dc_quant(qindex, 8);
let ac = ac_quant(qindex, 8);
let step = ac.max(dc);
let mut max_err = 0i32;
for _ in 0..40 {
let residual: Vec<i32> = (0..n * n)
.map(|_| (xs(&mut s) % 321) as i32 - 160)
.collect();
let mut coeffs = vec![0i32; n * n];
crate::encode::forward_transform(&residual, n, tx, &mut coeffs);
let mut levels = vec![0i32; n * n];
let mut dqcoeff = vec![0i32; n * n];
let eob = quantize(
&coeffs,
scan,
dc,
ac,
ac as i64 / 2, dq_shift,
&mut levels,
&mut dqcoeff,
);
for (idx, &p) in scan.iter().enumerate() {
let pos = p as usize;
let st = if idx == 0 { dc } else { ac };
let mag =
((levels[pos].unsigned_abs() as i64 * st as i64) >> dq_shift) as i32;
let want = if levels[pos] < 0 { -mag } else { mag };
assert_eq!(dqcoeff[pos], want, "dequant identity {n}x{n} pos {pos}");
if idx >= eob {
assert_eq!(levels[pos], 0, "level past EOB must be zero");
}
}
let mut dest = vec![base as u16; n * n];
inverse_transform_add(&dqcoeff, n, tx, &mut dest, n, 4095);
for i in 0..n * n {
max_err = max_err.max((dest[i] as i32 - base - residual[i]).abs());
}
}
assert!(
max_err <= step,
"{n}x{n} {tx:?} q{qindex}: max pixel err {max_err} > step {step}"
);
}
}
}
}