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        let mut ht_decoder = None;
134        decoder_context.set_output_region(None);
135        let decode_result = j2c::decode(
136            self.codestream,
137            &self.header,
138            retained_baseline_bytes,
139            decoder_context,
140            &mut ht_decoder,
141        );
142        decoder_context.set_output_region(None);
143        decode_result?;
144
145        let components = &decoder_context.tile_decode_context.channel_data;
146        let component_owner_capacity = components.capacity();
147        let num_components =
148            u16::try_from(components.len()).map_err(|_| ValidationError::TooManyChannels)?;
149        let width = self.width();
150        let height = self.height();
151        let pixel_count = checked_decode_sample_count(width, height)?;
152        let bytes_per_sample = native_bytes_per_sample(bit_depth)?;
153        let capacity =
154            checked_decode_byte_len3(pixel_count, usize::from(num_components), bytes_per_sample)?;
155        let mut budget = NativeOutputBudget::for_decoded_channels(
156            retained_baseline_bytes,
157            components,
158            component_owner_capacity,
159        )?;
160        budget.include_bit_capacity(components.len())?;
161        budget.include_elements::<u8>(capacity)?;
162        let component_signed = Self::try_component_signedness(components)?;
163        budget.include_bit_capacity_overage(components.len(), component_signed.capacity())?;
164        let signed = component_signed.iter().all(|signed| *signed);
165        let mut data = Vec::new();
166        try_reserve_decode_elements(&mut data, capacity)?;
167        budget.include_capacity_overage::<u8>(capacity, data.capacity())?;
168        for index in 0..pixel_count {
169            for component in components {
170                Self::push_component_native_sample_bytes(&mut data, component, index, bit_depth);
171            }
172        }
173        if data.len() != capacity {
174            bail!(DecodingError::CodeBlockDecodeFailure);
175        }
176        let bitmap = RawBitmap {
177            data,
178            width,
179            height,
180            bit_depth,
181            signed,
182            component_signed,
183            num_components,
184            bytes_per_sample: u8::try_from(bytes_per_sample)
185                .map_err(|_| ValidationError::ImageTooLarge)?,
186        };
187        NativeOutputBudget::validate_raw_pack(
188            retained_baseline_bytes,
189            components,
190            component_owner_capacity,
191            &bitmap,
192        )?;
193        Ok(bitmap)
194    }
195
196    /// Decode a region of the image at native bit depth using a caller-provided
197    /// decoder context.
198    ///
199    /// # Errors
200    ///
201    /// Returns an error when the region is invalid or decoding, sizing, and packing fail.
202    pub fn decode_native_region_with_context(
203        &self,
204        roi: (u32, u32, u32, u32),
205        decoder_context: &mut DecoderContext<'a>,
206    ) -> Result<RawBitmap> {
207        validate_roi((self.width(), self.height()), roi)?;
208        if self.requires_exact_integer_decode() {
209            return self.decode_native_region_via_full_decode(roi, decoder_context);
210        }
211        let bit_depth = self.uniform_header_bit_depth()?;
212        let retained_image_bytes = self.retained_metadata_bytes()?;
213        let mut ht_decoder = None;
214        decoder_context.set_output_region(Some(roi));
215        let decode_result = j2c::decode(
216            self.codestream,
217            &self.header,
218            retained_image_bytes,
219            decoder_context,
220            &mut ht_decoder,
221        );
222        decoder_context.set_output_region(None);
223        decode_result?;
224
225        let components = &decoder_context.tile_decode_context.channel_data;
226        let component_owner_capacity = components.capacity();
227        let num_components =
228            u16::try_from(components.len()).map_err(|_| ValidationError::TooManyChannels)?;
229        let bytes_per_sample = native_bytes_per_sample(bit_depth)?;
230        let (_x, _y, width, height) = roi;
231        let capacity = checked_decode_byte_len4(
232            width as usize,
233            height as usize,
234            usize::from(num_components),
235            bytes_per_sample,
236        )?;
237        let mut budget = NativeOutputBudget::for_decoded_channels(
238            retained_image_bytes,
239            components,
240            component_owner_capacity,
241        )?;
242        budget.include_bit_capacity(components.len())?;
243        budget.include_elements::<u8>(capacity)?;
244        let mut data = Vec::new();
245        let component_signed = Self::try_component_signedness(components)?;
246        budget.include_bit_capacity_overage(components.len(), component_signed.capacity())?;
247        try_reserve_decode_elements(&mut data, capacity)?;
248        budget.include_capacity_overage::<u8>(capacity, data.capacity())?;
249        let signed = component_signed.iter().all(|signed| *signed);
250
251        for row in 0..height as usize {
252            for col in 0..width as usize {
253                let index = row * width as usize + col;
254                for component in components {
255                    Self::push_component_native_sample_bytes(
256                        &mut data, component, index, bit_depth,
257                    );
258                }
259            }
260        }
261        if data.len() != capacity {
262            bail!(DecodingError::CodeBlockDecodeFailure);
263        }
264
265        let bitmap = RawBitmap {
266            data,
267            width,
268            height,
269            bit_depth,
270            signed,
271            component_signed,
272            num_components,
273            bytes_per_sample: u8::try_from(bytes_per_sample)
274                .map_err(|_| ValidationError::ImageTooLarge)?,
275        };
276        NativeOutputBudget::validate_raw_pack(
277            retained_image_bytes,
278            components,
279            component_owner_capacity,
280            &bitmap,
281        )?;
282        Ok(bitmap)
283    }
284
285    fn try_component_signedness(components: &[ComponentData]) -> Result<Vec<bool>> {
286        let mut signedness = Vec::new();
287        try_reserve_decode_elements(&mut signedness, components.len())?;
288        signedness.extend(components.iter().map(|component| component.signed));
289        Ok(signedness)
290    }
291
292    pub(super) fn component_plane_sampling_at(&self, component_idx: usize) -> (u8, u8) {
293        if self.settings.resolve_palette_indices && self.boxes.palette.is_some() {
294            return (1, 1);
295        }
296        self.header
297            .component_infos
298            .get(component_idx)
299            .map_or((1, 1), |component| {
300                (
301                    component.size_info.horizontal_resolution,
302                    component.size_info.vertical_resolution,
303                )
304            })
305    }
306
307    pub(super) fn try_borrow_component_planes<'ctx>(
308        &self,
309        components: &'ctx [ComponentData],
310        component_owner_capacity: usize,
311        dimensions: (u32, u32),
312    ) -> Result<DecodedComponents<'ctx>> {
313        let retained_image_bytes = self.retained_metadata_bytes()?;
314        let mut budget = NativeOutputBudget::for_decoded_channels(
315            retained_image_bytes,
316            components,
317            component_owner_capacity,
318        )?;
319        budget.include_elements::<ComponentPlane<'_>>(components.len())?;
320        budget.include_color_space_clone(&self.color_space)?;
321        let color_space = try_clone_color_space(&self.color_space)?;
322        budget.include_color_space_clone_overage(&self.color_space, &color_space)?;
323        let mut planes = Vec::new();
324        try_reserve_decode_elements(&mut planes, components.len())?;
325        budget
326            .include_capacity_overage::<ComponentPlane<'_>>(components.len(), planes.capacity())?;
327        for (component_idx, component) in components.iter().enumerate() {
328            planes.push(ComponentPlane {
329                samples: component.container.truncated(),
330                dimensions,
331                bit_depth: component.bit_depth,
332                signed: component.signed,
333                sampling: self.component_plane_sampling_at(component_idx),
334            });
335        }
336
337        let mut packed = DecodedComponents {
338            dimensions,
339            color_space,
340            has_alpha: self.has_alpha,
341            planes,
342            live_bytes: 0,
343        };
344        packed.live_bytes = NativeOutputBudget::validate_borrowed_pack(
345            retained_image_bytes,
346            components,
347            component_owner_capacity,
348            &packed,
349        )?;
350        Ok(packed)
351    }
352
353    fn uniform_header_bit_depth(&self) -> Result<u8> {
354        let Some(first) = self.header.component_infos.first() else {
355            bail!(DecodingError::CodeBlockDecodeFailure);
356        };
357        if self
358            .header
359            .component_infos
360            .iter()
361            .any(|component| component.size_info.precision != first.size_info.precision)
362        {
363            bail!(DecodingError::UnsupportedFeature(
364                "decode_native requires uniform component bit depths; use decode_components for mixed-depth images"
365            ));
366        }
367        if first.size_info.precision > 38 {
368            bail!(DecodingError::UnsupportedFeature(
369                "decode_native supports JPEG 2000 Part 1 component precision up to 38 bits"
370            ));
371        }
372        Ok(first.size_info.precision)
373    }
374
375    pub(super) fn validate_component_plane_precision(&self) -> Result<()> {
376        if self
377            .header
378            .component_infos
379            .iter()
380            .any(|component| component.size_info.precision > 24)
381        {
382            bail!(DecodingError::UnsupportedFeature(
383                "decode_components currently supports component planes up to 24 bits per component"
384            ));
385        }
386        Ok(())
387    }
388}