#![allow(dead_code)]
#![allow(clippy::needless_range_loop)]
pub mod classifier;
pub mod histogram;
pub mod iterator;
pub mod prediction;
pub mod segment;
use alloc::vec::Vec;
pub use classifier::{
ClassifierDiag, ImageContentType, classify_image_type, classify_image_type_diag,
content_type_to_tuning,
};
pub use histogram::{collect_histogram_bps, forward_dct_4x4};
pub use iterator::AnalysisIterator;
pub use segment::{assign_segments_kmeans, compute_segment_quant, smooth_segment_map};
pub const MAX_ALPHA: u8 = 255;
const ALPHA_SCALE: u32 = 2 * MAX_ALPHA as u32;
const MAX_COEFF_THRESH: usize = 31;
pub const NUM_SEGMENTS: usize = 4;
const MAX_INTRA16_MODE: usize = 2;
const MAX_UV_MODE: usize = 2;
const BPS: usize = 32;
const Y_OFF_ENC: usize = 0;
const U_OFF_ENC: usize = 16;
const V_OFF_ENC: usize = 24;
#[allow(clippy::erasing_op, clippy::identity_op)]
const VP8_DSP_SCAN: [usize; 24] = [
0 + 0 * BPS,
4 + 0 * BPS,
8 + 0 * BPS,
12 + 0 * BPS,
0 + 4 * BPS,
4 + 4 * BPS,
8 + 4 * BPS,
12 + 4 * BPS,
0 + 8 * BPS,
4 + 8 * BPS,
8 + 8 * BPS,
12 + 8 * BPS,
0 + 12 * BPS,
4 + 12 * BPS,
8 + 12 * BPS,
12 + 12 * BPS,
0 + 0 * BPS,
4 + 0 * BPS,
0 + 4 * BPS,
4 + 4 * BPS,
8 + 0 * BPS,
12 + 0 * BPS,
8 + 4 * BPS,
12 + 4 * BPS,
];
const I16DC16: usize = 0;
const I16TM16: usize = I16DC16 + 16;
#[allow(clippy::identity_op)] const I16VE16: usize = 1 * 16 * BPS;
const I16HE16: usize = I16VE16 + 16;
const VP8_I16_MODE_OFFSETS: [usize; 4] = [I16DC16, I16TM16, I16VE16, I16HE16];
const C8DC8: usize = 2 * 16 * BPS;
const C8TM8: usize = C8DC8 + 16;
const C8VE8: usize = 2 * 16 * BPS + 8 * BPS;
const C8HE8: usize = C8VE8 + 16;
const VP8_UV_MODE_OFFSETS: [usize; 4] = [C8DC8, C8TM8, C8VE8, C8HE8];
const PRED_SIZE_ENC: usize = 32 * BPS + 16 * BPS + 8 * BPS;
const YUV_SIZE_ENC: usize = BPS * 16;
#[derive(Default, Clone, Copy)]
pub struct DctHistogram {
pub max_value: u32,
pub last_non_zero: usize,
}
impl DctHistogram {
pub fn new() -> Self {
Self {
max_value: 0,
last_non_zero: 1,
}
}
pub fn from_distribution(distribution: &[u32; MAX_COEFF_THRESH + 1]) -> Self {
let mut max_value: u32 = 0;
let mut last_non_zero: usize = 1;
for (k, &count) in distribution.iter().enumerate() {
if count > 0 {
if count > max_value {
max_value = count;
}
last_non_zero = k;
}
}
Self {
max_value,
last_non_zero,
}
}
pub fn get_alpha(&self) -> i32 {
if self.max_value <= 1 {
return 0;
}
let value = ALPHA_SCALE * self.last_non_zero as u32 / self.max_value;
value as i32
}
}
#[inline]
fn final_alpha_value(alpha: i32) -> u8 {
let alpha = MAX_ALPHA as i32 - alpha;
alpha.clamp(0, MAX_ALPHA as i32) as u8
}
pub fn analyze_macroblock(it: &mut AnalysisIterator, method: u8, sns_strength: u8) -> (u8, i32) {
let (best_alpha, _best_mode) = it.analyze_best_intra16_mode();
let (best_uv_alpha, _uv_mode) = it.analyze_best_uv_mode();
let alpha = (3 * best_alpha + best_uv_alpha + 2) >> 2;
let alpha = if method >= 4 && sns_strength > 0 {
let masking = crate::encoder::psy::compute_masking_alpha(&it.yuv_in[Y_OFF_ENC..], BPS);
crate::encoder::psy::blend_masking_alpha(alpha, masking, method)
} else {
alpha
};
(final_alpha_value(alpha), best_uv_alpha)
}
pub struct AnalysisResult {
pub mb_alphas: Vec<u8>,
pub alpha_histogram: [u32; 256],
pub uv_alpha_avg: i32,
}
#[allow(clippy::too_many_arguments)]
pub fn analyze_image(
y_src: &[u8],
u_src: &[u8],
v_src: &[u8],
width: usize,
height: usize,
y_stride: usize,
uv_stride: usize,
method: u8,
sns_strength: u8,
) -> AnalysisResult {
let mut it = AnalysisIterator::new(width, height);
it.reset();
let total_mbs = it.mb_w * it.mb_h;
let mut mb_alphas = Vec::with_capacity(total_mbs);
let mut alpha_histogram = [0u32; 256];
let mut uv_alpha_sum: i64 = 0;
loop {
it.import(y_src, u_src, v_src, y_stride, uv_stride);
let (alpha, uv_alpha) = analyze_macroblock(&mut it, method, sns_strength);
mb_alphas.push(alpha);
alpha_histogram[alpha as usize] += 1;
uv_alpha_sum += uv_alpha as i64;
if !it.next() {
break;
}
}
let uv_alpha_avg = if total_mbs > 0 {
(uv_alpha_sum / total_mbs as i64) as i32
} else {
64 };
AnalysisResult {
mb_alphas,
alpha_histogram,
uv_alpha_avg,
}
}