use crate::av2::quant::{BASE_Q, qstep};
use crate::util::FastRound;
use std::cell::RefCell;
pub(crate) struct Basis {
pub(crate) scale: f32,
pub(crate) max_val: f32,
pub(crate) qstep: i32,
pub(crate) fwd: crate::av2::fdct::FwdKind,
}
pub(crate) const RDOQ_EOB_T: f32 = 0.9;
pub(crate) const DEFAULT_RDOQ_LAMBDA: f32 = 0.07;
pub(crate) fn hq_rdoq_lambda(base_q_idx: u32) -> f32 {
const BASE: f32 = DEFAULT_RDOQ_LAMBDA;
const PEAK: f32 = 0.13;
if base_q_idx >= 50 || base_q_idx == 0 {
return BASE;
}
let t = (50 - base_q_idx) as f32 / 46.0; let mut boost = PEAK * t.min(1.0);
if base_q_idx <= 4 {
boost *= base_q_idx as f32 / 5.0;
}
BASE + boost
}
fn rdoq_truncate_eob(lev: &mut [f32], prm: &[f32], t: f32) {
if t <= 0.0 {
return;
}
let mut k = lev.len().min(prm.len());
while k > 0 {
k -= 1;
if lev[k] == 0.0 {
continue; }
if prm[k] < t {
lev[k] = 0.0; } else {
break; }
}
}
pub(crate) struct Bases {
pub(crate) luma: Basis,
pub(crate) chroma420: Basis,
pub(crate) chroma422: Basis,
pub(crate) chroma444: Basis,
pub(crate) chroma444_64x32: Basis,
pub(crate) luma16x64: Basis,
pub(crate) luma64x16: Basis,
pub(crate) luma16x16: Basis,
pub(crate) luma16x16_adst: Basis,
pub(crate) luma16x16_adst_dct: Basis,
pub(crate) luma16x16_dct_adst: Basis,
pub(crate) luma8x32: Basis,
pub(crate) luma32x8: Basis,
pub(crate) luma16x32: Basis,
pub(crate) luma32x16: Basis,
pub(crate) c16x32: Basis,
pub(crate) c8x64: Basis,
pub(crate) c32x16: Basis,
pub(crate) c8x16: Basis,
pub(crate) c8x8: Basis,
pub(crate) c4x32: Basis,
pub(crate) c4x16: Basis,
pub(crate) c4x8: Basis,
pub(crate) c8x4: Basis,
pub(crate) c16x4: Basis,
pub(crate) c4x4: Basis,
pub(crate) c16x8: Basis,
}
struct FwdScratch {
resid: Box<[i32; 4096]>,
out: Box<[i32; 1024]>,
}
thread_local! {
static FWD_SCRATCH: RefCell<FwdScratch> =
RefCell::new(FwdScratch { resid: Box::new([0; 4096]), out: Box::new([0; 1024])});
}
impl Basis {
pub(crate) fn scale(&mut self, f: f32) {
self.scale *= f;
}
}
impl Bases {
pub(crate) fn set_bit_depth(&mut self, bit_depth: u8) {
let mv = ((1u32 << bit_depth) - 1) as f32;
self.luma.max_val = mv;
self.chroma420.max_val = mv;
self.chroma422.max_val = mv;
self.chroma444.max_val = mv;
self.chroma444_64x32.max_val = mv;
self.luma16x64.max_val = mv;
self.luma64x16.max_val = mv;
self.luma16x16.max_val = mv;
self.luma16x16_adst.max_val = mv;
self.luma16x16_adst_dct.max_val = mv;
self.luma16x16_dct_adst.max_val = mv;
self.luma8x32.max_val = mv;
self.luma32x8.max_val = mv;
self.c16x32.max_val = mv;
self.c8x64.max_val = mv;
self.c32x16.max_val = mv;
self.c8x16.max_val = mv;
self.c8x8.max_val = mv;
self.c4x32.max_val = mv;
self.c4x16.max_val = mv;
self.c16x4.max_val = mv;
self.c16x8.max_val = mv;
}
pub(crate) fn rescaled_to_q(mut self, base_q_idx: u32) -> Bases {
let f = qstep(base_q_idx) as f32 / qstep(BASE_Q) as f32;
if f != 1.0 {
self.luma.scale(f);
self.chroma420.scale(f);
self.chroma422.scale(f);
self.chroma444.scale(f);
self.chroma444_64x32.scale(f);
self.luma16x64.scale(f);
self.luma64x16.scale(f);
self.luma16x16.scale(f);
self.luma16x16_adst.scale(f);
self.luma16x16_adst_dct.scale(f);
self.luma16x16_dct_adst.scale(f);
self.luma8x32.scale(f);
self.luma32x8.scale(f);
self.luma16x32.scale(f);
self.luma32x16.scale(f);
self.c16x32.scale(f);
self.c8x64.scale(f);
self.c32x16.scale(f);
self.c8x16.scale(f);
self.c8x8.scale(f);
self.c4x32.scale(f);
self.c4x16.scale(f);
self.c16x4.scale(f);
self.c16x8.scale(f);
}
let qs = qstep(base_q_idx) as i32;
self.luma.qstep = qs;
self.chroma420.qstep = qs;
self.chroma444.qstep = qs;
self.luma16x16.qstep = qs;
self.chroma422.qstep = qs;
self.chroma444_64x32.qstep = qs;
self.luma16x16_adst.qstep = qs;
self.luma16x16_adst_dct.qstep = qs;
self.luma16x16_dct_adst.qstep = qs;
self.luma8x32.qstep = qs;
self.luma32x8.qstep = qs;
self.luma16x32.qstep = qs;
self.luma32x16.qstep = qs;
self.luma16x64.qstep = qs;
self.luma64x16.qstep = qs;
self.c16x32.qstep = qs;
self.c8x64.qstep = qs;
self.c32x16.qstep = qs;
self.c8x16.qstep = qs;
self.c8x8.qstep = qs;
self.c4x32.qstep = qs;
self.c4x16.qstep = qs;
self.c16x4.qstep = qs;
self.c16x8.qstep = qs;
self
}
}
impl Basis {
fn new() -> Basis {
Basis {
scale: 1.0,
max_val: 255.0,
qstep: qstep(BASE_Q) as i32,
fwd: crate::av2::fdct::FwdKind::Proj,
}
}
fn fwd_fast(&self, resid: &[f32], scan: &[u16], thresh: f32) -> Option<(Vec<f32>, Vec<f32>)> {
use crate::av2::fdct::{FwdKind, dc_tx_scale, fdct_rect, fwd16x16, quantize_to_levels};
let q = self.qstep;
Some(match self.fwd {
FwdKind::Proj => return None,
FwdKind::Dct(w, h) => {
let n = w * h;
let cw = w.min(32);
let factor = (1u32 << (3 + dc_tx_scale(w, h))) as f32;
FWD_SCRATCH.with(|cell| {
let s = &mut *cell.borrow_mut();
for (d, &v) in s.resid[..n].iter_mut().zip(resid) {
*d = v.fast_round() as i32;
}
let nc = fdct_rect(&s.resid[..n], w, h, s.out.as_mut_slice());
quantize_to_levels(&s.out[..nc], cw, q, factor, thresh, scan)
})
}
FwdKind::AdstAdst16 | FwdKind::AdstDct16 | FwdKind::DctAdst16 => {
let (col_adst, row_adst) = match self.fwd {
FwdKind::AdstAdst16 => (true, true),
FwdKind::AdstDct16 => (true, false), _ => (false, true), };
let mut r = [0i32; 256];
for (d, &s) in r.iter_mut().zip(resid) {
*d = s.fast_round() as i32;
}
quantize_to_levels(&fwd16x16(&r, col_adst, row_adst), 16, q, 8.0, thresh, scan)
}
})
}
pub(crate) fn project(&self, resid: &[f32], thresh: f32) -> Vec<f32> {
let (mut lev, prm) = self
.fwd_fast(resid, &crate::av2::tables::SCAN, thresh)
.expect("every basis carries a forward DCT");
rdoq_truncate_eob(&mut lev, &prm, RDOQ_EOB_T);
lev
}
pub(crate) fn project_with_prm(&self, resid: &[f32]) -> (Vec<f32>, Vec<f32>) {
self.fwd_fast(resid, &crate::av2::tables::SCAN, 0.0)
.expect("every basis carries a forward DCT")
}
pub(crate) fn project_scan(&self, resid: &[f32], thresh: f32, scan: &[u16]) -> Vec<f32> {
let (mut lev, prm) = self
.fwd_fast(resid, scan, thresh)
.expect("every basis carries a forward DCT");
rdoq_truncate_eob(&mut lev, &prm, RDOQ_EOB_T);
lev
}
pub(crate) fn project_scan_with_prm(
&self,
resid: &[f32],
scan: &[u16],
) -> (Vec<f32>, Vec<f32>) {
self.fwd_fast(resid, scan, 0.0)
.expect("every basis carries a forward DCT")
}
}
pub(crate) fn default_bases() -> Bases {
use crate::av2::fdct::FwdKind::*;
let mk = |fwd| {
let mut b = Basis::new();
b.fwd = fwd;
b
};
Bases {
luma: mk(Dct(32, 32)),
chroma420: mk(Dct(32, 32)),
chroma422: mk(Dct(32, 64)),
chroma444: mk(Dct(64, 64)),
chroma444_64x32: mk(Dct(64, 32)),
luma16x64: mk(Dct(16, 64)),
luma64x16: mk(Dct(64, 16)),
luma16x16: mk(Dct(16, 16)),
luma16x16_adst: mk(AdstAdst16),
luma16x16_adst_dct: mk(AdstDct16),
luma16x16_dct_adst: mk(DctAdst16),
luma8x32: mk(Dct(8, 32)),
luma32x8: mk(Dct(32, 8)),
luma16x32: mk(Dct(16, 32)),
luma32x16: mk(Dct(32, 16)),
c16x32: mk(Dct(16, 32)),
c8x64: mk(Dct(8, 64)),
c32x16: mk(Dct(32, 16)),
c8x16: mk(Dct(8, 16)),
c8x8: mk(Dct(8, 8)),
c4x32: mk(Dct(4, 32)),
c4x16: mk(Dct(4, 16)),
c4x8: mk(Dct(4, 8)),
c8x4: mk(Dct(8, 4)),
c16x4: mk(Dct(16, 4)),
c4x4: mk(Dct(4, 4)),
c16x8: mk(Dct(16, 8)),
}
}