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