use super::bits::BitWriter;
use super::enc_huff::{sf_delta_bits, sf_emit_delta, spectral_emit};
use super::enc_quant::{BOOKS, MAX_BANDS, QuantChannel, UNREPRESENTABLE};
use super::section::has_spectral;
fn section_header_bits(len: usize) -> usize {
4 + 5 * (len / 31 + 1)
}
fn range_book_bits(q: &QuantChannel, lo: usize, hi: usize, cb: usize) -> u32 {
let mut total = 0u32;
for b in lo..hi {
if !q.coded[b] {
continue;
}
let bits = q.bits[b][cb];
if bits == UNREPRESENTABLE {
return UNREPRESENTABLE;
}
total = total.saturating_add(bits);
}
total
}
fn best_book(q: &QuantChannel, lo: usize, hi: usize) -> Option<(u8, u32)> {
let mut best: Option<(u8, u32)> = None;
for cb in 1..BOOKS {
let bits = range_book_bits(q, lo, hi, cb);
if bits == UNREPRESENTABLE {
continue;
}
if best.is_none_or(|(_, b)| bits < b) {
best = Some((cb as u8, bits));
}
}
best
}
pub fn plan_books(q: &QuantChannel) -> [u8; MAX_BANDS] {
let n = q.n_bands;
let mut sfb_cb = [0u8; MAX_BANDS];
for (b, slot) in sfb_cb.iter_mut().enumerate().take(n) {
if q.coded[b] {
*slot = best_book(q, b, b + 1).map(|(cb, _)| cb).unwrap_or(11);
}
}
let mut improved = true;
while improved {
improved = false;
let mut lo = 0usize;
while lo < n {
if !q.coded[lo] {
lo += 1;
continue;
}
let mut hi = lo + 1;
while hi < n && q.coded[hi] {
hi += 1;
}
if merge_run(q, &mut sfb_cb, lo, hi) {
improved = true;
}
lo = hi;
}
}
sfb_cb
}
fn merge_run(q: &QuantChannel, sfb_cb: &mut [u8; MAX_BANDS], lo: usize, hi: usize) -> bool {
let mut merged = false;
let mut i = lo;
while i < hi {
let mut j = i + 1;
while j < hi && sfb_cb[j] == sfb_cb[i] {
j += 1;
}
if j < hi {
let mut k = j + 1;
while k < hi && sfb_cb[k] == sfb_cb[j] {
k += 1;
}
let now = section_header_bits(j - i) + section_header_bits(k - j);
let one = section_header_bits(k - i);
let saved_headers = now as i64 - one as i64;
if let Some((cb, bits_merged)) = best_book(q, i, k) {
let cur = range_book_bits(q, i, j, usize::from(sfb_cb[i]))
.saturating_add(range_book_bits(q, j, k, usize::from(sfb_cb[j])));
if i64::from(bits_merged) - i64::from(cur) < saved_headers {
sfb_cb[i..k].fill(cb);
merged = true;
}
}
}
i = j;
}
merged
}
pub fn emit_ics_info(w: &mut BitWriter, max_sfb: u8) {
w.write_bit(false); w.write(0, 2); w.write_bit(true); w.write(u32::from(max_sfb), 6);
w.write_bit(false); }
pub fn emit_section_data(w: &mut BitWriter, sfb_cb: &[u8; MAX_BANDS], max_sfb: usize) {
let mut k = 0usize;
while k < max_sfb {
let cb = sfb_cb[k];
let mut len = 1usize;
while k + len < max_sfb && sfb_cb[k + len] == cb {
len += 1;
}
w.write(u32::from(cb), 4);
let mut rem = len;
while rem >= 31 {
w.write(31, 5);
rem -= 31;
}
w.write(rem as u32, 5);
k += len;
}
}
pub fn emit_scale_factors(
w: &mut BitWriter,
sfb_cb: &[u8; MAX_BANDS],
q: &QuantChannel,
global_gain: u8,
) {
let mut prev = i32::from(global_gain);
let bands = sfb_cb.iter().zip(q.sf.iter()).take(q.n_bands);
for (&cb, &sf) in bands {
if !has_spectral(cb) {
continue;
}
sf_emit_delta(w, sf - prev);
prev = sf;
}
}
pub fn emit_spectral(
w: &mut BitWriter,
offsets: &[u16],
sfb_cb: &[u8; MAX_BANDS],
q: &QuantChannel,
) {
let bands = sfb_cb.iter().zip(offsets.windows(2)).take(q.n_bands);
for (&cb, win) in bands {
if !has_spectral(cb) {
continue;
}
let lo = usize::from(win[0]);
let hi = usize::from(win[1]);
let step = if cb <= 4 { 4 } else { 2 };
let mut i = lo;
while i < hi {
spectral_emit(cb, &q.quant[i..i + step], w);
i += step;
}
}
}
pub fn channel_body_bits(
sfb_cb: &[u8; MAX_BANDS],
q: &QuantChannel,
global_gain: u8,
standalone: bool,
) -> usize {
let mut bits = 8; if standalone {
bits += 11; }
let mut k = 0usize;
while k < q.n_bands {
let mut len = 1usize;
while k + len < q.n_bands && sfb_cb[k + len] == sfb_cb[k] {
len += 1;
}
bits += section_header_bits(len);
k += len;
}
let mut prev = i32::from(global_gain);
let bands = sfb_cb
.iter()
.zip(q.sf.iter())
.zip(q.bits.iter())
.take(q.n_bands);
for ((&cb, &sf), row) in bands {
if !has_spectral(cb) {
continue;
}
bits += sf_delta_bits(sf - prev);
prev = sf;
bits += row[usize::from(cb)] as usize;
}
bits += 3; bits
}
pub fn emit_channel_body(
w: &mut BitWriter,
offsets: &[u16],
sfb_cb: &[u8; MAX_BANDS],
q: &QuantChannel,
global_gain: u8,
standalone: bool,
) {
w.write(u32::from(global_gain), 8);
if standalone {
emit_ics_info(w, q.n_bands as u8);
}
emit_section_data(w, sfb_cb, q.n_bands);
emit_scale_factors(w, sfb_cb, q, global_gain);
w.write_bit(false); w.write_bit(false); w.write_bit(false); emit_spectral(w, offsets, sfb_cb, q);
}
#[cfg(test)]
#[path = "enc_section_tests.rs"]
mod enc_section_tests;