use alloc::vec::Vec;
use super::super::DecodeSettings;
use super::auxiliary::{com_marker, plm_marker, ppm_marker, tlm_marker};
use super::markers;
use super::size::size_marker;
use super::validation::{skipped_levels_to_reach_target, validate};
use super::{
coc_marker, cod_marker, poc_marker, qcc_marker, qcd_marker, rgn_marker, skip_marker_segment,
CodingStyleComponent, ComponentSizeInfo, Header, QuantizationInfo,
};
use crate::error::{bail, DecodingError, MarkerError, Result, ValidationError};
use crate::reader::BitReader;
mod allocation;
mod components;
use allocation::{
try_extend_progression_changes, try_flatten_packet_lengths, try_flatten_ppm_packets,
try_none_vec, HeaderMarkerBudget,
};
use components::build_component_infos;
#[expect(
clippy::similar_names,
reason = "paired axis, subband, and marker names follow JPEG 2000 specification notation"
)]
#[expect(
clippy::too_many_lines,
reason = "the ordered JPEG 2000 state machine stays cohesive to preserve marker, packet, pass, and sample order"
)]
pub(crate) fn read_header<'a>(
reader: &mut BitReader<'a>,
settings: &DecodeSettings,
retained_baseline_bytes: usize,
) -> Result<Header<'a>> {
if reader.read_marker()? != markers::SIZ {
bail!(MarkerError::Expected("SIZ"));
}
let mut marker_budget = HeaderMarkerBudget::with_retained_baseline(retained_baseline_bytes)?;
let mut size_data = size_marker(reader, marker_budget.remaining_bytes())?;
marker_budget.account_capacity::<ComponentSizeInfo>(size_data.component_sizes.capacity())?;
let mut cod = None;
let mut qcd = None;
let num_components = u16::try_from(size_data.component_sizes.len())
.map_err(|_| ValidationError::TooManyChannels)?;
let mut cod_components: Vec<Option<CodingStyleComponent>> =
try_none_vec(usize::from(num_components), &mut marker_budget)?;
let mut qcd_components: Vec<Option<QuantizationInfo>> =
try_none_vec(usize::from(num_components), &mut marker_budget)?;
let mut rgn_components: Vec<Option<u8>> =
try_none_vec(usize::from(num_components), &mut marker_budget)?;
let mut progression_changes = Vec::new();
let mut plm_markers = Vec::new();
let mut ppm_markers = Vec::new();
loop {
match reader.peek_marker().ok_or(MarkerError::Invalid)? {
markers::SOT => break,
markers::CAP | markers::CPF => {
reader.read_marker()?;
skip_marker_segment(reader).ok_or(MarkerError::ParseFailure("CAP/CPF"))?;
}
markers::COD => {
reader.read_marker()?;
let replacement = cod_marker(reader)?;
let old_count = cod
.as_ref()
.map_or(0, |current: &super::CodingStyleDefault| {
current
.component_parameters
.parameters
.precinct_exponents
.capacity()
});
let new_count = replacement
.component_parameters
.parameters
.precinct_exponents
.capacity();
marker_budget.account_capacity::<(u8, u8)>(new_count)?;
cod = Some(replacement);
marker_budget.release_capacity::<(u8, u8)>(old_count)?;
}
markers::COC => {
reader.read_marker()?;
let (component_index, coc) = coc_marker(reader, num_components)?;
let slot = cod_components
.get_mut(component_index as usize)
.ok_or(MarkerError::ParseFailure("COC"))?;
let old_count = slot.as_ref().map_or(0, |current| {
current.parameters.precinct_exponents.capacity()
});
marker_budget
.account_capacity::<(u8, u8)>(coc.parameters.precinct_exponents.capacity())?;
*slot = Some(coc);
marker_budget.release_capacity::<(u8, u8)>(old_count)?;
}
markers::QCD => {
reader.read_marker()?;
let replacement = qcd_marker(reader)?;
let old_count = qcd.as_ref().map_or(0, |current: &super::QuantizationInfo| {
current.step_sizes.capacity()
});
marker_budget
.account_capacity::<super::StepSize>(replacement.step_sizes.capacity())?;
qcd = Some(replacement);
marker_budget.release_capacity::<super::StepSize>(old_count)?;
}
markers::QCC => {
reader.read_marker()?;
let (component_index, qcc) = qcc_marker(reader, num_components)?;
let slot = qcd_components
.get_mut(component_index as usize)
.ok_or(MarkerError::ParseFailure("QCC"))?;
let old_count = slot
.as_ref()
.map_or(0, |current| current.step_sizes.capacity());
marker_budget.account_capacity::<super::StepSize>(qcc.step_sizes.capacity())?;
*slot = Some(qcc);
marker_budget.release_capacity::<super::StepSize>(old_count)?;
}
markers::POC => {
reader.read_marker()?;
let num_layers = cod
.as_ref()
.ok_or(MarkerError::ParseFailure("POC"))?
.num_layers;
let changes = poc_marker(
reader,
num_components,
num_layers,
marker_budget.remaining_bytes() / 2,
)?;
try_extend_progression_changes(
&mut progression_changes,
changes,
&mut marker_budget,
)?;
}
markers::RGN => {
reader.read_marker()?;
let rgn =
rgn_marker(reader, num_components).ok_or(MarkerError::ParseFailure("RGN"))?;
if rgn.style != 0 {
bail!(DecodingError::UnsupportedFeature("explicit ROI coding"));
}
*rgn_components
.get_mut(rgn.component_index as usize)
.ok_or(MarkerError::ParseFailure("RGN"))? = Some(rgn.shift);
}
markers::TLM => {
reader.read_marker()?;
tlm_marker(reader).ok_or(MarkerError::ParseFailure("TLM"))?;
}
markers::PLM => {
reader.read_marker()?;
marker_budget.try_reserve_next(&mut plm_markers)?;
let marker = plm_marker(reader, marker_budget.remaining_bytes())?;
marker_budget.account_capacity::<u32>(marker.packet_lengths.capacity())?;
plm_markers.push(marker);
}
markers::COM => {
reader.read_marker()?;
com_marker(reader).ok_or(MarkerError::ParseFailure("COM"))?;
}
markers::PPM => {
reader.read_marker()?;
marker_budget.try_reserve_next(&mut ppm_markers)?;
let marker = ppm_marker(reader, marker_budget.remaining_bytes())?;
marker_budget
.account_capacity::<super::PpmPacket<'_>>(marker.packets.capacity())?;
ppm_markers.push(marker);
}
markers::CRG => {
reader.read_marker()?;
skip_marker_segment(reader);
}
(0x30..=0x3F) => {
reader.read_marker()?;
}
_ => {
bail!(MarkerError::Unsupported);
}
}
}
let cod = cod.ok_or(MarkerError::Missing("COD"))?;
let qcd = qcd.ok_or(MarkerError::Missing("QCD"))?;
let component_infos = build_component_infos(
&size_data.component_sizes,
&cod_components,
&qcd_components,
&rgn_components,
&cod,
&qcd,
&mut marker_budget,
)?;
let min_num_resolution_levels = component_infos
.iter()
.map(super::model::ComponentInfo::num_resolution_levels)
.min()
.ok_or(ValidationError::InvalidComponentMetadata)?;
let skipped_resolution_levels =
if let Some((target_width, target_height)) = settings.target_resolution {
if target_width == 0 || target_height == 0 {
bail!(ValidationError::InvalidDimensions);
}
let width_log =
skipped_levels_to_reach_target(size_data.checked_image_width()?, target_width);
let height_log =
skipped_levels_to_reach_target(size_data.checked_image_height()?, target_height);
width_log.min(height_log)
} else {
0
}
.min(min_num_resolution_levels - 1);
let resolution_shrink_factor = 1u32
.checked_shl(u32::from(skipped_resolution_levels))
.ok_or(ValidationError::InvalidDimensions)?;
size_data.x_resolution_shrink_factor = size_data
.x_resolution_shrink_factor
.checked_mul(resolution_shrink_factor)
.ok_or(ValidationError::InvalidDimensions)?;
size_data.y_resolution_shrink_factor = size_data
.y_resolution_shrink_factor
.checked_mul(resolution_shrink_factor)
.ok_or(ValidationError::InvalidDimensions)?;
size_data.checked_image_width()?;
size_data.checked_image_height()?;
ppm_markers.sort_by_key(|ppm_marker| ppm_marker.sequence_idx);
plm_markers.sort_by_key(|plm_marker| plm_marker.sequence_idx);
let header = Header {
size_data,
global_coding_style: cod,
component_infos,
progression_changes,
plm_packet_lengths: try_flatten_packet_lengths(plm_markers, &mut marker_budget)?,
ppm_packets: try_flatten_ppm_packets(ppm_markers, &mut marker_budget)?,
skipped_resolution_levels,
strict: settings.strict,
};
validate(&header)?;
Ok(header)
}