use super::bits::BitWriter;
use super::enc_ps_est::{PS_BANDS, PsFrameParams};
use super::error::{Error, Result};
use super::ps_huffman::{
HUFF_ICC_DF, HUFF_ICC_DT, HUFF_IID_DF, HUFF_IID_DT, HUFF_IID_FINE_DF, HUFF_IID_FINE_DT,
};
use super::sbr_element::EXTENSION_ID_PS;
pub(crate) const PS_EXT_MAX_BYTES: usize = 15 + 255;
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub(crate) struct PsBits {
pub bytes: Vec<u8>,
pub bits: usize,
}
#[derive(Debug, Clone, Default)]
pub(crate) struct PsWriter {
prev: Option<PsFrameParams>,
fine: bool,
ps_bits: u64,
ext_bits: u64,
}
impl PsWriter {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn with_fine_iid(mut self) -> Self {
self.fine = true;
self
}
pub(crate) fn ps_bits(&self) -> u64 {
self.ps_bits
}
pub(crate) fn ext_bits(&self) -> u64 {
self.ext_bits
}
pub(crate) fn reset(&mut self) {
self.prev = None;
self.ps_bits = 0;
self.ext_bits = 0;
}
pub(crate) fn frame(&mut self, params: Option<&PsFrameParams>, header: bool) -> Result<PsBits> {
let mut w = BitWriter::new();
w.write_bit(header); if header {
w.write_bit(true); w.write(if self.fine { 4 } else { 1 }, 3);
w.write_bit(true); w.write(1, 3); w.write_bit(false); } else if self.prev.is_none() && params.is_some() {
return Err(Error::PsDataInvalid);
}
w.write_bit(false); let Some(p) = params else {
w.write(0, 2); return Ok(self.account(w));
};
let bound: i8 = if self.fine { 15 } else { 7 };
if p.iid.iter().any(|i| !(-bound..=bound).contains(i)) || p.icc.iter().any(|&c| c > 7) {
return Err(Error::PsDataInvalid);
}
w.write(1, 2); let prev = if header { None } else { self.prev.as_ref() };
let iid: [i32; PS_BANDS] = p.iid.map(i32::from);
let icc: [i32; PS_BANDS] = p.icc.map(i32::from);
let (df, dt, lav) = if self.fine {
(&HUFF_IID_FINE_DF[..], &HUFF_IID_FINE_DT[..], 30)
} else {
(&HUFF_IID_DF[..], &HUFF_IID_DT[..], 14)
};
write_row(
&mut w,
&iid,
prev.map(|q| q.iid.map(i32::from)),
df,
dt,
lav,
)?;
write_row(
&mut w,
&icc,
prev.map(|q| q.icc.map(i32::from)),
&HUFF_ICC_DF,
&HUFF_ICC_DT,
7,
)?;
self.prev = Some(*p);
Ok(self.account(w))
}
fn account(&mut self, w: BitWriter) -> PsBits {
let bits = done(w);
self.ps_bits += bits.bits as u64;
let cnt = (2 + bits.bits).div_ceil(8);
let size_field: u64 = if cnt < 15 { 4 } else { 12 };
self.ext_bits += size_field + 8 * cnt as u64;
bits
}
}
fn done(w: BitWriter) -> PsBits {
let bits = w.bit_len() as usize;
PsBits {
bytes: w.finish(),
bits,
}
}
fn write_row(
w: &mut BitWriter,
now: &[i32; PS_BANDS],
prev: Option<[i32; PS_BANDS]>,
df: &[(u8, u32)],
dt: &[(u8, u32)],
lav: i32,
) -> Result<()> {
let mut d_f = [0i32; PS_BANDS];
for b in 0..PS_BANDS {
d_f[b] = now[b] - if b == 0 { 0 } else { now[b - 1] };
}
let cost = |table: &[(u8, u32)], d: &[i32; PS_BANDS]| -> Result<u32> {
d.iter().try_fold(0u32, |acc, &v| {
let (len, _) = table.get((v + lav) as usize).ok_or(Error::PsDataInvalid)?;
Ok(acc + u32::from(*len))
})
};
let mut pick = (false, d_f, df);
if let Some(prev) = prev {
let mut d_t = [0i32; PS_BANDS];
for b in 0..PS_BANDS {
d_t[b] = now[b] - prev[b];
}
if cost(dt, &d_t)? < cost(df, &d_f)? {
pick = (true, d_t, dt);
}
}
cost(pick.2, &pick.1)?; w.write_bit(pick.0);
for &v in &pick.1 {
let (len, code) = pick.2[(v + lav) as usize];
w.write(code, u32::from(len));
}
Ok(())
}
pub(crate) fn write_extended_data(w: &mut BitWriter, ps: &PsBits) -> Result<()> {
let cnt = (2 + ps.bits).div_ceil(8);
if ps.bits == 0 || cnt > PS_EXT_MAX_BYTES || ps.bytes.len() * 8 < ps.bits {
return Err(Error::PsDataInvalid);
}
w.write_bit(true); if cnt < 15 {
w.write(cnt as u32, 4);
} else {
w.write(15, 4);
w.write((cnt - 15) as u32, 8); }
w.write(u32::from(EXTENSION_ID_PS), 2);
let mut left = ps.bits;
for &byte in &ps.bytes {
let n = left.min(8);
if n == 0 {
break;
}
w.write(u32::from(byte) >> (8 - n), n as u32);
left -= n;
}
let fill = cnt * 8 - 2 - ps.bits;
if fill > 0 {
w.write(0, fill as u32);
}
Ok(())
}
#[cfg(test)]
#[path = "enc_ps_bits_tests.rs"]
mod enc_ps_bits_tests;