diff --git a/jxl/src/headers/frame_header.rs b/jxl/src/headers/frame_header.rs index a43c629..8ee9d48 100644 --- a/jxl/src/headers/frame_header.rs +++ b/jxl/src/headers/frame_header.rs @@ -220,6 +220,30 @@ struct RestorationFilter { extensions: Extensions, } +#[derive(UnconditionalCoder, Debug, PartialEq)] +pub struct Permutation {} + +pub struct TocNonserialized { + pub permuted: bool, + pub num_entries: u32, + pub entries: Vec, +} + +#[derive(UnconditionalCoder, Debug, PartialEq)] +#[nonserialized(TocNonserialized)] +pub struct Toc { + #[default(false)] + permuted: bool, + + #[condition(permuted)] + #[default(Permutation::default())] + permutation: Permutation, + + #[coder(u2S(Bits(10), Bits(14) + 1024, Bits(22) + 17408, Bits(30) + 4211712))] + #[size_coder(explicit(nonserialized.num_entries))] + entries: Vec, +} + pub struct FrameHeaderNonserialized { pub xyb_encoded: bool, pub num_extra_channels: u32, @@ -377,6 +401,36 @@ pub struct FrameHeader { } impl FrameHeader { + fn num_toc_entries(&self) -> u32 { + const GROUP_DIM: u32 = 256; + const BLOCK_DIM: u32 = 8; + const H_SHIFT: [u32; 4] = [0, 1, 1, 0]; + const V_SHIFT: [u32; 4] = [0, 1, 0, 1]; + let mut maxhs = 0; + let mut maxvs = 0; + for ch in self.jpeg_upsampling { + maxhs = maxhs.max(H_SHIFT[ch as usize]); + maxvs = maxvs.max(V_SHIFT[ch as usize]); + } + let xsize_blocks = self.width.div_ceil(BLOCK_DIM << maxhs) << maxhs; + let ysize_blocks = self.height.div_ceil(BLOCK_DIM << maxvs) << maxvs; + + let group_dim = (GROUP_DIM >> 1) << self.group_size_shift; + + let xsize_groups = self.width.div_ceil(group_dim); + let ysize_groups = self.height.div_ceil(group_dim); + let xsize_dc_groups = xsize_blocks.div_ceil(group_dim); + let ysize_dc_groups = ysize_blocks.div_ceil(group_dim); + + let num_groups = xsize_groups * ysize_groups; + let num_dc_groups = xsize_dc_groups * ysize_dc_groups; + + if num_groups == 1 && self.passes.num_passes == 1 { + 1 + } else { + 2 + num_dc_groups + } + } fn check(&self, nonserialized: &FrameHeaderNonserialized) -> Result<(), Error> { if self.upsampling > 1 { if let Some((info, upsampling)) = nonserialized @@ -408,7 +462,7 @@ impl FrameHeader { } #[cfg(test)] -mod test_frame_header { +mod test { use super::*; use crate::{ bit_reader::BitReader, @@ -416,7 +470,7 @@ mod test_frame_header { headers::{FileHeaders, JxlHeader}, }; - fn test_frame_header(image: &[u8], correct_frame_header: FrameHeader) { + pub fn read_headers(image: &[u8]) -> Result<(FrameHeader, Toc), Error> { let codestream = ContainerParser::collect_codestream(image).unwrap(); let mut br = BitReader::new(&codestream); let fh = FileHeaders::read(&mut br).unwrap(); @@ -439,12 +493,49 @@ mod test_frame_header { }, ) .unwrap(); + let toc = Toc::read_unconditional( + &(), + &mut br, + &TocNonserialized { + permuted: false, + num_entries: 0, + entries: [].to_vec(), + }, + ) + .unwrap(); + Ok((frame_header, toc)) + } + + fn test_toc(image: &[u8], correct_toc: Toc) { + let (_frame_header, toc) = read_headers(image).unwrap(); + assert_eq!(correct_toc, toc); + } + #[test] + fn test_basic_toc() { + test_toc( + &[ + 0xFF, 0x0A, 0x00, 0x90, 0x01, 0x00, 0x12, 0x88, 0x02, 0x00, 0xD4, 0x00, 0x55, 0x0F, + 0x00, 0x00, 0xA8, 0x50, 0x19, 0x65, 0xDC, 0xE0, 0xE5, 0x5C, 0xCF, 0x97, 0x1F, 0x3A, + 0x2C, 0xA6, 0x6D, 0x5C, 0x67, 0x68, 0xAB, 0x6D, 0x0B, 0x4B, 0x12, 0x45, 0xC6, 0xB1, + 0x49, 0x3A, 0x81, 0x43, 0x92, 0x58, 0x04, 0x36, 0x2E, 0x98, 0x07, 0x18, 0x00, 0x86, + 0x99, 0x03, 0x27, 0x33, 0x50, 0xE4, 0x4A, 0x12, 0x00, + ], + Toc { + permuted: false, + permutation: super::Permutation::default(), + entries: [].to_vec(), + }, + ); + } + + fn test_frame_header(image: &[u8], correct_frame_header: FrameHeader) { + let (frame_header, _toc) = read_headers(image).unwrap(); assert_eq!(correct_frame_header, frame_header); } #[test] - fn test_basic() { + fn test_basic_frame_header() { test_frame_header( &[ 0xFF, 0x0A, 0x00, 0x90, 0x01, 0x00, 0x12, 0x88, 0x02, 0x00, 0xD4, 0x00, 0x55, 0x0F,