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::{
11    checked_decode_byte_len3, checked_decode_byte_len4, checked_decode_sample_count,
12    native_bytes_per_sample, try_reserve_decode_elements, validate_roi,
13};
14
15use super::native::{try_clone_color_space, NativeOutputBudget};
16use super::Image;
17
18impl<'a> Image<'a> {
19    /// Decode the image at native bit depth without scaling to 8-bit.
20    ///
21    /// For images with bit depth ≤ 8, returns pixel data as `Vec<u8>`.
22    /// For images with bit depth > 8 (e.g., 12-bit or 16-bit), returns
23    /// pixel data as little-endian `u16` values packed into `Vec<u8>`.
24    ///
25    /// This is essential for medical imaging (DICOM) where 12-bit and 16-bit
26    /// images must preserve their full dynamic range.
27    ///
28    /// # Errors
29    ///
30    /// Returns an error when decoding or native sample packing fails.
31    pub fn decode_native(&self) -> Result<RawBitmap> {
32        let mut decoder_context = DecoderContext::default();
33        self.decode_native_with_context(&mut decoder_context)
34    }
35
36    /// Decode at native bit depth while accounting an already-live external
37    /// allocation, such as the encoded `Vec` being round-trip validated.
38    ///
39    /// `retained_capacity` must be the allocator capacity of that external
40    /// owner, not merely its logical length.
41    ///
42    /// # Errors
43    ///
44    /// Returns an error when aggregate retained allocation accounting,
45    /// decoding, sizing, or native sample packing fails.
46    #[doc(hidden)]
47    pub fn decode_native_with_retained_capacity(
48        &self,
49        retained_capacity: usize,
50    ) -> Result<RawBitmap> {
51        let retained_baseline_bytes = super::allocation::combine_retained_bytes(
52            retained_capacity,
53            self.retained_metadata_bytes()?,
54        )?;
55        let mut decoder_context = DecoderContext::default();
56        self.decode_native_with_context_and_retained_baseline(
57            &mut decoder_context,
58            retained_baseline_bytes,
59        )
60    }
61
62    /// Extract reversible 5/3 wavelet coefficients for coefficient-domain
63    /// classic JPEG 2000 to HTJ2K recoding.
64    ///
65    /// This decodes classic Tier-1 code-blocks into dequantized reversible
66    /// wavelet coefficients, but does not run inverse DWT or color conversion.
67    #[doc(hidden)]
68    pub fn decode_reversible_53_coefficients(&self) -> Result<Reversible53CoefficientImage> {
69        let mut decoder_context = DecoderContext::default();
70        self.decode_reversible_53_coefficients_with_context(&mut decoder_context)
71    }
72
73    /// Extract reversible 5/3 wavelet coefficients using a caller-provided
74    /// decoder context.
75    #[doc(hidden)]
76    pub fn decode_reversible_53_coefficients_with_context(
77        &self,
78        decoder_context: &mut DecoderContext<'a>,
79    ) -> Result<Reversible53CoefficientImage> {
80        j2c::recode::extract_reversible_53_coefficients(
81            self.codestream,
82            &self.header,
83            self.retained_metadata_bytes()?,
84            decoder_context,
85        )
86    }
87
88    /// Decode a region of the image at native bit depth.
89    ///
90    /// # Errors
91    ///
92    /// Returns an error when the region is invalid or decoding and packing fail.
93    pub fn decode_native_region(&self, roi: (u32, u32, u32, u32)) -> Result<RawBitmap> {
94        self.decode_native_region_with_context(roi, &mut DecoderContext::default())
95    }
96
97    /// Decode a source-coordinate region into owned native-bit-depth component
98    /// planes.
99    ///
100    /// # Errors
101    ///
102    /// Returns an error when the region is invalid or decoding and packing fail.
103    pub fn decode_native_region_components(
104        &self,
105        roi: (u32, u32, u32, u32),
106    ) -> Result<DecodedNativeComponents> {
107        self.decode_native_region_components_with_context(roi, &mut DecoderContext::default())
108    }
109
110    /// Decode the image at native bit depth using a caller-provided decoder
111    /// context so allocations can be reused across repeated decodes.
112    ///
113    /// # Errors
114    ///
115    /// Returns an error when decoding, sizing, or native sample packing fails.
116    pub fn decode_native_with_context(
117        &self,
118        decoder_context: &mut DecoderContext<'a>,
119    ) -> Result<RawBitmap> {
120        let retained_baseline_bytes = self.retained_metadata_bytes()?;
121        self.decode_native_with_context_and_retained_baseline(
122            decoder_context,
123            retained_baseline_bytes,
124        )
125    }
126
127    pub(crate) fn decode_native_with_context_and_retained_baseline(
128        &self,
129        decoder_context: &mut DecoderContext<'a>,
130        retained_baseline_bytes: usize,
131    ) -> Result<RawBitmap> {
132        let bit_depth = self.uniform_header_bit_depth()?;
133        self.decode_with_output_region_and_ht_decoder_with_retained_baseline(
134            decoder_context,
135            None,
136            None,
137            retained_baseline_bytes,
138        )?;
139
140        let components = &decoder_context.tile_decode_context.channel_data;
141        let component_owner_capacity = components.capacity();
142        let num_components =
143            u16::try_from(components.len()).map_err(|_| ValidationError::TooManyChannels)?;
144        let width = self.width();
145        let height = self.height();
146        let pixel_count = checked_decode_sample_count(width, height)?;
147        let bytes_per_sample = native_bytes_per_sample(bit_depth)?;
148        let capacity =
149            checked_decode_byte_len3(pixel_count, usize::from(num_components), bytes_per_sample)?;
150        let mut budget = NativeOutputBudget::for_decoded_channels(
151            retained_baseline_bytes,
152            components,
153            component_owner_capacity,
154        )?;
155        budget.include_bit_capacity(components.len())?;
156        budget.include_elements::<u8>(capacity)?;
157        let component_signed = Self::try_component_signedness(components)?;
158        budget.include_bit_capacity_overage(components.len(), component_signed.capacity())?;
159        let signed = component_signed.iter().all(|signed| *signed);
160        let mut data = Vec::new();
161        try_reserve_decode_elements(&mut data, capacity)?;
162        budget.include_capacity_overage::<u8>(capacity, data.capacity())?;
163        for index in 0..pixel_count {
164            for component in components {
165                Self::push_component_native_sample_bytes(&mut data, component, index, bit_depth);
166            }
167        }
168        if data.len() != capacity {
169            bail!(DecodingError::CodeBlockDecodeFailure);
170        }
171        let bitmap = RawBitmap {
172            data,
173            width,
174            height,
175            bit_depth,
176            signed,
177            component_signed,
178            num_components,
179            bytes_per_sample: u8::try_from(bytes_per_sample)
180                .map_err(|_| ValidationError::ImageTooLarge)?,
181        };
182        NativeOutputBudget::validate_raw_pack(
183            retained_baseline_bytes,
184            components,
185            component_owner_capacity,
186            &bitmap,
187        )?;
188        Ok(bitmap)
189    }
190
191    /// Decode a region of the image at native bit depth using a caller-provided
192    /// decoder context.
193    ///
194    /// # Errors
195    ///
196    /// Returns an error when the region is invalid or decoding, sizing, and packing fail.
197    pub fn decode_native_region_with_context(
198        &self,
199        roi: (u32, u32, u32, u32),
200        decoder_context: &mut DecoderContext<'a>,
201    ) -> Result<RawBitmap> {
202        validate_roi((self.width(), self.height()), roi)?;
203        if self.requires_exact_integer_decode() {
204            return self.decode_native_region_via_full_decode(roi, decoder_context);
205        }
206        let bit_depth = self.uniform_header_bit_depth()?;
207        self.decode_with_output_region(decoder_context, Some(roi))?;
208
209        let components = &decoder_context.tile_decode_context.channel_data;
210        let component_owner_capacity = components.capacity();
211        let num_components =
212            u16::try_from(components.len()).map_err(|_| ValidationError::TooManyChannels)?;
213        let bytes_per_sample = native_bytes_per_sample(bit_depth)?;
214        let (_x, _y, width, height) = roi;
215        let capacity = checked_decode_byte_len4(
216            width as usize,
217            height as usize,
218            usize::from(num_components),
219            bytes_per_sample,
220        )?;
221        let retained_image_bytes = self.retained_metadata_bytes()?;
222        let mut budget = NativeOutputBudget::for_decoded_channels(
223            retained_image_bytes,
224            components,
225            component_owner_capacity,
226        )?;
227        budget.include_bit_capacity(components.len())?;
228        budget.include_elements::<u8>(capacity)?;
229        let mut data = Vec::new();
230        let component_signed = Self::try_component_signedness(components)?;
231        budget.include_bit_capacity_overage(components.len(), component_signed.capacity())?;
232        try_reserve_decode_elements(&mut data, capacity)?;
233        budget.include_capacity_overage::<u8>(capacity, data.capacity())?;
234        let signed = component_signed.iter().all(|signed| *signed);
235
236        for row in 0..height as usize {
237            for col in 0..width as usize {
238                let index = row * width as usize + col;
239                for component in components {
240                    Self::push_component_native_sample_bytes(
241                        &mut data, component, index, bit_depth,
242                    );
243                }
244            }
245        }
246        if data.len() != capacity {
247            bail!(DecodingError::CodeBlockDecodeFailure);
248        }
249
250        let bitmap = RawBitmap {
251            data,
252            width,
253            height,
254            bit_depth,
255            signed,
256            component_signed,
257            num_components,
258            bytes_per_sample: u8::try_from(bytes_per_sample)
259                .map_err(|_| ValidationError::ImageTooLarge)?,
260        };
261        NativeOutputBudget::validate_raw_pack(
262            retained_image_bytes,
263            components,
264            component_owner_capacity,
265            &bitmap,
266        )?;
267        Ok(bitmap)
268    }
269
270    fn try_component_signedness(components: &[ComponentData]) -> Result<Vec<bool>> {
271        let mut signedness = Vec::new();
272        try_reserve_decode_elements(&mut signedness, components.len())?;
273        signedness.extend(components.iter().map(|component| component.signed));
274        Ok(signedness)
275    }
276
277    pub(super) fn component_plane_sampling_at(&self, component_idx: usize) -> (u8, u8) {
278        if self.settings.resolve_palette_indices && self.boxes.palette.is_some() {
279            return (1, 1);
280        }
281        self.header
282            .component_infos
283            .get(component_idx)
284            .map_or((1, 1), |component| {
285                (
286                    component.size_info.horizontal_resolution,
287                    component.size_info.vertical_resolution,
288                )
289            })
290    }
291
292    pub(super) fn try_borrow_component_planes<'ctx>(
293        &self,
294        components: &'ctx [ComponentData],
295        component_owner_capacity: usize,
296        dimensions: (u32, u32),
297    ) -> Result<DecodedComponents<'ctx>> {
298        let retained_image_bytes = self.retained_metadata_bytes()?;
299        let mut budget = NativeOutputBudget::for_component_pack(
300            retained_image_bytes,
301            components,
302            component_owner_capacity,
303        )?;
304        budget.include_elements::<ComponentPlane<'_>>(components.len())?;
305        budget.include_color_space_clone(&self.color_space)?;
306        let color_space = try_clone_color_space(&self.color_space)?;
307        budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
308        let mut planes = Vec::new();
309        try_reserve_decode_elements(&mut planes, components.len())?;
310        budget
311            .include_capacity_overage::<ComponentPlane<'_>>(components.len(), planes.capacity())?;
312        for (component_idx, component) in components.iter().enumerate() {
313            planes.push(ComponentPlane {
314                samples: component.container.truncated(),
315                dimensions,
316                bit_depth: component.bit_depth,
317                signed: component.signed,
318                sampling: self.component_plane_sampling_at(component_idx),
319            });
320        }
321
322        let mut packed = DecodedComponents {
323            dimensions,
324            color_space,
325            has_alpha: self.has_alpha,
326            planes,
327            live_bytes: 0,
328        };
329        packed.live_bytes = NativeOutputBudget::validate_borrowed_pack(
330            retained_image_bytes,
331            components,
332            component_owner_capacity,
333            &packed,
334        )?;
335        Ok(packed)
336    }
337
338    fn uniform_header_bit_depth(&self) -> Result<u8> {
339        let Some(first) = self.header.component_infos.first() else {
340            bail!(DecodingError::CodeBlockDecodeFailure);
341        };
342        if self
343            .header
344            .component_infos
345            .iter()
346            .any(|component| component.size_info.precision != first.size_info.precision)
347        {
348            bail!(DecodingError::UnsupportedFeature(
349                "decode_native requires uniform component bit depths; use decode_components for mixed-depth images"
350            ));
351        }
352        if first.size_info.precision > 38 {
353            bail!(DecodingError::UnsupportedFeature(
354                "decode_native supports JPEG 2000 Part 1 component precision up to 38 bits"
355            ));
356        }
357        Ok(first.size_info.precision)
358    }
359
360    pub(super) fn validate_component_plane_precision(&self) -> Result<()> {
361        if self
362            .header
363            .component_infos
364            .iter()
365            .any(|component| component.size_info.precision > 24)
366        {
367            bail!(DecodingError::UnsupportedFeature(
368                "decode_components currently supports component planes up to 24 bits per component"
369            ));
370        }
371        Ok(())
372    }
373}