1use alloc::vec::Vec;
4
5use crate::error::{err, ValidationError};
6use crate::j2c::mct::DeferredMct;
7use crate::j2c::{self, ComponentData, Header};
8use crate::jp2::colr::EnumeratedColorspace;
9use crate::jp2::{self, DecodedImage, ImageBoxes};
10use crate::CpuDecodeParallelism;
11use crate::{
12 checked_decode_byte_len3, convert_color_space, interleave_and_convert_after_mct,
13 interleave_and_convert_region, resolve_palette_indices, try_resize_decode_elements,
14 validate_and_reorder_channels, validate_interleaved_output_buffer, validate_roi, Bitmap,
15 ColorSpace, DecodedComponents, DecodedNativeComponents, DecoderContext, DecodingError,
16 FormatError, HtCodeBlockDecoder, Result, CODESTREAM_MAGIC, JP2_MAGIC,
17};
18
19mod allocation;
20mod compare;
21#[cfg(test)]
22mod contract_tests;
23mod direct_api;
24mod native;
25mod output_api;
26#[cfg(feature = "std")]
27pub(crate) mod packet_directory;
28use self::allocation::retained_metadata_bytes;
29pub(crate) use self::allocation::{retained_container_metadata_bytes, DecodeOwnerBudget};
30use self::native::{try_clone_color_space, NativeOutputBudget};
31
32type Rgb8DecodedImage<'ctx, 'boxes> = (
36 Option<DeferredMct>,
37 &'ctx [f32],
38 [&'ctx mut [f32]; 2],
39 DecodedImage<'ctx, 'boxes>,
40 usize,
41);
42
43#[derive(Debug, Copy, Clone)]
45pub struct DecodeSettings {
46 pub resolve_palette_indices: bool,
58 pub strict: bool,
64 pub target_resolution: Option<(u32, u32)>,
66}
67
68impl DecodeSettings {
69 #[must_use]
86 pub const fn lenient() -> Self {
87 Self {
88 resolve_palette_indices: true,
89 strict: false,
90 target_resolution: None,
91 }
92 }
93
94 #[must_use]
96 pub const fn strict() -> Self {
97 Self {
98 resolve_palette_indices: true,
99 strict: true,
100 target_resolution: None,
101 }
102 }
103
104 #[must_use]
107 pub const fn lenient_tolerance_enabled(&self) -> bool {
108 !self.strict
109 }
110}
111
112impl Default for DecodeSettings {
113 fn default() -> Self {
114 Self::strict()
115 }
116}
117
118pub struct Image<'a> {
120 pub(crate) encoded_input: &'a [u8],
123 pub(crate) codestream: &'a [u8],
125 pub(crate) header: Header<'a>,
127 pub(crate) boxes: ImageBoxes,
130 pub(crate) settings: DecodeSettings,
132 pub(crate) used_lenient_metadata_recovery: bool,
135 pub(crate) has_alpha: bool,
137 pub(crate) color_space: ColorSpace,
139 retained_baseline_bytes: usize,
147 #[cfg(feature = "std")]
148 pub(crate) packet_cache: packet_directory::PacketDirectoryCache,
149}
150
151#[doc(hidden)]
156pub struct PreparedRegionDecoder<'image, 'context, 'a> {
157 image: &'image Image<'a>,
158 decoder_context: &'context mut DecoderContext<'a>,
159 tiles: j2c::ParsedTiles<'a>,
160 retained_image_bytes: usize,
161 retained_session_bytes: usize,
162}
163
164#[derive(Clone, Copy)]
165pub(crate) struct ImageSource<'a> {
166 encoded_input: &'a [u8],
167 codestream: &'a [u8],
168}
169
170impl<'a> ImageSource<'a> {
171 pub(crate) const fn new(encoded_input: &'a [u8], codestream: &'a [u8]) -> Self {
172 Self {
173 encoded_input,
174 codestream,
175 }
176 }
177}
178
179pub(crate) struct ImageProperties {
180 boxes: ImageBoxes,
181 settings: DecodeSettings,
182 color_space: ColorSpace,
183 has_alpha: bool,
184 used_lenient_metadata_recovery: bool,
185}
186
187impl ImageProperties {
188 pub(crate) const fn new(
189 boxes: ImageBoxes,
190 settings: DecodeSettings,
191 color_space: ColorSpace,
192 has_alpha: bool,
193 used_lenient_metadata_recovery: bool,
194 ) -> Self {
195 Self {
196 boxes,
197 settings,
198 color_space,
199 has_alpha,
200 used_lenient_metadata_recovery,
201 }
202 }
203}
204
205impl<'a> Image<'a> {
206 #[doc(hidden)]
212 pub fn prepare_region_decoder_with_context<'image, 'context>(
213 &'image self,
214 decoder_context: &'context mut DecoderContext<'a>,
215 ) -> Result<PreparedRegionDecoder<'image, 'context, 'a>> {
216 let retained_image_bytes = allocation::combine_retained_bytes(
217 self.retained_baseline_bytes,
218 self.retained_header_metadata_bytes()?,
219 )?;
220 let tiles = self.with_packet_cache_retry(|| {
221 j2c::prepare_region_tiles(
222 self.codestream,
223 &self.header,
224 self.retained_planning_bytes()?,
225 decoder_context,
226 )
227 })?;
228 let retained_session_bytes = retained_image_bytes
229 .checked_add(tiles.metadata_owner_bytes())
230 .ok_or(ValidationError::ImageTooLarge)?;
231 Ok(PreparedRegionDecoder {
232 image: self,
233 decoder_context,
234 tiles,
235 retained_image_bytes,
236 retained_session_bytes,
237 })
238 }
239
240 pub(crate) fn from_parsed_parts(
241 source: ImageSource<'a>,
242 header: Header<'a>,
243 properties: ImageProperties,
244 ) -> Result<Self> {
245 Self::from_parsed_parts_with_retained_baseline(source, header, properties, 0)
246 }
247
248 pub(crate) fn from_parsed_parts_with_retained_baseline(
249 source: ImageSource<'a>,
250 header: Header<'a>,
251 properties: ImageProperties,
252 retained_baseline_bytes: usize,
253 ) -> Result<Self> {
254 let ImageProperties {
255 boxes,
256 settings,
257 color_space,
258 has_alpha,
259 used_lenient_metadata_recovery,
260 } = properties;
261 let metadata_bytes = retained_metadata_bytes(&header, &boxes, &color_space)?;
262 allocation::combine_retained_bytes(retained_baseline_bytes, metadata_bytes)?;
263 Ok(Self {
264 encoded_input: source.encoded_input,
265 codestream: source.codestream,
266 header,
267 boxes,
268 settings,
269 used_lenient_metadata_recovery,
270 has_alpha,
271 color_space,
272 retained_baseline_bytes,
273 #[cfg(feature = "std")]
274 packet_cache: packet_directory::PacketDirectoryCache::default(),
275 })
276 }
277
278 fn retained_header_metadata_bytes(&self) -> Result<usize> {
279 retained_metadata_bytes(&self.header, &self.boxes, &self.color_space)
280 }
281
282 pub(crate) fn retained_metadata_bytes(&self) -> Result<usize> {
283 let bytes = self.retained_header_metadata_bytes()?;
284 #[cfg(feature = "std")]
285 let bytes = allocation::combine_retained_bytes(bytes, self.packet_cache.retained_bytes())?;
286 Ok(bytes)
287 }
288
289 pub(crate) fn retained_planning_bytes(&self) -> Result<usize> {
290 allocation::combine_retained_bytes(
291 self.retained_baseline_bytes,
292 self.retained_metadata_bytes()?,
293 )
294 }
295
296 #[doc(hidden)]
303 pub fn retained_allocation_bytes(&self) -> Result<usize> {
304 self.retained_metadata_bytes()
305 }
306
307 #[doc(hidden)]
309 #[must_use]
310 pub const fn used_lenient_metadata_recovery(&self) -> bool {
311 self.used_lenient_metadata_recovery
312 }
313
314 pub fn new(data: &'a [u8], settings: &DecodeSettings) -> Result<Self> {
320 if data.starts_with(JP2_MAGIC) {
321 jp2::parse(data, *settings, None)
322 } else if data.starts_with(CODESTREAM_MAGIC) {
323 j2c::parse(data, settings, None)
324 } else {
325 err!(FormatError::InvalidSignature)
326 }
327 }
328
329 #[doc(hidden)]
340 pub fn new_with_reduction(
341 data: &'a [u8],
342 settings: &DecodeSettings,
343 reduction_levels: u8,
344 ) -> Result<Self> {
345 if settings.target_resolution.is_some() {
346 return err!(DecodingError::UnsupportedFeature(
347 "exact reduction cannot be combined with a target-resolution hint",
348 ));
349 }
350 if data.starts_with(JP2_MAGIC) {
351 jp2::parse(data, *settings, Some(reduction_levels))
352 } else if data.starts_with(CODESTREAM_MAGIC) {
353 j2c::parse(data, settings, Some(reduction_levels))
354 } else {
355 err!(FormatError::InvalidSignature)
356 }
357 }
358
359 #[doc(hidden)]
370 pub fn new_with_retained_baseline(
371 data: &'a [u8],
372 settings: &DecodeSettings,
373 retained_baseline_bytes: usize,
374 ) -> Result<Self> {
375 if retained_baseline_bytes == 0 {
376 return Self::new(data, settings);
377 }
378 if data.starts_with(JP2_MAGIC) {
379 jp2::parse_with_retained_baseline(data, *settings, retained_baseline_bytes, None)
380 } else if data.starts_with(CODESTREAM_MAGIC) {
381 j2c::parse_with_retained_baseline(data, settings, retained_baseline_bytes, None)
382 } else {
383 err!(FormatError::InvalidSignature)
384 }
385 }
386
387 #[must_use]
389 pub fn has_alpha(&self) -> bool {
390 self.has_alpha
391 }
392
393 #[must_use]
395 pub fn color_space(&self) -> &ColorSpace {
396 &self.color_space
397 }
398
399 #[doc(hidden)]
401 #[must_use]
402 pub fn primary_icc_profile(&self) -> Option<&[u8]> {
403 match self
404 .boxes
405 .primary_color_specification()
406 .map(|specification| &specification.color_space)
407 {
408 Some(jp2::colr::ColorSpace::Icc(profile)) => Some(profile),
409 _ => None,
410 }
411 }
412
413 #[must_use]
415 pub fn width(&self) -> u32 {
416 self.header.size_data.image_width()
417 }
418
419 #[must_use]
421 pub fn height(&self) -> u32 {
422 self.header.size_data.image_height()
423 }
424
425 #[doc(hidden)]
427 #[must_use]
428 pub fn uses_high_throughput_block_coding(&self) -> bool {
429 self.header
430 .global_coding_style
431 .component_parameters
432 .parameters
433 .code_block_style
434 .uses_high_throughput_block_coding()
435 }
436
437 #[must_use]
440 pub fn original_bit_depth(&self) -> u8 {
441 self.header.component_infos[0].size_info.precision
443 }
444
445 #[doc(hidden)]
447 #[must_use]
448 pub fn supports_direct_device_plane_reuse(&self) -> bool {
449 if self.settings.resolve_palette_indices && self.boxes.palette.is_some() {
450 return false;
451 }
452 if self.boxes.channel_definition.is_some() {
453 return false;
454 }
455 !matches!(
456 self.boxes
457 .primary_color_specification()
458 .map(|spec| &spec.color_space),
459 Some(jp2::colr::ColorSpace::Enumerated(
460 EnumeratedColorspace::Sycc | EnumeratedColorspace::CieLab(_)
461 ))
462 )
463 }
464
465 #[doc(hidden)]
467 #[must_use]
468 pub fn tile_count(&self) -> u32 {
469 self.header.size_data.num_tiles()
470 }
471
472 pub fn decode(&self) -> Result<Vec<u8>> {
479 let bitmap = self.decode_with_context(&mut DecoderContext::default())?;
480 Ok(bitmap.data)
481 }
482
483 pub fn decode_with_context(&self, decoder_context: &mut DecoderContext<'a>) -> Result<Bitmap> {
490 self.with_packet_cache_retry(|| {
491 let retained_image_bytes = self.retained_header_metadata_bytes()?;
492 let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
493 let (mct, first_ll, scratch, mut decoded_image, retained_image_bytes) =
494 self.decode_image_for_rgb8(decoder_context, None, retained_image_bytes)?;
495 let component_owner_capacity = decoded_image.decoded_components.capacity();
496 let buffer_size = checked_decode_byte_len3(
497 self.width() as usize,
498 self.height() as usize,
499 decoded_image.decoded_components.len(),
500 )?;
501 let mut budget = NativeOutputBudget::for_decoded_channels(
502 retained_image_bytes,
503 decoded_image.decoded_components,
504 component_owner_capacity,
505 )?;
506 budget.include_elements::<u8>(buffer_size)?;
507 budget.include_color_space_clone(&self.color_space)?;
508
509 let color_space = try_clone_color_space(&self.color_space)?;
510 budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
511 let mut data = Vec::new();
512 try_resize_decode_elements(&mut data, buffer_size, 0_u8)?;
513 budget.include_capacity_overage::<u8>(buffer_size, data.capacity())?;
514 validate_interleaved_output_buffer(&decoded_image, &data)?;
515 interleave_and_convert_after_mct(
516 &mut decoded_image,
517 &mut data,
518 mct,
519 first_ll,
520 scratch,
521 parallel,
522 )?;
523 let bitmap = Bitmap {
524 color_space,
525 data,
526 has_alpha: self.has_alpha,
527 width: self.width(),
528 height: self.height(),
529 original_bit_depth: self.original_bit_depth(),
530 };
531 NativeOutputBudget::validate_bitmap_pack(
532 retained_image_bytes,
533 decoded_image.decoded_components,
534 component_owner_capacity,
535 &bitmap,
536 )?;
537 Ok(bitmap)
538 })
539 }
540
541 pub fn decode_components_with_context<'ctx>(
548 &self,
549 decoder_context: &'ctx mut DecoderContext<'a>,
550 ) -> Result<DecodedComponents<'ctx>> {
551 self.decode_borrowed_component_planes(decoder_context, None, None, false)
552 }
553
554 pub fn decode_native_components(&self) -> Result<DecodedNativeComponents> {
564 let mut decoder_context = DecoderContext::default();
565 self.decode_native_components_with_context(&mut decoder_context)
566 }
567
568 #[doc(hidden)]
579 pub fn decode_native_components_with_retained_capacity(
580 &self,
581 retained_capacity: usize,
582 ) -> Result<DecodedNativeComponents> {
583 let retained_baseline_bytes = allocation::combine_retained_bytes(
584 retained_capacity,
585 self.retained_header_metadata_bytes()?,
586 )?;
587 let mut decoder_context = DecoderContext::default();
588 self.decode_native_components_with_context_and_retained_baseline(
589 &mut decoder_context,
590 retained_baseline_bytes,
591 )
592 }
593
594 pub fn decode_native_components_with_context(
601 &self,
602 decoder_context: &mut DecoderContext<'a>,
603 ) -> Result<DecodedNativeComponents> {
604 let retained_baseline_bytes = self.retained_header_metadata_bytes()?;
605 self.decode_native_components_with_context_and_retained_baseline(
606 decoder_context,
607 retained_baseline_bytes,
608 )
609 }
610
611 fn decode_native_components_with_context_and_retained_baseline(
612 &self,
613 decoder_context: &mut DecoderContext<'a>,
614 retained_baseline_bytes: usize,
615 ) -> Result<DecodedNativeComponents> {
616 self.with_packet_cache_retry(|| {
617 let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
618 let (decoded_image, retained_baseline_bytes) =
619 self.decode_image(decoder_context, None, None, true, retained_baseline_bytes)?;
620 let DecodedImage {
621 decoded_components,
622 boxes: _,
623 } = decoded_image;
624 let component_owner_capacity = decoded_components.capacity();
625 self.pack_native_component_planes(
626 decoded_components,
627 component_owner_capacity,
628 (self.width(), self.height()),
629 retained_baseline_bytes,
630 parallel,
631 )
632 })
633 }
634
635 #[doc(hidden)]
637 pub fn decode_components_with_ht_decoder<'ctx>(
638 &self,
639 decoder_context: &'ctx mut DecoderContext<'a>,
640 ht_decoder: &mut dyn HtCodeBlockDecoder,
641 ) -> Result<DecodedComponents<'ctx>> {
642 self.decode_borrowed_component_planes(decoder_context, None, Some(ht_decoder), false)
643 }
644
645 pub fn decode_region_components_with_context<'ctx>(
652 &self,
653 roi: (u32, u32, u32, u32),
654 decoder_context: &'ctx mut DecoderContext<'a>,
655 ) -> Result<DecodedComponents<'ctx>> {
656 self.decode_borrowed_component_planes(decoder_context, Some(roi), None, false)
657 }
658
659 pub fn decode_native_region_components_with_context(
666 &self,
667 roi: (u32, u32, u32, u32),
668 decoder_context: &mut DecoderContext<'a>,
669 ) -> Result<DecodedNativeComponents> {
670 self.with_packet_cache_retry(|| {
671 validate_roi((self.width(), self.height()), roi)?;
672 if self.requires_exact_integer_decode() {
673 return self.decode_native_region_components_via_full_decode(roi, decoder_context);
674 }
675 let (_x, _y, width, height) = roi;
676 let retained_image_bytes = self.retained_header_metadata_bytes()?;
677 let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
678 let (decoded_image, retained_image_bytes) =
679 self.decode_image(decoder_context, Some(roi), None, true, retained_image_bytes)?;
680 let DecodedImage {
681 decoded_components,
682 boxes: _,
683 } = decoded_image;
684 let component_owner_capacity = decoded_components.capacity();
685 self.pack_native_component_planes(
686 decoded_components,
687 component_owner_capacity,
688 (width, height),
689 retained_image_bytes,
690 parallel,
691 )
692 })
693 }
694
695 #[doc(hidden)]
698 pub fn decode_region_components_with_ht_decoder<'ctx>(
699 &self,
700 decoder_context: &'ctx mut DecoderContext<'a>,
701 roi: (u32, u32, u32, u32),
702 ht_decoder: &mut dyn HtCodeBlockDecoder,
703 ) -> Result<DecodedComponents<'ctx>> {
704 self.decode_borrowed_component_planes(decoder_context, Some(roi), Some(ht_decoder), false)
705 }
706
707 pub fn decode_region(&self, roi: (u32, u32, u32, u32)) -> Result<Bitmap> {
713 self.decode_region_with_context(roi, &mut DecoderContext::default())
714 }
715
716 pub fn decode_region_with_context(
723 &self,
724 roi: (u32, u32, u32, u32),
725 decoder_context: &mut DecoderContext<'a>,
726 ) -> Result<Bitmap> {
727 validate_roi((self.width(), self.height()), roi)?;
728 self.with_packet_cache_retry(|| {
729 let retained_image_bytes = self.retained_header_metadata_bytes()?;
730 let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
731 let (mct, first_ll, scratch, mut decoded_image, retained_image_bytes) =
732 self.decode_image_for_rgb8(decoder_context, Some(roi), retained_image_bytes)?;
733 let component_owner_capacity = decoded_image.decoded_components.capacity();
734 let (_x, _y, width, height) = roi;
735 let data_len = checked_decode_byte_len3(
736 width as usize,
737 height as usize,
738 decoded_image.decoded_components.len(),
739 )?;
740 let mut budget = NativeOutputBudget::for_decoded_channels(
741 retained_image_bytes,
742 decoded_image.decoded_components,
743 component_owner_capacity,
744 )?;
745 budget.include_elements::<u8>(data_len)?;
746 budget.include_color_space_clone(&self.color_space)?;
747
748 let color_space = try_clone_color_space(&self.color_space)?;
749 budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
750 let mut data = Vec::new();
751 try_resize_decode_elements(&mut data, data_len, 0_u8)?;
752 budget.include_capacity_overage::<u8>(data_len, data.capacity())?;
753 if mct.is_some() {
754 interleave_and_convert_after_mct(
755 &mut decoded_image,
756 &mut data,
757 mct,
758 first_ll,
759 scratch,
760 parallel,
761 )?;
762 } else {
763 interleave_and_convert_region(
764 &mut decoded_image,
765 width as usize,
766 (0, 0, width, height),
767 &mut data,
768 )?;
769 }
770 let bitmap = Bitmap {
771 color_space,
772 data,
773 has_alpha: self.has_alpha,
774 width,
775 height,
776 original_bit_depth: self.original_bit_depth(),
777 };
778 NativeOutputBudget::validate_bitmap_pack(
779 retained_image_bytes,
780 decoded_image.decoded_components,
781 component_owner_capacity,
782 &bitmap,
783 )?;
784 Ok(bitmap)
785 })
786 }
787
788 pub fn decode_into(
802 &self,
803 buf: &mut [u8],
804 decoder_context: &mut DecoderContext<'a>,
805 ) -> Result<()> {
806 self.with_packet_cache_retry(|| {
807 let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
808 let retained = self.retained_header_metadata_bytes()?;
809 let (mct, first_ll, scratch, mut decoded_image, _retained_image_bytes) =
810 self.decode_image_for_rgb8(decoder_context, None, retained)?;
811 validate_interleaved_output_buffer(&decoded_image, buf)?;
812 interleave_and_convert_after_mct(
813 &mut decoded_image,
814 buf,
815 mct,
816 first_ll,
817 scratch,
818 parallel,
819 )?;
820
821 Ok(())
822 })
823 }
824
825 fn decode_image<'ctx>(
826 &self,
827 decoder_context: &'ctx mut DecoderContext<'a>,
828 output_region: Option<(u32, u32, u32, u32)>,
829 ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
830 round_irreversible_output: bool,
831 retained_baseline_bytes: usize,
832 ) -> Result<(DecodedImage<'ctx, '_>, usize)> {
833 let retained_baseline_bytes = self.run_decode(
834 decoder_context,
835 output_region,
836 ht_decoder,
837 round_irreversible_output,
838 retained_baseline_bytes,
839 )?;
840 let decoded = self.finish_decoded_image(
841 &mut decoder_context.tile_decode_context.channel_data,
842 retained_baseline_bytes,
843 )?;
844 Ok((decoded, retained_baseline_bytes))
845 }
846
847 fn decode_image_for_rgb8<'ctx>(
852 &self,
853 decoder_context: &'ctx mut DecoderContext<'a>,
854 output_region: Option<(u32, u32, u32, u32)>,
855 retained_baseline_bytes: usize,
856 ) -> Result<Rgb8DecodedImage<'ctx, '_>> {
857 let defer_mct = self.permits_line_region_idwt();
858 #[cfg(test)]
859 let defer_mct = defer_mct && !decoder_context.tile_decode_context.staged_rgb8_pack;
860 decoder_context.set_defer_mct(defer_mct);
861 let decoded = self.run_decode(
862 decoder_context,
863 output_region,
864 None,
865 true,
866 retained_baseline_bytes,
867 );
868 decoder_context.set_defer_mct(false);
869 let retained_baseline_bytes = decoded?;
870 let tile_ctx = &mut decoder_context.tile_decode_context;
871 let mct = tile_ctx.deferred_mct.take();
872 let image =
873 self.finish_decoded_image(&mut tile_ctx.channel_data, retained_baseline_bytes)?;
874 Ok((
875 mct,
876 &tile_ctx.idwt_output.coefficients,
877 [
878 &mut tile_ctx.idwt_scratch_buffer,
879 &mut tile_ctx.idwt_band_scratch,
880 ],
881 image,
882 retained_baseline_bytes,
883 ))
884 }
885
886 #[cfg_attr(
889 not(feature = "std"),
890 expect(
891 clippy::unused_self,
892 reason = "without std there is no packet cache, so only the std retry reads self"
893 )
894 )]
895 fn with_packet_cache_retry<T>(&self, mut operation: impl FnMut() -> Result<T>) -> Result<T> {
896 let result = operation();
897 #[cfg(feature = "std")]
898 if result.as_ref().is_err_and(j2c::is_capacity_error) {
899 if let Some(directory) = self.packet_cache.snapshot() {
900 if self.packet_cache.evict_if_unshared(&directory) {
901 drop(directory);
902 return operation();
903 }
904 }
905 }
906 result
907 }
908
909 fn run_decode(
910 &self,
911 decoder_context: &mut DecoderContext<'a>,
912 output_region: Option<(u32, u32, u32, u32)>,
913 ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
914 round_irreversible_output: bool,
915 retained_baseline_bytes: usize,
916 ) -> Result<usize> {
917 self.run_decode_rows(
918 decoder_context,
919 output_region,
920 ht_decoder,
921 round_irreversible_output,
922 retained_baseline_bytes,
923 None,
924 )
925 }
926
927 pub(super) fn run_decode_rows(
931 &self,
932 decoder_context: &mut DecoderContext<'a>,
933 output_region: Option<(u32, u32, u32, u32)>,
934 ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
935 round_irreversible_output: bool,
936 retained_baseline_bytes: usize,
937 row_sink: Option<&mut dyn crate::ComponentRowSink>,
938 ) -> Result<usize> {
939 let mut ht_decoder = ht_decoder;
940 let mut row_sink = row_sink;
941 decoder_context.storage.release_all_allocations();
942 #[cfg(feature = "std")]
943 let directory = self.region_packet_directory(
944 retained_baseline_bytes,
945 decoder_context,
946 output_region.is_some() && ht_decoder.is_none() && self.permits_line_region_idwt(),
947 None,
948 );
949 decoder_context.set_output_region(output_region);
950 decoder_context.set_round_irreversible_output(round_irreversible_output);
951 decoder_context.permit_line_region_idwt(self.permits_line_region_idwt());
952 #[cfg(feature = "std")]
953 let decode_result = allocation::combine_retained_bytes(
954 retained_baseline_bytes,
955 directory
956 .as_deref()
957 .map_or(0, packet_directory::PacketDirectoryCache::directory_bytes),
958 )
959 .and_then(|source_bytes| {
960 let index_directory = directory
961 .as_ref()
962 .filter(|_| output_region.is_some() && ht_decoder.is_none());
963 j2c::decode_rows(
964 self.codestream,
965 &self.header,
966 source_bytes,
967 decoder_context,
968 &mut ht_decoder,
969 &mut row_sink,
970 index_directory,
971 )
972 .map(|()| source_bytes)
973 });
974 #[cfg(not(feature = "std"))]
975 let decode_result = j2c::decode_rows(
976 self.codestream,
977 &self.header,
978 retained_baseline_bytes,
979 decoder_context,
980 &mut ht_decoder,
981 &mut row_sink,
982 )
983 .map(|()| retained_baseline_bytes);
984 #[cfg(feature = "std")]
987 let decode_result = if decode_result.as_ref().is_err_and(j2c::is_capacity_error)
988 && decoder_context.tile_decode_context.streamed_samples == 0
989 && directory
990 .as_ref()
991 .is_some_and(|directory| self.packet_cache.evict_if_unshared(directory))
992 {
993 drop(directory);
994 j2c::decode_rows(
995 self.codestream,
996 &self.header,
997 retained_baseline_bytes,
998 decoder_context,
999 &mut ht_decoder,
1000 &mut row_sink,
1001 None,
1002 )
1003 .map(|()| retained_baseline_bytes)
1004 } else {
1005 decode_result
1006 };
1007 decoder_context.set_output_region(None);
1008 decoder_context.set_round_irreversible_output(false);
1009 decoder_context.permit_line_region_idwt(false);
1010 decode_result
1011 }
1012
1013 #[cfg(feature = "std")]
1014 fn region_packet_directory(
1015 &self,
1016 retained_baseline_bytes: usize,
1017 decoder_context: &DecoderContext<'a>,
1018 prepare: bool,
1019 prepared_tiles: Option<&j2c::ParsedTiles<'a>>,
1020 ) -> Option<alloc::sync::Arc<j2c::PacketDirectory>> {
1021 if !prepare {
1022 return self.packet_cache.snapshot();
1023 }
1024 self.packet_cache.for_region(|limit| {
1025 let live = retained_baseline_bytes
1026 .checked_add(decoder_context.retained_decode_bytes().ok()?)?
1027 .checked_add(
1028 core::mem::size_of::<j2c::PacketDirectory>()
1029 + 2 * core::mem::size_of::<usize>(),
1030 )?;
1031 if let Some(tiles) = prepared_tiles {
1032 return tiles.build_packet_directory(&self.header, live, limit);
1033 }
1034 let tiles = j2c::prepare_region_tiles(
1035 self.codestream,
1036 &self.header,
1037 live,
1038 &mut DecoderContext::default(),
1039 )
1040 .ok()?;
1041 let retained = live.checked_add(tiles.metadata_owner_bytes())?;
1042 tiles.build_packet_directory(&self.header, retained, limit)
1043 })
1044 }
1045
1046 pub(crate) fn permits_line_region_idwt(&self) -> bool {
1050 self.boxes.palette.is_none()
1051 && self.boxes.channel_definition.is_none()
1052 && !matches!(
1053 self.boxes
1054 .primary_color_specification()
1055 .map(|specification| &specification.color_space),
1056 Some(jp2::colr::ColorSpace::Enumerated(
1057 EnumeratedColorspace::Sycc | EnumeratedColorspace::CieLab(_)
1058 ))
1059 )
1060 }
1061
1062 fn finish_decoded_image<'ctx>(
1063 &self,
1064 channel_data: &'ctx mut Vec<ComponentData>,
1065 retained_baseline_bytes: usize,
1066 ) -> Result<DecodedImage<'ctx, '_>> {
1067 let settings = &self.settings;
1068 let mut decoded_image = DecodedImage {
1069 decoded_components: channel_data,
1070 boxes: &self.boxes,
1071 };
1072
1073 if settings.resolve_palette_indices {
1074 let components = core::mem::take(decoded_image.decoded_components);
1075 *decoded_image.decoded_components =
1076 resolve_palette_indices(components, decoded_image.boxes, retained_baseline_bytes)?;
1077 }
1078
1079 if let Some(cdef) = decoded_image
1080 .boxes
1081 .primary_color_specification()
1082 .and(decoded_image.boxes.channel_definition.as_ref())
1083 {
1084 validate_and_reorder_channels(
1085 cdef,
1086 decoded_image.decoded_components,
1087 retained_baseline_bytes,
1088 )?;
1089 }
1090
1091 let bit_depth = decoded_image
1092 .decoded_components
1093 .first()
1094 .ok_or(DecodingError::CodeBlockDecodeFailure)?
1095 .bit_depth;
1096 convert_color_space(&mut decoded_image, bit_depth)?;
1097 Ok(decoded_image)
1098 }
1099}
1100
1101impl PreparedRegionDecoder<'_, '_, '_> {
1102 pub fn decode_region_components(
1108 &mut self,
1109 roi: (u32, u32, u32, u32),
1110 ) -> Result<DecodedComponents<'_>> {
1111 self.decode_region_component_planes(roi, false)
1112 }
1113
1114 #[doc(hidden)]
1122 pub fn decode_region_components_for_integer_output(
1123 &mut self,
1124 roi: (u32, u32, u32, u32),
1125 ) -> Result<DecodedComponents<'_>> {
1126 self.decode_region_component_planes(roi, true)
1127 }
1128
1129 fn decode_region_component_planes(
1130 &mut self,
1131 roi: (u32, u32, u32, u32),
1132 round_irreversible_output: bool,
1133 ) -> Result<DecodedComponents<'_>> {
1134 self.image.decode_prepared_region_component_planes(
1135 &mut self.tiles,
1136 self.retained_image_bytes,
1137 self.retained_session_bytes,
1138 roi,
1139 round_irreversible_output,
1140 self.decoder_context,
1141 )
1142 }
1143}
1144
1145impl<'a> Image<'a> {
1146 fn decode_prepared_region_component_planes<'ctx>(
1147 &self,
1148 tiles: &mut j2c::ParsedTiles<'a>,
1149 retained_image_bytes: usize,
1150 retained_session_bytes: usize,
1151 roi: (u32, u32, u32, u32),
1152 round_irreversible_output: bool,
1153 decoder_context: &'ctx mut DecoderContext<'a>,
1154 ) -> Result<DecodedComponents<'ctx>> {
1155 validate_roi((self.width(), self.height()), roi)?;
1156 self.validate_component_plane_precision()?;
1157 #[cfg(feature = "std")]
1158 tiles.set_packet_directory(None);
1159 #[cfg(feature = "std")]
1160 let mut directory = self.region_packet_directory(
1161 retained_session_bytes,
1162 decoder_context,
1163 self.permits_line_region_idwt(),
1164 Some(tiles),
1165 );
1166 #[cfg(feature = "std")]
1167 let extra_bytes = directory
1168 .as_deref()
1169 .map_or(0, packet_directory::PacketDirectoryCache::directory_bytes);
1170 #[cfg(not(feature = "std"))]
1171 let extra_bytes = 0;
1172 #[cfg(feature = "std")]
1173 tiles.set_packet_directory(directory.clone());
1174 decoder_context.set_output_region(Some(roi));
1175 decoder_context.set_round_irreversible_output(round_irreversible_output);
1176 decoder_context.permit_line_region_idwt(self.permits_line_region_idwt());
1177 let decode_result = allocation::combine_retained_bytes(retained_image_bytes, extra_bytes)
1178 .and_then(|source_bytes| {
1179 let session_bytes =
1180 allocation::combine_retained_bytes(retained_session_bytes, extra_bytes)?;
1181 j2c::decode_preparsed(&self.header, source_bytes, tiles, decoder_context)
1182 .map(|()| session_bytes)
1183 });
1184 #[cfg(feature = "std")]
1185 let decode_result = if decode_result.as_ref().is_err_and(j2c::is_capacity_error) {
1186 tiles.set_packet_directory(None);
1187 if directory
1188 .as_ref()
1189 .is_some_and(|directory| self.packet_cache.evict_if_unshared(directory))
1190 {
1191 drop(directory.take());
1192 j2c::decode_preparsed(&self.header, retained_image_bytes, tiles, decoder_context)
1193 .map(|()| retained_session_bytes)
1194 } else {
1195 decode_result
1196 }
1197 } else {
1198 decode_result
1199 };
1200 decoder_context.set_output_region(None);
1201 decoder_context.set_round_irreversible_output(false);
1202 decoder_context.permit_line_region_idwt(false);
1203 #[cfg(feature = "std")]
1204 {
1205 tiles.set_packet_directory(None);
1206 drop(directory);
1207 }
1208 let charged_session_bytes = decode_result?;
1209 let (_x, _y, width, height) = roi;
1210 let decoded_image = self.finish_decoded_image(
1211 &mut decoder_context.tile_decode_context.channel_data,
1212 charged_session_bytes,
1213 )?;
1214 let DecodedImage {
1215 decoded_components,
1216 boxes: _,
1217 } = decoded_image;
1218 self.try_borrow_component_planes_with_retained_baseline(
1219 decoded_components.as_slice(),
1220 decoded_components.capacity(),
1221 (width, height),
1222 retained_session_bytes,
1223 )
1224 }
1225}