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