#![allow(dead_code)]
use super::ac_strategy::AcStrategyMap;
use super::coeff_order::{NUM_ORDER_BUCKETS, STRATEGY_TO_BUCKET};
pub const NON_ZERO_BUCKETS: usize = 37;
pub const NUM_AC_STRATEGY_CODES: usize = 27;
pub const NUM_BLOCK_CTXS: usize = 4;
pub const ZERO_DENSITY_CONTEXT_COUNT: usize = 458;
#[allow(dead_code)]
pub const ZERO_DENSITY_CONTEXT_LIMIT: usize = 474;
pub const NUM_AC_CONTEXTS: usize = NUM_BLOCK_CTXS * (NON_ZERO_BUCKETS + ZERO_DENSITY_CONTEXT_COUNT);
pub const MAX_BLOCK_CTXS: usize = 16;
static COEFF_FREQ_CONTEXT: [u16; 64] = [
0xBAD, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 16, 16, 17, 17, 18, 18, 19,
19, 20, 20, 21, 21, 22, 22, 23, 23, 23, 23, 24, 24, 24, 24, 25, 25, 25, 25, 26, 26, 26, 26, 27,
27, 27, 27, 28, 28, 28, 28, 29, 29, 29, 29, 30, 30, 30, 30,
];
static COEFF_NUM_NONZERO_CONTEXT: [u16; 64] = [
0xBAD, 0, 31, 62, 62, 93, 93, 93, 93, 123, 123, 123, 123, 152, 152, 152, 152, 152, 152, 152,
152, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 180, 206, 206, 206, 206, 206, 206,
206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206, 206,
206, 206, 206, 206, 206, 206,
];
#[allow(dead_code)]
pub static COMPACT_BLOCK_CONTEXT_MAP: [u8; 39] = [
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, ];
#[derive(Debug, Clone)]
pub struct BlockCtxMap {
pub qf_thresholds: Vec<u32>,
pub ctx_map: Vec<u8>,
pub num_ctxs: usize,
}
impl Default for BlockCtxMap {
fn default() -> Self {
BlockCtxMap {
qf_thresholds: vec![],
ctx_map: COMPACT_BLOCK_CONTEXT_MAP.to_vec(),
num_ctxs: NUM_BLOCK_CTXS,
}
}
}
impl BlockCtxMap {
#[inline]
pub fn block_context(&self, c: usize, strategy_code: u8, qf: u32) -> usize {
let order_id = STRATEGY_TO_BUCKET[strategy_code as usize] as usize;
let mut qf_idx = 0usize;
for &t in &self.qf_thresholds {
if qf > t {
qf_idx += 1;
}
}
let num_qf_segments = self.qf_thresholds.len() + 1;
let c_swapped = if c < 2 { c ^ 1 } else { 2 };
let idx = (c_swapped * NUM_ORDER_BUCKETS + order_id) * num_qf_segments + qf_idx;
self.ctx_map[idx] as usize
}
#[inline]
pub fn num_ac_contexts(&self) -> usize {
self.num_ctxs * (NON_ZERO_BUCKETS + ZERO_DENSITY_CONTEXT_COUNT)
}
#[inline]
pub fn zero_density_contexts_offset(&self, block_ctx: usize) -> usize {
self.num_ctxs * NON_ZERO_BUCKETS + ZERO_DENSITY_CONTEXT_COUNT * block_ctx
}
#[inline]
pub fn non_zero_context(&self, non_zeros: usize, block_ctx: usize) -> usize {
let nz_bucket = if non_zeros < 8 {
non_zeros
} else if non_zeros >= 64 {
36
} else {
4 + non_zeros / 2
};
nz_bucket * self.num_ctxs + block_ctx
}
}
pub fn compute_block_ctx_map(
quant_field: &[u8],
ac_strategy: &AcStrategyMap,
distance: f32,
xsize_blocks: usize,
ysize_blocks: usize,
) -> BlockCtxMap {
let tot = xsize_blocks * ysize_blocks;
let size_for_ctx_model = ((1u64 << 10) as f64 * distance as f64) as usize;
if tot < size_for_ctx_model {
return BlockCtxMap::default();
}
let mut qf_counts = [0usize; 256];
let mut qf_ord_counts = [[0usize; 256]; NUM_ORDER_BUCKETS];
let mut ord_counts = [0usize; NUM_ORDER_BUCKETS];
for by in 0..ysize_blocks {
for bx in 0..xsize_blocks {
let qf = quant_field[by * xsize_blocks + bx] as usize;
let strategy_code = ac_strategy.strategy_code(bx, by);
let ord = STRATEGY_TO_BUCKET[strategy_code as usize] as usize;
qf_counts[qf] += 1;
qf_ord_counts[ord][qf] += 1;
ord_counts[ord] += 1;
}
}
let size_for_qf_split = ((1u64 << 13) as f64 * distance as f64) as usize;
let num_qf_segments: usize = if tot < size_for_qf_split { 1 } else { 2 };
let mut qf_thresholds: Vec<u32> = Vec::new();
if num_qf_segments > 1 {
let mut cumsum = 0usize;
let mut next = 1usize;
let mut last_cut = 256usize;
let mut cut = tot * next / num_qf_segments;
for j in 0u32..256 {
cumsum += qf_counts[j as usize];
if cumsum > cut {
if j != 0 {
qf_thresholds.push(j);
}
last_cut = j as usize;
while cumsum > cut {
next += 1;
cut = tot * next / num_qf_segments;
}
} else if next > qf_thresholds.len() + 1 && j as usize - 1 == last_cut && j != 0 {
qf_thresholds.push(j);
}
}
}
let num_qf_segs = qf_thresholds.len() + 1;
let num_cells = NUM_ORDER_BUCKETS * num_qf_segs;
let mut counts = vec![0usize; num_cells];
let mut qft_pos = 0usize;
for j in 0u32..256 {
if qft_pos < qf_thresholds.len() && j == qf_thresholds[qft_pos] {
qft_pos += 1;
}
for ord in 0..NUM_ORDER_BUCKETS {
counts[ord * num_qf_segs + qft_pos] += qf_ord_counts[ord][j as usize];
}
}
let mut remap: Vec<u8> = (0..num_cells as u8).collect();
let mut clusters: Vec<u8> = remap.clone();
let nb_clusters_luma = (tot / size_for_ctx_model / 2).clamp(2, 9);
let nb_clusters_chroma = (tot / size_for_ctx_model / 3).clamp(1, 5);
while clusters.len() > nb_clusters_luma {
clusters.sort_by(|&a, &b| counts[b as usize].cmp(&counts[a as usize]));
let last = clusters.len() - 1;
let second_last = last - 1;
counts[clusters[second_last] as usize] += counts[clusters[last] as usize];
counts[clusters[last] as usize] = 0;
remap[clusters[last] as usize] = clusters[second_last];
clusters.pop();
}
for i in 0..remap.len() {
while remap[remap[i] as usize] != remap[i] {
remap[i] = remap[remap[i] as usize];
}
}
let mut remap_remap = vec![u8::MAX; num_cells];
let mut num_luma: u8 = 0;
for i in 0..remap.len() {
if remap_remap[remap[i] as usize] == u8::MAX {
remap_remap[remap[i] as usize] = num_luma;
num_luma += 1;
}
remap[i] = remap_remap[remap[i] as usize];
}
let section_size = NUM_ORDER_BUCKETS * num_qf_segs;
let mut ctx_map = vec![0u8; section_size * 3];
ctx_map[..section_size].copy_from_slice(&remap[..section_size]);
let chroma_max = nb_clusters_chroma as u8 - 1;
for i in section_size..section_size * 3 {
let luma_ctx = remap[i % section_size];
ctx_map[i] = num_luma + luma_ctx.min(chroma_max);
}
let num_ctxs = *ctx_map.iter().max().unwrap_or(&0) as usize + 1;
BlockCtxMap {
qf_thresholds,
ctx_map,
num_ctxs,
}
}
static BLOCK_CONTEXT_MAP: [u8; 81] = [
2, 2, 2, 2, 2, 2, 3, 3, 0, 0, 3, 3, 2, 2, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0,
2, 2, 2, 2, 2, 2, 3, 3, 0, 0, 3, 3, 2, 2, 0, 0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0,
];
#[inline]
pub const fn block_context(c: usize, ac_strategy_code: u8) -> usize {
BLOCK_CONTEXT_MAP[c * NUM_AC_STRATEGY_CODES + ac_strategy_code as usize] as usize
}
#[inline]
pub fn zero_density_context(
nonzeros_left: usize,
k: usize,
covered_blocks: usize,
log2_covered_blocks: usize,
prev: usize,
) -> usize {
let nonzeros_left = (nonzeros_left + covered_blocks - 1) >> log2_covered_blocks;
let k = k >> log2_covered_blocks;
(COEFF_NUM_NONZERO_CONTEXT[nonzeros_left] as usize + COEFF_FREQ_CONTEXT[k] as usize) * 2 + prev
}
#[inline]
pub const fn zero_density_contexts_offset(block_ctx: usize) -> usize {
NUM_BLOCK_CTXS * NON_ZERO_BUCKETS + ZERO_DENSITY_CONTEXT_COUNT * block_ctx
}
#[inline]
pub const fn non_zero_context(non_zeros: usize, block_ctx: usize) -> usize {
let nz_bucket = if non_zeros < 8 {
non_zeros
} else if non_zeros >= 64 {
36
} else {
4 + non_zeros / 2
};
nz_bucket * NUM_BLOCK_CTXS + block_ctx
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_non_zero_context() {
for i in 0..8 {
assert_eq!(non_zero_context(i, 0), i * NUM_BLOCK_CTXS);
}
assert_eq!(non_zero_context(8, 0), (4 + 4) * NUM_BLOCK_CTXS);
assert_eq!(non_zero_context(10, 0), (4 + 5) * NUM_BLOCK_CTXS);
assert_eq!(non_zero_context(64, 0), 36 * NUM_BLOCK_CTXS);
assert_eq!(non_zero_context(100, 0), 36 * NUM_BLOCK_CTXS);
}
#[test]
fn test_zero_density_context_bounds() {
for nz in 1..64 {
for k in 1..64 {
for prev in 0..2 {
let ctx = zero_density_context(nz, k, 1, 0, prev);
assert!(
ctx < ZERO_DENSITY_CONTEXT_LIMIT,
"ctx {} >= {}",
ctx,
ZERO_DENSITY_CONTEXT_LIMIT
);
}
}
}
}
#[test]
fn test_block_context() {
let ctx_y = block_context(1, 0);
assert_eq!(ctx_y, 0);
let ctx_y_16 = block_context(1, 6);
assert_eq!(ctx_y_16, 1);
let ctx_x = block_context(0, 0);
assert_eq!(ctx_x, 2);
}
#[test]
fn test_block_ctx_map_default() {
let map = BlockCtxMap::default();
assert_eq!(map.num_ctxs, NUM_BLOCK_CTXS);
assert!(map.qf_thresholds.is_empty());
assert_eq!(map.ctx_map.len(), 39);
assert_eq!(map.block_context(1, 0, 5), block_context(1, 0));
assert_eq!(map.block_context(1, 6, 5), block_context(1, 6));
assert_eq!(map.block_context(0, 0, 5), block_context(0, 0));
}
#[test]
fn test_block_ctx_map_dynamic_methods() {
let map = BlockCtxMap::default();
assert_eq!(map.non_zero_context(5, 0), non_zero_context(5, 0));
assert_eq!(map.non_zero_context(8, 2), non_zero_context(8, 2));
assert_eq!(
map.zero_density_contexts_offset(0),
zero_density_contexts_offset(0)
);
assert_eq!(
map.zero_density_contexts_offset(3),
zero_density_contexts_offset(3)
);
assert_eq!(map.num_ac_contexts(), NUM_AC_CONTEXTS);
}
#[test]
fn test_block_ctx_map_num_ctxs_bounded() {
let map = BlockCtxMap::default();
assert!(map.num_ctxs <= MAX_BLOCK_CTXS);
}
}