#![allow(dead_code)]
#[cfg(feature = "decoder")]
pub mod arithmetic;
#[cfg(feature = "decoder")]
pub mod decoder;
pub mod encoder;
#[cfg(feature = "decoder")]
pub use arithmetic::ArithmeticDecoder;
#[cfg(feature = "decoder")]
pub use decoder::{EntropyDecoder, EntropyDecoderState};
pub use encoder::EntropyEncoder;
pub const MAX_DC_DIFF: i16 = 2047;
pub const MAX_AC_COEFF: i16 = 1023;
static CATEGORY_TABLE: [u8; 4096] = {
let mut table = [0u8; 4096];
let mut i = 0i32;
while i < 4096 {
let value = i - 2048;
table[i as usize] = if value == 0 {
0
} else {
let abs_val = if value < 0 { -value } else { value } as u32;
(32 - abs_val.leading_zeros()) as u8
};
i += 1;
}
table
};
#[inline(always)]
#[must_use]
pub fn category(value: i16) -> u8 {
let idx = (value as i32 + 2048) as usize;
if idx < 4096 {
CATEGORY_TABLE[idx]
} else {
if value == 0 {
0
} else {
16 - value.unsigned_abs().leading_zeros() as u8
}
}
}
#[inline]
#[must_use]
pub fn category_scalar(value: i16) -> u8 {
if value == 0 {
return 0;
}
let abs_val = value.unsigned_abs();
16 - abs_val.leading_zeros() as u8
}
#[inline]
#[must_use]
pub fn additional_bits(value: i16) -> u16 {
if value >= 0 {
value as u16
} else {
(value - 1) as u16 & ((1u16 << category(value)) - 1)
}
}
#[inline(always)]
#[must_use]
pub fn additional_bits_with_cat(value: i16, cat: u8) -> u16 {
if value >= 0 {
value as u16
} else {
(value - 1) as u16 & ((1u16 << cat) - 1)
}
}
#[inline]
#[must_use]
pub fn decode_value(category: u8, bits: u16) -> i16 {
if category == 0 {
return 0;
}
if category > 15 {
return bits as i16;
}
let half = 1u16 << (category - 1);
if bits >= half {
bits as i16
} else {
let max_val = (1i32 << category) - 1;
((bits as i32) - max_val) as i16
}
}
#[inline(always)]
pub fn huff_extend(x: i32, s: i32) -> i32 {
x + ((((x) - (1 << ((s) - 1))) >> 31) & (((-1) << (s)) + 1))
}
pub fn encode_block_to_writer(
coeffs: &[i16; 64],
dc_table: &crate::huffman::HuffmanEncodeTable,
ac_table: &crate::huffman::HuffmanEncodeTable,
prev_dc: i16,
writer: &mut crate::foundation::bitstream::BitWriter,
) -> crate::error::Result<()> {
let dc = coeffs[0];
let dc_diff = dc - prev_dc;
let dc_cat = category(dc_diff);
let (code, len) = dc_table.encode(dc_cat);
if dc_cat > 0 {
let additional = additional_bits_with_cat(dc_diff, dc_cat);
writer.write_code_and_extra(code, len, additional, dc_cat);
} else {
writer.write_bits(code, len);
}
let mut run = 0u8;
for i in 1..64 {
let ac = coeffs[i];
if ac == 0 {
run += 1;
} else {
while run >= 16 {
let (code, len) = ac_table.encode(0xF0); writer.write_bits(code, len);
run -= 16;
}
let ac_cat = category(ac);
let symbol = (run << 4) | ac_cat;
let (code, len) = ac_table.encode(symbol);
let additional = additional_bits_with_cat(ac, ac_cat);
writer.write_code_and_extra(code, len, additional, ac_cat);
run = 0;
}
}
if run > 0 {
let (code, len) = ac_table.encode(0x00); writer.write_bits(code, len);
}
Ok(())
}
#[derive(Clone, Debug)]
pub struct StreamingEntropyState {
pub prev_dc: [i16; 3],
pub mcu_idx: usize,
pub restart_count: u8,
}
impl StreamingEntropyState {
#[must_use]
pub fn new() -> Self {
Self {
prev_dc: [0; 3],
mcu_idx: 0,
restart_count: 0,
}
}
}
impl Default for StreamingEntropyState {
fn default() -> Self {
Self::new()
}
}
pub fn encode_blocks_mcu_order(
y_blocks: &[[i16; 64]],
cb_blocks: &[[i16; 64]],
cr_blocks: &[[i16; 64]],
tables: &crate::huffman::optimize::HuffmanTableSet,
writer: &mut crate::foundation::bitstream::BitWriter,
is_color: bool,
state: &mut StreamingEntropyState,
subsampling: crate::types::Subsampling,
width: usize,
restart_interval: u16,
total_mcus: usize,
) -> crate::error::Result<()> {
use crate::types::Subsampling;
let dc_luma = &tables.dc_luma.table;
let ac_luma = &tables.ac_luma.table;
let dc_chroma = &tables.dc_chroma.table;
let ac_chroma = &tables.ac_chroma.table;
let (h_samp, v_samp) = match subsampling {
Subsampling::S444 => (1, 1),
Subsampling::S422 => (2, 1),
Subsampling::S420 => (2, 2),
Subsampling::S440 => (1, 2),
};
let y_blocks_w = (width + 7) / 8;
let c_blocks_w = ((width + h_samp - 1) / h_samp + 7) / 8;
let mcu_h = (y_blocks_w + h_samp - 1) / h_samp;
let y_rows_in_batch = y_blocks.len().checked_div(y_blocks_w).unwrap_or(0);
let mcu_rows_in_batch = if h_samp == 1 && v_samp == 1 {
y_rows_in_batch
} else {
(y_rows_in_batch + v_samp - 1) / v_samp
};
const ZERO_BLOCK: [i16; 64] = [0i16; 64];
for mcu_row_offset in 0..mcu_rows_in_batch {
for mcu_x in 0..mcu_h {
for dy in 0..v_samp {
for dx in 0..h_samp {
let col = mcu_x * h_samp + dx;
let row = mcu_row_offset * v_samp + dy;
let block = if col < y_blocks_w && row < y_rows_in_batch {
y_blocks.get(row * y_blocks_w + col).unwrap_or(&ZERO_BLOCK)
} else {
&ZERO_BLOCK
};
encode_block_to_writer(block, dc_luma, ac_luma, state.prev_dc[0], writer)?;
state.prev_dc[0] = block[0];
}
}
if is_color {
let c_row = mcu_row_offset;
let c_col = mcu_x;
let c_idx = c_row * c_blocks_w + c_col;
let cb = cb_blocks.get(c_idx).unwrap_or(&ZERO_BLOCK);
encode_block_to_writer(cb, dc_chroma, ac_chroma, state.prev_dc[1], writer)?;
state.prev_dc[1] = cb[0];
let cr = cr_blocks.get(c_idx).unwrap_or(&ZERO_BLOCK);
encode_block_to_writer(cr, dc_chroma, ac_chroma, state.prev_dc[2], writer)?;
state.prev_dc[2] = cr[0];
}
state.mcu_idx += 1;
if restart_interval > 0
&& state.mcu_idx < total_mcus
&& state.mcu_idx % restart_interval as usize == 0
{
writer.flush_restart_marker(state.restart_count)?;
state.restart_count = (state.restart_count + 1) & 0x07;
state.prev_dc = [0; 3];
}
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_category() {
assert_eq!(category(0), 0);
assert_eq!(category(1), 1);
assert_eq!(category(-1), 1);
assert_eq!(category(2), 2);
assert_eq!(category(-2), 2);
assert_eq!(category(3), 2);
assert_eq!(category(-3), 2);
assert_eq!(category(4), 3);
assert_eq!(category(7), 3);
assert_eq!(category(255), 8);
assert_eq!(category(-255), 8);
}
#[test]
fn test_value_roundtrip() {
for value in -1023i16..=1023 {
let cat = category(value);
let bits = additional_bits(value);
let recovered = decode_value(cat, bits);
assert_eq!(value, recovered, "Failed for {}", value);
}
}
#[test]
fn test_additional_bits() {
assert_eq!(additional_bits(1), 1);
assert_eq!(additional_bits(2), 2);
assert_eq!(additional_bits(3), 3);
assert_eq!(additional_bits(-1), 0);
assert_eq!(additional_bits(-2), 1);
assert_eq!(additional_bits(-3), 0);
}
#[test]
fn test_decode_value_edge_cases() {
assert_eq!(decode_value(0, 0), 0);
assert_eq!(decode_value(0, 5), 0);
assert_eq!(decode_value(16, 100), 100);
assert_eq!(decode_value(20, 50), 50);
assert_eq!(decode_value(1, 0), -1);
assert_eq!(decode_value(1, 1), 1);
assert_eq!(decode_value(2, 0), -3);
assert_eq!(decode_value(2, 1), -2);
assert_eq!(decode_value(2, 2), 2);
assert_eq!(decode_value(2, 3), 3);
}
#[test]
fn test_category_large_values() {
assert_eq!(category(2047), 11);
assert_eq!(category(-2047), 11);
assert_eq!(category(1023), 10);
assert_eq!(category(1024), 11);
assert_eq!(category(511), 9);
assert_eq!(category(512), 10);
}
}