use crate::lossy::bool_enc::BoolEncoder;
use crate::lossy::constants::{
COEFFS_UPDATE_PROBA, CoeffProbas, CoeffUpdateFlags, NUM_BANDS, NUM_CTX, NUM_PROBAS, NUM_TYPES,
};
use crate::lossy::decode::FilterHeader;
const START_CODE: [u8; 3] = [0x9d, 0x01, 0x2a];
#[must_use]
pub(crate) const fn frame_header_bytes(
first_partition_size: u32,
width: u16,
height: u16,
) -> [u8; 10] {
let tag = (1u32 << 4) | (first_partition_size << 5);
let t = tag.to_le_bytes();
let w = (width & 0x3fff).to_le_bytes();
let h = (height & 0x3fff).to_le_bytes();
[
t[0],
t[1],
t[2],
START_CODE[0],
START_CODE[1],
START_CODE[2],
w[0],
w[1],
h[0],
h[1],
]
}
#[derive(Clone, Copy)]
pub(crate) struct SegmentParams {
pub(crate) quantizer: [i32; 4],
pub(crate) tree_probs: [u8; 3],
}
#[derive(Clone, Copy)]
pub(crate) struct HeaderParams<'a> {
pub(crate) base_q: i32,
pub(crate) filter: &'a FilterHeader,
pub(crate) segments: Option<SegmentParams>,
}
#[expect(
clippy::cast_sign_loss,
reason = "clamp(0, max) guarantees a non-negative value, so `as u32` cannot lose sign"
)]
fn clamped_u32(value: i32, max: i32) -> u32 {
value.clamp(0, max) as u32
}
pub(crate) fn write_control_header(
enc: &mut BoolEncoder,
header: HeaderParams<'_>,
probas: &CoeffProbas,
updated: &CoeffUpdateFlags,
use_skip: bool,
skip_p: u8,
) {
enc.put_flag(false); enc.put_flag(false);
match header.segments {
None => enc.put_flag(false), Some(seg) => {
enc.put_flag(true); enc.put_flag(true); enc.put_flag(true); enc.put_flag(false); for &q in &seg.quantizer {
if q == 0 {
enc.put_flag(false);
} else {
enc.put_flag(true);
enc.put_signed(7, q);
}
}
for _ in 0..4 {
enc.put_flag(false);
}
for &p in &seg.tree_probs {
enc.put_flag(true);
enc.put_literal(8, u32::from(p));
}
},
}
let filter = header.filter;
enc.put_flag(filter.simple); enc.put_literal(6, clamped_u32(filter.level, 63));
enc.put_literal(3, clamped_u32(filter.sharpness, 7));
enc.put_flag(false);
enc.put_literal(2, 0);
enc.put_literal(7, clamped_u32(header.base_q, 127));
for _ in 0..5 {
enc.put_flag(false);
}
enc.put_flag(false);
for t in 0..NUM_TYPES {
for b in 0..NUM_BANDS {
for c in 0..NUM_CTX {
for p in 0..NUM_PROBAS {
if updated[t][b][c][p] {
enc.put_bool(COEFFS_UPDATE_PROBA[t][b][c][p], true);
enc.put_literal(8, u32::from(probas[t][b][c][p]));
} else {
enc.put_bool(COEFFS_UPDATE_PROBA[t][b][c][p], false);
}
}
}
}
}
enc.put_flag(use_skip);
if use_skip {
enc.put_literal(8, u32::from(skip_p));
}
}
#[cfg(test)]
mod tests {
use super::{HeaderParams, SegmentParams, frame_header_bytes, write_control_header};
use crate::lossy::bool_dec::BoolDecoder;
use crate::lossy::bool_enc::BoolEncoder;
use crate::lossy::constants::{COEFFS_PROBA_0, CoeffUpdateFlags};
use crate::lossy::decode::{FilterHeader, Frame};
use crate::lossy::frame_header::FrameHeader;
fn params(base_q: i32, filter: &FilterHeader) -> HeaderParams<'_> {
HeaderParams {
base_q,
filter,
segments: None,
}
}
#[test]
fn frame_header_round_trips_through_parse_key_frame() {
let bytes = frame_header_bytes(1234, 640, 480);
let fh = FrameHeader::parse_key_frame(&bytes).unwrap();
assert!(fh.key_frame);
assert!(fh.show_frame);
assert_eq!(fh.version, 0);
assert_eq!(fh.first_partition_size, 1234);
assert_eq!((fh.width, fh.height), (640, 480));
assert_eq!((fh.x_scale, fh.y_scale), (0, 0));
}
#[test]
fn frame_header_handles_max_dimensions_and_zero_partition() {
let bytes = frame_header_bytes(0, 16383, 16383);
let fh = FrameHeader::parse_key_frame(&bytes).unwrap();
assert_eq!(fh.first_partition_size, 0);
assert_eq!((fh.width, fh.height), (16383, 16383));
}
#[test]
fn control_header_round_trips_through_the_decoder() {
let mut enc = BoolEncoder::new();
let no_updates = CoeffUpdateFlags::default();
let filter = FilterHeader::default();
write_control_header(
&mut enc,
params(32, &filter),
&COEFFS_PROBA_0,
&no_updates,
false,
0,
);
let bytes = enc.finish();
let mut frame = Frame::test_frame(1, 1);
let mut br = BoolDecoder::new(&bytes);
frame.parse_headers(&mut br);
let parts = frame.parse_partitions(&mut br, &[]).unwrap();
frame.parse_quant(&mut br);
let _refresh = br.read_flag();
frame.parse_proba(&mut br);
assert!(!frame.segment.use_segment, "segmentation off");
assert_eq!(frame.filter.level, 0, "filter level 0");
assert_eq!(frame.filter_type, 0, "filter off");
assert_eq!(frame.num_parts, 1, "one token partition");
assert_eq!(parts.len(), 1);
assert_eq!(frame.dqm[0].y1, [29, 36], "base quantizer decoded");
assert_eq!(frame.proba.bands, COEFFS_PROBA_0, "default probabilities");
assert!(!frame.proba.use_skip, "no skip flag");
}
#[test]
fn control_header_transmits_distinct_base_quantizers() {
let no_updates = CoeffUpdateFlags::default();
let filter = FilterHeader::default();
for &(base_q, y1) in &[(0i32, [4, 4]), (64, [59, 78]), (127, [157, 284])] {
let mut enc = BoolEncoder::new();
write_control_header(
&mut enc,
params(base_q, &filter),
&COEFFS_PROBA_0,
&no_updates,
false,
0,
);
let bytes = enc.finish();
let mut frame = Frame::test_frame(1, 1);
let mut br = BoolDecoder::new(&bytes);
frame.parse_headers(&mut br);
frame.parse_partitions(&mut br, &[]).unwrap();
frame.parse_quant(&mut br);
assert_eq!(frame.dqm[0].y1, y1, "base_q {base_q}");
}
}
#[test]
fn control_header_round_trips_the_segment_header() {
let seg = SegmentParams {
quantizer: [8, -12, 0, 20],
tree_probs: [200, 60, 140],
};
let filter = FilterHeader::default();
let base_q = 48;
let mut enc = BoolEncoder::new();
let header = HeaderParams {
base_q,
filter: &filter,
segments: Some(seg),
};
let no_updates = CoeffUpdateFlags::default();
write_control_header(&mut enc, header, &COEFFS_PROBA_0, &no_updates, false, 0);
let bytes = enc.finish();
let mut frame = Frame::test_frame(1, 1);
let mut br = BoolDecoder::new(&bytes);
frame.parse_headers(&mut br);
frame.parse_partitions(&mut br, &[]).unwrap();
frame.parse_quant(&mut br);
assert!(frame.segment.use_segment, "segmentation on");
assert!(frame.segment.update_map, "segment map retransmitted");
assert!(!frame.segment.absolute_delta, "relative deltas");
assert_eq!(
frame.segment.quantizer,
[8, -12, 0, 20],
"per-segment quantizers"
);
assert_eq!(
frame.segment.filter_strength,
[0, 0, 0, 0],
"no filter deltas"
);
assert_eq!(frame.proba.segments, [200, 60, 140], "segment tree probs");
let q0 = crate::lossy::quant::Quantizer::new(base_q + 8);
assert_eq!(
frame.dqm[0].y1,
[q0.y1.dc.q, q0.y1.ac.q],
"segment 0 dequant"
);
let q2 = crate::lossy::quant::Quantizer::new(base_q);
assert_eq!(
frame.dqm[2].y1,
[q2.y1.dc.q, q2.y1.ac.q],
"segment 2 dequant"
);
}
#[test]
fn control_header_round_trips_a_normal_loop_filter() {
let no_updates = CoeffUpdateFlags::default();
let filter = FilterHeader {
simple: false,
level: 27,
sharpness: 3,
..FilterHeader::default()
};
let mut enc = BoolEncoder::new();
write_control_header(
&mut enc,
params(48, &filter),
&COEFFS_PROBA_0,
&no_updates,
false,
0,
);
let bytes = enc.finish();
let mut frame = Frame::test_frame(1, 1);
let mut br = BoolDecoder::new(&bytes);
frame.parse_headers(&mut br);
frame.parse_partitions(&mut br, &[]).unwrap();
frame.parse_quant(&mut br);
assert!(!frame.filter.simple, "normal filter");
assert_eq!(frame.filter.level, 27, "filter level");
assert_eq!(frame.filter.sharpness, 3, "filter sharpness");
assert!(!frame.filter.use_lf_delta, "no loop-filter deltas");
assert_eq!(frame.filter_type, 2, "normal filter type");
}
}