#![allow(dead_code)]
#![allow(clippy::needless_range_loop)]
use super::super::tables::{MAX_VARIABLE_LEVEL, VP8_ENC_BANDS, VP8_ENTROPY_COST};
use super::vp8_bit_cost;
pub const NUM_TYPES: usize = 4;
pub const NUM_BANDS: usize = 8;
pub const NUM_CTX: usize = 3;
pub const NUM_PROBAS: usize = 11;
#[derive(Clone, Default)]
pub struct ProbaStats {
pub stats: [[[[u32; NUM_PROBAS]; NUM_CTX]; NUM_BANDS]; NUM_TYPES],
}
impl ProbaStats {
pub fn new() -> Self {
Self::default()
}
pub fn reset(&mut self) {
for t in self.stats.iter_mut() {
for b in t.iter_mut() {
for c in b.iter_mut() {
for p in c.iter_mut() {
*p = 0;
}
}
}
}
}
#[inline]
pub fn record(&mut self, t: usize, b: usize, c: usize, p: usize, bit: bool) {
let stats = &mut self.stats[t][b][c][p];
if *stats >= 0xfffe_0000 {
*stats = ((*stats + 1) >> 1) & 0x7fff_7fff;
}
*stats += 0x0001_0000 + if bit { 1 } else { 0 };
}
pub fn calc_proba(&self, t: usize, b: usize, c: usize, p: usize) -> u8 {
let stats = self.stats[t][b][c][p];
let nb = stats & 0xffff; let total = stats >> 16; if total == 0 {
return 255;
}
let proba = 255 - (nb * 255 / total);
proba as u8
}
pub fn should_update(
&self,
t: usize,
b: usize,
c: usize,
p: usize,
old_proba: u8,
update_proba: u8,
) -> (bool, u8, i32) {
let stats = self.stats[t][b][c][p];
let nb = (stats & 0xffff) as i32; let total = (stats >> 16) as i32;
if total == 0 {
return (false, old_proba, 0);
}
let new_p = self.calc_proba(t, b, c, p);
let old_cost = branch_cost(nb, total, old_proba) + vp8_bit_cost(false, update_proba) as i32;
let new_cost =
branch_cost(nb, total, new_p) + vp8_bit_cost(true, update_proba) as i32 + 8 * 256;
let savings = old_cost - new_cost;
(savings > 0, new_p, savings)
}
}
#[inline]
fn branch_cost(nb: i32, total: i32, proba: u8) -> i32 {
let cost_1 = VP8_ENTROPY_COST[255 - proba as usize] as i32;
let cost_0 = VP8_ENTROPY_COST[proba as usize] as i32;
nb * cost_1 + (total - nb) * cost_0
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum TokenType {
I16AC = 0,
I16DC = 1,
Chroma = 2,
I4 = 3,
}
pub fn record_coeffs(
coeffs: &[i32],
token_type: TokenType,
first: usize,
ctx: usize,
stats: &mut ProbaStats,
) {
let t = token_type as usize;
let mut n = first;
let mut context = ctx;
let last = coeffs
.iter()
.rposition(|&c| c != 0)
.map(|i| i as i32)
.unwrap_or(-1);
let end_of_block = if last >= 0 { (last + 1) as usize } else { 0 };
if end_of_block <= first {
let band = VP8_ENC_BANDS[first] as usize;
stats.record(t, band, context, 0, false); return;
}
let mut skip_eob = false;
while n < end_of_block {
let band = VP8_ENC_BANDS[n] as usize;
let v = coeffs[n].unsigned_abs();
n += 1;
if !skip_eob {
stats.record(t, band, context, 0, true); }
if v == 0 {
stats.record(t, band, context, 1, false);
skip_eob = true;
context = 0;
continue;
}
stats.record(t, band, context, 1, true);
if v == 1 {
stats.record(t, band, context, 2, false);
context = 1;
} else {
stats.record(t, band, context, 2, true);
let v = v.min(MAX_VARIABLE_LEVEL as u32);
if v <= 4 {
stats.record(t, band, context, 3, false);
if v == 2 {
stats.record(t, band, context, 4, false);
} else {
stats.record(t, band, context, 4, true);
stats.record(t, band, context, 5, v == 4);
}
} else if v <= 10 {
stats.record(t, band, context, 3, true);
stats.record(t, band, context, 6, false);
stats.record(t, band, context, 7, v > 6);
} else {
stats.record(t, band, context, 3, true);
stats.record(t, band, context, 6, true);
if v < 3 + (8 << 2) {
stats.record(t, band, context, 8, false);
stats.record(t, band, context, 9, v >= 3 + (8 << 1));
} else {
stats.record(t, band, context, 8, true);
stats.record(t, band, context, 10, v >= 3 + (8 << 3));
}
}
context = 2;
}
}
if n < 16 {
let band = VP8_ENC_BANDS[n] as usize;
stats.record(t, band, context, 0, false); }
}