#![allow(dead_code)]
#![allow(clippy::needless_range_loop)]
use super::stats::{NUM_BANDS, NUM_CTX, NUM_PROBAS, NUM_TYPES};
use super::vp8_bit_cost;
use crate::common::types::TokenProbTables;
use super::super::tables::{MAX_LEVEL, MAX_VARIABLE_LEVEL, VP8_ENC_BANDS, VP8_LEVEL_CODES};
pub const LEVEL_COST_ARRAY_SIZE: usize = 128;
pub type LevelCostArray = [u16; LEVEL_COST_ARRAY_SIZE];
pub type LevelCostTables = [[[LevelCostArray; NUM_CTX]; NUM_BANDS]; NUM_TYPES];
pub type RemappedCosts = [[usize; NUM_CTX]; 16];
fn variable_level_cost(level: usize, probas: &[u8; NUM_PROBAS]) -> u16 {
if level == 0 {
return 0;
}
let idx = level.min(MAX_VARIABLE_LEVEL) - 1;
let pattern = VP8_LEVEL_CODES[idx][0];
let bits = VP8_LEVEL_CODES[idx][1];
let mut cost = 0u16;
let mut p = pattern;
let mut b = bits;
let mut i = 2;
while p != 0 {
if (p & 1) != 0 {
cost += vp8_bit_cost((b & 1) != 0, probas[i]);
}
b >>= 1;
p >>= 1;
i += 1;
}
cost
}
#[derive(Clone)]
pub struct LevelCosts {
pub level_cost: LevelCostTables,
remapped: [RemappedCosts; NUM_TYPES],
eob_cost: [[[u16; NUM_CTX]; NUM_BANDS]; NUM_TYPES],
init_cost: [[[u16; NUM_CTX]; NUM_BANDS]; NUM_TYPES],
dirty: bool,
}
impl Default for LevelCosts {
fn default() -> Self {
Self::new()
}
}
impl LevelCosts {
pub fn new() -> Self {
Self {
level_cost: [[[[0u16; LEVEL_COST_ARRAY_SIZE]; NUM_CTX]; NUM_BANDS]; NUM_TYPES],
remapped: [[[0usize; NUM_CTX]; 16]; NUM_TYPES],
eob_cost: [[[0u16; NUM_CTX]; NUM_BANDS]; NUM_TYPES],
init_cost: [[[0u16; NUM_CTX]; NUM_BANDS]; NUM_TYPES],
dirty: true,
}
}
pub fn mark_dirty(&mut self) {
self.dirty = true;
}
pub fn is_dirty(&self) -> bool {
self.dirty
}
#[allow(clippy::needless_range_loop)] pub fn calculate(&mut self, probs: &TokenProbTables) {
if !self.dirty {
return;
}
for ctype in 0..NUM_TYPES {
for band in 0..NUM_BANDS {
for ctx in 0..NUM_CTX {
let p = &probs[ctype][band][ctx];
let cost0 = if ctx > 0 { vp8_bit_cost(true, p[0]) } else { 0 };
let cost_base = vp8_bit_cost(true, p[1]) + cost0;
self.level_cost[ctype][band][ctx][0] = vp8_bit_cost(false, p[1]) + cost0;
for v in 1..=MAX_VARIABLE_LEVEL {
self.level_cost[ctype][band][ctx][v] =
cost_base + variable_level_cost(v, p);
}
let max_cost = self.level_cost[ctype][band][ctx][MAX_VARIABLE_LEVEL];
for v in (MAX_VARIABLE_LEVEL + 1)..LEVEL_COST_ARRAY_SIZE {
self.level_cost[ctype][band][ctx][v] = max_cost;
}
self.eob_cost[ctype][band][ctx] = vp8_bit_cost(false, p[0]);
self.init_cost[ctype][band][ctx] = vp8_bit_cost(true, p[0]);
}
}
for n in 0..16 {
let band = VP8_ENC_BANDS[n] as usize;
for ctx in 0..NUM_CTX {
self.remapped[ctype][n][ctx] = band;
}
}
}
self.dirty = false;
}
#[inline]
pub fn get_level_cost(&self, ctype: usize, position: usize, ctx: usize, level: usize) -> u32 {
use super::super::tables::VP8_LEVEL_FIXED_COSTS;
let fixed = VP8_LEVEL_FIXED_COSTS[level.min(MAX_LEVEL)] as u32;
let band = self.remapped[ctype][position][ctx];
let variable = self.level_cost[ctype][band][ctx][level.min(MAX_VARIABLE_LEVEL)] as u32;
fixed + variable
}
#[inline]
pub fn get_cost_table(&self, ctype: usize, position: usize, ctx: usize) -> &LevelCostArray {
let band = self.remapped[ctype][position][ctx];
&self.level_cost[ctype][band][ctx]
}
#[inline]
pub fn get_eob_cost(&self, ctype: usize, position: usize, ctx: usize) -> u16 {
let next_pos = (position + 1).min(15);
let band = VP8_ENC_BANDS[next_pos] as usize;
self.eob_cost[ctype][band][ctx]
}
#[inline]
pub fn get_skip_eob_cost(&self, ctype: usize, first: usize, ctx: usize) -> u16 {
let band = VP8_ENC_BANDS[first] as usize;
self.eob_cost[ctype][band][ctx]
}
#[inline]
pub fn get_init_cost(&self, ctype: usize, first: usize, ctx: usize) -> u16 {
let band = VP8_ENC_BANDS[first] as usize;
self.init_cost[ctype][band][ctx]
}
#[inline]
pub fn get_remapped_costs(&self, ctype: usize) -> [[&LevelCostArray; NUM_CTX]; 16] {
let lc = &self.level_cost[ctype];
core::array::from_fn(|n| {
let band = VP8_ENC_BANDS[n] as usize;
core::array::from_fn(|ctx| &lc[band][ctx])
})
}
}