use crate::intrapred::DC_PRED;
use crate::tables::{COEFF_BASE_RANGE, EOB_BITW, NUM_BASE_LEVELS};
#[allow(dead_code)]
pub(crate) fn est_block_bits(cf: &[i32], scan: &[u32]) -> u32 {
let Some(eob) = scan.iter().rposition(|&rc| cf[rc as usize] != 0) else {
return 1;
};
let mag: u32 = scan[..=eob]
.iter()
.map(|&rc| cf[rc as usize].unsigned_abs())
.sum();
mag + EOB_BITW * (eob as u32 + 1)
}
fn coef_rate_bits_slow(level: u32) -> f32 {
match level {
0 => 0.9,
1 => 1.7 + 1.0,
2 => 2.6 + 1.0,
_ => {
let mut b = 3.0 + 1.0;
let total_br = ((level as i32) - 3).min(COEFF_BASE_RANGE);
let steps = (total_br / 3 + 1) as f32;
b += steps * 1.3;
if level >= 15 {
let v = level - 15;
b += 2.0 * ((32 - (v + 1).leading_zeros()) as f32) - 1.0;
}
b
}
}
}
pub(crate) static COEF_RATE_LUT: [f32; 64] = [
0.9, 2.7, 3.6, 5.3, 5.3, 5.3, 6.6, 6.6, 6.6, 7.9, 7.9, 7.9, 9.2, 9.2, 9.2, 11.5, 13.5, 13.5,
15.5, 15.5, 15.5, 15.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 17.5, 19.5, 19.5, 19.5, 19.5,
19.5, 19.5, 19.5, 19.5, 19.5, 19.5, 19.5, 19.5, 19.5, 19.5, 19.5, 19.5, 21.5, 21.5, 21.5, 21.5,
21.5, 21.5, 21.5, 21.5, 21.5, 21.5, 21.5, 21.5, 21.5, 21.5, 21.5, 21.5, 21.5, 21.5,
];
#[inline]
pub(crate) fn coef_rate_bits(level: u32) -> f32 {
if (level as usize) < 64 {
COEF_RATE_LUT[level as usize]
} else {
coef_rate_bits_slow(level)
}
}
pub(crate) const TRELLIS_LAMBDA0: f32 = 0.05;
pub(crate) const ADST_ADST_TX8_IDX: usize = 4;
pub(crate) const ADST_DCT_TX8_IDX: usize = 5;
pub(crate) const DCT_ADST_TX8_IDX: usize = 6;
pub(crate) const ADST_ADST_TX16_IDX: usize = 2;
pub(crate) const ADST_DCT_TX16_IDX: usize = 3;
pub(crate) const DCT_ADST_TX16_IDX: usize = 4;
pub(crate) fn trellis_lambda() -> f32 {
TRELLIS_LAMBDA0
}
#[inline]
fn def_kf_rd_multiplier(q: f32) -> f32 {
3.3 + 0.0015 * q
}
#[inline]
pub(crate) fn aom_ssimulacra2_rdmult_weight(qindex: u8) -> f32 {
let w = (((255i32 - qindex as i32) * 3) / 4).clamp(0, 72) + 128;
w as f32 / 128.0
}
#[allow(dead_code)]
pub(crate) const AOM_RDMULT_CALIB: f32 = 1.0 / (1 << 4) as f32;
#[allow(dead_code)]
#[inline]
pub(crate) fn mode_lambda_aom(dc_q: f32, qindex: u8, bd: u8, tune_ssimulacra2: bool) -> f32 {
let mut rdmult = dc_q * dc_q * def_kf_rd_multiplier(dc_q);
if tune_ssimulacra2 {
rdmult *= aom_ssimulacra2_rdmult_weight(qindex);
}
rdmult *= match bd {
10 => 1.0 / (1 << 4) as f32,
12 => 1.0 / (1 << 8) as f32,
_ => 1.0,
};
rdmult * (1.0 / 1024.0) * AOM_RDMULT_CALIB
}
#[inline]
pub(crate) fn mode_lambda_weight(qindex: u8) -> f32 {
aom_ssimulacra2_rdmult_weight(qindex)
}
pub(crate) const COST_Q_FRAC: u32 = 22;
pub(crate) const COST_Q_SCALE_INV: f32 = 1.0 / (1u32 << COST_Q_FRAC) as f32;
pub(crate) fn cost_q_table() -> &'static [u32; 32769] {
static TABLE: std::sync::OnceLock<Box<[u32; 32769]>> = std::sync::OnceLock::new();
TABLE.get_or_init(|| {
let mut t = Box::new([0u32; 32769]);
for (p, slot) in t.iter_mut().enumerate().skip(1) {
let bits = -((p as f32) * (1.0 / 32768.0)).log2();
*slot = (bits * (1u32 << COST_Q_FRAC) as f32).round() as u32;
}
t
})
}
#[inline]
pub(crate) fn cdf_cost(cdf: &[u16], s: usize) -> f32 {
let fl = if s > 0 { cdf[s - 1] as i32 } else { 32768 };
let fh = cdf[s] as i32;
let p = (fl - fh).max(1) as usize;
cost_q_table()[p] as f32 * COST_Q_SCALE_INV
}
#[inline]
pub(crate) fn golomb_cost(v: u32) -> f32 {
let len = 32 - (v + 1).leading_zeros();
(2 * len - 1) as f32
}
pub(crate) fn hi_tok_cost(m: u32, br_cdf: &[u16]) -> f32 {
let total_br = (m as i32 - (NUM_BASE_LEVELS + 1)).min(COEFF_BASE_RANGE);
let mut coded = 0i32;
let mut bits = 0.0f32;
for _ in 0..(COEFF_BASE_RANGE / 3) {
let s = (total_br - coded).min(3);
bits += cdf_cost(br_cdf, s as usize);
coded += s;
if s < 3 {
break;
}
}
if m >= 15 {
bits += golomb_cost(m - 15);
}
bits
}
pub(crate) fn block_rate_bits(cf: &[i32], scan: &[u32]) -> f32 {
let Some(eob) = scan.iter().rposition(|&rc| cf[rc as usize] != 0) else {
return 1.0; };
let mut bits = 2.0f32; for &rc32 in &scan[..=eob] {
bits += coef_rate_bits(cf[rc32 as usize].unsigned_abs());
}
bits
}
#[cfg_attr(not(test), allow(dead_code))]
pub(crate) const MODE_LAMBDA0: f32 = 0.02;
pub(crate) const MODE_AOM_CALIB: f32 = 0.009005174719460433;
#[inline]
pub(crate) fn mode_lambda_q(dc_q: f32) -> f32 {
MODE_AOM_CALIB * dc_q * dc_q * def_kf_rd_multiplier(dc_q)
}
#[inline]
pub(crate) fn rate_cost(lambda: f32, rate: f32) -> f32 {
lambda * rate
}
#[inline]
pub(crate) fn rd_cost_i64(distortion: i64, lambda: f32, rate: f32) -> f32 {
distortion as f32 + rate_cost(lambda, rate)
}
#[inline]
pub(crate) fn mode_signal_bits(m: usize) -> f32 {
if m == DC_PRED { 0.0 } else { MODE_SIGNAL_BITS }
}
pub(crate) const MODE_SIGNAL_BITS: f32 = 30.0;
#[cfg(test)]
mod tests {
use super::*;
use crate::quant::{ac_q, dc_q};
fn aom_kf_rdmult(dcq: f32) -> f32 {
dcq * dcq * (3.3 + 0.0015 * dcq)
}
#[test]
fn mode_lambda_matches_aom_kf_shape() {
let mut ratios = vec![];
for q in (16u8..=240).step_by(8) {
let dcq = dc_q(q, 8) as f32;
let lam = mode_lambda_q(dcq);
ratios.push(lam / aom_kf_rdmult(dcq));
}
let (lo, hi) = (
ratios.iter().cloned().fold(f32::MAX, f32::min),
ratios.iter().cloned().fold(0.0, f32::max),
);
assert!(
(hi - lo) / hi < 1e-5,
"lambda not proportional to aom rdmult"
);
}
#[test]
fn reference_q128_preserved() {
let acq = ac_q(128, 8) as f32;
let dcq = dc_q(128, 8) as f32;
let legacy = MODE_LAMBDA0 * acq * acq;
assert!((mode_lambda_q(dcq) - legacy).abs() / legacy < 1e-6);
}
#[test]
fn old_law_diverged_from_aom() {
let r = |q: u8| {
let acq = ac_q(q, 8) as f32;
(MODE_LAMBDA0 * acq * acq) / aom_kf_rdmult(dc_q(q, 8) as f32)
};
assert!(
r(224) / r(32) > 1.5,
"expected old law to over-weight high q"
);
}
}