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, None)
233        } else if data.starts_with(CODESTREAM_MAGIC) {
234            j2c::parse(data, settings, None)
235        } else {
236            err!(FormatError::InvalidSignature)
237        }
238    }
239
240    /// Parse an image at an exact JPEG 2000 resolution reduction level.
241    ///
242    /// This low-level adapter exists for the `j2k` facade. A reduction of zero
243    /// preserves full resolution; each additional level halves both axes.
244    ///
245    /// # Errors
246    ///
247    /// Returns an error when a target-resolution hint is also configured, the
248    /// reduction is not representable, the codestream has too few resolution
249    /// levels, or the input is invalid.
250    #[doc(hidden)]
251    pub fn new_with_reduction(
252        data: &'a [u8],
253        settings: &DecodeSettings,
254        reduction_levels: u8,
255    ) -> Result<Self> {
256        if settings.target_resolution.is_some() {
257            return err!(DecodingError::UnsupportedFeature(
258                "exact reduction cannot be combined with a target-resolution hint",
259            ));
260        }
261        if data.starts_with(JP2_MAGIC) {
262            jp2::parse(data, *settings, Some(reduction_levels))
263        } else if data.starts_with(CODESTREAM_MAGIC) {
264            j2c::parse(data, settings, Some(reduction_levels))
265        } else {
266            err!(FormatError::InvalidSignature)
267        }
268    }
269
270    /// Parse an image while accounting already-live codec-owned allocations.
271    ///
272    /// This adapter is used when validation parses another image while an
273    /// encoded output and earlier parsed metadata remain live.
274    ///
275    /// # Errors
276    ///
277    /// Returns an error when the input is invalid or aggregate parser-owned
278    /// allocations exceed the native decode cap.
279    #[doc(hidden)]
280    pub fn new_with_retained_baseline(
281        data: &'a [u8],
282        settings: &DecodeSettings,
283        retained_baseline_bytes: usize,
284    ) -> Result<Self> {
285        if retained_baseline_bytes == 0 {
286            return Self::new(data, settings);
287        }
288        if data.starts_with(JP2_MAGIC) {
289            jp2::parse_with_retained_baseline(data, *settings, retained_baseline_bytes, None)
290        } else if data.starts_with(CODESTREAM_MAGIC) {
291            j2c::parse_with_retained_baseline(data, settings, retained_baseline_bytes, None)
292        } else {
293            err!(FormatError::InvalidSignature)
294        }
295    }
296
297    /// Whether the image has an alpha channel.
298    #[must_use]
299    pub fn has_alpha(&self) -> bool {
300        self.has_alpha
301    }
302
303    /// The color space of the image.
304    #[must_use]
305    pub fn color_space(&self) -> &ColorSpace {
306        &self.color_space
307    }
308
309    /// Return the primary JP2 restricted ICC profile, when present.
310    #[doc(hidden)]
311    #[must_use]
312    pub fn primary_icc_profile(&self) -> Option<&[u8]> {
313        match self
314            .boxes
315            .primary_color_specification()
316            .map(|specification| &specification.color_space)
317        {
318            Some(jp2::colr::ColorSpace::Icc(profile)) => Some(profile),
319            _ => None,
320        }
321    }
322
323    /// The width of the image.
324    #[must_use]
325    pub fn width(&self) -> u32 {
326        self.header.size_data.image_width()
327    }
328
329    /// The height of the image.
330    #[must_use]
331    pub fn height(&self) -> u32 {
332        self.header.size_data.image_height()
333    }
334
335    /// The original bit depth of the image. You usually don't need to do anything
336    /// with this parameter, it just exists for informational purposes.
337    #[must_use]
338    pub fn original_bit_depth(&self) -> u8 {
339        // Note that this only works if all components have the same precision.
340        self.header.component_infos[0].size_info.precision
341    }
342
343    /// Whether decode finishes with additional host-side component mutation or reordering.
344    #[doc(hidden)]
345    #[must_use]
346    pub fn supports_direct_device_plane_reuse(&self) -> bool {
347        if self.settings.resolve_palette_indices && self.boxes.palette.is_some() {
348            return false;
349        }
350        if self.boxes.channel_definition.is_some() {
351            return false;
352        }
353        !matches!(
354            self.boxes
355                .primary_color_specification()
356                .map(|spec| &spec.color_space),
357            Some(jp2::colr::ColorSpace::Enumerated(
358                EnumeratedColorspace::Sycc | EnumeratedColorspace::CieLab(_)
359            ))
360        )
361    }
362
363    /// Decode the image and return its decoded result as a `Vec<u8>`, with each
364    /// channel interleaved.
365    ///
366    /// # Errors
367    ///
368    /// Returns an error when image validation, decoding, or output allocation fails.
369    pub fn decode(&self) -> Result<Vec<u8>> {
370        let bitmap = self.decode_with_context(&mut DecoderContext::default())?;
371        Ok(bitmap.data)
372    }
373
374    /// Decode the image and return its decoded result using a caller-provided
375    /// decoder context so allocations can be reused across repeated decodes.
376    ///
377    /// # Errors
378    ///
379    /// Returns an error when image validation, decoding, or output allocation fails.
380    pub fn decode_with_context(&self, decoder_context: &mut DecoderContext<'a>) -> Result<Bitmap> {
381        (|| {
382            let retained_image_bytes = self.retained_metadata_bytes()?;
383            let mut decoded_image =
384                self.decode_image(decoder_context, None, None, retained_image_bytes)?;
385            let component_owner_capacity = decoded_image.decoded_components.capacity();
386            let buffer_size = checked_decode_byte_len3(
387                self.width() as usize,
388                self.height() as usize,
389                decoded_image.decoded_components.len(),
390            )?;
391            let mut budget = NativeOutputBudget::for_decoded_channels(
392                retained_image_bytes,
393                decoded_image.decoded_components,
394                component_owner_capacity,
395            )?;
396            budget.include_elements::<u8>(buffer_size)?;
397            budget.include_color_space_clone(&self.color_space)?;
398
399            let color_space = try_clone_color_space(&self.color_space)?;
400            budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
401            let mut data = Vec::new();
402            try_resize_decode_elements(&mut data, buffer_size, 0_u8)?;
403            budget.include_capacity_overage::<u8>(buffer_size, data.capacity())?;
404            validate_interleaved_output_buffer(&decoded_image, &data)?;
405            interleave_and_convert(&mut decoded_image, &mut data)?;
406            let bitmap = Bitmap {
407                color_space,
408                data,
409                has_alpha: self.has_alpha,
410                width: self.width(),
411                height: self.height(),
412                original_bit_depth: self.original_bit_depth(),
413            };
414            NativeOutputBudget::validate_bitmap_pack(
415                retained_image_bytes,
416                decoded_image.decoded_components,
417                component_owner_capacity,
418                &bitmap,
419            )?;
420            Ok(bitmap)
421        })()
422    }
423
424    /// Decode the image into borrowed component planes using a caller-provided
425    /// decoder context so allocations can be reused across repeated decodes.
426    ///
427    /// # Errors
428    ///
429    /// Returns an error when component precision is unsupported or decoding fails.
430    pub fn decode_components_with_context<'ctx>(
431        &self,
432        decoder_context: &'ctx mut DecoderContext<'a>,
433    ) -> Result<DecodedComponents<'ctx>> {
434        self.validate_component_plane_precision()?;
435        let decoded_image =
436            self.decode_image(decoder_context, None, None, self.retained_metadata_bytes()?)?;
437        let DecodedImage {
438            decoded_components,
439            boxes: _,
440        } = decoded_image;
441        self.try_borrow_component_planes(
442            decoded_components.as_slice(),
443            decoded_components.capacity(),
444            (self.width(), self.height()),
445        )
446    }
447
448    /// Decode the image into owned native-bit-depth component planes.
449    ///
450    /// Unlike [`Self::decode_native`], this preserves per-component bit depth
451    /// and signedness metadata and does not require all components to share a
452    /// single packed interleaved representation.
453    ///
454    /// # Errors
455    ///
456    /// Returns an error when validation, decoding, or native sample packing fails.
457    pub fn decode_native_components(&self) -> Result<DecodedNativeComponents> {
458        let mut decoder_context = DecoderContext::default();
459        self.decode_native_components_with_context(&mut decoder_context)
460    }
461
462    /// Decode owned native component planes while accounting an already-live
463    /// external allocation, such as the encoded `Vec` being validated.
464    ///
465    /// `retained_capacity` must be the allocator capacity of that external
466    /// owner, not merely its logical length.
467    ///
468    /// # Errors
469    ///
470    /// Returns an error when aggregate retained allocation accounting,
471    /// decoding, or native component packing fails.
472    #[doc(hidden)]
473    pub fn decode_native_components_with_retained_capacity(
474        &self,
475        retained_capacity: usize,
476    ) -> Result<DecodedNativeComponents> {
477        let retained_baseline_bytes =
478            allocation::combine_retained_bytes(retained_capacity, self.retained_metadata_bytes()?)?;
479        let mut decoder_context = DecoderContext::default();
480        self.decode_native_components_with_context_and_retained_baseline(
481            &mut decoder_context,
482            retained_baseline_bytes,
483        )
484    }
485
486    /// Decode the image into owned native-bit-depth component planes using a
487    /// caller-provided decoder context.
488    ///
489    /// # Errors
490    ///
491    /// Returns an error when validation, decoding, or native sample packing fails.
492    pub fn decode_native_components_with_context(
493        &self,
494        decoder_context: &mut DecoderContext<'a>,
495    ) -> Result<DecodedNativeComponents> {
496        let retained_baseline_bytes = self.retained_metadata_bytes()?;
497        self.decode_native_components_with_context_and_retained_baseline(
498            decoder_context,
499            retained_baseline_bytes,
500        )
501    }
502
503    fn decode_native_components_with_context_and_retained_baseline(
504        &self,
505        decoder_context: &mut DecoderContext<'a>,
506        retained_baseline_bytes: usize,
507    ) -> Result<DecodedNativeComponents> {
508        let decoded_image =
509            self.decode_image(decoder_context, None, None, retained_baseline_bytes)?;
510        let DecodedImage {
511            decoded_components,
512            boxes: _,
513        } = decoded_image;
514        let component_owner_capacity = decoded_components.capacity();
515        self.pack_native_component_planes(
516            decoded_components,
517            component_owner_capacity,
518            (self.width(), self.height()),
519            retained_baseline_bytes,
520        )
521    }
522
523    /// Decode borrowed component planes while delegating HTJ2K code-block decode.
524    #[doc(hidden)]
525    pub fn decode_components_with_ht_decoder<'ctx>(
526        &self,
527        decoder_context: &'ctx mut DecoderContext<'a>,
528        ht_decoder: &mut dyn HtCodeBlockDecoder,
529    ) -> Result<DecodedComponents<'ctx>> {
530        self.validate_component_plane_precision()?;
531        let decoded_image = self.decode_image(
532            decoder_context,
533            None,
534            Some(ht_decoder),
535            self.retained_metadata_bytes()?,
536        )?;
537        let DecodedImage {
538            decoded_components,
539            boxes: _,
540        } = decoded_image;
541        self.try_borrow_component_planes(
542            decoded_components.as_slice(),
543            decoded_components.capacity(),
544            (self.width(), self.height()),
545        )
546    }
547
548    /// Decode borrowed component planes for a requested region using a
549    /// caller-provided decoder context.
550    ///
551    /// # Errors
552    ///
553    /// Returns an error when the region is invalid, precision is unsupported, or decoding fails.
554    pub fn decode_region_components_with_context<'ctx>(
555        &self,
556        roi: (u32, u32, u32, u32),
557        decoder_context: &'ctx mut DecoderContext<'a>,
558    ) -> Result<DecodedComponents<'ctx>> {
559        validate_roi((self.width(), self.height()), roi)?;
560        self.validate_component_plane_precision()?;
561        let (_x, _y, width, height) = roi;
562        let decoded_image = self.decode_image(
563            decoder_context,
564            Some(roi),
565            None,
566            self.retained_metadata_bytes()?,
567        )?;
568        let DecodedImage {
569            decoded_components,
570            boxes: _,
571        } = decoded_image;
572        self.try_borrow_component_planes(
573            decoded_components.as_slice(),
574            decoded_components.capacity(),
575            (width, height),
576        )
577    }
578
579    /// Decode a source-coordinate region into owned native-bit-depth component
580    /// planes using a caller-provided decoder context.
581    ///
582    /// # Errors
583    ///
584    /// Returns an error when the region is invalid or decoding and packing fail.
585    pub fn decode_native_region_components_with_context(
586        &self,
587        roi: (u32, u32, u32, u32),
588        decoder_context: &mut DecoderContext<'a>,
589    ) -> Result<DecodedNativeComponents> {
590        validate_roi((self.width(), self.height()), roi)?;
591        if self.requires_exact_integer_decode() {
592            return self.decode_native_region_components_via_full_decode(roi, decoder_context);
593        }
594        let (_x, _y, width, height) = roi;
595        let retained_image_bytes = self.retained_metadata_bytes()?;
596        let decoded_image =
597            self.decode_image(decoder_context, Some(roi), None, retained_image_bytes)?;
598        let DecodedImage {
599            decoded_components,
600            boxes: _,
601        } = decoded_image;
602        let component_owner_capacity = decoded_components.capacity();
603        self.pack_native_component_planes(
604            decoded_components,
605            component_owner_capacity,
606            (width, height),
607            retained_image_bytes,
608        )
609    }
610
611    /// Decode borrowed component planes for a requested region while
612    /// delegating code-block/transform stages through the adapter backend hook.
613    #[doc(hidden)]
614    pub fn decode_region_components_with_ht_decoder<'ctx>(
615        &self,
616        decoder_context: &'ctx mut DecoderContext<'a>,
617        roi: (u32, u32, u32, u32),
618        ht_decoder: &mut dyn HtCodeBlockDecoder,
619    ) -> Result<DecodedComponents<'ctx>> {
620        validate_roi((self.width(), self.height()), roi)?;
621        self.validate_component_plane_precision()?;
622        let (_x, _y, width, height) = roi;
623        let decoded_image = self.decode_image(
624            decoder_context,
625            Some(roi),
626            Some(ht_decoder),
627            self.retained_metadata_bytes()?,
628        )?;
629        let DecodedImage {
630            decoded_components,
631            boxes: _,
632        } = decoded_image;
633        self.try_borrow_component_planes(
634            decoded_components.as_slice(),
635            decoded_components.capacity(),
636            (width, height),
637        )
638    }
639
640    /// Decode a region of the image and return it as an 8-bit interleaved bitmap.
641    ///
642    /// # Errors
643    ///
644    /// Returns an error when the region is invalid or decoding fails.
645    pub fn decode_region(&self, roi: (u32, u32, u32, u32)) -> Result<Bitmap> {
646        self.decode_region_with_context(roi, &mut DecoderContext::default())
647    }
648
649    /// Decode a region of the image and return it as an 8-bit interleaved bitmap
650    /// using a caller-provided decoder context.
651    ///
652    /// # Errors
653    ///
654    /// Returns an error when the region is invalid, decoding fails, or output sizing overflows.
655    pub fn decode_region_with_context(
656        &self,
657        roi: (u32, u32, u32, u32),
658        decoder_context: &mut DecoderContext<'a>,
659    ) -> Result<Bitmap> {
660        validate_roi((self.width(), self.height()), roi)?;
661        (|| {
662            let retained_image_bytes = self.retained_metadata_bytes()?;
663            let mut decoded_image =
664                self.decode_image(decoder_context, Some(roi), None, retained_image_bytes)?;
665            let component_owner_capacity = decoded_image.decoded_components.capacity();
666            let (_x, _y, width, height) = roi;
667            let data_len = checked_decode_byte_len3(
668                width as usize,
669                height as usize,
670                decoded_image.decoded_components.len(),
671            )?;
672            let mut budget = NativeOutputBudget::for_decoded_channels(
673                retained_image_bytes,
674                decoded_image.decoded_components,
675                component_owner_capacity,
676            )?;
677            budget.include_elements::<u8>(data_len)?;
678            budget.include_color_space_clone(&self.color_space)?;
679
680            let color_space = try_clone_color_space(&self.color_space)?;
681            budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
682            let mut data = Vec::new();
683            try_resize_decode_elements(&mut data, data_len, 0_u8)?;
684            budget.include_capacity_overage::<u8>(data_len, data.capacity())?;
685            interleave_and_convert_region(
686                &mut decoded_image,
687                width as usize,
688                (0, 0, width, height),
689                &mut data,
690            );
691            let bitmap = Bitmap {
692                color_space,
693                data,
694                has_alpha: self.has_alpha,
695                width,
696                height,
697                original_bit_depth: self.original_bit_depth(),
698            };
699            NativeOutputBudget::validate_bitmap_pack(
700                retained_image_bytes,
701                decoded_image.decoded_components,
702                component_owner_capacity,
703                &bitmap,
704            )?;
705            Ok(bitmap)
706        })()
707    }
708
709    /// Decode the image into the given buffer.
710    ///
711    /// This method does the same as [`Image::decode`], but you can provide
712    /// a custom buffer for the output, as well as a decoder context. Doing
713    /// so allows the internal decode engine to reuse memory allocations, so
714    /// this is especially recommended if you plan on converting multiple
715    /// images in the same session.
716    ///
717    /// The buffer must have the correct size.
718    ///
719    /// # Errors
720    ///
721    /// Returns an error when decoding fails or `buf` is too small for the image.
722    pub fn decode_into(
723        &self,
724        buf: &mut [u8],
725        decoder_context: &mut DecoderContext<'a>,
726    ) -> Result<()> {
727        let mut decoded_image =
728            self.decode_image(decoder_context, None, None, self.retained_metadata_bytes()?)?;
729        validate_interleaved_output_buffer(&decoded_image, buf)?;
730        interleave_and_convert(&mut decoded_image, buf)?;
731
732        Ok(())
733    }
734
735    fn decode_image<'ctx>(
736        &self,
737        decoder_context: &'ctx mut DecoderContext<'a>,
738        output_region: Option<(u32, u32, u32, u32)>,
739        ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
740        retained_baseline_bytes: usize,
741    ) -> Result<DecodedImage<'ctx, '_>> {
742        let settings = &self.settings;
743        let mut ht_decoder = ht_decoder;
744        decoder_context.set_output_region(output_region);
745        let decode_result = j2c::decode(
746            self.codestream,
747            &self.header,
748            retained_baseline_bytes,
749            decoder_context,
750            &mut ht_decoder,
751        );
752        decoder_context.set_output_region(None);
753        decode_result?;
754        let mut decoded_image = DecodedImage {
755            decoded_components: &mut decoder_context.tile_decode_context.channel_data,
756            boxes: &self.boxes,
757        };
758
759        if settings.resolve_palette_indices {
760            let components = core::mem::take(decoded_image.decoded_components);
761            *decoded_image.decoded_components =
762                resolve_palette_indices(components, decoded_image.boxes, retained_baseline_bytes)?;
763        }
764
765        if let Some(cdef) = &decoded_image.boxes.channel_definition {
766            validate_and_reorder_channels(
767                cdef,
768                decoded_image.decoded_components,
769                retained_baseline_bytes,
770            )?;
771        }
772
773        let bit_depth = decoded_image
774            .decoded_components
775            .first()
776            .ok_or(DecodingError::CodeBlockDecodeFailure)?
777            .bit_depth;
778        convert_color_space(&mut decoded_image, bit_depth)?;
779        Ok(decoded_image)
780    }
781}