Skip to main content

j2k_types/decode_plan/
referenced.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3use alloc::vec::Vec;
4
5use super::{
6    J2kDirectColorPlan, J2kDirectGrayscalePlan, J2kDirectRgbaPlan, J2kRect, J2kWaveletTransform,
7};
8use crate::{HtCodeBlockPayloadRanges, J2kClassicCodeBlockPayload, J2kCodestreamRange};
9
10/// Contiguous range of compressed-payload records belonging to one tile plan.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub struct J2kReferencedPayloadRecordSpan {
13    /// Index of the first payload record.
14    pub first_record: usize,
15    /// Number of payload records.
16    pub record_count: usize,
17}
18
19impl J2kReferencedPayloadRecordSpan {
20    /// Exclusive payload-record index, or `None` on overflow.
21    #[must_use]
22    pub const fn end_record(self) -> Option<usize> {
23        self.first_record.checked_add(self.record_count)
24    }
25}
26
27/// Direct execution geometry for one codestream tile.
28#[derive(Debug)]
29pub enum J2kReferencedTileGeometry {
30    /// One-component grayscale tile.
31    Grayscale(J2kDirectGrayscalePlan),
32    /// Three-component RGB tile.
33    Color(J2kDirectColorPlan),
34    /// Four-component RGBA tile.
35    Rgba(J2kDirectRgbaPlan),
36}
37
38/// One independently executable tile in a referenced direct plan.
39#[derive(Debug)]
40pub struct J2kReferencedTilePlan {
41    tile_index: usize,
42    decoded_rect: J2kRect,
43    destination_rect: J2kRect,
44    payload_records: J2kReferencedPayloadRecordSpan,
45    pub(super) classic_payloads: Vec<J2kClassicCodeBlockPayload>,
46    pub(super) classic_ranges: Vec<J2kCodestreamRange>,
47    wavelet_transform: J2kWaveletTransform,
48    geometry: J2kReferencedTileGeometry,
49}
50
51impl J2kReferencedTilePlan {
52    /// Construct one producer-validated tile plan.
53    #[expect(
54        clippy::too_many_arguments,
55        reason = "tile geometry, payload ownership, and transform facts remain explicit"
56    )]
57    #[must_use]
58    pub fn new(
59        tile_index: usize,
60        decoded_rect: J2kRect,
61        destination_rect: J2kRect,
62        payload_records: J2kReferencedPayloadRecordSpan,
63        classic_payloads: Vec<J2kClassicCodeBlockPayload>,
64        classic_ranges: Vec<J2kCodestreamRange>,
65        wavelet_transform: J2kWaveletTransform,
66        geometry: J2kReferencedTileGeometry,
67    ) -> Self {
68        Self {
69            tile_index,
70            decoded_rect,
71            destination_rect,
72            payload_records,
73            classic_payloads,
74            classic_ranges,
75            wavelet_transform,
76            geometry,
77        }
78    }
79
80    /// Zero-based codestream tile index in raster order.
81    #[must_use]
82    pub const fn tile_index(&self) -> usize {
83        self.tile_index
84    }
85
86    /// Tile/output-region intersection in reduced full-image coordinates.
87    #[must_use]
88    pub const fn decoded_rect(&self) -> J2kRect {
89        self.decoded_rect
90    }
91
92    /// Tile/output-region intersection in dense destination coordinates.
93    #[must_use]
94    pub const fn destination_rect(&self) -> J2kRect {
95        self.destination_rect
96    }
97
98    /// Payload-record span in the parent plan.
99    #[must_use]
100    pub const fn payload_records(&self) -> J2kReferencedPayloadRecordSpan {
101        self.payload_records
102    }
103
104    /// Classic payload descriptors retained by a mixed tile.
105    #[must_use]
106    pub fn classic_payloads(&self) -> &[J2kClassicCodeBlockPayload] {
107        &self.classic_payloads
108    }
109
110    /// Encoded-input ranges retained by a mixed tile.
111    #[must_use]
112    pub fn classic_ranges(&self) -> &[J2kCodestreamRange] {
113        &self.classic_ranges
114    }
115
116    /// Grayscale geometry when present.
117    #[must_use]
118    pub const fn grayscale_geometry(&self) -> Option<&J2kDirectGrayscalePlan> {
119        match &self.geometry {
120            J2kReferencedTileGeometry::Grayscale(value) => Some(value),
121            J2kReferencedTileGeometry::Color(_) | J2kReferencedTileGeometry::Rgba(_) => None,
122        }
123    }
124
125    /// RGB geometry when present.
126    #[must_use]
127    pub const fn color_geometry(&self) -> Option<&J2kDirectColorPlan> {
128        match &self.geometry {
129            J2kReferencedTileGeometry::Color(value) => Some(value),
130            J2kReferencedTileGeometry::Grayscale(_) | J2kReferencedTileGeometry::Rgba(_) => None,
131        }
132    }
133
134    /// RGBA geometry when present.
135    #[must_use]
136    pub const fn rgba_geometry(&self) -> Option<&J2kDirectRgbaPlan> {
137        match &self.geometry {
138            J2kReferencedTileGeometry::Rgba(value) => Some(value),
139            J2kReferencedTileGeometry::Grayscale(_) | J2kReferencedTileGeometry::Color(_) => None,
140        }
141    }
142
143    /// Effective wavelet transform after coding-style overrides.
144    #[must_use]
145    pub const fn wavelet_transform(&self) -> J2kWaveletTransform {
146        self.wavelet_transform
147    }
148}
149
150/// Borrowed image-level geometry shared by classic and HTJ2K plans.
151#[derive(Debug, Clone, Copy)]
152pub struct J2kReferencedImageGeometry<'a> {
153    tiles: &'a [J2kReferencedTilePlan],
154    full_dimensions: (u32, u32),
155    output_rect: J2kRect,
156}
157
158impl<'a> J2kReferencedImageGeometry<'a> {
159    const fn new(
160        tiles: &'a [J2kReferencedTilePlan],
161        full_dimensions: (u32, u32),
162        output_rect: J2kRect,
163    ) -> Self {
164        Self {
165            tiles,
166            full_dimensions,
167            output_rect,
168        }
169    }
170
171    /// Per-tile direct execution plans in raster order.
172    #[must_use]
173    pub const fn tiles(self) -> &'a [J2kReferencedTilePlan] {
174        self.tiles
175    }
176
177    /// Whether no tile geometry is present.
178    #[must_use]
179    pub const fn is_empty(self) -> bool {
180        self.tiles.is_empty()
181    }
182
183    /// Reduced full-image dimensions before an optional region.
184    #[must_use]
185    pub const fn full_dimensions(self) -> (u32, u32) {
186        self.full_dimensions
187    }
188
189    /// Requested output rectangle in reduced full-image coordinates.
190    #[must_use]
191    pub const fn output_rect(self) -> J2kRect {
192        self.output_rect
193    }
194
195    /// Whether every tile is grayscale.
196    #[must_use]
197    pub fn is_grayscale(self) -> bool {
198        !self.is_empty()
199            && self
200                .tiles
201                .iter()
202                .all(|tile| tile.grayscale_geometry().is_some())
203    }
204
205    /// Whether every tile is RGB.
206    #[must_use]
207    pub fn is_color(self) -> bool {
208        !self.is_empty()
209            && self
210                .tiles
211                .iter()
212                .all(|tile| tile.color_geometry().is_some())
213    }
214
215    /// Whether every tile is RGBA.
216    #[must_use]
217    pub fn is_rgba(self) -> bool {
218        !self.is_empty() && self.tiles.iter().all(|tile| tile.rgba_geometry().is_some())
219    }
220
221    /// Grayscale geometry for a legacy single-tile plan.
222    #[must_use]
223    pub fn grayscale_geometry(self) -> Option<&'a J2kDirectGrayscalePlan> {
224        (self.tiles.len() == 1)
225            .then(|| self.tiles[0].grayscale_geometry())
226            .flatten()
227    }
228
229    /// RGB geometry for a legacy single-tile plan.
230    #[must_use]
231    pub fn color_geometry(self) -> Option<&'a J2kDirectColorPlan> {
232        (self.tiles.len() == 1)
233            .then(|| self.tiles[0].color_geometry())
234            .flatten()
235    }
236
237    /// RGBA geometry for a legacy single-tile plan.
238    #[must_use]
239    pub fn rgba_geometry(self) -> Option<&'a J2kDirectRgbaPlan> {
240        (self.tiles.len() == 1)
241            .then(|| self.tiles[0].rgba_geometry())
242            .flatten()
243    }
244
245    /// Common wavelet transform when all tiles agree.
246    #[must_use]
247    pub fn uniform_wavelet_transform(self) -> Option<J2kWaveletTransform> {
248        let first = self.tiles.first()?.wavelet_transform();
249        self.tiles
250            .iter()
251            .all(|tile| tile.wavelet_transform() == first)
252            .then_some(first)
253    }
254}
255
256/// Owned classic JPEG 2000 execution geometry referencing caller-owned bytes.
257#[derive(Debug)]
258pub enum J2kReferencedClassicPlan {
259    /// One-component grayscale plan.
260    Grayscale {
261        /// Per-tile geometry.
262        tiles: Vec<J2kReferencedTilePlan>,
263        /// Reduced full-image dimensions.
264        full_dimensions: (u32, u32),
265        /// Requested output rectangle.
266        output_rect: J2kRect,
267        /// Payload descriptors.
268        payloads: Vec<J2kClassicCodeBlockPayload>,
269        /// Ordered encoded-input ranges referenced by the payload descriptors.
270        ranges: Vec<J2kCodestreamRange>,
271    },
272    /// Three-component RGB plan.
273    Color {
274        /// Per-tile geometry.
275        tiles: Vec<J2kReferencedTilePlan>,
276        /// Reduced full-image dimensions.
277        full_dimensions: (u32, u32),
278        /// Requested output rectangle.
279        output_rect: J2kRect,
280        /// Payload descriptors.
281        payloads: Vec<J2kClassicCodeBlockPayload>,
282        /// Ordered encoded-input ranges referenced by the payload descriptors.
283        ranges: Vec<J2kCodestreamRange>,
284    },
285    /// Four-component RGBA plan.
286    Rgba {
287        /// Per-tile geometry.
288        tiles: Vec<J2kReferencedTilePlan>,
289        /// Reduced full-image dimensions.
290        full_dimensions: (u32, u32),
291        /// Requested output rectangle.
292        output_rect: J2kRect,
293        /// Payload descriptors.
294        payloads: Vec<J2kClassicCodeBlockPayload>,
295        /// Ordered encoded-input ranges referenced by the payload descriptors.
296        ranges: Vec<J2kCodestreamRange>,
297    },
298}
299
300/// Owned HTJ2K execution geometry referencing caller-owned bytes.
301#[derive(Debug)]
302pub enum J2kReferencedHtj2kPlan {
303    /// One-component grayscale plan.
304    Grayscale {
305        /// Per-tile geometry.
306        tiles: Vec<J2kReferencedTilePlan>,
307        /// Reduced full-image dimensions.
308        full_dimensions: (u32, u32),
309        /// Requested output rectangle.
310        output_rect: J2kRect,
311        /// Payload records.
312        payloads: Vec<HtCodeBlockPayloadRanges>,
313    },
314    /// Three-component RGB plan.
315    Color {
316        /// Per-tile geometry.
317        tiles: Vec<J2kReferencedTilePlan>,
318        /// Reduced full-image dimensions.
319        full_dimensions: (u32, u32),
320        /// Requested output rectangle.
321        output_rect: J2kRect,
322        /// Payload records.
323        payloads: Vec<HtCodeBlockPayloadRanges>,
324    },
325    /// Four-component RGBA plan.
326    Rgba {
327        /// Per-tile geometry.
328        tiles: Vec<J2kReferencedTilePlan>,
329        /// Reduced full-image dimensions.
330        full_dimensions: (u32, u32),
331        /// Requested output rectangle.
332        output_rect: J2kRect,
333        /// Payload records.
334        payloads: Vec<HtCodeBlockPayloadRanges>,
335    },
336}
337
338macro_rules! shared_plan_methods {
339    ($name:ident) => {
340        impl $name {
341            /// Grayscale geometry for a legacy single-tile plan.
342            #[must_use]
343            pub fn grayscale_geometry(&self) -> Option<&J2kDirectGrayscalePlan> {
344                self.image_geometry().grayscale_geometry()
345            }
346
347            /// RGB geometry for a legacy single-tile plan.
348            #[must_use]
349            pub fn color_geometry(&self) -> Option<&J2kDirectColorPlan> {
350                self.image_geometry().color_geometry()
351            }
352
353            /// RGBA geometry for a legacy single-tile plan.
354            #[must_use]
355            pub fn rgba_geometry(&self) -> Option<&J2kDirectRgbaPlan> {
356                self.image_geometry().rgba_geometry()
357            }
358
359            /// Shared image-level geometry.
360            #[must_use]
361            pub const fn image_geometry(&self) -> J2kReferencedImageGeometry<'_> {
362                match self {
363                    Self::Grayscale {
364                        tiles,
365                        full_dimensions,
366                        output_rect,
367                        ..
368                    }
369                    | Self::Color {
370                        tiles,
371                        full_dimensions,
372                        output_rect,
373                        ..
374                    }
375                    | Self::Rgba {
376                        tiles,
377                        full_dimensions,
378                        output_rect,
379                        ..
380                    } => J2kReferencedImageGeometry::new(
381                        tiles.as_slice(),
382                        *full_dimensions,
383                        *output_rect,
384                    ),
385                }
386            }
387
388            /// Per-tile execution plans.
389            #[must_use]
390            pub fn tiles(&self) -> &[J2kReferencedTilePlan] {
391                self.image_geometry().tiles()
392            }
393
394            /// Reduced full-image dimensions.
395            #[must_use]
396            pub const fn full_dimensions(&self) -> (u32, u32) {
397                self.image_geometry().full_dimensions()
398            }
399
400            /// Requested output rectangle.
401            #[must_use]
402            pub const fn output_rect(&self) -> J2kRect {
403                self.image_geometry().output_rect()
404            }
405        }
406    };
407}
408
409shared_plan_methods!(J2kReferencedClassicPlan);
410shared_plan_methods!(J2kReferencedHtj2kPlan);
411
412impl J2kReferencedClassicPlan {
413    /// Payload descriptors in geometry traversal order.
414    #[must_use]
415    pub fn payloads(&self) -> &[J2kClassicCodeBlockPayload] {
416        match self {
417            Self::Grayscale { payloads, .. }
418            | Self::Color { payloads, .. }
419            | Self::Rgba { payloads, .. } => payloads,
420        }
421    }
422
423    /// Encoded-input ranges referenced by [`Self::payloads`].
424    #[must_use]
425    pub fn ranges(&self) -> &[J2kCodestreamRange] {
426        match self {
427            Self::Grayscale { ranges, .. }
428            | Self::Color { ranges, .. }
429            | Self::Rgba { ranges, .. } => ranges,
430        }
431    }
432
433    /// Construct a producer-validated grayscale plan.
434    #[must_use]
435    pub fn grayscale(
436        tiles: Vec<J2kReferencedTilePlan>,
437        full_dimensions: (u32, u32),
438        output_rect: J2kRect,
439        payloads: Vec<J2kClassicCodeBlockPayload>,
440        ranges: Vec<J2kCodestreamRange>,
441    ) -> Self {
442        Self::Grayscale {
443            tiles,
444            full_dimensions,
445            output_rect,
446            payloads,
447            ranges,
448        }
449    }
450
451    /// Construct a producer-validated RGB plan.
452    #[must_use]
453    pub fn color(
454        tiles: Vec<J2kReferencedTilePlan>,
455        full_dimensions: (u32, u32),
456        output_rect: J2kRect,
457        payloads: Vec<J2kClassicCodeBlockPayload>,
458        ranges: Vec<J2kCodestreamRange>,
459    ) -> Self {
460        Self::Color {
461            tiles,
462            full_dimensions,
463            output_rect,
464            payloads,
465            ranges,
466        }
467    }
468
469    /// Construct a producer-validated RGBA plan.
470    #[must_use]
471    pub fn rgba(
472        tiles: Vec<J2kReferencedTilePlan>,
473        full_dimensions: (u32, u32),
474        output_rect: J2kRect,
475        payloads: Vec<J2kClassicCodeBlockPayload>,
476        ranges: Vec<J2kCodestreamRange>,
477    ) -> Self {
478        Self::Rgba {
479            tiles,
480            full_dimensions,
481            output_rect,
482            payloads,
483            ranges,
484        }
485    }
486}
487
488impl J2kReferencedHtj2kPlan {
489    /// Payload records in geometry traversal order.
490    #[must_use]
491    pub fn payloads(&self) -> &[HtCodeBlockPayloadRanges] {
492        match self {
493            Self::Grayscale { payloads, .. }
494            | Self::Color { payloads, .. }
495            | Self::Rgba { payloads, .. } => payloads,
496        }
497    }
498
499    /// Construct a producer-validated grayscale plan.
500    #[must_use]
501    pub fn grayscale(
502        tiles: Vec<J2kReferencedTilePlan>,
503        full_dimensions: (u32, u32),
504        output_rect: J2kRect,
505        payloads: Vec<HtCodeBlockPayloadRanges>,
506    ) -> Self {
507        Self::Grayscale {
508            tiles,
509            full_dimensions,
510            output_rect,
511            payloads,
512        }
513    }
514
515    /// Construct a producer-validated RGB plan.
516    #[must_use]
517    pub fn color(
518        tiles: Vec<J2kReferencedTilePlan>,
519        full_dimensions: (u32, u32),
520        output_rect: J2kRect,
521        payloads: Vec<HtCodeBlockPayloadRanges>,
522    ) -> Self {
523        Self::Color {
524            tiles,
525            full_dimensions,
526            output_rect,
527            payloads,
528        }
529    }
530
531    /// Construct a producer-validated RGBA plan.
532    #[must_use]
533    pub fn rgba(
534        tiles: Vec<J2kReferencedTilePlan>,
535        full_dimensions: (u32, u32),
536        output_rect: J2kRect,
537        payloads: Vec<HtCodeBlockPayloadRanges>,
538    ) -> Self {
539        Self::Rgba {
540            tiles,
541            full_dimensions,
542            output_rect,
543            payloads,
544        }
545    }
546}