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.decode_borrowed_component_planes(decoder_context, None, None, false)
477    }
478
479    /// Decode the image into owned native-bit-depth component planes.
480    ///
481    /// Unlike [`Self::decode_native`], this preserves per-component bit depth
482    /// and signedness metadata and does not require all components to share a
483    /// single packed interleaved representation.
484    ///
485    /// # Errors
486    ///
487    /// Returns an error when validation, decoding, or native sample packing fails.
488    pub fn decode_native_components(&self) -> Result<DecodedNativeComponents> {
489        let mut decoder_context = DecoderContext::default();
490        self.decode_native_components_with_context(&mut decoder_context)
491    }
492
493    /// Decode owned native component planes while accounting an already-live
494    /// external allocation, such as the encoded `Vec` being validated.
495    ///
496    /// `retained_capacity` must be the allocator capacity of that external
497    /// owner, not merely its logical length.
498    ///
499    /// # Errors
500    ///
501    /// Returns an error when aggregate retained allocation accounting,
502    /// decoding, or native component packing fails.
503    #[doc(hidden)]
504    pub fn decode_native_components_with_retained_capacity(
505        &self,
506        retained_capacity: usize,
507    ) -> Result<DecodedNativeComponents> {
508        let retained_baseline_bytes =
509            allocation::combine_retained_bytes(retained_capacity, self.retained_metadata_bytes()?)?;
510        let mut decoder_context = DecoderContext::default();
511        self.decode_native_components_with_context_and_retained_baseline(
512            &mut decoder_context,
513            retained_baseline_bytes,
514        )
515    }
516
517    /// Decode the image into owned native-bit-depth component planes using a
518    /// caller-provided decoder context.
519    ///
520    /// # Errors
521    ///
522    /// Returns an error when validation, decoding, or native sample packing fails.
523    pub fn decode_native_components_with_context(
524        &self,
525        decoder_context: &mut DecoderContext<'a>,
526    ) -> Result<DecodedNativeComponents> {
527        let retained_baseline_bytes = self.retained_metadata_bytes()?;
528        self.decode_native_components_with_context_and_retained_baseline(
529            decoder_context,
530            retained_baseline_bytes,
531        )
532    }
533
534    fn decode_native_components_with_context_and_retained_baseline(
535        &self,
536        decoder_context: &mut DecoderContext<'a>,
537        retained_baseline_bytes: usize,
538    ) -> Result<DecodedNativeComponents> {
539        let decoded_image =
540            self.decode_image(decoder_context, None, None, true, retained_baseline_bytes)?;
541        let DecodedImage {
542            decoded_components,
543            boxes: _,
544        } = decoded_image;
545        let component_owner_capacity = decoded_components.capacity();
546        self.pack_native_component_planes(
547            decoded_components,
548            component_owner_capacity,
549            (self.width(), self.height()),
550            retained_baseline_bytes,
551        )
552    }
553
554    /// Decode borrowed component planes while delegating HTJ2K code-block decode.
555    #[doc(hidden)]
556    pub fn decode_components_with_ht_decoder<'ctx>(
557        &self,
558        decoder_context: &'ctx mut DecoderContext<'a>,
559        ht_decoder: &mut dyn HtCodeBlockDecoder,
560    ) -> Result<DecodedComponents<'ctx>> {
561        self.decode_borrowed_component_planes(decoder_context, None, Some(ht_decoder), false)
562    }
563
564    /// Decode borrowed component planes for a requested region using a
565    /// caller-provided decoder context.
566    ///
567    /// # Errors
568    ///
569    /// Returns an error when the region is invalid, precision is unsupported, or decoding fails.
570    pub fn decode_region_components_with_context<'ctx>(
571        &self,
572        roi: (u32, u32, u32, u32),
573        decoder_context: &'ctx mut DecoderContext<'a>,
574    ) -> Result<DecodedComponents<'ctx>> {
575        self.decode_borrowed_component_planes(decoder_context, Some(roi), None, false)
576    }
577
578    /// Decode a source-coordinate region into owned native-bit-depth component
579    /// planes using a caller-provided decoder context.
580    ///
581    /// # Errors
582    ///
583    /// Returns an error when the region is invalid or decoding and packing fail.
584    pub fn decode_native_region_components_with_context(
585        &self,
586        roi: (u32, u32, u32, u32),
587        decoder_context: &mut DecoderContext<'a>,
588    ) -> Result<DecodedNativeComponents> {
589        validate_roi((self.width(), self.height()), roi)?;
590        if self.requires_exact_integer_decode() {
591            return self.decode_native_region_components_via_full_decode(roi, decoder_context);
592        }
593        let (_x, _y, width, height) = roi;
594        let retained_image_bytes = self.retained_metadata_bytes()?;
595        let decoded_image =
596            self.decode_image(decoder_context, Some(roi), None, true, retained_image_bytes)?;
597        let DecodedImage {
598            decoded_components,
599            boxes: _,
600        } = decoded_image;
601        let component_owner_capacity = decoded_components.capacity();
602        self.pack_native_component_planes(
603            decoded_components,
604            component_owner_capacity,
605            (width, height),
606            retained_image_bytes,
607        )
608    }
609
610    /// Decode borrowed component planes for a requested region while
611    /// delegating code-block/transform stages through the adapter backend hook.
612    #[doc(hidden)]
613    pub fn decode_region_components_with_ht_decoder<'ctx>(
614        &self,
615        decoder_context: &'ctx mut DecoderContext<'a>,
616        roi: (u32, u32, u32, u32),
617        ht_decoder: &mut dyn HtCodeBlockDecoder,
618    ) -> Result<DecodedComponents<'ctx>> {
619        self.decode_borrowed_component_planes(decoder_context, Some(roi), Some(ht_decoder), false)
620    }
621
622    /// Decode a region of the image and return it as an 8-bit interleaved bitmap.
623    ///
624    /// # Errors
625    ///
626    /// Returns an error when the region is invalid or decoding fails.
627    pub fn decode_region(&self, roi: (u32, u32, u32, u32)) -> Result<Bitmap> {
628        self.decode_region_with_context(roi, &mut DecoderContext::default())
629    }
630
631    /// Decode a region of the image and return it as an 8-bit interleaved bitmap
632    /// using a caller-provided decoder context.
633    ///
634    /// # Errors
635    ///
636    /// Returns an error when the region is invalid, decoding fails, or output sizing overflows.
637    pub fn decode_region_with_context(
638        &self,
639        roi: (u32, u32, u32, u32),
640        decoder_context: &mut DecoderContext<'a>,
641    ) -> Result<Bitmap> {
642        validate_roi((self.width(), self.height()), roi)?;
643        (|| {
644            let retained_image_bytes = self.retained_metadata_bytes()?;
645            let mut decoded_image =
646                self.decode_image(decoder_context, Some(roi), None, true, retained_image_bytes)?;
647            let component_owner_capacity = decoded_image.decoded_components.capacity();
648            let (_x, _y, width, height) = roi;
649            let data_len = checked_decode_byte_len3(
650                width as usize,
651                height as usize,
652                decoded_image.decoded_components.len(),
653            )?;
654            let mut budget = NativeOutputBudget::for_decoded_channels(
655                retained_image_bytes,
656                decoded_image.decoded_components,
657                component_owner_capacity,
658            )?;
659            budget.include_elements::<u8>(data_len)?;
660            budget.include_color_space_clone(&self.color_space)?;
661
662            let color_space = try_clone_color_space(&self.color_space)?;
663            budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
664            let mut data = Vec::new();
665            try_resize_decode_elements(&mut data, data_len, 0_u8)?;
666            budget.include_capacity_overage::<u8>(data_len, data.capacity())?;
667            interleave_and_convert_region(
668                &mut decoded_image,
669                width as usize,
670                (0, 0, width, height),
671                &mut data,
672            )?;
673            let bitmap = Bitmap {
674                color_space,
675                data,
676                has_alpha: self.has_alpha,
677                width,
678                height,
679                original_bit_depth: self.original_bit_depth(),
680            };
681            NativeOutputBudget::validate_bitmap_pack(
682                retained_image_bytes,
683                decoded_image.decoded_components,
684                component_owner_capacity,
685                &bitmap,
686            )?;
687            Ok(bitmap)
688        })()
689    }
690
691    /// Decode the image into the given buffer.
692    ///
693    /// This method does the same as [`Image::decode`], but you can provide
694    /// a custom buffer for the output, as well as a decoder context. Doing
695    /// so allows the internal decode engine to reuse memory allocations, so
696    /// this is especially recommended if you plan on converting multiple
697    /// images in the same session.
698    ///
699    /// The buffer must have the correct size.
700    ///
701    /// # Errors
702    ///
703    /// Returns an error when decoding fails or `buf` is too small for the image.
704    pub fn decode_into(
705        &self,
706        buf: &mut [u8],
707        decoder_context: &mut DecoderContext<'a>,
708    ) -> Result<()> {
709        let mut decoded_image = self.decode_image(
710            decoder_context,
711            None,
712            None,
713            true,
714            self.retained_metadata_bytes()?,
715        )?;
716        validate_interleaved_output_buffer(&decoded_image, buf)?;
717        interleave_and_convert(&mut decoded_image, buf)?;
718
719        Ok(())
720    }
721
722    fn decode_image<'ctx>(
723        &self,
724        decoder_context: &'ctx mut DecoderContext<'a>,
725        output_region: Option<(u32, u32, u32, u32)>,
726        ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
727        round_irreversible_output: bool,
728        retained_baseline_bytes: usize,
729    ) -> Result<DecodedImage<'ctx, '_>> {
730        let mut ht_decoder = ht_decoder;
731        decoder_context.set_output_region(output_region);
732        decoder_context.set_round_irreversible_output(round_irreversible_output);
733        let decode_result = j2c::decode(
734            self.codestream,
735            &self.header,
736            retained_baseline_bytes,
737            decoder_context,
738            &mut ht_decoder,
739        );
740        decoder_context.set_output_region(None);
741        decoder_context.set_round_irreversible_output(false);
742        decode_result?;
743        self.finish_decoded_image(decoder_context, retained_baseline_bytes)
744    }
745
746    fn finish_decoded_image<'ctx>(
747        &self,
748        decoder_context: &'ctx mut DecoderContext<'a>,
749        retained_baseline_bytes: usize,
750    ) -> Result<DecodedImage<'ctx, '_>> {
751        let settings = &self.settings;
752        let mut decoded_image = DecodedImage {
753            decoded_components: &mut decoder_context.tile_decode_context.channel_data,
754            boxes: &self.boxes,
755        };
756
757        if settings.resolve_palette_indices {
758            let components = core::mem::take(decoded_image.decoded_components);
759            *decoded_image.decoded_components =
760                resolve_palette_indices(components, decoded_image.boxes, retained_baseline_bytes)?;
761        }
762
763        if let Some(cdef) = decoded_image
764            .boxes
765            .primary_color_specification()
766            .and(decoded_image.boxes.channel_definition.as_ref())
767        {
768            validate_and_reorder_channels(
769                cdef,
770                decoded_image.decoded_components,
771                retained_baseline_bytes,
772            )?;
773        }
774
775        let bit_depth = decoded_image
776            .decoded_components
777            .first()
778            .ok_or(DecodingError::CodeBlockDecodeFailure)?
779            .bit_depth;
780        convert_color_space(&mut decoded_image, bit_depth)?;
781        Ok(decoded_image)
782    }
783}
784
785impl PreparedRegionDecoder<'_, '_, '_> {
786    /// Decode one source-coordinate region using the retained tile graph.
787    ///
788    /// # Errors
789    ///
790    /// Returns an error when the region, component precision, or codestream is invalid.
791    pub fn decode_region_components(
792        &mut self,
793        roi: (u32, u32, u32, u32),
794    ) -> Result<DecodedComponents<'_>> {
795        self.decode_region_component_planes(roi, false)
796    }
797
798    /// Rounded-for-integer-output counterpart of
799    /// [`Self::decode_region_components`]; see
800    /// [`Image::decode_components_for_integer_output_with_context`].
801    ///
802    /// # Errors
803    ///
804    /// Returns an error when the region, component precision, or codestream is invalid.
805    #[doc(hidden)]
806    pub fn decode_region_components_for_integer_output(
807        &mut self,
808        roi: (u32, u32, u32, u32),
809    ) -> Result<DecodedComponents<'_>> {
810        self.decode_region_component_planes(roi, true)
811    }
812
813    fn decode_region_component_planes(
814        &mut self,
815        roi: (u32, u32, u32, u32),
816        round_irreversible_output: bool,
817    ) -> Result<DecodedComponents<'_>> {
818        validate_roi((self.image.width(), self.image.height()), roi)?;
819        self.image.validate_component_plane_precision()?;
820        self.decoder_context.set_output_region(Some(roi));
821        self.decoder_context
822            .set_round_irreversible_output(round_irreversible_output);
823        let decode_result = j2c::decode_preparsed(
824            &self.image.header,
825            self.retained_image_bytes,
826            &self.tiles,
827            self.decoder_context,
828        );
829        self.decoder_context.set_output_region(None);
830        self.decoder_context.set_round_irreversible_output(false);
831        decode_result?;
832        let (_x, _y, width, height) = roi;
833        let decoded_image = self
834            .image
835            .finish_decoded_image(self.decoder_context, self.retained_session_bytes)?;
836        let DecodedImage {
837            decoded_components,
838            boxes: _,
839        } = decoded_image;
840        self.image
841            .try_borrow_component_planes_with_retained_baseline(
842                decoded_components.as_slice(),
843                decoded_components.capacity(),
844                (width, height),
845                self.retained_session_bytes,
846            )
847    }
848}