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