Skip to main content

j2k_native/image/
output_api.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Native, coefficient, and borrowed-component decode output surfaces.
4
5use alloc::vec::Vec;
6
7use crate::color::{ComponentPlane, DecodedComponents, DecodedNativeComponents, RawBitmap};
8use crate::error::{bail, DecodingError, Result, ValidationError};
9use crate::j2c::{self, ComponentData, DecoderContext, Reversible53CoefficientImage};
10use crate::jp2::DecodedImage;
11use crate::{
12    checked_decode_byte_len3, checked_decode_byte_len4, checked_decode_sample_count,
13    native_bytes_per_sample, try_reserve_decode_elements, validate_roi, HtCodeBlockDecoder,
14};
15
16use super::native::{try_clone_color_space, NativeOutputBudget};
17use super::Image;
18
19impl<'a> Image<'a> {
20    /// Decode the image at native bit depth without scaling to 8-bit.
21    ///
22    /// For images with bit depth ≤ 8, returns pixel data as `Vec<u8>`.
23    /// For images with bit depth > 8 (e.g., 12-bit or 16-bit), returns
24    /// pixel data as little-endian `u16` values packed into `Vec<u8>`.
25    ///
26    /// This is essential for medical imaging (DICOM) where 12-bit and 16-bit
27    /// images must preserve their full dynamic range.
28    ///
29    /// # Errors
30    ///
31    /// Returns an error when decoding or native sample packing fails.
32    pub fn decode_native(&self) -> Result<RawBitmap> {
33        let mut decoder_context = DecoderContext::default();
34        self.decode_native_with_context(&mut decoder_context)
35    }
36
37    /// Decode at native bit depth while accounting an already-live external
38    /// allocation, such as the encoded `Vec` being round-trip validated.
39    ///
40    /// `retained_capacity` must be the allocator capacity of that external
41    /// owner, not merely its logical length.
42    ///
43    /// # Errors
44    ///
45    /// Returns an error when aggregate retained allocation accounting,
46    /// decoding, sizing, or native sample packing fails.
47    #[doc(hidden)]
48    pub fn decode_native_with_retained_capacity(
49        &self,
50        retained_capacity: usize,
51    ) -> Result<RawBitmap> {
52        let retained_baseline_bytes = super::allocation::combine_retained_bytes(
53            retained_capacity,
54            self.retained_metadata_bytes()?,
55        )?;
56        let mut decoder_context = DecoderContext::default();
57        self.decode_native_with_context_and_retained_baseline(
58            &mut decoder_context,
59            retained_baseline_bytes,
60        )
61    }
62
63    /// Extract reversible 5/3 wavelet coefficients for coefficient-domain
64    /// classic JPEG 2000 to HTJ2K recoding.
65    ///
66    /// This decodes classic Tier-1 code-blocks into dequantized reversible
67    /// wavelet coefficients, but does not run inverse DWT or color conversion.
68    #[doc(hidden)]
69    pub fn decode_reversible_53_coefficients(&self) -> Result<Reversible53CoefficientImage> {
70        let mut decoder_context = DecoderContext::default();
71        self.decode_reversible_53_coefficients_with_context(&mut decoder_context)
72    }
73
74    /// Extract reversible 5/3 wavelet coefficients using a caller-provided
75    /// decoder context.
76    #[doc(hidden)]
77    pub fn decode_reversible_53_coefficients_with_context(
78        &self,
79        decoder_context: &mut DecoderContext<'a>,
80    ) -> Result<Reversible53CoefficientImage> {
81        j2c::recode::extract_reversible_53_coefficients(
82            self.codestream,
83            &self.header,
84            self.retained_metadata_bytes()?,
85            decoder_context,
86        )
87    }
88
89    /// Decode a region of the image at native bit depth.
90    ///
91    /// # Errors
92    ///
93    /// Returns an error when the region is invalid or decoding and packing fail.
94    pub fn decode_native_region(&self, roi: (u32, u32, u32, u32)) -> Result<RawBitmap> {
95        self.decode_native_region_with_context(roi, &mut DecoderContext::default())
96    }
97
98    /// Decode a source-coordinate region into owned native-bit-depth component
99    /// planes.
100    ///
101    /// # Errors
102    ///
103    /// Returns an error when the region is invalid or decoding and packing fail.
104    pub fn decode_native_region_components(
105        &self,
106        roi: (u32, u32, u32, u32),
107    ) -> Result<DecodedNativeComponents> {
108        self.decode_native_region_components_with_context(roi, &mut DecoderContext::default())
109    }
110
111    /// Decode the image at native bit depth using a caller-provided decoder
112    /// context so allocations can be reused across repeated decodes.
113    ///
114    /// # Errors
115    ///
116    /// Returns an error when decoding, sizing, or native sample packing fails.
117    pub fn decode_native_with_context(
118        &self,
119        decoder_context: &mut DecoderContext<'a>,
120    ) -> Result<RawBitmap> {
121        let retained_baseline_bytes = self.retained_metadata_bytes()?;
122        self.decode_native_with_context_and_retained_baseline(
123            decoder_context,
124            retained_baseline_bytes,
125        )
126    }
127
128    pub(crate) fn decode_native_with_context_and_retained_baseline(
129        &self,
130        decoder_context: &mut DecoderContext<'a>,
131        retained_baseline_bytes: usize,
132    ) -> Result<RawBitmap> {
133        let bit_depth = self.uniform_header_bit_depth()?;
134        let mut ht_decoder = None;
135        decoder_context.set_output_region(None);
136        decoder_context.set_round_irreversible_output(true);
137        let decode_result = j2c::decode(
138            self.codestream,
139            &self.header,
140            retained_baseline_bytes,
141            decoder_context,
142            &mut ht_decoder,
143        );
144        decoder_context.set_output_region(None);
145        decoder_context.set_round_irreversible_output(false);
146        decode_result?;
147
148        let components = &decoder_context.tile_decode_context.channel_data;
149        let component_owner_capacity = components.capacity();
150        let num_components =
151            u16::try_from(components.len()).map_err(|_| ValidationError::TooManyChannels)?;
152        let width = self.width();
153        let height = self.height();
154        let pixel_count = checked_decode_sample_count(width, height)?;
155        let bytes_per_sample = native_bytes_per_sample(bit_depth)?;
156        let capacity =
157            checked_decode_byte_len3(pixel_count, usize::from(num_components), bytes_per_sample)?;
158        let mut budget = NativeOutputBudget::for_decoded_channels(
159            retained_baseline_bytes,
160            components,
161            component_owner_capacity,
162        )?;
163        budget.include_bit_capacity(components.len())?;
164        budget.include_elements::<u8>(capacity)?;
165        let component_signed = Self::try_component_signedness(components)?;
166        budget.include_bit_capacity_overage(components.len(), component_signed.capacity())?;
167        let signed = component_signed.iter().all(|signed| *signed);
168        let mut data = Vec::new();
169        try_reserve_decode_elements(&mut data, capacity)?;
170        budget.include_capacity_overage::<u8>(capacity, data.capacity())?;
171        for index in 0..pixel_count {
172            for component in components {
173                Self::push_component_native_sample_bytes(&mut data, component, index, bit_depth);
174            }
175        }
176        if data.len() != capacity {
177            bail!(DecodingError::CodeBlockDecodeFailure);
178        }
179        let bitmap = RawBitmap {
180            data,
181            width,
182            height,
183            bit_depth,
184            signed,
185            component_signed,
186            num_components,
187            bytes_per_sample: u8::try_from(bytes_per_sample)
188                .map_err(|_| ValidationError::ImageTooLarge)?,
189        };
190        NativeOutputBudget::validate_raw_pack(
191            retained_baseline_bytes,
192            components,
193            component_owner_capacity,
194            &bitmap,
195        )?;
196        Ok(bitmap)
197    }
198
199    /// Decode a region of the image at native bit depth using a caller-provided
200    /// decoder context.
201    ///
202    /// # Errors
203    ///
204    /// Returns an error when the region is invalid or decoding, sizing, and packing fail.
205    pub fn decode_native_region_with_context(
206        &self,
207        roi: (u32, u32, u32, u32),
208        decoder_context: &mut DecoderContext<'a>,
209    ) -> Result<RawBitmap> {
210        validate_roi((self.width(), self.height()), roi)?;
211        if self.requires_exact_integer_decode() {
212            return self.decode_native_region_via_full_decode(roi, decoder_context);
213        }
214        let bit_depth = self.uniform_header_bit_depth()?;
215        let retained_image_bytes = self.retained_metadata_bytes()?;
216        let mut ht_decoder = None;
217        decoder_context.set_output_region(Some(roi));
218        decoder_context.set_round_irreversible_output(true);
219        let decode_result = j2c::decode(
220            self.codestream,
221            &self.header,
222            retained_image_bytes,
223            decoder_context,
224            &mut ht_decoder,
225        );
226        decoder_context.set_output_region(None);
227        decoder_context.set_round_irreversible_output(false);
228        decode_result?;
229
230        let components = &decoder_context.tile_decode_context.channel_data;
231        let component_owner_capacity = components.capacity();
232        let num_components =
233            u16::try_from(components.len()).map_err(|_| ValidationError::TooManyChannels)?;
234        let bytes_per_sample = native_bytes_per_sample(bit_depth)?;
235        let (_x, _y, width, height) = roi;
236        let capacity = checked_decode_byte_len4(
237            width as usize,
238            height as usize,
239            usize::from(num_components),
240            bytes_per_sample,
241        )?;
242        let mut budget = NativeOutputBudget::for_decoded_channels(
243            retained_image_bytes,
244            components,
245            component_owner_capacity,
246        )?;
247        budget.include_bit_capacity(components.len())?;
248        budget.include_elements::<u8>(capacity)?;
249        let mut data = Vec::new();
250        let component_signed = Self::try_component_signedness(components)?;
251        budget.include_bit_capacity_overage(components.len(), component_signed.capacity())?;
252        try_reserve_decode_elements(&mut data, capacity)?;
253        budget.include_capacity_overage::<u8>(capacity, data.capacity())?;
254        let signed = component_signed.iter().all(|signed| *signed);
255
256        for row in 0..height as usize {
257            for col in 0..width as usize {
258                let index = row * width as usize + col;
259                for component in components {
260                    Self::push_component_native_sample_bytes(
261                        &mut data, component, index, bit_depth,
262                    );
263                }
264            }
265        }
266        if data.len() != capacity {
267            bail!(DecodingError::CodeBlockDecodeFailure);
268        }
269
270        let bitmap = RawBitmap {
271            data,
272            width,
273            height,
274            bit_depth,
275            signed,
276            component_signed,
277            num_components,
278            bytes_per_sample: u8::try_from(bytes_per_sample)
279                .map_err(|_| ValidationError::ImageTooLarge)?,
280        };
281        NativeOutputBudget::validate_raw_pack(
282            retained_image_bytes,
283            components,
284            component_owner_capacity,
285            &bitmap,
286        )?;
287        Ok(bitmap)
288    }
289
290    fn try_component_signedness(components: &[ComponentData]) -> Result<Vec<bool>> {
291        let mut signedness = Vec::new();
292        try_reserve_decode_elements(&mut signedness, components.len())?;
293        signedness.extend(components.iter().map(|component| component.signed));
294        Ok(signedness)
295    }
296
297    pub(super) fn component_plane_sampling_at(&self, component_idx: usize) -> (u8, u8) {
298        if self.settings.resolve_palette_indices && self.boxes.palette.is_some() {
299            return (1, 1);
300        }
301        self.header
302            .component_infos
303            .get(component_idx)
304            .map_or((1, 1), |component| {
305                (
306                    component.size_info.horizontal_resolution,
307                    component.size_info.vertical_resolution,
308                )
309            })
310    }
311
312    pub(super) fn try_borrow_component_planes<'ctx>(
313        &self,
314        components: &'ctx [ComponentData],
315        component_owner_capacity: usize,
316        dimensions: (u32, u32),
317    ) -> Result<DecodedComponents<'ctx>> {
318        let retained_image_bytes = self.retained_metadata_bytes()?;
319        self.try_borrow_component_planes_with_retained_baseline(
320            components,
321            component_owner_capacity,
322            dimensions,
323            retained_image_bytes,
324        )
325    }
326
327    pub(super) fn try_borrow_component_planes_with_retained_baseline<'ctx>(
328        &self,
329        components: &'ctx [ComponentData],
330        component_owner_capacity: usize,
331        dimensions: (u32, u32),
332        retained_image_bytes: usize,
333    ) -> Result<DecodedComponents<'ctx>> {
334        let mut budget = NativeOutputBudget::for_decoded_channels(
335            retained_image_bytes,
336            components,
337            component_owner_capacity,
338        )?;
339        budget.include_elements::<ComponentPlane<'_>>(components.len())?;
340        budget.include_color_space_clone(&self.color_space)?;
341        let color_space = try_clone_color_space(&self.color_space)?;
342        budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
343        let mut planes = Vec::new();
344        try_reserve_decode_elements(&mut planes, components.len())?;
345        budget
346            .include_capacity_overage::<ComponentPlane<'_>>(components.len(), planes.capacity())?;
347        for (component_idx, component) in components.iter().enumerate() {
348            planes.push(ComponentPlane {
349                samples: component.container.truncated(),
350                dimensions,
351                bit_depth: component.bit_depth,
352                signed: component.signed,
353                sampling: self.component_plane_sampling_at(component_idx),
354            });
355        }
356
357        let mut packed = DecodedComponents {
358            dimensions,
359            color_space,
360            has_alpha: self.has_alpha,
361            planes,
362            live_bytes: 0,
363        };
364        packed.live_bytes = NativeOutputBudget::validate_borrowed_pack(
365            retained_image_bytes,
366            components,
367            component_owner_capacity,
368            &packed,
369        )?;
370        Ok(packed)
371    }
372
373    fn uniform_header_bit_depth(&self) -> Result<u8> {
374        let Some(first) = self.header.component_infos.first() else {
375            bail!(DecodingError::CodeBlockDecodeFailure);
376        };
377        if self
378            .header
379            .component_infos
380            .iter()
381            .any(|component| component.size_info.precision != first.size_info.precision)
382        {
383            bail!(DecodingError::UnsupportedFeature(
384                "decode_native requires uniform component bit depths; use decode_components for mixed-depth images"
385            ));
386        }
387        if first.size_info.precision > 38 {
388            bail!(DecodingError::UnsupportedFeature(
389                "decode_native supports JPEG 2000 Part 1 component precision up to 38 bits"
390            ));
391        }
392        Ok(first.size_info.precision)
393    }
394
395    pub(super) fn validate_component_plane_precision(&self) -> Result<()> {
396        if self
397            .header
398            .component_infos
399            .iter()
400            .any(|component| component.size_info.precision > 24)
401        {
402            bail!(DecodingError::UnsupportedFeature(
403                "decode_components currently supports component planes up to 24 bits per component"
404            ));
405        }
406        Ok(())
407    }
408}
409
410/// Borrowed component planes rounded for integer output.
411///
412/// The plain component-plane methods return unrounded irreversible (9/7)
413/// samples with the unsigned level shift already added. Rounding those planes
414/// afterwards differs from [`Image::decode`] on exact ties, because adding the
415/// shift costs one bit of f32 precision. These methods round each centered
416/// sample ties-to-even before the shift, as the interleaved decode paths do, so
417/// converting their planes to integer samples reproduces that output.
418/// Reversible (5/3) samples are unchanged.
419impl<'a> Image<'a> {
420    /// Rounded-for-integer-output counterpart of
421    /// [`Self::decode_components_with_context`].
422    ///
423    /// # Errors
424    ///
425    /// Returns an error when component precision is unsupported or decoding fails.
426    #[doc(hidden)]
427    pub fn decode_components_for_integer_output_with_context<'ctx>(
428        &self,
429        decoder_context: &'ctx mut DecoderContext<'a>,
430    ) -> Result<DecodedComponents<'ctx>> {
431        self.decode_borrowed_component_planes(decoder_context, None, None, true)
432    }
433
434    /// Rounded-for-integer-output counterpart of
435    /// [`Self::decode_region_components_with_context`].
436    ///
437    /// # Errors
438    ///
439    /// Returns an error when the region is invalid, precision is unsupported, or decoding fails.
440    #[doc(hidden)]
441    pub fn decode_region_components_for_integer_output_with_context<'ctx>(
442        &self,
443        roi: (u32, u32, u32, u32),
444        decoder_context: &'ctx mut DecoderContext<'a>,
445    ) -> Result<DecodedComponents<'ctx>> {
446        self.decode_borrowed_component_planes(decoder_context, Some(roi), None, true)
447    }
448
449    /// Rounded-for-integer-output counterpart of
450    /// [`Self::decode_components_with_ht_decoder`].
451    ///
452    /// # Errors
453    ///
454    /// Returns an error when component precision is unsupported or decoding fails.
455    #[doc(hidden)]
456    pub fn decode_components_for_integer_output_with_ht_decoder<'ctx>(
457        &self,
458        decoder_context: &'ctx mut DecoderContext<'a>,
459        ht_decoder: &mut dyn HtCodeBlockDecoder,
460    ) -> Result<DecodedComponents<'ctx>> {
461        self.decode_borrowed_component_planes(decoder_context, None, Some(ht_decoder), true)
462    }
463
464    /// Rounded-for-integer-output counterpart of
465    /// [`Self::decode_region_components_with_ht_decoder`].
466    ///
467    /// # Errors
468    ///
469    /// Returns an error when the region is invalid, precision is unsupported, or decoding fails.
470    #[doc(hidden)]
471    pub fn decode_region_components_for_integer_output_with_ht_decoder<'ctx>(
472        &self,
473        decoder_context: &'ctx mut DecoderContext<'a>,
474        roi: (u32, u32, u32, u32),
475        ht_decoder: &mut dyn HtCodeBlockDecoder,
476    ) -> Result<DecodedComponents<'ctx>> {
477        self.decode_borrowed_component_planes(decoder_context, Some(roi), Some(ht_decoder), true)
478    }
479
480    pub(super) fn decode_borrowed_component_planes<'ctx>(
481        &self,
482        decoder_context: &'ctx mut DecoderContext<'a>,
483        roi: Option<(u32, u32, u32, u32)>,
484        ht_decoder: Option<&mut dyn HtCodeBlockDecoder>,
485        round_irreversible_output: bool,
486    ) -> Result<DecodedComponents<'ctx>> {
487        let dimensions = match roi {
488            Some(roi) => {
489                validate_roi((self.width(), self.height()), roi)?;
490                (roi.2, roi.3)
491            }
492            None => (self.width(), self.height()),
493        };
494        self.validate_component_plane_precision()?;
495        let decoded_image = self.decode_image(
496            decoder_context,
497            roi,
498            ht_decoder,
499            round_irreversible_output,
500            self.retained_metadata_bytes()?,
501        )?;
502        let DecodedImage {
503            decoded_components,
504            boxes: _,
505        } = decoded_image;
506        self.try_borrow_component_planes(
507            decoded_components.as_slice(),
508            decoded_components.capacity(),
509            dimensions,
510        )
511    }
512}