use super::bitreader::BitReader;
use super::vlc::{default_magnitude_table, default_run_table, VlcTable};
use super::vlc_encode::{read_escape_value, MAG_ESCAPE_BASE, RUN_ESCAPE_BASE};
use super::{JxsError, JxsResult};
#[derive(Debug, Clone)]
pub struct SubbandCoeffs {
pub width: usize,
pub height: usize,
pub coeffs: Vec<i32>,
}
impl SubbandCoeffs {
pub fn zeros(width: usize, height: usize) -> Self {
Self {
width,
height,
coeffs: vec![0i32; width * height],
}
}
pub fn len(&self) -> usize {
self.width * self.height
}
pub fn is_empty(&self) -> bool {
self.coeffs.is_empty()
}
}
pub fn decode_subband(
reader: &mut BitReader<'_>,
run_table: &VlcTable,
mag_table: &VlcTable,
width: usize,
height: usize,
) -> JxsResult<SubbandCoeffs> {
let num_coeffs = width * height;
let mut coeffs = vec![0i32; num_coeffs];
let mut idx = 0usize;
while idx < num_coeffs {
let peek = reader.peek_bits_u32(32);
match run_table.lookup(peek) {
None => {
reader.skip_bits(1)?;
}
Some(run_result) => {
let run_bits = run_result.bits_consumed;
let run_val = run_result.value;
reader.skip_bits(run_bits)?;
let run = if run_val < 0 {
(RUN_ESCAPE_BASE + read_escape_value(reader)?) as usize
} else {
run_val as usize
};
idx = (idx + run).min(num_coeffs);
if idx >= num_coeffs {
break;
}
let peek2 = reader.peek_bits_u32(32);
match mag_table.lookup(peek2) {
None => {
reader.skip_bits(1)?;
}
Some(mag_result) => {
let mag_bits = mag_result.bits_consumed;
let mag_level = mag_result.value;
reader.skip_bits(mag_bits)?;
let level = if mag_level < 0 {
MAG_ESCAPE_BASE + read_escape_value(reader)? as i32
} else {
mag_level as i32
};
let sign_bit = reader.read_bit()?;
let signed_val = if sign_bit == 0 { level } else { -level };
coeffs[idx] = signed_val;
idx += 1;
}
}
}
}
}
Ok(SubbandCoeffs {
width,
height,
coeffs,
})
}
pub fn decode_slice_subbands(
slice_data: &[u8],
frame_width: usize,
frame_height: usize,
) -> JxsResult<(SubbandCoeffs, SubbandCoeffs, SubbandCoeffs, SubbandCoeffs)> {
let n_low_w = (frame_width + 1) / 2;
let n_high_w = frame_width / 2;
let n_low_h = (frame_height + 1) / 2;
let n_high_h = frame_height / 2;
let mut reader = BitReader::new(slice_data);
let run_table = default_run_table();
let mag_table = default_magnitude_table();
let ll = decode_subband(&mut reader, &run_table, &mag_table, n_low_w, n_low_h)?;
let hl = decode_subband(&mut reader, &run_table, &mag_table, n_high_w, n_low_h)?;
let lh = decode_subband(&mut reader, &run_table, &mag_table, n_low_w, n_high_h)?;
let hh = decode_subband(&mut reader, &run_table, &mag_table, n_high_w, n_high_h)?;
Ok((ll, hl, lh, hh))
}
pub fn try_decode_constant_grey(
slice_data: &[u8],
frame_width: usize,
frame_height: usize,
constant_ll_value: i32,
) -> Option<(SubbandCoeffs, SubbandCoeffs, SubbandCoeffs, SubbandCoeffs)> {
let n_low_w = (frame_width + 1) / 2;
let n_high_w = frame_width / 2;
let n_low_h = (frame_height + 1) / 2;
let n_high_h = frame_height / 2;
if slice_data.is_empty() {
let ll = SubbandCoeffs {
width: n_low_w,
height: n_low_h,
coeffs: vec![constant_ll_value; n_low_w * n_low_h],
};
let hl = SubbandCoeffs::zeros(n_high_w, n_low_h);
let lh = SubbandCoeffs::zeros(n_low_w, n_high_h);
let hh = SubbandCoeffs::zeros(n_high_w, n_high_h);
return Some((ll, hl, lh, hh));
}
None
}
#[cfg(test)]
mod tests {
use super::*;
use crate::jpegxs::vlc::{default_magnitude_table, default_run_table};
#[test]
fn subband_coeffs_zeros() {
let sb = SubbandCoeffs::zeros(4, 4);
assert_eq!(sb.width, 4);
assert_eq!(sb.height, 4);
assert_eq!(sb.len(), 16);
assert!(sb.coeffs.iter().all(|&v| v == 0));
}
#[test]
fn decode_all_zero_slice_returns_zeros() {
let result = try_decode_constant_grey(&[], 4, 4, 64);
assert!(result.is_some());
let (ll, hl, lh, hh) = result.unwrap();
assert_eq!(ll.width, 2);
assert_eq!(ll.height, 2);
assert_eq!(ll.coeffs, vec![64i32; 4]);
assert!(hl.coeffs.iter().all(|&v| v == 0));
assert!(lh.coeffs.iter().all(|&v| v == 0));
assert!(hh.coeffs.iter().all(|&v| v == 0));
}
#[test]
fn decode_subband_empty_data_returns_zeros() {
let data: &[u8] = &[];
let mut reader = BitReader::new(data);
let run_table = default_run_table();
let mag_table = default_magnitude_table();
let result = decode_subband(&mut reader, &run_table, &mag_table, 2, 2);
match result {
Ok(sb) => assert_eq!(sb.coeffs, vec![0i32; 4]),
Err(JxsError::TruncatedStream { .. }) => {}
Err(e) => panic!("unexpected error: {e}"),
}
}
#[test]
fn decode_subband_run0_value1_positive() {
let data = &[0b0000_0000u8];
let mut reader = BitReader::new(data);
let run_table = default_run_table();
let mag_table = default_magnitude_table();
let result = decode_subband(&mut reader, &run_table, &mag_table, 2, 1);
match result {
Ok(sb) => {
assert_eq!(sb.width, 2);
assert_eq!(sb.height, 1);
assert_eq!(sb.coeffs[0], 1, "first coefficient should be +1");
}
Err(JxsError::TruncatedStream { .. }) => {
}
Err(e) => panic!("unexpected error: {e}"),
}
}
#[test]
fn decode_subband_run0_value1_negative() {
let data = &[0b0010_0000u8];
let mut reader = BitReader::new(data);
let run_table = default_run_table();
let mag_table = default_magnitude_table();
let result = decode_subband(&mut reader, &run_table, &mag_table, 1, 1);
match result {
Ok(sb) => assert_eq!(sb.coeffs[0], -1, "coefficient should be -1"),
Err(JxsError::TruncatedStream { .. }) => {}
Err(e) => panic!("unexpected error: {e}"),
}
}
}