Skip to main content

j2k_native/
image.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use 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;
26use self::allocation::retained_metadata_bytes;
27pub(crate) use self::allocation::{retained_container_metadata_bytes, DecodeOwnerBudget};
28use self::native::{try_clone_color_space, NativeOutputBudget};
29
30/// What [`Image::decode_image_for_rgb8`] leaves to the RGB8 pack: the
31/// deferred MCT, the first component's LL band of a last level the MCT
32/// carries, and the image.
33type Rgb8DecodedImage<'ctx, 'boxes> = (
34    Option<DeferredMct>,
35    &'ctx [f32],
36    [&'ctx mut [f32]; 2],
37    DecodedImage<'ctx, 'boxes>,
38);
39
40/// Settings to apply during decoding.
41#[derive(Debug, Copy, Clone)]
42pub struct DecodeSettings {
43    /// Whether palette indices should be resolved.
44    ///
45    /// JPEG2000 images can be stored in two different ways. First, by storing
46    /// RGB values (depending on the color space) for each pixel. Secondly, by
47    /// only storing a single index for each channel, and then resolving the
48    /// actual color using the index.
49    ///
50    /// If you disable this option, in case you have an image with palette
51    /// indices, they will not be resolved, but instead a grayscale image
52    /// will be returned, with each pixel value corresponding to the palette
53    /// index of the location.
54    pub resolve_palette_indices: bool,
55    /// Whether strict mode should be enabled when decoding.
56    ///
57    /// The default is strict. Lenient mode is limited to the recoveries listed
58    /// on [`DecodeSettings::lenient`]; it never relaxes codestream, bounds,
59    /// overflow, allocation, or resource-limit validation.
60    pub strict: bool,
61    /// A hint for the target resolution that the image should be decoded at.
62    pub target_resolution: Option<(u32, u32)>,
63}
64
65impl DecodeSettings {
66    /// Compatibility settings for explicitly recoverable JP2/JPH metadata.
67    ///
68    /// Lenient mode may:
69    ///
70    /// - ignore a malformed trailing top-level box after the required image
71    ///   header and codestream boxes were parsed;
72    /// - ignore a malformed trailing child box after the required `ihdr` and
73    ///   `colr` boxes were parsed;
74    /// - ignore a malformed optional `cdef` or `pclr` box, preserving any
75    ///   earlier complete value; and
76    /// - infer an undeclared alpha channel when an otherwise consistent JP2
77    ///   color declaration has exactly one extra codestream component.
78    ///
79    /// Raw codestream validation and entropy decoding remain strict. Bounds,
80    /// integer-overflow, allocation, and resource-limit checks are identical
81    /// in both modes.
82    #[must_use]
83    pub const fn lenient() -> Self {
84        Self {
85            resolve_palette_indices: true,
86            strict: false,
87            target_resolution: None,
88        }
89    }
90
91    /// Strict decode settings: malformed metadata is an error.
92    #[must_use]
93    pub const fn strict() -> Self {
94        Self {
95            resolve_palette_indices: true,
96            strict: true,
97            target_resolution: None,
98        }
99    }
100
101    /// Whether the settings permit lenient tolerance of malformed optional
102    /// metadata.
103    #[must_use]
104    pub const fn lenient_tolerance_enabled(&self) -> bool {
105        !self.strict
106    }
107}
108
109impl Default for DecodeSettings {
110    fn default() -> Self {
111        Self::strict()
112    }
113}
114
115/// A JPEG2000 image or codestream.
116pub struct Image<'a> {
117    /// Complete encoded input retained by the caller. Referenced execution
118    /// plans express compressed payload ranges relative to this owner.
119    pub(crate) encoded_input: &'a [u8],
120    /// The tile-part payload used by the legacy JPEG 2000 decoder.
121    pub(crate) codestream: &'a [u8],
122    /// The header of the J2C codestream.
123    pub(crate) header: Header<'a>,
124    /// The JP2 boxes of the image. In the case of a raw codestream, we
125    /// will synthesize the necessary boxes.
126    pub(crate) boxes: ImageBoxes,
127    /// Settings that should be applied during decoding.
128    pub(crate) settings: DecodeSettings,
129    /// Whether parsing used one of the explicitly documented lenient
130    /// container-metadata recoveries.
131    pub(crate) used_lenient_metadata_recovery: bool,
132    /// Whether the image has an alpha channel.
133    pub(crate) has_alpha: bool,
134    /// The color space of the image.
135    pub(crate) color_space: ColorSpace,
136    /// Codec-owned bytes already live when this image was parsed, set by
137    /// `new_with_retained_baseline`. Only the planning entry points charge it:
138    /// `prepare_region_decoder_with_context` and the `build_*_plan*_with_context`
139    /// direct and referenced planners. Pixel decode entry points (`decode*`,
140    /// `decode_native*`, `decode_into`) charge only this image's metadata;
141    /// callers that need the baseline there pass it explicitly through the
142    /// `*_with_retained_capacity` variants.
143    retained_baseline_bytes: usize,
144}
145
146/// Scoped region decoder that retains one parsed tile graph across calls.
147///
148/// This implementation-facing session is used by bounded row decode so each
149/// stripe repeats only the ROI decode work, not codestream tile parsing.
150#[doc(hidden)]
151pub struct PreparedRegionDecoder<'image, 'context, 'a> {
152    image: &'image Image<'a>,
153    decoder_context: &'context mut DecoderContext<'a>,
154    tiles: j2c::ParsedTiles<'a>,
155    retained_image_bytes: usize,
156    retained_session_bytes: usize,
157}
158
159#[derive(Clone, Copy)]
160pub(crate) struct ImageSource<'a> {
161    encoded_input: &'a [u8],
162    codestream: &'a [u8],
163}
164
165impl<'a> ImageSource<'a> {
166    pub(crate) const fn new(encoded_input: &'a [u8], codestream: &'a [u8]) -> Self {
167        Self {
168            encoded_input,
169            codestream,
170        }
171    }
172}
173
174pub(crate) struct ImageProperties {
175    boxes: ImageBoxes,
176    settings: DecodeSettings,
177    color_space: ColorSpace,
178    has_alpha: bool,
179    used_lenient_metadata_recovery: bool,
180}
181
182impl ImageProperties {
183    pub(crate) const fn new(
184        boxes: ImageBoxes,
185        settings: DecodeSettings,
186        color_space: ColorSpace,
187        has_alpha: bool,
188        used_lenient_metadata_recovery: bool,
189    ) -> Self {
190        Self {
191            boxes,
192            settings,
193            color_space,
194            has_alpha,
195            used_lenient_metadata_recovery,
196        }
197    }
198}
199
200impl<'a> Image<'a> {
201    /// Parse and retain the tile graph for repeated region decode calls.
202    ///
203    /// # Errors
204    ///
205    /// Returns an error when tile parsing or aggregate allocation validation fails.
206    #[doc(hidden)]
207    pub fn prepare_region_decoder_with_context<'image, 'context>(
208        &'image self,
209        decoder_context: &'context mut DecoderContext<'a>,
210    ) -> Result<PreparedRegionDecoder<'image, 'context, 'a>> {
211        let retained_image_bytes = self.retained_planning_bytes()?;
212        let tiles = j2c::prepare_region_tiles(
213            self.codestream,
214            &self.header,
215            retained_image_bytes,
216            decoder_context,
217        )?;
218        let retained_session_bytes = retained_image_bytes
219            .checked_add(tiles.metadata_owner_bytes())
220            .ok_or(ValidationError::ImageTooLarge)?;
221        Ok(PreparedRegionDecoder {
222            image: self,
223            decoder_context,
224            tiles,
225            retained_image_bytes,
226            retained_session_bytes,
227        })
228    }
229
230    pub(crate) fn from_parsed_parts(
231        source: ImageSource<'a>,
232        header: Header<'a>,
233        properties: ImageProperties,
234    ) -> Result<Self> {
235        Self::from_parsed_parts_with_retained_baseline(source, header, properties, 0)
236    }
237
238    pub(crate) fn from_parsed_parts_with_retained_baseline(
239        source: ImageSource<'a>,
240        header: Header<'a>,
241        properties: ImageProperties,
242        retained_baseline_bytes: usize,
243    ) -> Result<Self> {
244        let ImageProperties {
245            boxes,
246            settings,
247            color_space,
248            has_alpha,
249            used_lenient_metadata_recovery,
250        } = properties;
251        let metadata_bytes = retained_metadata_bytes(&header, &boxes, &color_space)?;
252        allocation::combine_retained_bytes(retained_baseline_bytes, metadata_bytes)?;
253        Ok(Self {
254            encoded_input: source.encoded_input,
255            codestream: source.codestream,
256            header,
257            boxes,
258            settings,
259            used_lenient_metadata_recovery,
260            has_alpha,
261            color_space,
262            retained_baseline_bytes,
263        })
264    }
265
266    pub(crate) fn retained_metadata_bytes(&self) -> Result<usize> {
267        retained_metadata_bytes(&self.header, &self.boxes, &self.color_space)
268    }
269
270    pub(crate) fn retained_planning_bytes(&self) -> Result<usize> {
271        allocation::combine_retained_bytes(
272            self.retained_baseline_bytes,
273            self.retained_metadata_bytes()?,
274        )
275    }
276
277    /// Return the allocator capacities retained by this parsed image.
278    ///
279    /// # Errors
280    ///
281    /// Returns an error if nested metadata capacity arithmetic overflows or
282    /// exceeds the native decode cap.
283    #[doc(hidden)]
284    pub fn retained_allocation_bytes(&self) -> Result<usize> {
285        self.retained_metadata_bytes()
286    }
287
288    /// Whether parsing used an explicitly documented lenient metadata recovery.
289    #[doc(hidden)]
290    #[must_use]
291    pub const fn used_lenient_metadata_recovery(&self) -> bool {
292        self.used_lenient_metadata_recovery
293    }
294
295    /// Try to create a new JPEG2000 image from the given data.
296    ///
297    /// # Errors
298    ///
299    /// Returns an error when the input signature, container, or codestream is invalid.
300    pub fn new(data: &'a [u8], settings: &DecodeSettings) -> Result<Self> {
301        if data.starts_with(JP2_MAGIC) {
302            jp2::parse(data, *settings, None)
303        } else if data.starts_with(CODESTREAM_MAGIC) {
304            j2c::parse(data, settings, None)
305        } else {
306            err!(FormatError::InvalidSignature)
307        }
308    }
309
310    /// Parse an image at an exact JPEG 2000 resolution reduction level.
311    ///
312    /// This low-level adapter exists for the `j2k` facade. A reduction of zero
313    /// preserves full resolution; each additional level halves both axes.
314    ///
315    /// # Errors
316    ///
317    /// Returns an error when a target-resolution hint is also configured, the
318    /// reduction is not representable, the codestream has too few resolution
319    /// levels, or the input is invalid.
320    #[doc(hidden)]
321    pub fn new_with_reduction(
322        data: &'a [u8],
323        settings: &DecodeSettings,
324        reduction_levels: u8,
325    ) -> Result<Self> {
326        if settings.target_resolution.is_some() {
327            return err!(DecodingError::UnsupportedFeature(
328                "exact reduction cannot be combined with a target-resolution hint",
329            ));
330        }
331        if data.starts_with(JP2_MAGIC) {
332            jp2::parse(data, *settings, Some(reduction_levels))
333        } else if data.starts_with(CODESTREAM_MAGIC) {
334            j2c::parse(data, settings, Some(reduction_levels))
335        } else {
336            err!(FormatError::InvalidSignature)
337        }
338    }
339
340    /// Parse an image while accounting already-live codec-owned allocations.
341    ///
342    /// This adapter is used when validation parses another image while an
343    /// encoded output and earlier parsed metadata remain live. The baseline is
344    /// retained for later tile and direct-plan allocation phases on this image.
345    ///
346    /// # Errors
347    ///
348    /// Returns an error when the input is invalid or aggregate parser-owned
349    /// allocations exceed the native decode cap.
350    #[doc(hidden)]
351    pub fn new_with_retained_baseline(
352        data: &'a [u8],
353        settings: &DecodeSettings,
354        retained_baseline_bytes: usize,
355    ) -> Result<Self> {
356        if retained_baseline_bytes == 0 {
357            return Self::new(data, settings);
358        }
359        if data.starts_with(JP2_MAGIC) {
360            jp2::parse_with_retained_baseline(data, *settings, retained_baseline_bytes, None)
361        } else if data.starts_with(CODESTREAM_MAGIC) {
362            j2c::parse_with_retained_baseline(data, settings, retained_baseline_bytes, None)
363        } else {
364            err!(FormatError::InvalidSignature)
365        }
366    }
367
368    /// Whether the image has an alpha channel.
369    #[must_use]
370    pub fn has_alpha(&self) -> bool {
371        self.has_alpha
372    }
373
374    /// The color space of the image.
375    #[must_use]
376    pub fn color_space(&self) -> &ColorSpace {
377        &self.color_space
378    }
379
380    /// Return the primary JP2 restricted ICC profile, when present.
381    #[doc(hidden)]
382    #[must_use]
383    pub fn primary_icc_profile(&self) -> Option<&[u8]> {
384        match self
385            .boxes
386            .primary_color_specification()
387            .map(|specification| &specification.color_space)
388        {
389            Some(jp2::colr::ColorSpace::Icc(profile)) => Some(profile),
390            _ => None,
391        }
392    }
393
394    /// The width of the image.
395    #[must_use]
396    pub fn width(&self) -> u32 {
397        self.header.size_data.image_width()
398    }
399
400    /// The height of the image.
401    #[must_use]
402    pub fn height(&self) -> u32 {
403        self.header.size_data.image_height()
404    }
405
406    /// Whether the main header's default coding style selects HT block coding.
407    #[doc(hidden)]
408    #[must_use]
409    pub fn uses_high_throughput_block_coding(&self) -> bool {
410        self.header
411            .global_coding_style
412            .component_parameters
413            .parameters
414            .code_block_style
415            .uses_high_throughput_block_coding()
416    }
417
418    /// The original bit depth of the image. You usually don't need to do anything
419    /// with this parameter, it just exists for informational purposes.
420    #[must_use]
421    pub fn original_bit_depth(&self) -> u8 {
422        // Note that this only works if all components have the same precision.
423        self.header.component_infos[0].size_info.precision
424    }
425
426    /// Whether decode finishes with additional host-side component mutation or reordering.
427    #[doc(hidden)]
428    #[must_use]
429    pub fn supports_direct_device_plane_reuse(&self) -> bool {
430        if self.settings.resolve_palette_indices && self.boxes.palette.is_some() {
431            return false;
432        }
433        if self.boxes.channel_definition.is_some() {
434            return false;
435        }
436        !matches!(
437            self.boxes
438                .primary_color_specification()
439                .map(|spec| &spec.color_space),
440            Some(jp2::colr::ColorSpace::Enumerated(
441                EnumeratedColorspace::Sycc | EnumeratedColorspace::CieLab(_)
442            ))
443        )
444    }
445
446    /// Number of tiles in the codestream's tile grid.
447    #[doc(hidden)]
448    #[must_use]
449    pub fn tile_count(&self) -> u32 {
450        self.header.size_data.num_tiles()
451    }
452
453    /// Decode the image and return its decoded result as a `Vec<u8>`, with each
454    /// channel interleaved.
455    ///
456    /// # Errors
457    ///
458    /// Returns an error when image validation, decoding, or output allocation fails.
459    pub fn decode(&self) -> Result<Vec<u8>> {
460        let bitmap = self.decode_with_context(&mut DecoderContext::default())?;
461        Ok(bitmap.data)
462    }
463
464    /// Decode the image and return its decoded result using a caller-provided
465    /// decoder context so allocations can be reused across repeated decodes.
466    ///
467    /// # Errors
468    ///
469    /// Returns an error when image validation, decoding, or output allocation fails.
470    pub fn decode_with_context(&self, decoder_context: &mut DecoderContext<'a>) -> Result<Bitmap> {
471        (|| {
472            let retained_image_bytes = self.retained_metadata_bytes()?;
473            let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
474            let (mct, first_ll, scratch, mut decoded_image) =
475                self.decode_image_for_rgb8(decoder_context, retained_image_bytes)?;
476            let component_owner_capacity = decoded_image.decoded_components.capacity();
477            let buffer_size = checked_decode_byte_len3(
478                self.width() as usize,
479                self.height() as usize,
480                decoded_image.decoded_components.len(),
481            )?;
482            let mut budget = NativeOutputBudget::for_decoded_channels(
483                retained_image_bytes,
484                decoded_image.decoded_components,
485                component_owner_capacity,
486            )?;
487            budget.include_elements::<u8>(buffer_size)?;
488            budget.include_color_space_clone(&self.color_space)?;
489
490            let color_space = try_clone_color_space(&self.color_space)?;
491            budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
492            let mut data = Vec::new();
493            try_resize_decode_elements(&mut data, buffer_size, 0_u8)?;
494            budget.include_capacity_overage::<u8>(buffer_size, data.capacity())?;
495            validate_interleaved_output_buffer(&decoded_image, &data)?;
496            interleave_and_convert_after_mct(
497                &mut decoded_image,
498                &mut data,
499                mct,
500                first_ll,
501                scratch,
502                parallel,
503            )?;
504            let bitmap = Bitmap {
505                color_space,
506                data,
507                has_alpha: self.has_alpha,
508                width: self.width(),
509                height: self.height(),
510                original_bit_depth: self.original_bit_depth(),
511            };
512            NativeOutputBudget::validate_bitmap_pack(
513                retained_image_bytes,
514                decoded_image.decoded_components,
515                component_owner_capacity,
516                &bitmap,
517            )?;
518            Ok(bitmap)
519        })()
520    }
521
522    /// Decode the image into borrowed component planes using a caller-provided
523    /// decoder context so allocations can be reused across repeated decodes.
524    ///
525    /// # Errors
526    ///
527    /// Returns an error when component precision is unsupported or decoding fails.
528    pub fn decode_components_with_context<'ctx>(
529        &self,
530        decoder_context: &'ctx mut DecoderContext<'a>,
531    ) -> Result<DecodedComponents<'ctx>> {
532        self.decode_borrowed_component_planes(decoder_context, None, None, false)
533    }
534
535    /// Decode the image into owned native-bit-depth component planes.
536    ///
537    /// Unlike [`Self::decode_native`], this preserves per-component bit depth
538    /// and signedness metadata and does not require all components to share a
539    /// single packed interleaved representation.
540    ///
541    /// # Errors
542    ///
543    /// Returns an error when validation, decoding, or native sample packing fails.
544    pub fn decode_native_components(&self) -> Result<DecodedNativeComponents> {
545        let mut decoder_context = DecoderContext::default();
546        self.decode_native_components_with_context(&mut decoder_context)
547    }
548
549    /// Decode owned native component planes while accounting an already-live
550    /// external allocation, such as the encoded `Vec` being validated.
551    ///
552    /// `retained_capacity` must be the allocator capacity of that external
553    /// owner, not merely its logical length.
554    ///
555    /// # Errors
556    ///
557    /// Returns an error when aggregate retained allocation accounting,
558    /// decoding, or native component packing fails.
559    #[doc(hidden)]
560    pub fn decode_native_components_with_retained_capacity(
561        &self,
562        retained_capacity: usize,
563    ) -> Result<DecodedNativeComponents> {
564        let retained_baseline_bytes =
565            allocation::combine_retained_bytes(retained_capacity, self.retained_metadata_bytes()?)?;
566        let mut decoder_context = DecoderContext::default();
567        self.decode_native_components_with_context_and_retained_baseline(
568            &mut decoder_context,
569            retained_baseline_bytes,
570        )
571    }
572
573    /// Decode the image into owned native-bit-depth component planes using a
574    /// caller-provided decoder context.
575    ///
576    /// # Errors
577    ///
578    /// Returns an error when validation, decoding, or native sample packing fails.
579    pub fn decode_native_components_with_context(
580        &self,
581        decoder_context: &mut DecoderContext<'a>,
582    ) -> Result<DecodedNativeComponents> {
583        let retained_baseline_bytes = self.retained_metadata_bytes()?;
584        self.decode_native_components_with_context_and_retained_baseline(
585            decoder_context,
586            retained_baseline_bytes,
587        )
588    }
589
590    fn decode_native_components_with_context_and_retained_baseline(
591        &self,
592        decoder_context: &mut DecoderContext<'a>,
593        retained_baseline_bytes: usize,
594    ) -> Result<DecodedNativeComponents> {
595        let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
596        let decoded_image =
597            self.decode_image(decoder_context, None, None, true, retained_baseline_bytes)?;
598        let DecodedImage {
599            decoded_components,
600            boxes: _,
601        } = decoded_image;
602        let component_owner_capacity = decoded_components.capacity();
603        self.pack_native_component_planes(
604            decoded_components,
605            component_owner_capacity,
606            (self.width(), self.height()),
607            retained_baseline_bytes,
608            parallel,
609        )
610    }
611
612    /// Decode borrowed component planes while delegating HTJ2K code-block decode.
613    #[doc(hidden)]
614    pub fn decode_components_with_ht_decoder<'ctx>(
615        &self,
616        decoder_context: &'ctx mut DecoderContext<'a>,
617        ht_decoder: &mut dyn HtCodeBlockDecoder,
618    ) -> Result<DecodedComponents<'ctx>> {
619        self.decode_borrowed_component_planes(decoder_context, None, Some(ht_decoder), false)
620    }
621
622    /// Decode borrowed component planes for a requested region using a
623    /// caller-provided decoder context.
624    ///
625    /// # Errors
626    ///
627    /// Returns an error when the region is invalid, precision is unsupported, or decoding fails.
628    pub fn decode_region_components_with_context<'ctx>(
629        &self,
630        roi: (u32, u32, u32, u32),
631        decoder_context: &'ctx mut DecoderContext<'a>,
632    ) -> Result<DecodedComponents<'ctx>> {
633        self.decode_borrowed_component_planes(decoder_context, Some(roi), None, false)
634    }
635
636    /// Decode a source-coordinate region into owned native-bit-depth component
637    /// planes using a caller-provided decoder context.
638    ///
639    /// # Errors
640    ///
641    /// Returns an error when the region is invalid or decoding and packing fail.
642    pub fn decode_native_region_components_with_context(
643        &self,
644        roi: (u32, u32, u32, u32),
645        decoder_context: &mut DecoderContext<'a>,
646    ) -> Result<DecodedNativeComponents> {
647        validate_roi((self.width(), self.height()), roi)?;
648        if self.requires_exact_integer_decode() {
649            return self.decode_native_region_components_via_full_decode(roi, decoder_context);
650        }
651        let (_x, _y, width, height) = roi;
652        let retained_image_bytes = self.retained_metadata_bytes()?;
653        let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
654        let decoded_image =
655            self.decode_image(decoder_context, Some(roi), None, true, retained_image_bytes)?;
656        let DecodedImage {
657            decoded_components,
658            boxes: _,
659        } = decoded_image;
660        let component_owner_capacity = decoded_components.capacity();
661        self.pack_native_component_planes(
662            decoded_components,
663            component_owner_capacity,
664            (width, height),
665            retained_image_bytes,
666            parallel,
667        )
668    }
669
670    /// Decode borrowed component planes for a requested region while
671    /// delegating code-block/transform stages through the adapter backend hook.
672    #[doc(hidden)]
673    pub fn decode_region_components_with_ht_decoder<'ctx>(
674        &self,
675        decoder_context: &'ctx mut DecoderContext<'a>,
676        roi: (u32, u32, u32, u32),
677        ht_decoder: &mut dyn HtCodeBlockDecoder,
678    ) -> Result<DecodedComponents<'ctx>> {
679        self.decode_borrowed_component_planes(decoder_context, Some(roi), Some(ht_decoder), false)
680    }
681
682    /// Decode a region of the image and return it as an 8-bit interleaved bitmap.
683    ///
684    /// # Errors
685    ///
686    /// Returns an error when the region is invalid or decoding fails.
687    pub fn decode_region(&self, roi: (u32, u32, u32, u32)) -> Result<Bitmap> {
688        self.decode_region_with_context(roi, &mut DecoderContext::default())
689    }
690
691    /// Decode a region of the image and return it as an 8-bit interleaved bitmap
692    /// using a caller-provided decoder context.
693    ///
694    /// # Errors
695    ///
696    /// Returns an error when the region is invalid, decoding fails, or output sizing overflows.
697    pub fn decode_region_with_context(
698        &self,
699        roi: (u32, u32, u32, u32),
700        decoder_context: &mut DecoderContext<'a>,
701    ) -> Result<Bitmap> {
702        validate_roi((self.width(), self.height()), roi)?;
703        (|| {
704            let retained_image_bytes = self.retained_metadata_bytes()?;
705            let mut decoded_image =
706                self.decode_image(decoder_context, Some(roi), None, true, retained_image_bytes)?;
707            let component_owner_capacity = decoded_image.decoded_components.capacity();
708            let (_x, _y, width, height) = roi;
709            let data_len = checked_decode_byte_len3(
710                width as usize,
711                height as usize,
712                decoded_image.decoded_components.len(),
713            )?;
714            let mut budget = NativeOutputBudget::for_decoded_channels(
715                retained_image_bytes,
716                decoded_image.decoded_components,
717                component_owner_capacity,
718            )?;
719            budget.include_elements::<u8>(data_len)?;
720            budget.include_color_space_clone(&self.color_space)?;
721
722            let color_space = try_clone_color_space(&self.color_space)?;
723            budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
724            let mut data = Vec::new();
725            try_resize_decode_elements(&mut data, data_len, 0_u8)?;
726            budget.include_capacity_overage::<u8>(data_len, data.capacity())?;
727            interleave_and_convert_region(
728                &mut decoded_image,
729                width as usize,
730                (0, 0, width, height),
731                &mut data,
732            )?;
733            let bitmap = Bitmap {
734                color_space,
735                data,
736                has_alpha: self.has_alpha,
737                width,
738                height,
739                original_bit_depth: self.original_bit_depth(),
740            };
741            NativeOutputBudget::validate_bitmap_pack(
742                retained_image_bytes,
743                decoded_image.decoded_components,
744                component_owner_capacity,
745                &bitmap,
746            )?;
747            Ok(bitmap)
748        })()
749    }
750
751    /// Decode the image into the given buffer.
752    ///
753    /// This method does the same as [`Image::decode`], but you can provide
754    /// a custom buffer for the output, as well as a decoder context. Doing
755    /// so allows the internal decode engine to reuse memory allocations, so
756    /// this is especially recommended if you plan on converting multiple
757    /// images in the same session.
758    ///
759    /// The buffer must have the correct size.
760    ///
761    /// # Errors
762    ///
763    /// Returns an error when decoding fails or `buf` is too small for the image.
764    pub fn decode_into(
765        &self,
766        buf: &mut [u8],
767        decoder_context: &mut DecoderContext<'a>,
768    ) -> Result<()> {
769        let parallel = decoder_context.cpu_decode_parallelism() == CpuDecodeParallelism::Auto;
770        let retained = self.retained_metadata_bytes()?;
771        let (mct, first_ll, scratch, mut decoded_image) =
772            self.decode_image_for_rgb8(decoder_context, retained)?;
773        validate_interleaved_output_buffer(&decoded_image, buf)?;
774        interleave_and_convert_after_mct(
775            &mut decoded_image,
776            buf,
777            mct,
778            first_ll,
779            scratch,
780            parallel,
781        )?;
782
783        Ok(())
784    }
785
786    fn decode_image<'ctx>(
787        &self,
788        decoder_context: &'ctx mut DecoderContext<'a>,
789        output_region: Option<(u32, u32, u32, u32)>,
790        ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
791        round_irreversible_output: bool,
792        retained_baseline_bytes: usize,
793    ) -> Result<DecodedImage<'ctx, '_>> {
794        self.run_decode(
795            decoder_context,
796            output_region,
797            ht_decoder,
798            round_irreversible_output,
799            retained_baseline_bytes,
800        )?;
801        self.finish_decoded_image(
802            &mut decoder_context.tile_decode_context.channel_data,
803            retained_baseline_bytes,
804        )
805    }
806
807    /// [`Self::decode_image`] for an RGB8 pack. A plain colour image (see
808    /// [`Self::permits_line_region_idwt`]) leaves its inverse MCT, and
809    /// possibly its last IDWT level, to the pack, which receives them with
810    /// the image.
811    fn decode_image_for_rgb8<'ctx>(
812        &self,
813        decoder_context: &'ctx mut DecoderContext<'a>,
814        retained_baseline_bytes: usize,
815    ) -> Result<Rgb8DecodedImage<'ctx, '_>> {
816        decoder_context.set_defer_mct(self.permits_line_region_idwt());
817        let decoded = self.run_decode(decoder_context, None, None, true, retained_baseline_bytes);
818        decoder_context.set_defer_mct(false);
819        decoded?;
820        let tile_ctx = &mut decoder_context.tile_decode_context;
821        let mct = tile_ctx.deferred_mct.take();
822        let image =
823            self.finish_decoded_image(&mut tile_ctx.channel_data, retained_baseline_bytes)?;
824        Ok((
825            mct,
826            &tile_ctx.idwt_output.coefficients,
827            [
828                &mut tile_ctx.idwt_scratch_buffer,
829                &mut tile_ctx.idwt_band_scratch,
830            ],
831            image,
832        ))
833    }
834
835    fn run_decode(
836        &self,
837        decoder_context: &mut DecoderContext<'a>,
838        output_region: Option<(u32, u32, u32, u32)>,
839        ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
840        round_irreversible_output: bool,
841        retained_baseline_bytes: usize,
842    ) -> Result<()> {
843        let mut ht_decoder = ht_decoder;
844        decoder_context.set_output_region(output_region);
845        decoder_context.set_round_irreversible_output(round_irreversible_output);
846        decoder_context.permit_line_region_idwt(self.permits_line_region_idwt());
847        let decode_result = j2c::decode(
848            self.codestream,
849            &self.header,
850            retained_baseline_bytes,
851            decoder_context,
852            &mut ht_decoder,
853        );
854        decoder_context.set_output_region(None);
855        decoder_context.set_round_irreversible_output(false);
856        decoder_context.permit_line_region_idwt(false);
857        decode_result
858    }
859
860    /// Whether a region decode of this image may use the row-streamed region
861    /// IDWT: it has no palette, component-definition, sYCC or `CIELab`
862    /// post-processing, which keep the whole-window path.
863    pub(crate) fn permits_line_region_idwt(&self) -> bool {
864        self.boxes.palette.is_none()
865            && self.boxes.channel_definition.is_none()
866            && !matches!(
867                self.boxes
868                    .primary_color_specification()
869                    .map(|specification| &specification.color_space),
870                Some(jp2::colr::ColorSpace::Enumerated(
871                    EnumeratedColorspace::Sycc | EnumeratedColorspace::CieLab(_)
872                ))
873            )
874    }
875
876    fn finish_decoded_image<'ctx>(
877        &self,
878        channel_data: &'ctx mut Vec<ComponentData>,
879        retained_baseline_bytes: usize,
880    ) -> Result<DecodedImage<'ctx, '_>> {
881        let settings = &self.settings;
882        let mut decoded_image = DecodedImage {
883            decoded_components: channel_data,
884            boxes: &self.boxes,
885        };
886
887        if settings.resolve_palette_indices {
888            let components = core::mem::take(decoded_image.decoded_components);
889            *decoded_image.decoded_components =
890                resolve_palette_indices(components, decoded_image.boxes, retained_baseline_bytes)?;
891        }
892
893        if let Some(cdef) = decoded_image
894            .boxes
895            .primary_color_specification()
896            .and(decoded_image.boxes.channel_definition.as_ref())
897        {
898            validate_and_reorder_channels(
899                cdef,
900                decoded_image.decoded_components,
901                retained_baseline_bytes,
902            )?;
903        }
904
905        let bit_depth = decoded_image
906            .decoded_components
907            .first()
908            .ok_or(DecodingError::CodeBlockDecodeFailure)?
909            .bit_depth;
910        convert_color_space(&mut decoded_image, bit_depth)?;
911        Ok(decoded_image)
912    }
913}
914
915impl PreparedRegionDecoder<'_, '_, '_> {
916    /// Decode one source-coordinate region using the retained tile graph.
917    ///
918    /// # Errors
919    ///
920    /// Returns an error when the region, component precision, or codestream is invalid.
921    pub fn decode_region_components(
922        &mut self,
923        roi: (u32, u32, u32, u32),
924    ) -> Result<DecodedComponents<'_>> {
925        self.decode_region_component_planes(roi, false)
926    }
927
928    /// Rounded-for-integer-output counterpart of
929    /// [`Self::decode_region_components`]; see
930    /// [`Image::decode_components_for_integer_output_with_context`].
931    ///
932    /// # Errors
933    ///
934    /// Returns an error when the region, component precision, or codestream is invalid.
935    #[doc(hidden)]
936    pub fn decode_region_components_for_integer_output(
937        &mut self,
938        roi: (u32, u32, u32, u32),
939    ) -> Result<DecodedComponents<'_>> {
940        self.decode_region_component_planes(roi, true)
941    }
942
943    fn decode_region_component_planes(
944        &mut self,
945        roi: (u32, u32, u32, u32),
946        round_irreversible_output: bool,
947    ) -> Result<DecodedComponents<'_>> {
948        validate_roi((self.image.width(), self.image.height()), roi)?;
949        self.image.validate_component_plane_precision()?;
950        self.decoder_context.set_output_region(Some(roi));
951        self.decoder_context
952            .set_round_irreversible_output(round_irreversible_output);
953        self.decoder_context
954            .permit_line_region_idwt(self.image.permits_line_region_idwt());
955        let decode_result = j2c::decode_preparsed(
956            &self.image.header,
957            self.retained_image_bytes,
958            &self.tiles,
959            self.decoder_context,
960        );
961        self.decoder_context.set_output_region(None);
962        self.decoder_context.set_round_irreversible_output(false);
963        self.decoder_context.permit_line_region_idwt(false);
964        decode_result?;
965        let (_x, _y, width, height) = roi;
966        let decoded_image = self.image.finish_decoded_image(
967            &mut self.decoder_context.tile_decode_context.channel_data,
968            self.retained_session_bytes,
969        )?;
970        let DecodedImage {
971            decoded_components,
972            boxes: _,
973        } = decoded_image;
974        self.image
975            .try_borrow_component_planes_with_retained_baseline(
976                decoded_components.as_slice(),
977                decoded_components.capacity(),
978                (width, height),
979                self.retained_session_bytes,
980            )
981    }
982}