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;
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#[derive(Clone, Copy)]
127pub(crate) struct ImageSource<'a> {
128    encoded_input: &'a [u8],
129    codestream: &'a [u8],
130}
131
132impl<'a> ImageSource<'a> {
133    pub(crate) const fn new(encoded_input: &'a [u8], codestream: &'a [u8]) -> Self {
134        Self {
135            encoded_input,
136            codestream,
137        }
138    }
139}
140
141pub(crate) struct ImageProperties {
142    boxes: ImageBoxes,
143    settings: DecodeSettings,
144    color_space: ColorSpace,
145    has_alpha: bool,
146    used_lenient_metadata_recovery: bool,
147}
148
149impl ImageProperties {
150    pub(crate) const fn new(
151        boxes: ImageBoxes,
152        settings: DecodeSettings,
153        color_space: ColorSpace,
154        has_alpha: bool,
155        used_lenient_metadata_recovery: bool,
156    ) -> Self {
157        Self {
158            boxes,
159            settings,
160            color_space,
161            has_alpha,
162            used_lenient_metadata_recovery,
163        }
164    }
165}
166
167impl<'a> Image<'a> {
168    pub(crate) fn from_parsed_parts(
169        source: ImageSource<'a>,
170        header: Header<'a>,
171        properties: ImageProperties,
172    ) -> Result<Self> {
173        Self::from_parsed_parts_with_retained_baseline(source, header, properties, 0)
174    }
175
176    pub(crate) fn from_parsed_parts_with_retained_baseline(
177        source: ImageSource<'a>,
178        header: Header<'a>,
179        properties: ImageProperties,
180        retained_baseline_bytes: usize,
181    ) -> Result<Self> {
182        let ImageProperties {
183            boxes,
184            settings,
185            color_space,
186            has_alpha,
187            used_lenient_metadata_recovery,
188        } = properties;
189        let metadata_bytes = retained_metadata_bytes(&header, &boxes, &color_space)?;
190        allocation::combine_retained_bytes(retained_baseline_bytes, metadata_bytes)?;
191        Ok(Self {
192            encoded_input: source.encoded_input,
193            codestream: source.codestream,
194            header,
195            boxes,
196            settings,
197            used_lenient_metadata_recovery,
198            has_alpha,
199            color_space,
200        })
201    }
202
203    pub(crate) fn retained_metadata_bytes(&self) -> Result<usize> {
204        retained_metadata_bytes(&self.header, &self.boxes, &self.color_space)
205    }
206
207    /// Return the allocator capacities retained by this parsed image.
208    ///
209    /// # Errors
210    ///
211    /// Returns an error if nested metadata capacity arithmetic overflows or
212    /// exceeds the native decode cap.
213    #[doc(hidden)]
214    pub fn retained_allocation_bytes(&self) -> Result<usize> {
215        self.retained_metadata_bytes()
216    }
217
218    /// Whether parsing used an explicitly documented lenient metadata recovery.
219    #[doc(hidden)]
220    #[must_use]
221    pub const fn used_lenient_metadata_recovery(&self) -> bool {
222        self.used_lenient_metadata_recovery
223    }
224
225    /// Try to create a new JPEG2000 image from the given data.
226    ///
227    /// # Errors
228    ///
229    /// Returns an error when the input signature, container, or codestream is invalid.
230    pub fn new(data: &'a [u8], settings: &DecodeSettings) -> Result<Self> {
231        if data.starts_with(JP2_MAGIC) {
232            jp2::parse(data, *settings)
233        } else if data.starts_with(CODESTREAM_MAGIC) {
234            j2c::parse(data, settings)
235        } else {
236            err!(FormatError::InvalidSignature)
237        }
238    }
239
240    /// Parse an image while accounting already-live codec-owned allocations.
241    ///
242    /// This adapter is used when validation parses another image while an
243    /// encoded output and earlier parsed metadata remain live.
244    ///
245    /// # Errors
246    ///
247    /// Returns an error when the input is invalid or aggregate parser-owned
248    /// allocations exceed the native decode cap.
249    #[doc(hidden)]
250    pub fn new_with_retained_baseline(
251        data: &'a [u8],
252        settings: &DecodeSettings,
253        retained_baseline_bytes: usize,
254    ) -> Result<Self> {
255        if retained_baseline_bytes == 0 {
256            return Self::new(data, settings);
257        }
258        if data.starts_with(JP2_MAGIC) {
259            jp2::parse_with_retained_baseline(data, *settings, retained_baseline_bytes)
260        } else if data.starts_with(CODESTREAM_MAGIC) {
261            j2c::parse_with_retained_baseline(data, settings, retained_baseline_bytes)
262        } else {
263            err!(FormatError::InvalidSignature)
264        }
265    }
266
267    /// Whether the image has an alpha channel.
268    #[must_use]
269    pub fn has_alpha(&self) -> bool {
270        self.has_alpha
271    }
272
273    /// The color space of the image.
274    #[must_use]
275    pub fn color_space(&self) -> &ColorSpace {
276        &self.color_space
277    }
278
279    /// The width of the image.
280    #[must_use]
281    pub fn width(&self) -> u32 {
282        self.header.size_data.image_width()
283    }
284
285    /// The height of the image.
286    #[must_use]
287    pub fn height(&self) -> u32 {
288        self.header.size_data.image_height()
289    }
290
291    /// The original bit depth of the image. You usually don't need to do anything
292    /// with this parameter, it just exists for informational purposes.
293    #[must_use]
294    pub fn original_bit_depth(&self) -> u8 {
295        // Note that this only works if all components have the same precision.
296        self.header.component_infos[0].size_info.precision
297    }
298
299    /// Whether decode finishes with additional host-side component mutation or reordering.
300    #[doc(hidden)]
301    #[must_use]
302    pub fn supports_direct_device_plane_reuse(&self) -> bool {
303        if self.settings.resolve_palette_indices && self.boxes.palette.is_some() {
304            return false;
305        }
306        if self.boxes.channel_definition.is_some() {
307            return false;
308        }
309        !matches!(
310            self.boxes
311                .primary_color_specification()
312                .map(|spec| &spec.color_space),
313            Some(jp2::colr::ColorSpace::Enumerated(
314                EnumeratedColorspace::Sycc | EnumeratedColorspace::CieLab(_)
315            ))
316        )
317    }
318
319    /// Decode the image and return its decoded result as a `Vec<u8>`, with each
320    /// channel interleaved.
321    ///
322    /// # Errors
323    ///
324    /// Returns an error when image validation, decoding, or output allocation fails.
325    pub fn decode(&self) -> Result<Vec<u8>> {
326        let bitmap = self.decode_with_context(&mut DecoderContext::default())?;
327        Ok(bitmap.data)
328    }
329
330    /// Decode the image and return its decoded result using a caller-provided
331    /// decoder context so allocations can be reused across repeated decodes.
332    ///
333    /// # Errors
334    ///
335    /// Returns an error when image validation, decoding, or output allocation fails.
336    pub fn decode_with_context(&self, decoder_context: &mut DecoderContext<'a>) -> Result<Bitmap> {
337        (|| {
338            let retained_image_bytes = self.retained_metadata_bytes()?;
339            let mut decoded_image =
340                self.decode_image(decoder_context, None, None, retained_image_bytes)?;
341            let component_owner_capacity = decoded_image.decoded_components.capacity();
342            let buffer_size = checked_decode_byte_len3(
343                self.width() as usize,
344                self.height() as usize,
345                decoded_image.decoded_components.len(),
346            )?;
347            let mut budget = NativeOutputBudget::for_decoded_channels(
348                retained_image_bytes,
349                decoded_image.decoded_components,
350                component_owner_capacity,
351            )?;
352            budget.include_elements::<u8>(buffer_size)?;
353            budget.include_color_space_clone(&self.color_space)?;
354
355            let color_space = try_clone_color_space(&self.color_space)?;
356            budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
357            let mut data = Vec::new();
358            try_resize_decode_elements(&mut data, buffer_size, 0_u8)?;
359            budget.include_capacity_overage::<u8>(buffer_size, data.capacity())?;
360            validate_interleaved_output_buffer(&decoded_image, &data)?;
361            interleave_and_convert(&mut decoded_image, &mut data)?;
362            let bitmap = Bitmap {
363                color_space,
364                data,
365                has_alpha: self.has_alpha,
366                width: self.width(),
367                height: self.height(),
368                original_bit_depth: self.original_bit_depth(),
369            };
370            NativeOutputBudget::validate_bitmap_pack(
371                retained_image_bytes,
372                decoded_image.decoded_components,
373                component_owner_capacity,
374                &bitmap,
375            )?;
376            Ok(bitmap)
377        })()
378    }
379
380    /// Decode the image into borrowed component planes using a caller-provided
381    /// decoder context so allocations can be reused across repeated decodes.
382    ///
383    /// # Errors
384    ///
385    /// Returns an error when component precision is unsupported or decoding fails.
386    pub fn decode_components_with_context<'ctx>(
387        &self,
388        decoder_context: &'ctx mut DecoderContext<'a>,
389    ) -> Result<DecodedComponents<'ctx>> {
390        self.validate_component_plane_precision()?;
391        let decoded_image =
392            self.decode_image(decoder_context, None, None, self.retained_metadata_bytes()?)?;
393        let DecodedImage {
394            decoded_components,
395            boxes: _,
396        } = decoded_image;
397        self.try_borrow_component_planes(
398            decoded_components.as_slice(),
399            decoded_components.capacity(),
400            (self.width(), self.height()),
401        )
402    }
403
404    /// Decode the image into owned native-bit-depth component planes.
405    ///
406    /// Unlike [`Self::decode_native`], this preserves per-component bit depth
407    /// and signedness metadata and does not require all components to share a
408    /// single packed interleaved representation.
409    ///
410    /// # Errors
411    ///
412    /// Returns an error when validation, decoding, or native sample packing fails.
413    pub fn decode_native_components(&self) -> Result<DecodedNativeComponents> {
414        let mut decoder_context = DecoderContext::default();
415        self.decode_native_components_with_context(&mut decoder_context)
416    }
417
418    /// Decode owned native component planes while accounting an already-live
419    /// external allocation, such as the encoded `Vec` being validated.
420    ///
421    /// `retained_capacity` must be the allocator capacity of that external
422    /// owner, not merely its logical length.
423    ///
424    /// # Errors
425    ///
426    /// Returns an error when aggregate retained allocation accounting,
427    /// decoding, or native component packing fails.
428    #[doc(hidden)]
429    pub fn decode_native_components_with_retained_capacity(
430        &self,
431        retained_capacity: usize,
432    ) -> Result<DecodedNativeComponents> {
433        let retained_baseline_bytes =
434            allocation::combine_retained_bytes(retained_capacity, self.retained_metadata_bytes()?)?;
435        let mut decoder_context = DecoderContext::default();
436        self.decode_native_components_with_context_and_retained_baseline(
437            &mut decoder_context,
438            retained_baseline_bytes,
439        )
440    }
441
442    /// Decode the image into owned native-bit-depth component planes using a
443    /// caller-provided decoder context.
444    ///
445    /// # Errors
446    ///
447    /// Returns an error when validation, decoding, or native sample packing fails.
448    pub fn decode_native_components_with_context(
449        &self,
450        decoder_context: &mut DecoderContext<'a>,
451    ) -> Result<DecodedNativeComponents> {
452        let retained_baseline_bytes = self.retained_metadata_bytes()?;
453        self.decode_native_components_with_context_and_retained_baseline(
454            decoder_context,
455            retained_baseline_bytes,
456        )
457    }
458
459    fn decode_native_components_with_context_and_retained_baseline(
460        &self,
461        decoder_context: &mut DecoderContext<'a>,
462        retained_baseline_bytes: usize,
463    ) -> Result<DecodedNativeComponents> {
464        let decoded_image =
465            self.decode_image(decoder_context, None, None, retained_baseline_bytes)?;
466        let DecodedImage {
467            decoded_components,
468            boxes: _,
469        } = decoded_image;
470        let component_owner_capacity = decoded_components.capacity();
471        self.pack_native_component_planes(
472            decoded_components,
473            component_owner_capacity,
474            (self.width(), self.height()),
475            retained_baseline_bytes,
476        )
477    }
478
479    /// Decode borrowed component planes while delegating HTJ2K code-block decode.
480    #[doc(hidden)]
481    pub fn decode_components_with_ht_decoder<'ctx>(
482        &self,
483        decoder_context: &'ctx mut DecoderContext<'a>,
484        ht_decoder: &mut dyn HtCodeBlockDecoder,
485    ) -> Result<DecodedComponents<'ctx>> {
486        self.validate_component_plane_precision()?;
487        let decoded_image = self.decode_image(
488            decoder_context,
489            None,
490            Some(ht_decoder),
491            self.retained_metadata_bytes()?,
492        )?;
493        let DecodedImage {
494            decoded_components,
495            boxes: _,
496        } = decoded_image;
497        self.try_borrow_component_planes(
498            decoded_components.as_slice(),
499            decoded_components.capacity(),
500            (self.width(), self.height()),
501        )
502    }
503
504    /// Decode borrowed component planes for a requested region using a
505    /// caller-provided decoder context.
506    ///
507    /// # Errors
508    ///
509    /// Returns an error when the region is invalid, precision is unsupported, or decoding fails.
510    pub fn decode_region_components_with_context<'ctx>(
511        &self,
512        roi: (u32, u32, u32, u32),
513        decoder_context: &'ctx mut DecoderContext<'a>,
514    ) -> Result<DecodedComponents<'ctx>> {
515        validate_roi((self.width(), self.height()), roi)?;
516        self.validate_component_plane_precision()?;
517        let (_x, _y, width, height) = roi;
518        let decoded_image = self.decode_image(
519            decoder_context,
520            Some(roi),
521            None,
522            self.retained_metadata_bytes()?,
523        )?;
524        let DecodedImage {
525            decoded_components,
526            boxes: _,
527        } = decoded_image;
528        self.try_borrow_component_planes(
529            decoded_components.as_slice(),
530            decoded_components.capacity(),
531            (width, height),
532        )
533    }
534
535    /// Decode a source-coordinate region into owned native-bit-depth component
536    /// planes using a caller-provided decoder context.
537    ///
538    /// # Errors
539    ///
540    /// Returns an error when the region is invalid or decoding and packing fail.
541    pub fn decode_native_region_components_with_context(
542        &self,
543        roi: (u32, u32, u32, u32),
544        decoder_context: &mut DecoderContext<'a>,
545    ) -> Result<DecodedNativeComponents> {
546        validate_roi((self.width(), self.height()), roi)?;
547        if self.requires_exact_integer_decode() {
548            return self.decode_native_region_components_via_full_decode(roi, decoder_context);
549        }
550        let (_x, _y, width, height) = roi;
551        let retained_image_bytes = self.retained_metadata_bytes()?;
552        let decoded_image =
553            self.decode_image(decoder_context, Some(roi), None, retained_image_bytes)?;
554        let DecodedImage {
555            decoded_components,
556            boxes: _,
557        } = decoded_image;
558        let component_owner_capacity = decoded_components.capacity();
559        self.pack_native_component_planes(
560            decoded_components,
561            component_owner_capacity,
562            (width, height),
563            retained_image_bytes,
564        )
565    }
566
567    /// Decode borrowed component planes for a requested region while
568    /// delegating code-block/transform stages through the adapter backend hook.
569    #[doc(hidden)]
570    pub fn decode_region_components_with_ht_decoder<'ctx>(
571        &self,
572        decoder_context: &'ctx mut DecoderContext<'a>,
573        roi: (u32, u32, u32, u32),
574        ht_decoder: &mut dyn HtCodeBlockDecoder,
575    ) -> Result<DecodedComponents<'ctx>> {
576        validate_roi((self.width(), self.height()), roi)?;
577        self.validate_component_plane_precision()?;
578        let (_x, _y, width, height) = roi;
579        let decoded_image = self.decode_image(
580            decoder_context,
581            Some(roi),
582            Some(ht_decoder),
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            (width, height),
593        )
594    }
595
596    /// Decode a region of the image and return it as an 8-bit interleaved bitmap.
597    ///
598    /// # Errors
599    ///
600    /// Returns an error when the region is invalid or decoding fails.
601    pub fn decode_region(&self, roi: (u32, u32, u32, u32)) -> Result<Bitmap> {
602        self.decode_region_with_context(roi, &mut DecoderContext::default())
603    }
604
605    /// Decode a region of the image and return it as an 8-bit interleaved bitmap
606    /// using a caller-provided decoder context.
607    ///
608    /// # Errors
609    ///
610    /// Returns an error when the region is invalid, decoding fails, or output sizing overflows.
611    pub fn decode_region_with_context(
612        &self,
613        roi: (u32, u32, u32, u32),
614        decoder_context: &mut DecoderContext<'a>,
615    ) -> Result<Bitmap> {
616        validate_roi((self.width(), self.height()), roi)?;
617        (|| {
618            let retained_image_bytes = self.retained_metadata_bytes()?;
619            let mut decoded_image =
620                self.decode_image(decoder_context, Some(roi), None, retained_image_bytes)?;
621            let component_owner_capacity = decoded_image.decoded_components.capacity();
622            let (_x, _y, width, height) = roi;
623            let data_len = checked_decode_byte_len3(
624                width as usize,
625                height as usize,
626                decoded_image.decoded_components.len(),
627            )?;
628            let mut budget = NativeOutputBudget::for_decoded_channels(
629                retained_image_bytes,
630                decoded_image.decoded_components,
631                component_owner_capacity,
632            )?;
633            budget.include_elements::<u8>(data_len)?;
634            budget.include_color_space_clone(&self.color_space)?;
635
636            let color_space = try_clone_color_space(&self.color_space)?;
637            budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
638            let mut data = Vec::new();
639            try_resize_decode_elements(&mut data, data_len, 0_u8)?;
640            budget.include_capacity_overage::<u8>(data_len, data.capacity())?;
641            interleave_and_convert_region(
642                &mut decoded_image,
643                width as usize,
644                (0, 0, width, height),
645                &mut data,
646            );
647            let bitmap = Bitmap {
648                color_space,
649                data,
650                has_alpha: self.has_alpha,
651                width,
652                height,
653                original_bit_depth: self.original_bit_depth(),
654            };
655            NativeOutputBudget::validate_bitmap_pack(
656                retained_image_bytes,
657                decoded_image.decoded_components,
658                component_owner_capacity,
659                &bitmap,
660            )?;
661            Ok(bitmap)
662        })()
663    }
664
665    /// Decode the image into the given buffer.
666    ///
667    /// This method does the same as [`Image::decode`], but you can provide
668    /// a custom buffer for the output, as well as a decoder context. Doing
669    /// so allows the internal decode engine to reuse memory allocations, so
670    /// this is especially recommended if you plan on converting multiple
671    /// images in the same session.
672    ///
673    /// The buffer must have the correct size.
674    ///
675    /// # Errors
676    ///
677    /// Returns an error when decoding fails or `buf` is too small for the image.
678    pub fn decode_into(
679        &self,
680        buf: &mut [u8],
681        decoder_context: &mut DecoderContext<'a>,
682    ) -> Result<()> {
683        let mut decoded_image =
684            self.decode_image(decoder_context, None, None, self.retained_metadata_bytes()?)?;
685        validate_interleaved_output_buffer(&decoded_image, buf)?;
686        interleave_and_convert(&mut decoded_image, buf)?;
687
688        Ok(())
689    }
690
691    fn decode_image<'ctx>(
692        &self,
693        decoder_context: &'ctx mut DecoderContext<'a>,
694        output_region: Option<(u32, u32, u32, u32)>,
695        ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
696        retained_baseline_bytes: usize,
697    ) -> Result<DecodedImage<'ctx, '_>> {
698        let settings = &self.settings;
699        let mut ht_decoder = ht_decoder;
700        decoder_context.set_output_region(output_region);
701        let decode_result = j2c::decode(
702            self.codestream,
703            &self.header,
704            retained_baseline_bytes,
705            decoder_context,
706            &mut ht_decoder,
707        );
708        decoder_context.set_output_region(None);
709        decode_result?;
710        let mut decoded_image = DecodedImage {
711            decoded_components: &mut decoder_context.tile_decode_context.channel_data,
712            boxes: &self.boxes,
713        };
714
715        if settings.resolve_palette_indices {
716            let components = core::mem::take(decoded_image.decoded_components);
717            *decoded_image.decoded_components =
718                resolve_palette_indices(components, decoded_image.boxes, retained_baseline_bytes)?;
719        }
720
721        if let Some(cdef) = &decoded_image.boxes.channel_definition {
722            validate_and_reorder_channels(
723                cdef,
724                decoded_image.decoded_components,
725                retained_baseline_bytes,
726            )?;
727        }
728
729        let bit_depth = decoded_image
730            .decoded_components
731            .first()
732            .ok_or(DecodingError::CodeBlockDecodeFailure)?
733            .bit_depth;
734        convert_color_space(&mut decoded_image, bit_depth)?;
735        Ok(decoded_image)
736    }
737}