use alloc::vec::Vec;
use crate::error::err;
use crate::j2c::{self, Header};
use crate::jp2::colr::EnumeratedColorspace;
use crate::jp2::{self, DecodedImage, ImageBoxes};
use crate::{
checked_decode_byte_len3, convert_color_space, interleave_and_convert,
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;
use self::allocation::retained_metadata_bytes;
pub(crate) use self::allocation::{retained_container_metadata_bytes, DecodeOwnerBudget};
use self::native::{try_clone_color_space, NativeOutputBudget};
#[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::lenient()
}
}
pub struct Image<'a> {
pub(crate) codestream: &'a [u8],
pub(crate) header: Header<'a>,
pub(crate) boxes: ImageBoxes,
pub(crate) settings: DecodeSettings,
pub(crate) has_alpha: bool,
pub(crate) color_space: ColorSpace,
}
impl<'a> Image<'a> {
pub(crate) fn from_parsed_parts(
codestream: &'a [u8],
header: Header<'a>,
boxes: ImageBoxes,
settings: DecodeSettings,
color_space: ColorSpace,
has_alpha: bool,
) -> Result<Self> {
Self::from_parsed_parts_with_retained_baseline(
codestream,
header,
boxes,
settings,
color_space,
has_alpha,
0,
)
}
pub(crate) fn from_parsed_parts_with_retained_baseline(
codestream: &'a [u8],
header: Header<'a>,
boxes: ImageBoxes,
settings: DecodeSettings,
color_space: ColorSpace,
has_alpha: bool,
retained_baseline_bytes: usize,
) -> Result<Self> {
let metadata_bytes = retained_metadata_bytes(&header, &boxes, &color_space)?;
allocation::combine_retained_bytes(retained_baseline_bytes, metadata_bytes)?;
Ok(Self {
codestream,
header,
boxes,
settings,
has_alpha,
color_space,
})
}
pub(crate) fn retained_metadata_bytes(&self) -> Result<usize> {
retained_metadata_bytes(&self.header, &self.boxes, &self.color_space)
}
#[doc(hidden)]
pub fn retained_allocation_bytes(&self) -> Result<usize> {
self.retained_metadata_bytes()
}
pub fn new(data: &'a [u8], settings: &DecodeSettings) -> Result<Self> {
if data.starts_with(JP2_MAGIC) {
jp2::parse(data, *settings)
} else if data.starts_with(CODESTREAM_MAGIC) {
j2c::parse(data, settings)
} 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)
} else if data.starts_with(CODESTREAM_MAGIC) {
j2c::parse_with_retained_baseline(data, settings, retained_baseline_bytes)
} 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
}
#[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()
}
#[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(_)
))
)
}
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> {
(|| {
let retained_image_bytes = self.retained_metadata_bytes()?;
let mut decoded_image = self.prepare_decoded_image(decoder_context)?;
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_component_pack(
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(&mut decoded_image, &mut data)?;
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.validate_component_plane_precision()?;
let decoded_image = self.prepare_decoded_image(decoder_context)?;
let DecodedImage {
decoded_components,
boxes: _,
} = decoded_image;
self.try_borrow_component_planes(
decoded_components.as_slice(),
decoded_components.capacity(),
(self.width(), self.height()),
)
}
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_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_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> {
let decoded_image = self.prepare_decoded_image_with_retained_baseline(
decoder_context,
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,
)
}
#[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.validate_component_plane_precision()?;
let decoded_image =
self.prepare_decoded_image_with_ht_decoder(decoder_context, ht_decoder)?;
let DecodedImage {
decoded_components,
boxes: _,
} = decoded_image;
self.try_borrow_component_planes(
decoded_components.as_slice(),
decoded_components.capacity(),
(self.width(), self.height()),
)
}
pub fn decode_region_components_with_context<'ctx>(
&self,
roi: (u32, u32, u32, u32),
decoder_context: &'ctx mut DecoderContext<'a>,
) -> Result<DecodedComponents<'ctx>> {
validate_roi((self.width(), self.height()), roi)?;
self.validate_component_plane_precision()?;
let (_x, _y, width, height) = roi;
let decoded_image = self.prepare_decoded_image_with_region(decoder_context, Some(roi))?;
let DecodedImage {
decoded_components,
boxes: _,
} = decoded_image;
self.try_borrow_component_planes(
decoded_components.as_slice(),
decoded_components.capacity(),
(width, height),
)
}
pub fn decode_native_region_components_with_context(
&self,
roi: (u32, u32, u32, u32),
decoder_context: &mut DecoderContext<'a>,
) -> Result<DecodedNativeComponents> {
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 decoded_image = self.prepare_decoded_image_with_region(decoder_context, Some(roi))?;
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),
self.retained_metadata_bytes()?,
)
}
#[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>> {
validate_roi((self.width(), self.height()), roi)?;
self.validate_component_plane_precision()?;
let (_x, _y, width, height) = roi;
let decoded_image = self.prepare_decoded_image_with_region_and_ht_decoder(
decoder_context,
Some(roi),
Some(ht_decoder),
)?;
let DecodedImage {
decoded_components,
boxes: _,
} = decoded_image;
self.try_borrow_component_planes(
decoded_components.as_slice(),
decoded_components.capacity(),
(width, height),
)
}
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)?;
(|| {
let retained_image_bytes = self.retained_metadata_bytes()?;
let mut decoded_image =
self.prepare_decoded_image_with_region(decoder_context, Some(roi))?;
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_component_pack(
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())?;
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<()> {
let mut decoded_image = self.prepare_decoded_image(decoder_context)?;
validate_interleaved_output_buffer(&decoded_image, buf)?;
interleave_and_convert(&mut decoded_image, buf)?;
Ok(())
}
fn prepare_decoded_image<'ctx>(
&self,
decoder_context: &'ctx mut DecoderContext<'a>,
) -> Result<DecodedImage<'ctx, '_>> {
self.prepare_decoded_image_with_region(decoder_context, None)
}
fn prepare_decoded_image_with_retained_baseline<'ctx>(
&self,
decoder_context: &'ctx mut DecoderContext<'a>,
retained_baseline_bytes: usize,
) -> Result<DecodedImage<'ctx, '_>> {
self.prepare_decoded_image_with_region_and_ht_decoder_with_retained_baseline(
decoder_context,
None,
None,
retained_baseline_bytes,
)
}
fn prepare_decoded_image_with_ht_decoder<'ctx>(
&self,
decoder_context: &'ctx mut DecoderContext<'a>,
ht_decoder: &mut dyn HtCodeBlockDecoder,
) -> Result<DecodedImage<'ctx, '_>> {
self.prepare_decoded_image_with_region_and_ht_decoder(
decoder_context,
None,
Some(ht_decoder),
)
}
fn prepare_decoded_image_with_region<'ctx>(
&self,
decoder_context: &'ctx mut DecoderContext<'a>,
output_region: Option<(u32, u32, u32, u32)>,
) -> Result<DecodedImage<'ctx, '_>> {
self.prepare_decoded_image_with_region_and_ht_decoder(decoder_context, output_region, None)
}
fn prepare_decoded_image_with_region_and_ht_decoder<'ctx>(
&self,
decoder_context: &'ctx mut DecoderContext<'a>,
output_region: Option<(u32, u32, u32, u32)>,
ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
) -> Result<DecodedImage<'ctx, '_>> {
let retained_baseline_bytes = self.retained_metadata_bytes()?;
self.prepare_decoded_image_with_region_and_ht_decoder_with_retained_baseline(
decoder_context,
output_region,
ht_decoder,
retained_baseline_bytes,
)
}
fn prepare_decoded_image_with_region_and_ht_decoder_with_retained_baseline<'ctx>(
&self,
decoder_context: &'ctx mut DecoderContext<'a>,
output_region: Option<(u32, u32, u32, u32)>,
ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
retained_baseline_bytes: usize,
) -> Result<DecodedImage<'ctx, '_>> {
let settings = &self.settings;
self.decode_with_output_region_and_ht_decoder_with_retained_baseline(
decoder_context,
output_region,
ht_decoder,
retained_baseline_bytes,
)?;
let mut decoded_image = DecodedImage {
decoded_components: &mut decoder_context.tile_decode_context.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.channel_definition {
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)
}
fn decode_with_output_region(
&self,
decoder_context: &mut DecoderContext<'a>,
output_region: Option<(u32, u32, u32, u32)>,
) -> Result<()> {
self.decode_with_output_region_and_ht_decoder(decoder_context, output_region, None)
}
fn decode_with_output_region_and_ht_decoder(
&self,
decoder_context: &mut DecoderContext<'a>,
output_region: Option<(u32, u32, u32, u32)>,
ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
) -> Result<()> {
let retained_metadata_bytes = self.retained_metadata_bytes()?;
self.decode_with_output_region_and_ht_decoder_with_retained_baseline(
decoder_context,
output_region,
ht_decoder,
retained_metadata_bytes,
)
}
fn decode_with_output_region_and_ht_decoder_with_retained_baseline(
&self,
decoder_context: &mut DecoderContext<'a>,
output_region: Option<(u32, u32, u32, u32)>,
mut ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
retained_baseline_bytes: usize,
) -> Result<()> {
decoder_context.set_output_region(output_region);
let decode_result = j2c::decode(
self.codestream,
&self.header,
retained_baseline_bytes,
decoder_context,
&mut ht_decoder,
);
decoder_context.set_output_region(None);
decode_result
}
}