Skip to main content

j2k_native/color/
output_planes.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! Decoded component-plane owners and named facade handoff contracts.
4
5use alloc::vec::Vec;
6
7use super::ColorSpace;
8use crate::error::bail;
9use crate::{checked_decode_sample_count, DecodingError, Result};
10
11/// One owned decoded component plane at native bit depth.
12pub struct NativeComponentPlane {
13    pub(crate) data: Vec<u8>,
14    pub(crate) dimensions: (u32, u32),
15    pub(crate) bit_depth: u8,
16    pub(crate) signed: bool,
17    pub(crate) sampling: (u8, u8),
18    pub(crate) bytes_per_sample: u8,
19}
20
21/// Named allocation-free handoff of one owned native component plane.
22#[doc(hidden)]
23pub struct NativeComponentPlaneParts {
24    /// Packed component bytes.
25    pub data: Vec<u8>,
26    /// Component dimensions.
27    pub dimensions: (u32, u32),
28    /// Component bit depth.
29    pub bit_depth: u8,
30    /// Whether component samples are signed.
31    pub signed: bool,
32    /// Component sampling factors.
33    pub sampling: (u8, u8),
34    /// Bytes per packed sample.
35    pub bytes_per_sample: u8,
36}
37
38impl NativeComponentPlane {
39    /// Packed little-endian sample bytes for this component in row-major order.
40    #[must_use]
41    pub fn data(&self) -> &[u8] {
42        &self.data
43    }
44
45    crate::__j2k_component_plane_metadata_accessors!();
46
47    /// Bytes used for each packed little-endian sample in [`Self::data`].
48    #[must_use]
49    pub fn bytes_per_sample(&self) -> u8 {
50        self.bytes_per_sample
51    }
52
53    /// Return the byte capacity owned by this plane.
54    #[doc(hidden)]
55    #[must_use]
56    pub fn allocated_bytes(&self) -> usize {
57        self.data.capacity()
58    }
59
60    /// Consume this plane into an allocation-free named handoff.
61    #[doc(hidden)]
62    #[must_use]
63    pub fn into_parts(self) -> NativeComponentPlaneParts {
64        NativeComponentPlaneParts {
65            data: self.data,
66            dimensions: self.dimensions,
67            bit_depth: self.bit_depth,
68            signed: self.signed,
69            sampling: self.sampling,
70            bytes_per_sample: self.bytes_per_sample,
71        }
72    }
73}
74
75/// Owned decoded native-bit-depth component planes for an image.
76pub struct DecodedNativeComponents {
77    pub(crate) dimensions: (u32, u32),
78    pub(crate) color_space: ColorSpace,
79    pub(crate) has_alpha: bool,
80    pub(crate) planes: Vec<NativeComponentPlane>,
81}
82
83/// Named allocation-free handoff of owned native component planes.
84#[doc(hidden)]
85pub struct DecodedNativeComponentsParts {
86    /// Image dimensions.
87    pub dimensions: (u32, u32),
88    /// Decoded color space.
89    pub color_space: ColorSpace,
90    /// Whether an alpha plane is present.
91    pub has_alpha: bool,
92    /// Owned component planes.
93    pub planes: Vec<NativeComponentPlane>,
94}
95
96impl DecodedNativeComponents {
97    /// Dimensions of the decoded image represented by these planes.
98    #[must_use]
99    pub fn dimensions(&self) -> (u32, u32) {
100        self.dimensions
101    }
102
103    /// Color space after JPEG 2000 color conversion has been applied.
104    #[must_use]
105    pub fn color_space(&self) -> &ColorSpace {
106        &self.color_space
107    }
108
109    /// Whether the decoded image has an alpha channel.
110    #[must_use]
111    pub fn has_alpha(&self) -> bool {
112        self.has_alpha
113    }
114
115    /// Decoded component planes in display order.
116    #[must_use]
117    pub fn planes(&self) -> &[NativeComponentPlane] {
118        &self.planes
119    }
120
121    /// Return the actual heap capacity retained by this owned result.
122    #[doc(hidden)]
123    #[must_use]
124    pub fn allocated_bytes(&self) -> Option<usize> {
125        let mut bytes = self
126            .planes
127            .capacity()
128            .checked_mul(core::mem::size_of::<NativeComponentPlane>())?;
129        for plane in &self.planes {
130            bytes = bytes.checked_add(plane.allocated_bytes())?;
131        }
132        if let ColorSpace::Icc { profile, .. } = &self.color_space {
133            bytes = bytes.checked_add(profile.capacity())?;
134        }
135        Some(bytes)
136    }
137
138    /// Consume this result into an allocation-free named handoff.
139    #[doc(hidden)]
140    #[must_use]
141    pub fn into_parts(self) -> DecodedNativeComponentsParts {
142        DecodedNativeComponentsParts {
143            dimensions: self.dimensions,
144            color_space: self.color_space,
145            has_alpha: self.has_alpha,
146            planes: self.planes,
147        }
148    }
149}
150
151/// A borrowed decoded component plane.
152pub struct ComponentPlane<'a> {
153    pub(crate) samples: &'a [f32],
154    pub(crate) dimensions: (u32, u32),
155    pub(crate) bit_depth: u8,
156    pub(crate) signed: bool,
157    pub(crate) sampling: (u8, u8),
158}
159
160/// Named allocation-free handoff of one borrowed component plane.
161#[doc(hidden)]
162pub struct ComponentPlaneParts<'a> {
163    /// Borrowed component samples.
164    pub samples: &'a [f32],
165    /// Component dimensions.
166    pub dimensions: (u32, u32),
167    /// Component bit depth.
168    pub bit_depth: u8,
169    /// Whether component samples are signed.
170    pub signed: bool,
171    /// Component sampling factors.
172    pub sampling: (u8, u8),
173}
174
175impl<'a> ComponentPlane<'a> {
176    /// Component samples in row-major order.
177    #[must_use]
178    pub fn samples(&self) -> &'a [f32] {
179        self.samples
180    }
181
182    crate::__j2k_component_plane_metadata_accessors!();
183
184    /// Consume this borrowed plane into an allocation-free named handoff.
185    #[doc(hidden)]
186    #[must_use]
187    pub fn into_parts(self) -> ComponentPlaneParts<'a> {
188        ComponentPlaneParts {
189            samples: self.samples,
190            dimensions: self.dimensions,
191            bit_depth: self.bit_depth,
192            signed: self.signed,
193            sampling: self.sampling,
194        }
195    }
196}
197
198/// Receives a region decode's samples as they are finished, instead of as
199/// whole component planes.
200///
201/// Each call gives one rectangle of the region, at `(x, y)` in region
202/// coordinates, as one plane per component whose dimensions are the
203/// rectangle's. The samples are those the component planes would hold:
204/// rounded for integer output, with the inverse multi-component transform
205/// and level shift applied. Rectangles never overlap, and together they cover
206/// the region once. A decode that fails may already have delivered some.
207#[doc(hidden)]
208pub trait ComponentRowSink {
209    /// Receive the samples of one rectangle of the region.
210    fn write_rows(&mut self, x: u32, y: u32, planes: &[ComponentPlane<'_>]);
211}
212
213/// Borrowed decoded component planes for an image.
214pub struct DecodedComponents<'a> {
215    pub(crate) dimensions: (u32, u32),
216    pub(crate) color_space: ColorSpace,
217    pub(crate) has_alpha: bool,
218    pub(crate) planes: Vec<ComponentPlane<'a>>,
219    pub(crate) live_bytes: usize,
220}
221
222/// Named allocation-free handoff of borrowed decoded component planes.
223#[doc(hidden)]
224pub struct DecodedComponentsParts<'a> {
225    /// Image dimensions.
226    pub dimensions: (u32, u32),
227    /// Decoded color space.
228    pub color_space: ColorSpace,
229    /// Whether an alpha plane is present.
230    pub has_alpha: bool,
231    /// Borrowed component planes.
232    pub planes: Vec<ComponentPlane<'a>>,
233}
234
235impl<'a> DecodedComponents<'a> {
236    /// Dimensions of the decoded image represented by these planes.
237    #[must_use]
238    pub fn dimensions(&self) -> (u32, u32) {
239        self.dimensions
240    }
241
242    /// Color space after JPEG 2000 color conversion has been applied.
243    #[must_use]
244    pub fn color_space(&self) -> &ColorSpace {
245        &self.color_space
246    }
247
248    /// Whether the decoded image has an alpha channel.
249    #[must_use]
250    pub fn has_alpha(&self) -> bool {
251        self.has_alpha
252    }
253
254    /// Borrowed decoded component planes in display order.
255    #[must_use]
256    pub fn planes(&self) -> &[ComponentPlane<'a>] {
257        &self.planes
258    }
259
260    /// Return retained heap capacity that remains live with this result.
261    #[doc(hidden)]
262    #[must_use]
263    pub fn live_bytes(&self) -> usize {
264        self.live_bytes
265    }
266
267    /// Consume this result into an allocation-free named handoff.
268    #[doc(hidden)]
269    #[must_use]
270    pub fn into_parts(self) -> DecodedComponentsParts<'a> {
271        DecodedComponentsParts {
272            dimensions: self.dimensions,
273            color_space: self.color_space,
274            has_alpha: self.has_alpha,
275            planes: self.planes,
276        }
277    }
278}
279
280pub(crate) fn native_component_plane_dimensions(
281    reference_dimensions: (u32, u32),
282    sampling: (u8, u8),
283    sample_count: usize,
284) -> Result<(u32, u32)> {
285    let reference_sample_count =
286        checked_decode_sample_count(reference_dimensions.0, reference_dimensions.1)?;
287    if sample_count == reference_sample_count {
288        return Ok(reference_dimensions);
289    }
290
291    let (x_rsiz, y_rsiz) = sampling;
292    if x_rsiz == 0 || y_rsiz == 0 {
293        bail!(DecodingError::CodeBlockDecodeFailure);
294    }
295    let sampled_dimensions = (
296        reference_dimensions.0.div_ceil(u32::from(x_rsiz)),
297        reference_dimensions.1.div_ceil(u32::from(y_rsiz)),
298    );
299    let sampled_sample_count =
300        checked_decode_sample_count(sampled_dimensions.0, sampled_dimensions.1)?;
301    if sample_count == sampled_sample_count {
302        return Ok(sampled_dimensions);
303    }
304
305    bail!(DecodingError::CodeBlockDecodeFailure)
306}
307
308#[cfg(test)]
309mod tests {
310    use alloc::vec::Vec;
311    use core::mem::size_of;
312
313    use super::*;
314
315    #[test]
316    fn native_component_handoff_preserves_owned_capacities() {
317        let mut data = Vec::with_capacity(9);
318        data.push(3);
319        let mut planes = Vec::with_capacity(4);
320        planes.push(NativeComponentPlane {
321            data,
322            dimensions: (1, 1),
323            bit_depth: 8,
324            signed: false,
325            sampling: (1, 1),
326            bytes_per_sample: 1,
327        });
328        let mut profile = Vec::with_capacity(7);
329        profile.push(1);
330        let decoded = DecodedNativeComponents {
331            dimensions: (1, 1),
332            color_space: ColorSpace::Icc {
333                profile,
334                num_channels: 1,
335            },
336            has_alpha: false,
337            planes,
338        };
339        let expected = decoded.planes.capacity() * size_of::<NativeComponentPlane>()
340            + decoded.planes[0].data.capacity()
341            + match &decoded.color_space {
342                ColorSpace::Icc { profile, .. } => profile.capacity(),
343                _ => 0,
344            };
345        let plane_owner_capacity = decoded.planes.capacity();
346        let data_capacity = decoded.planes[0].data.capacity();
347        let profile_capacity = match &decoded.color_space {
348            ColorSpace::Icc { profile, .. } => profile.capacity(),
349            _ => 0,
350        };
351        assert_eq!(decoded.allocated_bytes(), Some(expected));
352
353        let DecodedNativeComponentsParts {
354            color_space,
355            planes,
356            ..
357        } = decoded.into_parts();
358        assert_eq!(planes.capacity(), plane_owner_capacity);
359        assert_eq!(planes[0].allocated_bytes(), data_capacity);
360        assert!(matches!(
361            color_space,
362            ColorSpace::Icc { profile, .. } if profile.capacity() == profile_capacity
363        ));
364    }
365
366    #[test]
367    fn borrowed_component_handoff_preserves_metadata_capacities() {
368        let samples = [2.0_f32];
369        let mut planes = Vec::with_capacity(3);
370        planes.push(ComponentPlane {
371            samples: &samples,
372            dimensions: (1, 1),
373            bit_depth: 8,
374            signed: false,
375            sampling: (1, 1),
376        });
377        let mut profile = Vec::with_capacity(5);
378        profile.push(1);
379        let decoded = DecodedComponents {
380            dimensions: (1, 1),
381            color_space: ColorSpace::Icc {
382                profile,
383                num_channels: 1,
384            },
385            has_alpha: false,
386            planes,
387            live_bytes: 123,
388        };
389        let plane_owner_capacity = decoded.planes.capacity();
390        let profile_capacity = match &decoded.color_space {
391            ColorSpace::Icc { profile, .. } => profile.capacity(),
392            _ => 0,
393        };
394
395        assert_eq!(decoded.live_bytes(), 123);
396        let DecodedComponentsParts {
397            color_space,
398            planes,
399            ..
400        } = decoded.into_parts();
401        assert_eq!(planes.capacity(), plane_owner_capacity);
402        assert!(core::ptr::eq(
403            planes[0].samples().as_ptr(),
404            samples.as_ptr()
405        ));
406        assert!(matches!(
407            color_space,
408            ColorSpace::Icc { profile, .. } if profile.capacity() == profile_capacity
409        ));
410    }
411}