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