use bytemuck::cast;
use wide::{i16x8, i32x8, u16x8};
use crate::consts::*;
use crate::enabled_features;
use crate::jpeg::block_based_image::AlignedBlock;
use crate::lepton_error::err_exit_code;
use crate::structs::block_context::NeighborData;
use crate::structs::idct::*;
use crate::structs::model::*;
use crate::structs::quantization_tables::*;
use crate::{ExitCode, Result};
pub struct ProbabilityTables {
left_present: bool,
above_present: bool,
all_present: bool,
}
pub static NO_NEIGHBORS: ProbabilityTables = ProbabilityTables::new(false, false);
pub static TOP_ONLY: ProbabilityTables = ProbabilityTables::new(false, true);
pub static LEFT_ONLY: ProbabilityTables = ProbabilityTables::new(true, false);
pub static ALL: ProbabilityTables = ProbabilityTables::new(true, true);
pub struct PredictDCResult {
pub predicted_dc: i32,
pub uncertainty: i16,
pub uncertainty2: i16,
pub next_edge_pixels_h: i16x8,
pub next_edge_pixels_v: i16x8,
}
impl ProbabilityTables {
pub const fn new(in_left_present: bool, in_above_present: bool) -> ProbabilityTables {
return ProbabilityTables {
left_present: in_left_present,
above_present: in_above_present,
all_present: in_left_present && in_above_present,
};
}
pub fn is_all_present(&self) -> bool {
self.all_present
}
pub fn is_left_present(&self) -> bool {
self.left_present
}
pub fn is_above_present(&self) -> bool {
self.above_present
}
pub fn adv_predict_or_unpredict_dc(
saved_dc: i16,
recover_original: bool,
predicted_val: i32,
) -> i32 {
let max_value = 1 << (MAX_EXPONENT - 1);
let min_value = -max_value;
let adjustment_factor = (2 * max_value) + 1;
let mut retval = predicted_val;
retval = saved_dc as i32 + if recover_original { retval } else { -retval };
if retval < min_value {
retval += adjustment_factor;
}
if retval > max_value {
retval -= adjustment_factor;
}
return retval;
}
pub fn get_color_index(component: usize) -> usize {
return if component == 0 { 0 } else { 1 };
}
#[inline(always)]
pub fn num_non_zeros_to_bin_7x7(num_non_zeros: usize) -> usize {
return usize::from(NON_ZERO_TO_BIN_7X7[num_non_zeros]);
}
pub fn calc_num_non_zeros_7x7_context_bin<const ALL_PRESENT: bool>(
&self,
neighbor_data: &NeighborData,
) -> u8 {
let mut num_non_zeros_above = 0;
let mut num_non_zeros_left = 0;
if ALL_PRESENT || self.above_present {
num_non_zeros_above = neighbor_data.neighbor_context_above.get_num_non_zeros();
}
if ALL_PRESENT || self.left_present {
num_non_zeros_left = neighbor_data.neighbor_context_left.get_num_non_zeros();
}
let num_non_zeros_context;
if (!ALL_PRESENT) && self.above_present && !self.left_present {
num_non_zeros_context = (num_non_zeros_above + 1) / 2;
} else if (!ALL_PRESENT) && self.left_present && !self.above_present {
num_non_zeros_context = (num_non_zeros_left + 1) / 2;
} else if ALL_PRESENT || (self.left_present && self.above_present) {
num_non_zeros_context = (num_non_zeros_above + num_non_zeros_left + 2) / 4;
} else {
num_non_zeros_context = 0;
}
return NON_ZERO_TO_BIN[usize::from(num_non_zeros_context)];
}
#[inline(never)]
pub fn calc_coefficient_context_7x7_aavg_block<const ALL_PRESENT: bool>(
&self,
left: &AlignedBlock,
above: &AlignedBlock,
above_left: &AlignedBlock,
) -> [u16; 64] {
let mut best_prior = [u16x8::ZERO; 8];
if ALL_PRESENT {
for i in 1..8 {
best_prior[i] =
((left.as_i16x8(i).unsigned_abs() + above.as_i16x8(i).unsigned_abs()) * 13
+ above_left.as_i16x8(i).unsigned_abs() * 6)
>> 5;
}
} else {
if self.left_present {
for i in 1..8 {
best_prior[i] = left.as_i16x8(i).unsigned_abs();
}
} else if self.above_present {
for i in 1..8 {
best_prior[i] = above.as_i16x8(i).unsigned_abs();
}
}
}
cast(best_prior)
}
#[inline(always)]
pub fn predict_current_edges(
neighbors_data: &NeighborData,
raster: &[i32x8; 8],
) -> (i32x8, i32x8) {
let mult: i32x8 = i32x8::from(ICOS_BASED_8192_SCALED);
let mut horiz_pred: [i32; 8] = neighbors_data
.neighbor_context_above
.get_horizontal_coef()
.to_array();
let mut vert_pred: i32x8 = neighbors_data.neighbor_context_left.get_vertical_coef();
for col in 1..8 {
vert_pred -= raster[col] * ICOS_BASED_8192_SCALED[col];
horiz_pred[col] = horiz_pred[col].wrapping_sub((raster[col] * mult).reduce_add());
}
(i32x8::from(horiz_pred), vert_pred)
}
#[inline(always)]
pub fn predict_next_edges(raster: &[i32x8; 8]) -> (i32x8, i32x8) {
let mult = i32x8::from(ICOS_BASED_8192_SCALED_PM);
let mut horiz_pred: [i32; 8] = [0; 8];
let mut vert_pred = ICOS_BASED_8192_SCALED_PM[0] * raster[0];
for col in 1..8 {
horiz_pred[col] = (mult * raster[col]).reduce_add();
vert_pred += ICOS_BASED_8192_SCALED_PM[col] * raster[col];
}
(i32x8::from(horiz_pred), vert_pred)
}
#[inline(always)]
pub fn calc_coefficient_context8_lak<const ALL_PRESENT: bool, const HORIZONTAL: bool>(
&self,
qt: &QuantizationTables,
coefficient_tr: usize,
pred: &[i32; 8],
) -> Result<i32> {
if !ALL_PRESENT
&& ((HORIZONTAL && !self.above_present) || (!HORIZONTAL && !self.left_present))
{
return Ok(0);
}
let best_prior: i32 = pred[if HORIZONTAL {
coefficient_tr >> 3
} else {
coefficient_tr
}];
let div = (qt.get_quantization_table_transposed()[coefficient_tr] as i32) << 13;
if let Some(x) = best_prior.checked_div(div) {
return Ok(x);
} else {
return err_exit_code(
ExitCode::UnsupportedJpegWithZeroIdct0,
"integer overflow in coefficient context calculation",
);
}
}
pub fn adv_predict_dc_pix<const ALL_PRESENT: bool>(
&self,
raster_cols: &[i32x8; 8],
q0: i32,
neighbor_data: &NeighborData,
enabled_features: &enabled_features::EnabledFeatures,
) -> PredictDCResult {
let pixels_sans_dc = run_idct(raster_cols);
#[inline]
fn calc_pred(a1: i16x8, a2: i16x8, is_16_bit: bool) -> i16x8 {
if is_16_bit {
let pixel_delta = a1 - a2;
let half_delta = (pixel_delta - (pixel_delta >> 15)) >> 1;
a1 + half_delta
} else {
let a1 = i32x8::from_i16x8(a1);
let a2 = i32x8::from_i16x8(a2);
let pixel_delta = a1 - a2;
let half_delta = (pixel_delta - (pixel_delta >> 31)) >> 1;
let result = a1 + half_delta;
i16x8::from_i32x8_truncate(result)
}
}
let a1 = pixels_sans_dc.as_i16x8(0);
let a2 = pixels_sans_dc.as_i16x8(1);
let v_pred = calc_pred(a1, a2, enabled_features.use_16bit_adv_predict);
let a1 = pixels_sans_dc.from_stride(0, 8);
let a2 = pixels_sans_dc.from_stride(1, 8);
let h_pred = calc_pred(a1, a2, enabled_features.use_16bit_adv_predict);
let a1 = pixels_sans_dc.as_i16x8(7);
let a2 = pixels_sans_dc.as_i16x8(6);
let next_edge_pixels_v = calc_pred(a1, a2, enabled_features.use_16bit_dc_estimate);
let a1 = pixels_sans_dc.from_stride(7, 8);
let a2 = pixels_sans_dc.from_stride(6, 8);
let next_edge_pixels_h = calc_pred(a1, a2, enabled_features.use_16bit_dc_estimate);
let min_dc;
let max_dc;
let mut avg_horizontal: i32;
let mut avg_vertical: i32;
if ALL_PRESENT {
let horiz = neighbor_data.neighbor_context_left.get_horizontal_pix() - h_pred;
let vert = neighbor_data.neighbor_context_above.get_vertical_pix() - v_pred;
min_dc = horiz.min(vert).reduce_min();
max_dc = horiz.max(vert).reduce_max();
avg_horizontal = i32x8::from_i16x8(horiz).reduce_add();
avg_vertical = i32x8::from_i16x8(vert).reduce_add();
} else if self.left_present {
let horiz = neighbor_data.neighbor_context_left.get_horizontal_pix() - h_pred;
min_dc = horiz.reduce_min();
max_dc = horiz.reduce_max();
avg_horizontal = i32x8::from_i16x8(horiz).reduce_add();
avg_vertical = avg_horizontal;
} else if self.above_present {
let vert = neighbor_data.neighbor_context_above.get_vertical_pix() - v_pred;
min_dc = vert.reduce_min();
max_dc = vert.reduce_max();
avg_vertical = i32x8::from_i16x8(vert).reduce_add();
avg_horizontal = avg_vertical;
} else {
return PredictDCResult {
predicted_dc: 0,
uncertainty: 0,
uncertainty2: 0,
next_edge_pixels_h,
next_edge_pixels_v,
};
}
let avgmed: i32 = (avg_vertical + avg_horizontal) >> 1;
let uncertainty_val = ((i32::from(max_dc) - i32::from(min_dc)) >> 3) as i16;
avg_horizontal -= avgmed;
avg_vertical -= avgmed;
let mut far_afield_value = avg_vertical;
if avg_horizontal.abs() < avg_vertical.abs() {
far_afield_value = avg_horizontal;
}
let uncertainty2_val = (far_afield_value >> 3) as i16;
return PredictDCResult {
predicted_dc: (avgmed / q0 + 4) >> 3,
uncertainty: uncertainty_val,
uncertainty2: uncertainty2_val,
next_edge_pixels_h,
next_edge_pixels_v,
};
}
}