#![allow(dead_code)]
#![allow(clippy::needless_range_loop)]
pub mod distortion;
pub mod lambda;
pub mod level_costs;
pub mod stats;
pub use distortion::{
FLATNESS_LIMIT_I4, FLATNESS_LIMIT_I16, FLATNESS_LIMIT_UV, FLATNESS_PENALTY, is_flat_coeffs,
is_flat_source_16, t_transform, tdisto_4x4, tdisto_8x8, tdisto_16x16,
};
pub use lambda::{
DEFAULT_SNS_STRENGTH, LAMBDA_I4, LAMBDA_I16, LAMBDA_UV, calc_i4_penalty, calc_lambda_i4,
calc_lambda_i16, calc_lambda_mode, calc_lambda_trellis_i4, calc_lambda_trellis_i16,
calc_lambda_trellis_uv, calc_lambda_uv, calc_tlambda, compute_filter_level,
compute_filter_level_with_beta, filter_strength_from_delta,
};
pub use level_costs::{LevelCostArray, LevelCostTables, LevelCosts, RemappedCosts};
pub use stats::{NUM_BANDS, NUM_CTX, NUM_PROBAS, NUM_TYPES, ProbaStats, TokenType, record_coeffs};
pub use super::tables::*;
pub use super::analysis::*;
pub use super::quantize::{MatrixType, QFIX, VP8Matrix, quantdiv, quantization_bias};
pub use super::psy::PsyConfig;
pub use super::trellis::trellis_quantize_block;
pub use super::residual_cost::{
Residual, get_cost_luma4, get_cost_luma16, get_cost_uv, get_residual_cost,
};
pub const RD_DISTO_MULT: u32 = 256;
#[inline]
pub fn vp8_bit_cost(bit: bool, prob: u8) -> u16 {
use super::tables::VP8_ENTROPY_COST;
if bit {
VP8_ENTROPY_COST[255 - prob as usize]
} else {
VP8_ENTROPY_COST[prob as usize]
}
}
#[inline]
pub fn estimate_residual_cost(coeffs: &[i32; 16], first: usize) -> u32 {
use super::tables::{MAX_LEVEL, VP8_LEVEL_FIXED_COSTS};
let mut cost = 0u32;
let mut last_nz = -1i32;
for (i, &c) in coeffs.iter().enumerate().rev() {
if c != 0 {
last_nz = i as i32;
break;
}
}
if last_nz < first as i32 {
return 256;
}
for &coeff in coeffs.iter().take(last_nz as usize + 1).skip(first) {
let level = coeff.unsigned_abs() as usize;
if level > 0 {
let level_clamped = level.min(MAX_LEVEL);
cost += u32::from(VP8_LEVEL_FIXED_COSTS[level_clamped]);
cost += 256;
} else {
cost += 128;
}
}
cost += 128;
cost
}
#[inline]
pub fn estimate_luma16_cost(blocks: &[[i32; 16]; 16], has_dc: bool) -> u32 {
let first = if has_dc { 0 } else { 1 };
blocks
.iter()
.map(|block| estimate_residual_cost(block, first))
.sum()
}
#[inline]
pub fn estimate_dc16_cost(dc_coeffs: &[i32; 16]) -> u32 {
estimate_residual_cost(dc_coeffs, 0)
}
#[inline]
pub fn rd_score(sse: u32, mode_cost: u16, lambda: u32) -> u64 {
let distortion = u64::from(sse) * u64::from(RD_DISTO_MULT);
let rate = u64::from(mode_cost) * u64::from(lambda);
distortion + rate
}
#[inline]
pub fn rd_score_with_coeffs(sse: u32, mode_cost: u16, coeff_cost: u32, lambda: u32) -> u64 {
let distortion = u64::from(sse) * u64::from(RD_DISTO_MULT);
let rate = (u64::from(mode_cost) + u64::from(coeff_cost)) * u64::from(lambda);
distortion + rate
}
#[inline]
pub fn rd_score_full(
sse: u32,
spectral_disto: i32,
mode_cost: u16,
coeff_cost: u32,
lambda: u32,
) -> i64 {
let rate = (i64::from(mode_cost) + i64::from(coeff_cost)) * i64::from(lambda);
let distortion = i64::from(RD_DISTO_MULT) * (i64::from(sse) + i64::from(spectral_disto));
rate + distortion
}
#[inline]
pub fn get_i4_mode_cost(top: usize, left: usize, mode: usize) -> u16 {
VP8_FIXED_COSTS_I4[top][left][mode]
}
#[cfg(test)]
mod tests {
use super::*;
use crate::encoder::psy::PsyConfig;
use crate::encoder::tables::VP8_ZIGZAG;
use crate::encoder::trellis::trellis_quantize_block;
#[test]
fn test_entropy_cost_table() {
assert!((VP8_ENTROPY_COST[128] as i32 - 256).abs() < 10);
assert!(VP8_ENTROPY_COST[255] < 10);
assert!(VP8_ENTROPY_COST[1] > 1500);
for i in 1..256 {
assert!(
VP8_ENTROPY_COST[i] <= VP8_ENTROPY_COST[i - 1],
"Entropy cost not monotonic at {}",
i
);
}
}
#[test]
fn test_bit_cost() {
assert_eq!(vp8_bit_cost(false, 128), vp8_bit_cost(true, 128));
assert!(vp8_bit_cost(false, 250) < vp8_bit_cost(false, 128));
assert!(vp8_bit_cost(true, 250) > vp8_bit_cost(true, 128));
}
#[test]
fn test_level_fixed_costs() {
assert_eq!(VP8_LEVEL_FIXED_COSTS[0], 0);
assert!(VP8_LEVEL_FIXED_COSTS[1] < VP8_LEVEL_FIXED_COSTS[100]);
assert!(VP8_LEVEL_FIXED_COSTS[100] < VP8_LEVEL_FIXED_COSTS[1000]);
assert_eq!(VP8_LEVEL_FIXED_COSTS.len(), MAX_LEVEL + 1);
}
#[test]
fn test_lambda_calculation() {
let q = 64u32;
let lambda_i4 = calc_lambda_i4(q);
let lambda_i16 = calc_lambda_i16(q);
let lambda_uv = calc_lambda_uv(q);
assert!(lambda_i16 > lambda_i4 * 50);
assert!(lambda_uv > lambda_i4);
assert!(lambda_uv < lambda_i16);
assert_eq!(lambda_i4, (3 * 64 * 64) >> 7);
assert_eq!(lambda_i16, 3 * 64 * 64);
assert_eq!(lambda_uv, (3 * 64 * 64) >> 6);
}
#[test]
fn test_rd_score() {
assert_eq!(rd_score(0, 0, LAMBDA_I16), 0);
assert_eq!(rd_score(100, 0, LAMBDA_I16), 100 * 256);
assert_eq!(rd_score(0, 663, LAMBDA_I16), 663 * 106);
let expected = 1000 * 256 + u64::from(FIXED_COSTS_I16[0]) * 106;
assert_eq!(rd_score(1000, FIXED_COSTS_I16[0], LAMBDA_I16), expected);
}
#[test]
fn test_estimate_residual_cost() {
let zeros = [0i32; 16];
let cost_zeros = estimate_residual_cost(&zeros, 0);
assert!(cost_zeros < 512, "Zero block cost should be ~1 bit");
let mut single_dc = [0i32; 16];
single_dc[0] = 1;
let cost_single = estimate_residual_cost(&single_dc, 0);
assert!(
cost_single > cost_zeros,
"Non-zero coefficient should cost more"
);
}
#[test]
fn test_trellis_basic() {
let matrix = VP8Matrix::new(50, 50, MatrixType::Y1);
let block = [
500i32, 50, 25, 10, 5, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, ];
let mut coeffs = block;
let mut trellis_out = [0i32; 16];
let lambda = 2187u32;
let mut level_costs = LevelCosts::new();
level_costs.calculate(&crate::common::types::COEFF_PROBS);
let trellis_nz = trellis_quantize_block(
&mut coeffs,
&mut trellis_out,
&matrix,
lambda,
0,
&level_costs,
3, 0, &PsyConfig::default(),
);
let mut simple_out = [0i32; 16];
for i in 0..16 {
let j = VP8_ZIGZAG[i]; simple_out[i] = matrix.quantize_coeff(block[j], j);
}
assert!(simple_out[0] != 0, "Simple should have non-zero DC");
let _ = trellis_nz; }
}