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