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/// Borrowed decoded component planes for an image.
199pub struct DecodedComponents<'a> {
200    pub(crate) dimensions: (u32, u32),
201    pub(crate) color_space: ColorSpace,
202    pub(crate) has_alpha: bool,
203    pub(crate) planes: Vec<ComponentPlane<'a>>,
204    pub(crate) live_bytes: usize,
205}
206
207/// Named allocation-free handoff of borrowed decoded component planes.
208#[doc(hidden)]
209pub struct DecodedComponentsParts<'a> {
210    /// Image dimensions.
211    pub dimensions: (u32, u32),
212    /// Decoded color space.
213    pub color_space: ColorSpace,
214    /// Whether an alpha plane is present.
215    pub has_alpha: bool,
216    /// Borrowed component planes.
217    pub planes: Vec<ComponentPlane<'a>>,
218}
219
220impl<'a> DecodedComponents<'a> {
221    /// Dimensions of the decoded image represented by these planes.
222    #[must_use]
223    pub fn dimensions(&self) -> (u32, u32) {
224        self.dimensions
225    }
226
227    /// Color space after JPEG 2000 color conversion has been applied.
228    #[must_use]
229    pub fn color_space(&self) -> &ColorSpace {
230        &self.color_space
231    }
232
233    /// Whether the decoded image has an alpha channel.
234    #[must_use]
235    pub fn has_alpha(&self) -> bool {
236        self.has_alpha
237    }
238
239    /// Borrowed decoded component planes in display order.
240    #[must_use]
241    pub fn planes(&self) -> &[ComponentPlane<'a>] {
242        &self.planes
243    }
244
245    /// Return retained heap capacity that remains live with this result.
246    #[doc(hidden)]
247    #[must_use]
248    pub fn live_bytes(&self) -> usize {
249        self.live_bytes
250    }
251
252    /// Consume this result into an allocation-free named handoff.
253    #[doc(hidden)]
254    #[must_use]
255    pub fn into_parts(self) -> DecodedComponentsParts<'a> {
256        DecodedComponentsParts {
257            dimensions: self.dimensions,
258            color_space: self.color_space,
259            has_alpha: self.has_alpha,
260            planes: self.planes,
261        }
262    }
263}
264
265pub(crate) fn native_component_plane_dimensions(
266    reference_dimensions: (u32, u32),
267    sampling: (u8, u8),
268    sample_count: usize,
269) -> Result<(u32, u32)> {
270    let reference_sample_count =
271        checked_decode_sample_count(reference_dimensions.0, reference_dimensions.1)?;
272    if sample_count == reference_sample_count {
273        return Ok(reference_dimensions);
274    }
275
276    let (x_rsiz, y_rsiz) = sampling;
277    if x_rsiz == 0 || y_rsiz == 0 {
278        bail!(DecodingError::CodeBlockDecodeFailure);
279    }
280    let sampled_dimensions = (
281        reference_dimensions.0.div_ceil(u32::from(x_rsiz)),
282        reference_dimensions.1.div_ceil(u32::from(y_rsiz)),
283    );
284    let sampled_sample_count =
285        checked_decode_sample_count(sampled_dimensions.0, sampled_dimensions.1)?;
286    if sample_count == sampled_sample_count {
287        return Ok(sampled_dimensions);
288    }
289
290    bail!(DecodingError::CodeBlockDecodeFailure)
291}
292
293#[cfg(test)]
294mod tests {
295    use alloc::vec::Vec;
296    use core::mem::size_of;
297
298    use super::*;
299
300    #[test]
301    fn native_component_handoff_preserves_owned_capacities() {
302        let mut data = Vec::with_capacity(9);
303        data.push(3);
304        let mut planes = Vec::with_capacity(4);
305        planes.push(NativeComponentPlane {
306            data,
307            dimensions: (1, 1),
308            bit_depth: 8,
309            signed: false,
310            sampling: (1, 1),
311            bytes_per_sample: 1,
312        });
313        let mut profile = Vec::with_capacity(7);
314        profile.push(1);
315        let decoded = DecodedNativeComponents {
316            dimensions: (1, 1),
317            color_space: ColorSpace::Icc {
318                profile,
319                num_channels: 1,
320            },
321            has_alpha: false,
322            planes,
323        };
324        let expected = decoded.planes.capacity() * size_of::<NativeComponentPlane>()
325            + decoded.planes[0].data.capacity()
326            + match &decoded.color_space {
327                ColorSpace::Icc { profile, .. } => profile.capacity(),
328                _ => 0,
329            };
330        let plane_owner_capacity = decoded.planes.capacity();
331        let data_capacity = decoded.planes[0].data.capacity();
332        let profile_capacity = match &decoded.color_space {
333            ColorSpace::Icc { profile, .. } => profile.capacity(),
334            _ => 0,
335        };
336        assert_eq!(decoded.allocated_bytes(), Some(expected));
337
338        let DecodedNativeComponentsParts {
339            color_space,
340            planes,
341            ..
342        } = decoded.into_parts();
343        assert_eq!(planes.capacity(), plane_owner_capacity);
344        assert_eq!(planes[0].allocated_bytes(), data_capacity);
345        assert!(matches!(
346            color_space,
347            ColorSpace::Icc { profile, .. } if profile.capacity() == profile_capacity
348        ));
349    }
350
351    #[test]
352    fn borrowed_component_handoff_preserves_metadata_capacities() {
353        let samples = [2.0_f32];
354        let mut planes = Vec::with_capacity(3);
355        planes.push(ComponentPlane {
356            samples: &samples,
357            dimensions: (1, 1),
358            bit_depth: 8,
359            signed: false,
360            sampling: (1, 1),
361        });
362        let mut profile = Vec::with_capacity(5);
363        profile.push(1);
364        let decoded = DecodedComponents {
365            dimensions: (1, 1),
366            color_space: ColorSpace::Icc {
367                profile,
368                num_channels: 1,
369            },
370            has_alpha: false,
371            planes,
372            live_bytes: 123,
373        };
374        let plane_owner_capacity = decoded.planes.capacity();
375        let profile_capacity = match &decoded.color_space {
376            ColorSpace::Icc { profile, .. } => profile.capacity(),
377            _ => 0,
378        };
379
380        assert_eq!(decoded.live_bytes(), 123);
381        let DecodedComponentsParts {
382            color_space,
383            planes,
384            ..
385        } = decoded.into_parts();
386        assert_eq!(planes.capacity(), plane_owner_capacity);
387        assert!(core::ptr::eq(
388            planes[0].samples().as_ptr(),
389            samples.as_ptr()
390        ));
391        assert!(matches!(
392            color_space,
393            ColorSpace::Icc { profile, .. } if profile.capacity() == profile_capacity
394        ));
395    }
396}