use alloc::vec::Vec;
use crate::error::{err, ValidationError};
use crate::j2c::mct::DeferredMct;
use crate::j2c::{self, ComponentData, Header};
use crate::jp2::colr::EnumeratedColorspace;
use crate::jp2::{self, DecodedImage, ImageBoxes};
use crate::CpuDecodeParallelism;
use crate::{
checked_decode_byte_len3, convert_color_space, interleave_and_convert_after_mct,
interleave_and_convert_region, resolve_palette_indices, try_resize_decode_elements,
validate_and_reorder_channels, validate_interleaved_output_buffer, validate_roi, Bitmap,
ColorSpace, DecodedComponents, DecodedNativeComponents, DecoderContext, DecodingError,
FormatError, HtCodeBlockDecoder, Result, CODESTREAM_MAGIC, JP2_MAGIC,
};
mod allocation;
mod compare;
#[cfg(test)]
mod contract_tests;
mod direct_api;
mod native;
mod output_api;
#[cfg(feature = "std")]
pub(crate) mod packet_directory;
use self::allocation::retained_metadata_bytes;
pub(crate) use self::allocation::{retained_container_metadata_bytes, DecodeOwnerBudget};
use self::native::{try_clone_color_space, NativeOutputBudget};
type Rgb8DecodedImage<'ctx, 'boxes> = (
Option<DeferredMct>,
&'ctx [f32],
[&'ctx mut [f32]; 2],
DecodedImage<'ctx, 'boxes>,
usize,
);
#[derive(Debug, Copy, Clone)]
pub struct DecodeSettings {
pub resolve_palette_indices: bool,
pub strict: bool,
pub target_resolution: Option<(u32, u32)>,
}
impl DecodeSettings {
#[must_use]
pub const fn lenient() -> Self {
Self {
resolve_palette_indices: true,
strict: false,
target_resolution: None,
}
}
#[must_use]
pub const fn strict() -> Self {
Self {
resolve_palette_indices: true,
strict: true,
target_resolution: None,
}
}
#[must_use]
pub const fn lenient_tolerance_enabled(&self) -> bool {
!self.strict
}
}
impl Default for DecodeSettings {
fn default() -> Self {
Self::strict()
}
}
pub struct Image<'a> {
pub(crate) encoded_input: &'a [u8],
pub(crate) codestream: &'a [u8],
pub(crate) header: Header<'a>,
pub(crate) boxes: ImageBoxes,
pub(crate) settings: DecodeSettings,
pub(crate) used_lenient_metadata_recovery: bool,
pub(crate) has_alpha: bool,
pub(crate) color_space: ColorSpace,
retained_baseline_bytes: usize,
#[cfg(feature = "std")]
pub(crate) packet_cache: packet_directory::PacketDirectoryCache,
}
#[doc(hidden)]
pub struct PreparedRegionDecoder<'image, 'context, 'a> {
image: &'image Image<'a>,
decoder_context: &'context mut DecoderContext<'a>,
tiles: j2c::ParsedTiles<'a>,
retained_image_bytes: usize,
retained_session_bytes: usize,
}
#[derive(Clone, Copy)]
pub(crate) struct ImageSource<'a> {
encoded_input: &'a [u8],
codestream: &'a [u8],
}
impl<'a> ImageSource<'a> {
pub(crate) const fn new(encoded_input: &'a [u8], codestream: &'a [u8]) -> Self {
Self {
encoded_input,
codestream,
}
}
}
pub(crate) struct ImageProperties {
boxes: ImageBoxes,
settings: DecodeSettings,
color_space: ColorSpace,
has_alpha: bool,
used_lenient_metadata_recovery: bool,
}
impl ImageProperties {
pub(crate) const fn new(
boxes: ImageBoxes,
settings: DecodeSettings,
color_space: ColorSpace,
has_alpha: bool,
used_lenient_metadata_recovery: bool,
) -> Self {
Self {
boxes,
settings,
color_space,
has_alpha,
used_lenient_metadata_recovery,
}
}
}
impl<'a> Image<'a> {
#[doc(hidden)]
pub fn prepare_region_decoder_with_context<'image, 'context>(
&'image self,
decoder_context: &'context mut DecoderContext<'a>,
) -> Result<PreparedRegionDecoder<'image, 'context, 'a>> {
let retained_image_bytes = allocation::combine_retained_bytes(
self.retained_baseline_bytes,
self.retained_header_metadata_bytes()?,
)?;
let tiles = self.with_packet_cache_retry(|| {
j2c::prepare_region_tiles(
self.codestream,
&self.header,
self.retained_planning_bytes()?,
decoder_context,
)
})?;
let retained_session_bytes = retained_image_bytes
.checked_add(tiles.metadata_owner_bytes())
.ok_or(ValidationError::ImageTooLarge)?;
Ok(PreparedRegionDecoder {
image: self,
decoder_context,
tiles,
retained_image_bytes,
retained_session_bytes,
})
}
pub(crate) fn from_parsed_parts(
source: ImageSource<'a>,
header: Header<'a>,
properties: ImageProperties,
) -> Result<Self> {
Self::from_parsed_parts_with_retained_baseline(source, header, properties, 0)
}
pub(crate) fn from_parsed_parts_with_retained_baseline(
source: ImageSource<'a>,
header: Header<'a>,
properties: ImageProperties,
retained_baseline_bytes: usize,
) -> Result<Self> {
let ImageProperties {
boxes,
settings,
color_space,
has_alpha,
used_lenient_metadata_recovery,
} = properties;
let metadata_bytes = retained_metadata_bytes(&header, &boxes, &color_space)?;
allocation::combine_retained_bytes(retained_baseline_bytes, metadata_bytes)?;
Ok(Self {
encoded_input: source.encoded_input,
codestream: source.codestream,
header,
boxes,
settings,
used_lenient_metadata_recovery,
has_alpha,
color_space,
retained_baseline_bytes,
#[cfg(feature = "std")]
packet_cache: packet_directory::PacketDirectoryCache::default(),
})
}
fn retained_header_metadata_bytes(&self) -> Result<usize> {
retained_metadata_bytes(&self.header, &self.boxes, &self.color_space)
}
pub(crate) fn retained_metadata_bytes(&self) -> Result<usize> {
let bytes = self.retained_header_metadata_bytes()?;
#[cfg(feature = "std")]
let bytes = allocation::combine_retained_bytes(bytes, self.packet_cache.retained_bytes())?;
Ok(bytes)
}
pub(crate) fn retained_planning_bytes(&self) -> Result<usize> {
allocation::combine_retained_bytes(
self.retained_baseline_bytes,
self.retained_metadata_bytes()?,
)
}
#[doc(hidden)]
pub fn retained_allocation_bytes(&self) -> Result<usize> {
self.retained_metadata_bytes()
}
#[doc(hidden)]
#[must_use]
pub const fn used_lenient_metadata_recovery(&self) -> bool {
self.used_lenient_metadata_recovery
}
pub fn new(data: &'a [u8], settings: &DecodeSettings) -> Result<Self> {
if data.starts_with(JP2_MAGIC) {
jp2::parse(data, *settings, None)
} else if data.starts_with(CODESTREAM_MAGIC) {
j2c::parse(data, settings, None)
} else {
err!(FormatError::InvalidSignature)
}
}
#[doc(hidden)]
pub fn new_with_reduction(
data: &'a [u8],
settings: &DecodeSettings,
reduction_levels: u8,
) -> Result<Self> {
if settings.target_resolution.is_some() {
return err!(DecodingError::UnsupportedFeature(
"exact reduction cannot be combined with a target-resolution hint",
));
}
if data.starts_with(JP2_MAGIC) {
jp2::parse(data, *settings, Some(reduction_levels))
} else if data.starts_with(CODESTREAM_MAGIC) {
j2c::parse(data, settings, Some(reduction_levels))
} else {
err!(FormatError::InvalidSignature)
}
}
#[doc(hidden)]
pub fn new_with_retained_baseline(
data: &'a [u8],
settings: &DecodeSettings,
retained_baseline_bytes: usize,
) -> Result<Self> {
if retained_baseline_bytes == 0 {
return Self::new(data, settings);
}
if data.starts_with(JP2_MAGIC) {
jp2::parse_with_retained_baseline(data, *settings, retained_baseline_bytes, None)
} else if data.starts_with(CODESTREAM_MAGIC) {
j2c::parse_with_retained_baseline(data, settings, retained_baseline_bytes, None)
} else {
err!(FormatError::InvalidSignature)
}
}
#[must_use]
pub fn has_alpha(&self) -> bool {
self.has_alpha
}
#[must_use]
pub fn color_space(&self) -> &ColorSpace {
&self.color_space
}
#[doc(hidden)]
#[must_use]
pub fn primary_icc_profile(&self) -> Option<&[u8]> {
match self
.boxes
.primary_color_specification()
.map(|specification| &specification.color_space)
{
Some(jp2::colr::ColorSpace::Icc(profile)) => Some(profile),
_ => None,
}
}
#[must_use]
pub fn width(&self) -> u32 {
self.header.size_data.image_width()
}
#[must_use]
pub fn height(&self) -> u32 {
self.header.size_data.image_height()
}
#[doc(hidden)]
#[must_use]
pub fn uses_high_throughput_block_coding(&self) -> bool {
self.header
.global_coding_style
.component_parameters
.parameters
.code_block_style
.uses_high_throughput_block_coding()
}
#[must_use]
pub fn original_bit_depth(&self) -> u8 {
self.header.component_infos[0].size_info.precision
}
#[doc(hidden)]
#[must_use]
pub fn supports_direct_device_plane_reuse(&self) -> bool {
if self.settings.resolve_palette_indices && self.boxes.palette.is_some() {
return false;
}
if self.boxes.channel_definition.is_some() {
return false;
}
!matches!(
self.boxes
.primary_color_specification()
.map(|spec| &spec.color_space),
Some(jp2::colr::ColorSpace::Enumerated(
EnumeratedColorspace::Sycc | EnumeratedColorspace::CieLab(_)
))
)
}
#[doc(hidden)]
#[must_use]
pub fn tile_count(&self) -> u32 {
self.header.size_data.num_tiles()
}
pub fn decode(&self) -> Result<Vec<u8>> {
let bitmap = self.decode_with_context(&mut DecoderContext::default())?;
Ok(bitmap.data)
}
pub fn decode_with_context(&self, decoder_context: &mut DecoderContext<'a>) -> Result<Bitmap> {
self.with_packet_cache_retry(|| {
let retained_image_bytes = self.retained_header_metadata_bytes()?;
let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
let (mct, first_ll, scratch, mut decoded_image, retained_image_bytes) =
self.decode_image_for_rgb8(decoder_context, None, retained_image_bytes)?;
let component_owner_capacity = decoded_image.decoded_components.capacity();
let buffer_size = checked_decode_byte_len3(
self.width() as usize,
self.height() as usize,
decoded_image.decoded_components.len(),
)?;
let mut budget = NativeOutputBudget::for_decoded_channels(
retained_image_bytes,
decoded_image.decoded_components,
component_owner_capacity,
)?;
budget.include_elements::<u8>(buffer_size)?;
budget.include_color_space_clone(&self.color_space)?;
let color_space = try_clone_color_space(&self.color_space)?;
budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
let mut data = Vec::new();
try_resize_decode_elements(&mut data, buffer_size, 0_u8)?;
budget.include_capacity_overage::<u8>(buffer_size, data.capacity())?;
validate_interleaved_output_buffer(&decoded_image, &data)?;
interleave_and_convert_after_mct(
&mut decoded_image,
&mut data,
mct,
first_ll,
scratch,
parallel,
)?;
let bitmap = Bitmap {
color_space,
data,
has_alpha: self.has_alpha,
width: self.width(),
height: self.height(),
original_bit_depth: self.original_bit_depth(),
};
NativeOutputBudget::validate_bitmap_pack(
retained_image_bytes,
decoded_image.decoded_components,
component_owner_capacity,
&bitmap,
)?;
Ok(bitmap)
})
}
pub fn decode_components_with_context<'ctx>(
&self,
decoder_context: &'ctx mut DecoderContext<'a>,
) -> Result<DecodedComponents<'ctx>> {
self.decode_borrowed_component_planes(decoder_context, None, None, false)
}
pub fn decode_native_components(&self) -> Result<DecodedNativeComponents> {
let mut decoder_context = DecoderContext::default();
self.decode_native_components_with_context(&mut decoder_context)
}
#[doc(hidden)]
pub fn decode_native_components_with_retained_capacity(
&self,
retained_capacity: usize,
) -> Result<DecodedNativeComponents> {
let retained_baseline_bytes = allocation::combine_retained_bytes(
retained_capacity,
self.retained_header_metadata_bytes()?,
)?;
let mut decoder_context = DecoderContext::default();
self.decode_native_components_with_context_and_retained_baseline(
&mut decoder_context,
retained_baseline_bytes,
)
}
pub fn decode_native_components_with_context(
&self,
decoder_context: &mut DecoderContext<'a>,
) -> Result<DecodedNativeComponents> {
let retained_baseline_bytes = self.retained_header_metadata_bytes()?;
self.decode_native_components_with_context_and_retained_baseline(
decoder_context,
retained_baseline_bytes,
)
}
fn decode_native_components_with_context_and_retained_baseline(
&self,
decoder_context: &mut DecoderContext<'a>,
retained_baseline_bytes: usize,
) -> Result<DecodedNativeComponents> {
self.with_packet_cache_retry(|| {
let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
let (decoded_image, retained_baseline_bytes) =
self.decode_image(decoder_context, None, None, true, retained_baseline_bytes)?;
let DecodedImage {
decoded_components,
boxes: _,
} = decoded_image;
let component_owner_capacity = decoded_components.capacity();
self.pack_native_component_planes(
decoded_components,
component_owner_capacity,
(self.width(), self.height()),
retained_baseline_bytes,
parallel,
)
})
}
#[doc(hidden)]
pub fn decode_components_with_ht_decoder<'ctx>(
&self,
decoder_context: &'ctx mut DecoderContext<'a>,
ht_decoder: &mut dyn HtCodeBlockDecoder,
) -> Result<DecodedComponents<'ctx>> {
self.decode_borrowed_component_planes(decoder_context, None, Some(ht_decoder), false)
}
pub fn decode_region_components_with_context<'ctx>(
&self,
roi: (u32, u32, u32, u32),
decoder_context: &'ctx mut DecoderContext<'a>,
) -> Result<DecodedComponents<'ctx>> {
self.decode_borrowed_component_planes(decoder_context, Some(roi), None, false)
}
pub fn decode_native_region_components_with_context(
&self,
roi: (u32, u32, u32, u32),
decoder_context: &mut DecoderContext<'a>,
) -> Result<DecodedNativeComponents> {
self.with_packet_cache_retry(|| {
validate_roi((self.width(), self.height()), roi)?;
if self.requires_exact_integer_decode() {
return self.decode_native_region_components_via_full_decode(roi, decoder_context);
}
let (_x, _y, width, height) = roi;
let retained_image_bytes = self.retained_header_metadata_bytes()?;
let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
let (decoded_image, retained_image_bytes) =
self.decode_image(decoder_context, Some(roi), None, true, retained_image_bytes)?;
let DecodedImage {
decoded_components,
boxes: _,
} = decoded_image;
let component_owner_capacity = decoded_components.capacity();
self.pack_native_component_planes(
decoded_components,
component_owner_capacity,
(width, height),
retained_image_bytes,
parallel,
)
})
}
#[doc(hidden)]
pub fn decode_region_components_with_ht_decoder<'ctx>(
&self,
decoder_context: &'ctx mut DecoderContext<'a>,
roi: (u32, u32, u32, u32),
ht_decoder: &mut dyn HtCodeBlockDecoder,
) -> Result<DecodedComponents<'ctx>> {
self.decode_borrowed_component_planes(decoder_context, Some(roi), Some(ht_decoder), false)
}
pub fn decode_region(&self, roi: (u32, u32, u32, u32)) -> Result<Bitmap> {
self.decode_region_with_context(roi, &mut DecoderContext::default())
}
pub fn decode_region_with_context(
&self,
roi: (u32, u32, u32, u32),
decoder_context: &mut DecoderContext<'a>,
) -> Result<Bitmap> {
validate_roi((self.width(), self.height()), roi)?;
self.with_packet_cache_retry(|| {
let retained_image_bytes = self.retained_header_metadata_bytes()?;
let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
let (mct, first_ll, scratch, mut decoded_image, retained_image_bytes) =
self.decode_image_for_rgb8(decoder_context, Some(roi), retained_image_bytes)?;
let component_owner_capacity = decoded_image.decoded_components.capacity();
let (_x, _y, width, height) = roi;
let data_len = checked_decode_byte_len3(
width as usize,
height as usize,
decoded_image.decoded_components.len(),
)?;
let mut budget = NativeOutputBudget::for_decoded_channels(
retained_image_bytes,
decoded_image.decoded_components,
component_owner_capacity,
)?;
budget.include_elements::<u8>(data_len)?;
budget.include_color_space_clone(&self.color_space)?;
let color_space = try_clone_color_space(&self.color_space)?;
budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
let mut data = Vec::new();
try_resize_decode_elements(&mut data, data_len, 0_u8)?;
budget.include_capacity_overage::<u8>(data_len, data.capacity())?;
if mct.is_some() {
interleave_and_convert_after_mct(
&mut decoded_image,
&mut data,
mct,
first_ll,
scratch,
parallel,
)?;
} else {
interleave_and_convert_region(
&mut decoded_image,
width as usize,
(0, 0, width, height),
&mut data,
)?;
}
let bitmap = Bitmap {
color_space,
data,
has_alpha: self.has_alpha,
width,
height,
original_bit_depth: self.original_bit_depth(),
};
NativeOutputBudget::validate_bitmap_pack(
retained_image_bytes,
decoded_image.decoded_components,
component_owner_capacity,
&bitmap,
)?;
Ok(bitmap)
})
}
pub fn decode_into(
&self,
buf: &mut [u8],
decoder_context: &mut DecoderContext<'a>,
) -> Result<()> {
self.with_packet_cache_retry(|| {
let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
let retained = self.retained_header_metadata_bytes()?;
let (mct, first_ll, scratch, mut decoded_image, _retained_image_bytes) =
self.decode_image_for_rgb8(decoder_context, None, retained)?;
validate_interleaved_output_buffer(&decoded_image, buf)?;
interleave_and_convert_after_mct(
&mut decoded_image,
buf,
mct,
first_ll,
scratch,
parallel,
)?;
Ok(())
})
}
fn decode_image<'ctx>(
&self,
decoder_context: &'ctx mut DecoderContext<'a>,
output_region: Option<(u32, u32, u32, u32)>,
ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
round_irreversible_output: bool,
retained_baseline_bytes: usize,
) -> Result<(DecodedImage<'ctx, '_>, usize)> {
let retained_baseline_bytes = self.run_decode(
decoder_context,
output_region,
ht_decoder,
round_irreversible_output,
retained_baseline_bytes,
)?;
let decoded = self.finish_decoded_image(
&mut decoder_context.tile_decode_context.channel_data,
retained_baseline_bytes,
)?;
Ok((decoded, retained_baseline_bytes))
}
fn decode_image_for_rgb8<'ctx>(
&self,
decoder_context: &'ctx mut DecoderContext<'a>,
output_region: Option<(u32, u32, u32, u32)>,
retained_baseline_bytes: usize,
) -> Result<Rgb8DecodedImage<'ctx, '_>> {
let defer_mct = self.permits_line_region_idwt();
#[cfg(test)]
let defer_mct = defer_mct && !decoder_context.tile_decode_context.staged_rgb8_pack;
decoder_context.set_defer_mct(defer_mct);
let decoded = self.run_decode(
decoder_context,
output_region,
None,
true,
retained_baseline_bytes,
);
decoder_context.set_defer_mct(false);
let retained_baseline_bytes = decoded?;
let tile_ctx = &mut decoder_context.tile_decode_context;
let mct = tile_ctx.deferred_mct.take();
let image =
self.finish_decoded_image(&mut tile_ctx.channel_data, retained_baseline_bytes)?;
Ok((
mct,
&tile_ctx.idwt_output.coefficients,
[
&mut tile_ctx.idwt_scratch_buffer,
&mut tile_ctx.idwt_band_scratch,
],
image,
retained_baseline_bytes,
))
}
#[cfg_attr(
not(feature = "std"),
expect(
clippy::unused_self,
reason = "without std there is no packet cache, so only the std retry reads self"
)
)]
fn with_packet_cache_retry<T>(&self, mut operation: impl FnMut() -> Result<T>) -> Result<T> {
let result = operation();
#[cfg(feature = "std")]
if result.as_ref().is_err_and(j2c::is_capacity_error) {
if let Some(directory) = self.packet_cache.snapshot() {
if self.packet_cache.evict_if_unshared(&directory) {
drop(directory);
return operation();
}
}
}
result
}
fn run_decode(
&self,
decoder_context: &mut DecoderContext<'a>,
output_region: Option<(u32, u32, u32, u32)>,
ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
round_irreversible_output: bool,
retained_baseline_bytes: usize,
) -> Result<usize> {
self.run_decode_rows(
decoder_context,
output_region,
ht_decoder,
round_irreversible_output,
retained_baseline_bytes,
None,
)
}
pub(super) fn run_decode_rows(
&self,
decoder_context: &mut DecoderContext<'a>,
output_region: Option<(u32, u32, u32, u32)>,
ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
round_irreversible_output: bool,
retained_baseline_bytes: usize,
row_sink: Option<&mut dyn crate::ComponentRowSink>,
) -> Result<usize> {
let mut ht_decoder = ht_decoder;
let mut row_sink = row_sink;
decoder_context.storage.release_all_allocations();
#[cfg(feature = "std")]
let directory = self.region_packet_directory(
retained_baseline_bytes,
decoder_context,
output_region.is_some() && ht_decoder.is_none() && self.permits_line_region_idwt(),
None,
);
decoder_context.set_output_region(output_region);
decoder_context.set_round_irreversible_output(round_irreversible_output);
decoder_context.permit_line_region_idwt(self.permits_line_region_idwt());
#[cfg(feature = "std")]
let decode_result = allocation::combine_retained_bytes(
retained_baseline_bytes,
directory
.as_deref()
.map_or(0, packet_directory::PacketDirectoryCache::directory_bytes),
)
.and_then(|source_bytes| {
let index_directory = directory
.as_ref()
.filter(|_| output_region.is_some() && ht_decoder.is_none());
j2c::decode_rows(
self.codestream,
&self.header,
source_bytes,
decoder_context,
&mut ht_decoder,
&mut row_sink,
index_directory,
)
.map(|()| source_bytes)
});
#[cfg(not(feature = "std"))]
let decode_result = j2c::decode_rows(
self.codestream,
&self.header,
retained_baseline_bytes,
decoder_context,
&mut ht_decoder,
&mut row_sink,
)
.map(|()| retained_baseline_bytes);
#[cfg(feature = "std")]
let decode_result = if decode_result.as_ref().is_err_and(j2c::is_capacity_error)
&& decoder_context.tile_decode_context.streamed_samples == 0
&& directory
.as_ref()
.is_some_and(|directory| self.packet_cache.evict_if_unshared(directory))
{
drop(directory);
j2c::decode_rows(
self.codestream,
&self.header,
retained_baseline_bytes,
decoder_context,
&mut ht_decoder,
&mut row_sink,
None,
)
.map(|()| retained_baseline_bytes)
} else {
decode_result
};
decoder_context.set_output_region(None);
decoder_context.set_round_irreversible_output(false);
decoder_context.permit_line_region_idwt(false);
decode_result
}
#[cfg(feature = "std")]
fn region_packet_directory(
&self,
retained_baseline_bytes: usize,
decoder_context: &DecoderContext<'a>,
prepare: bool,
prepared_tiles: Option<&j2c::ParsedTiles<'a>>,
) -> Option<alloc::sync::Arc<j2c::PacketDirectory>> {
if !prepare {
return self.packet_cache.snapshot();
}
self.packet_cache.for_region(|limit| {
let live = retained_baseline_bytes
.checked_add(decoder_context.retained_decode_bytes().ok()?)?
.checked_add(
core::mem::size_of::<j2c::PacketDirectory>()
+ 2 * core::mem::size_of::<usize>(),
)?;
if let Some(tiles) = prepared_tiles {
return tiles.build_packet_directory(&self.header, live, limit);
}
let tiles = j2c::prepare_region_tiles(
self.codestream,
&self.header,
live,
&mut DecoderContext::default(),
)
.ok()?;
let retained = live.checked_add(tiles.metadata_owner_bytes())?;
tiles.build_packet_directory(&self.header, retained, limit)
})
}
pub(crate) fn permits_line_region_idwt(&self) -> bool {
self.boxes.palette.is_none()
&& self.boxes.channel_definition.is_none()
&& !matches!(
self.boxes
.primary_color_specification()
.map(|specification| &specification.color_space),
Some(jp2::colr::ColorSpace::Enumerated(
EnumeratedColorspace::Sycc | EnumeratedColorspace::CieLab(_)
))
)
}
fn finish_decoded_image<'ctx>(
&self,
channel_data: &'ctx mut Vec<ComponentData>,
retained_baseline_bytes: usize,
) -> Result<DecodedImage<'ctx, '_>> {
let settings = &self.settings;
let mut decoded_image = DecodedImage {
decoded_components: channel_data,
boxes: &self.boxes,
};
if settings.resolve_palette_indices {
let components = core::mem::take(decoded_image.decoded_components);
*decoded_image.decoded_components =
resolve_palette_indices(components, decoded_image.boxes, retained_baseline_bytes)?;
}
if let Some(cdef) = decoded_image
.boxes
.primary_color_specification()
.and(decoded_image.boxes.channel_definition.as_ref())
{
validate_and_reorder_channels(
cdef,
decoded_image.decoded_components,
retained_baseline_bytes,
)?;
}
let bit_depth = decoded_image
.decoded_components
.first()
.ok_or(DecodingError::CodeBlockDecodeFailure)?
.bit_depth;
convert_color_space(&mut decoded_image, bit_depth)?;
Ok(decoded_image)
}
}
impl PreparedRegionDecoder<'_, '_, '_> {
pub fn decode_region_components(
&mut self,
roi: (u32, u32, u32, u32),
) -> Result<DecodedComponents<'_>> {
self.decode_region_component_planes(roi, false)
}
#[doc(hidden)]
pub fn decode_region_components_for_integer_output(
&mut self,
roi: (u32, u32, u32, u32),
) -> Result<DecodedComponents<'_>> {
self.decode_region_component_planes(roi, true)
}
fn decode_region_component_planes(
&mut self,
roi: (u32, u32, u32, u32),
round_irreversible_output: bool,
) -> Result<DecodedComponents<'_>> {
self.image.decode_prepared_region_component_planes(
&mut self.tiles,
self.retained_image_bytes,
self.retained_session_bytes,
roi,
round_irreversible_output,
self.decoder_context,
)
}
}
impl<'a> Image<'a> {
fn decode_prepared_region_component_planes<'ctx>(
&self,
tiles: &mut j2c::ParsedTiles<'a>,
retained_image_bytes: usize,
retained_session_bytes: usize,
roi: (u32, u32, u32, u32),
round_irreversible_output: bool,
decoder_context: &'ctx mut DecoderContext<'a>,
) -> Result<DecodedComponents<'ctx>> {
validate_roi((self.width(), self.height()), roi)?;
self.validate_component_plane_precision()?;
#[cfg(feature = "std")]
tiles.set_packet_directory(None);
#[cfg(feature = "std")]
let mut directory = self.region_packet_directory(
retained_session_bytes,
decoder_context,
self.permits_line_region_idwt(),
Some(tiles),
);
#[cfg(feature = "std")]
let extra_bytes = directory
.as_deref()
.map_or(0, packet_directory::PacketDirectoryCache::directory_bytes);
#[cfg(not(feature = "std"))]
let extra_bytes = 0;
#[cfg(feature = "std")]
tiles.set_packet_directory(directory.clone());
decoder_context.set_output_region(Some(roi));
decoder_context.set_round_irreversible_output(round_irreversible_output);
decoder_context.permit_line_region_idwt(self.permits_line_region_idwt());
let decode_result = allocation::combine_retained_bytes(retained_image_bytes, extra_bytes)
.and_then(|source_bytes| {
let session_bytes =
allocation::combine_retained_bytes(retained_session_bytes, extra_bytes)?;
j2c::decode_preparsed(&self.header, source_bytes, tiles, decoder_context)
.map(|()| session_bytes)
});
#[cfg(feature = "std")]
let decode_result = if decode_result.as_ref().is_err_and(j2c::is_capacity_error) {
tiles.set_packet_directory(None);
if directory
.as_ref()
.is_some_and(|directory| self.packet_cache.evict_if_unshared(directory))
{
drop(directory.take());
j2c::decode_preparsed(&self.header, retained_image_bytes, tiles, decoder_context)
.map(|()| retained_session_bytes)
} else {
decode_result
}
} else {
decode_result
};
decoder_context.set_output_region(None);
decoder_context.set_round_irreversible_output(false);
decoder_context.permit_line_region_idwt(false);
#[cfg(feature = "std")]
{
tiles.set_packet_directory(None);
drop(directory);
}
let charged_session_bytes = decode_result?;
let (_x, _y, width, height) = roi;
let decoded_image = self.finish_decoded_image(
&mut decoder_context.tile_decode_context.channel_data,
charged_session_bytes,
)?;
let DecodedImage {
decoded_components,
boxes: _,
} = decoded_image;
self.try_borrow_component_planes_with_retained_baseline(
decoded_components.as_slice(),
decoded_components.capacity(),
(width, height),
retained_session_bytes,
)
}
}