use half::f16;
use super::{half_float_quantizer::algo_quantize, PackedStream};
use crate::error::{Error, Result};
pub(super) struct QuantTables {
pub(super) y: [f32; 64],
pub(super) half_y: [u16; 64],
pub(super) cbcr: [f32; 64],
pub(super) half_cbcr: [u16; 64],
}
impl QuantTables {
pub(super) fn new(quant_base_error: f32) -> Self {
const JPEG_Y: [i32; 64] = [
16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57,
69, 56, 14, 17, 22, 29, 51, 87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55,
64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101, 72, 92, 95, 98, 112, 100,
103, 99,
];
const JPEG_CBCR: [i32; 64] = [
17, 18, 24, 47, 99, 99, 99, 99, 18, 21, 26, 66, 99, 99, 99, 99, 24, 26, 56, 99, 99, 99,
99, 99, 47, 66, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
];
let quant_base_error = quant_base_error.max(0.0);
let mut y = [0.0; 64];
let mut half_y = [0; 64];
let mut cbcr = [0.0; 64];
let mut half_cbcr = [0; 64];
for index in 0..64 {
y[index] = quant_base_error * JPEG_Y[index] as f32 / 10.0;
half_y[index] = f16::from_f32(y[index]).to_bits();
cbcr[index] = quant_base_error * JPEG_CBCR[index] as f32 / 17.0;
half_cbcr[index] = f16::from_f32(cbcr[index]).to_bits();
}
Self {
y,
half_y,
cbcr,
half_cbcr,
}
}
}
pub(super) fn quantize_coefficients_to_zigzag(
dct_values: &[f32; 64],
tolerances: &[f32; 64],
half_tolerances: &[u16; 64],
) -> [u16; 64] {
const INV_REMAP: [usize; 64] = [
0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9,
11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60,
21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63,
];
let mut half_zig = [0u16; 64];
for i in 0..64 {
let src = f16::from_f32(dct_values[i]).to_bits();
let quantized = algo_quantize(
src as u32,
half_tolerances[i] as u32,
tolerances[i],
f16::from_bits(src).to_f32(),
);
half_zig[INV_REMAP[i]] = quantized as u16;
}
half_zig
}
pub(super) fn rle_ac(block: &[u16; 64], ac: &mut Vec<u16>) {
let mut dct_comp = 1;
while dct_comp < 64 {
if block[dct_comp] != 0 {
ac.push(block[dct_comp]);
dct_comp += 1;
continue;
}
let mut run_len = 1;
while dct_comp + run_len < 64 && block[dct_comp + run_len] == 0 {
run_len += 1;
}
if run_len == 1 {
ac.push(block[dct_comp]);
} else if run_len + dct_comp == 64 {
ac.push(0xff00);
} else {
ac.push(0xff00 | run_len as u16);
}
dct_comp += run_len;
}
}
pub(super) fn un_rle_ac(ac: &mut PackedStream<'_>, block: &mut [u16; 64]) -> Result<usize> {
let mut last_non_zero = 0;
let mut position = 1;
while position < 64 {
let value = ac.next().ok_or_else(|| Error::invalid("truncated DWA AC data"))?;
if (value & 0xff00) == 0xff00 {
let count = (value & 0xff) as usize;
position += if count == 0 {
64
} else {
count
};
} else {
last_non_zero = position;
block[position] = value;
position += 1;
}
}
Ok(last_non_zero)
}
pub(super) fn from_half_zigzag(zig_zag: &[u16; 64], dst: &mut [f32; 64]) {
const SRC_INDICES: [usize; 64] = [
0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9,
11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60,
21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63,
];
for (slot, &src_index) in dst.iter_mut().zip(SRC_INDICES.iter()) {
*slot = f16::from_bits(zig_zag[src_index]).to_f32();
}
}
#[cfg(test)]
mod test {
use rand::{Rng, SeedableRng};
use super::*;
const SEED: [u8; 32] = [
250, 77, 33, 7, 42, 13, 200, 176, 22, 5, 66, 100, 19, 240, 8, 91, 3, 128, 9, 44, 201, 17,
88, 6, 255, 61, 30, 11, 2, 121, 99, 1,
];
fn assert_ac_roundtrips(block: [u16; 64]) {
let mut ac = Vec::new();
rle_ac(&block, &mut ac);
let mut stream = PackedStream::new(&ac);
let mut decoded = [0u16; 64];
un_rle_ac(&mut stream, &mut decoded).unwrap();
assert_eq!(decoded, block);
}
#[test]
fn ac_run_length_roundtrip_hardcoded() {
assert_ac_roundtrips([0u16; 64]);
let mut dense = [0u16; 64];
for (index, slot) in dense.iter_mut().enumerate().skip(1) {
*slot = index as u16;
}
assert_ac_roundtrips(dense);
let mut mixed = [0u16; 64];
mixed[1] = 5;
mixed[10] = 7;
mixed[11] = 0; mixed[12] = 9;
assert_ac_roundtrips(mixed);
}
#[test]
fn ac_run_length_roundtrip_seeded() {
let mut random = rand::rngs::StdRng::from_seed(SEED);
for _ in 0..64 {
let mut block = [0u16; 64];
for slot in block.iter_mut().skip(1) {
*slot = if random.gen_bool(0.3) {
0
} else {
random.gen_range(1..=0xfeff)
};
}
assert_ac_roundtrips(block);
}
}
}