use super::decode::{DecompositionStorage, TileDecompositions};
use super::rect::IntRect;
use super::tag_tree::TagTree;
use super::tile::{ResolutionTile, Tile};
use crate::error::{DecodingError, Result, ValidationError};
use crate::try_resize_decode_elements;
use core::{iter, ops::Range};
mod allocation;
use allocation::{
code_block_grid, prepare_decomposition_storage, push_preallocated, tag_tree_node_count,
DecompositionAllocationPlan,
};
pub(crate) use allocation::{release_unused_roi_workspace, BuildWorkspace};
pub(crate) fn build(
tile: &Tile<'_>,
storage: &mut DecompositionStorage<'_>,
retained_baseline_bytes: usize,
include_roi_workspace: bool,
workspace: BuildWorkspace,
) -> Result<()> {
build_decompositions(
tile,
storage,
retained_baseline_bytes,
include_roi_workspace,
workspace,
)
}
fn build_decompositions(
tile: &Tile<'_>,
storage: &mut DecompositionStorage<'_>,
retained_baseline_bytes: usize,
include_roi_workspace: bool,
workspace: BuildWorkspace,
) -> Result<()> {
if !build_storage_is_empty(storage) {
return Err(DecodingError::InvalidPrecinct.into());
}
let plan = prepare_decomposition_storage(
tile,
storage,
retained_baseline_bytes,
include_roi_workspace,
workspace,
)?;
try_resize_decode_elements(&mut storage.coefficients, plan.coefficients, 0.0)?;
if storage.exact_integer_decode {
try_resize_decode_elements(&mut storage.coefficients_i64, plan.coefficients, 0)?;
}
let mut coefficient_counter = 0usize;
for (component_idx, component_tile) in tile.component_tiles().enumerate() {
let d_start = storage.decompositions.len();
let mut resolution_tiles = component_tile.resolution_tiles();
let mut build_sub_band = |sub_band_type: SubBandType,
resolution_tile: &ResolutionTile<'_>,
storage: &mut DecompositionStorage<'_>|
-> Result<usize> {
let sub_band_rect = resolution_tile.sub_band_rect(sub_band_type);
ltrace!(
"r {} making sub-band {} for component {}",
resolution_tile.resolution,
sub_band_type as u8,
component_idx
);
ltrace!(
"Sub-band rect: [{},{} {}x{}], ll rect [{},{} {}x{}]",
sub_band_rect.x0,
sub_band_rect.y0,
sub_band_rect.width(),
sub_band_rect.height(),
resolution_tile.rect.x0,
resolution_tile.rect.y0,
resolution_tile.rect.width(),
resolution_tile.rect.height(),
);
let precincts = build_precincts(resolution_tile, sub_band_rect, tile, storage)?;
let added_coefficients = (sub_band_rect.width() as usize)
.checked_mul(sub_band_rect.height() as usize)
.ok_or(ValidationError::ImageTooLarge)?;
let coefficient_end = coefficient_counter
.checked_add(added_coefficients)
.ok_or(ValidationError::ImageTooLarge)?;
if coefficient_end > storage.coefficients.len() {
return Err(DecodingError::InvalidPrecinct.into());
}
let coefficients = coefficient_counter..coefficient_end;
coefficient_counter = coefficient_end;
let idx = storage.sub_bands.len();
push_preallocated(
&mut storage.sub_bands,
SubBand {
sub_band_type,
rect: sub_band_rect,
precincts: precincts.clone(),
coefficients,
},
)?;
Ok(idx)
};
let ll_resolution_tile = resolution_tiles
.next()
.ok_or(DecodingError::InvalidPrecinct)?;
let first_ll_sub_band = build_sub_band(SubBandType::LowLow, &ll_resolution_tile, storage)?;
for resolution_tile in resolution_tiles {
let decomposition = Decomposition {
sub_bands: [
build_sub_band(SubBandType::HighLow, &resolution_tile, storage)?,
build_sub_band(SubBandType::LowHigh, &resolution_tile, storage)?,
build_sub_band(SubBandType::HighHigh, &resolution_tile, storage)?,
],
rect: resolution_tile.rect,
};
push_preallocated(&mut storage.decompositions, decomposition)?;
}
let d_end = storage.decompositions.len();
push_preallocated(
&mut storage.tile_decompositions,
TileDecompositions {
decompositions: d_start..d_end,
first_ll_sub_band,
},
)?;
}
validate_built_storage(&plan, storage, coefficient_counter)?;
storage.structural_workspace_bytes = plan.total_bytes;
Ok(())
}
fn build_storage_is_empty(storage: &DecompositionStorage<'_>) -> bool {
storage.segments.is_empty()
&& storage.layers.is_empty()
&& storage.code_blocks.is_empty()
&& storage.precincts.is_empty()
&& storage.tag_tree_nodes.is_empty()
&& storage.coefficients.is_empty()
&& storage.coefficients_i64.is_empty()
&& storage.sub_bands.is_empty()
&& storage.decompositions.is_empty()
&& storage.tile_decompositions.is_empty()
}
fn validate_built_storage(
plan: &DecompositionAllocationPlan,
storage: &DecompositionStorage<'_>,
coefficient_count: usize,
) -> Result<()> {
let coefficient_lengths_match = coefficient_count == storage.coefficients.len()
&& (!storage.exact_integer_decode || coefficient_count == storage.coefficients_i64.len());
let structural_lengths_match = storage.tile_decompositions.len() == plan.tile_decompositions
&& storage.decompositions.len() == plan.decompositions
&& storage.sub_bands.len() == plan.sub_bands
&& storage.precincts.len() == plan.precincts
&& storage.code_blocks.len() == plan.code_blocks
&& storage.layers.len() == plan.layers
&& storage.tag_tree_nodes.len() == plan.tag_tree_nodes;
if !coefficient_lengths_match || !structural_lengths_match {
return Err(DecodingError::InvalidPrecinct.into());
}
Ok(())
}
fn build_precincts(
resolution_tile: &ResolutionTile<'_>,
sub_band_rect: IntRect,
tile: &Tile<'_>,
storage: &mut DecompositionStorage<'_>,
) -> Result<Range<usize>> {
let start = storage.precincts.len();
for precinct_data in resolution_tile
.precincts()
.ok_or(DecodingError::InvalidPrecinct)?
{
let grid = code_block_grid(resolution_tile, sub_band_rect, precinct_data.rect)?;
ltrace!(
"Precinct rect: [{},{} {}x{}], num_code_blocks_wide: {}, num_code_blocks_high: {}",
precinct_data.rect.x0,
precinct_data.rect.y0,
precinct_data.rect.width(),
precinct_data.rect.height(),
grid.columns,
grid.rows
);
let blocks = build_code_blocks(
grid.area,
sub_band_rect,
resolution_tile,
grid.columns,
grid.rows,
tile,
storage,
)?;
let tree_node_count = tag_tree_node_count(grid.columns, grid.rows)?;
let tree_nodes_end = tree_node_count
.checked_mul(2)
.and_then(|count| storage.tag_tree_nodes.len().checked_add(count))
.ok_or(ValidationError::ImageTooLarge)?;
if tree_nodes_end > storage.tag_tree_nodes.capacity() {
return Err(DecodingError::HostAllocationFailed.into());
}
let code_inclusion_tree =
TagTree::new(grid.columns, grid.rows, &mut storage.tag_tree_nodes);
let zero_bitplane_tree = TagTree::new(grid.columns, grid.rows, &mut storage.tag_tree_nodes);
if storage.tag_tree_nodes.len() != tree_nodes_end {
return Err(DecodingError::InvalidPrecinct.into());
}
push_preallocated(
&mut storage.precincts,
Precinct {
code_blocks: blocks,
code_inclusion_tree,
zero_bitplane_tree,
},
)?;
}
let end = storage.precincts.len();
Ok(start..end)
}
fn build_code_blocks(
code_block_area: IntRect,
sub_band_rect: IntRect,
tile_instance: &ResolutionTile<'_>,
code_blocks_x: u32,
code_blocks_y: u32,
tile: &Tile<'_>,
storage: &mut DecompositionStorage<'_>,
) -> Result<Range<usize>> {
let mut y = code_block_area.y0;
let code_block_width = tile_instance.code_block_width();
let code_block_height = tile_instance.code_block_height();
let start = storage.code_blocks.len();
for y_idx in 0..code_blocks_y {
let mut x = code_block_area.x0;
for x_idx in 0..code_blocks_x {
let area = IntRect::from_xywh(x, y, code_block_width, code_block_height)
.intersect(sub_band_rect);
ltrace!(
"Codeblock rect: [{},{} {}x{}]",
area.x0,
area.y0,
area.width(),
area.height(),
);
let start = storage.layers.len();
let end = start
.checked_add(usize::from(tile.num_layers))
.ok_or(ValidationError::ImageTooLarge)?;
if end > storage.layers.capacity() {
return Err(DecodingError::HostAllocationFailed.into());
}
storage.layers.extend(iter::repeat_n(
Layer {
segments: None,
},
tile.num_layers as usize,
));
if storage.layers.len() != end {
return Err(DecodingError::InvalidPrecinct.into());
}
push_preallocated(
&mut storage.code_blocks,
CodeBlock {
x_idx,
y_idx,
rect: area,
has_been_included: false,
missing_bit_planes: 0,
l_block: 3,
number_of_coding_passes: 0,
layers: start..end,
non_empty_layer_count: 0,
},
)?;
x = x
.checked_add(code_block_width)
.ok_or(ValidationError::ImageTooLarge)?;
}
y = y
.checked_add(code_block_height)
.ok_or(ValidationError::ImageTooLarge)?;
}
let end = storage.code_blocks.len();
Ok(start..end)
}
pub(crate) struct Decomposition {
pub(crate) sub_bands: [usize; 3],
pub(crate) rect: IntRect,
}
#[derive(Clone)]
#[expect(
clippy::struct_field_names,
reason = "sub_band_type matches JPEG 2000 specification terminology throughout the codec"
)]
pub(crate) struct SubBand {
pub(crate) sub_band_type: SubBandType,
pub(crate) rect: IntRect,
pub(crate) precincts: Range<usize>,
pub(crate) coefficients: Range<usize>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum SubBandType {
LowLow = 0,
HighLow = 1,
LowHigh = 2,
HighHigh = 3,
}
#[derive(Clone)]
pub(crate) struct Precinct {
pub(crate) code_blocks: Range<usize>,
pub(crate) code_inclusion_tree: TagTree,
pub(crate) zero_bitplane_tree: TagTree,
}
pub(crate) struct PrecinctData {
pub(crate) r_x: u32,
pub(crate) r_y: u32,
pub(crate) rect: IntRect,
pub(crate) idx: u64,
}
#[derive(Clone)]
pub(crate) struct CodeBlock {
pub(crate) rect: IntRect,
pub(crate) x_idx: u32,
pub(crate) y_idx: u32,
pub(crate) layers: Range<usize>,
pub(crate) has_been_included: bool,
pub(crate) missing_bit_planes: u8,
pub(crate) number_of_coding_passes: u8,
pub(crate) l_block: u32,
pub(crate) non_empty_layer_count: u8,
}
pub(crate) struct Segment<'a> {
pub(crate) idx: u8,
pub(crate) coding_pases: u8,
pub(crate) data_length: u32,
pub(crate) data: &'a [u8],
}
#[derive(Clone)]
pub(crate) struct Layer {
pub(crate) segments: Option<Range<usize>>,
}