use alloc::vec::Vec;
use super::{
J2kDirectColorPlan, J2kDirectGrayscalePlan, J2kDirectRgbaPlan, J2kRect, J2kWaveletTransform,
};
use crate::{HtCodeBlockPayloadRanges, J2kClassicCodeBlockPayload, J2kCodestreamRange};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct J2kReferencedPayloadRecordSpan {
pub first_record: usize,
pub record_count: usize,
}
impl J2kReferencedPayloadRecordSpan {
#[must_use]
pub const fn end_record(self) -> Option<usize> {
self.first_record.checked_add(self.record_count)
}
}
#[derive(Debug)]
pub enum J2kReferencedTileGeometry {
Grayscale(J2kDirectGrayscalePlan),
Color(J2kDirectColorPlan),
Rgba(J2kDirectRgbaPlan),
}
#[derive(Debug)]
pub struct J2kReferencedTilePlan {
tile_index: usize,
decoded_rect: J2kRect,
destination_rect: J2kRect,
payload_records: J2kReferencedPayloadRecordSpan,
pub(super) classic_payloads: Vec<J2kClassicCodeBlockPayload>,
pub(super) classic_ranges: Vec<J2kCodestreamRange>,
wavelet_transform: J2kWaveletTransform,
geometry: J2kReferencedTileGeometry,
}
impl J2kReferencedTilePlan {
#[expect(
clippy::too_many_arguments,
reason = "tile geometry, payload ownership, and transform facts remain explicit"
)]
#[must_use]
pub fn new(
tile_index: usize,
decoded_rect: J2kRect,
destination_rect: J2kRect,
payload_records: J2kReferencedPayloadRecordSpan,
classic_payloads: Vec<J2kClassicCodeBlockPayload>,
classic_ranges: Vec<J2kCodestreamRange>,
wavelet_transform: J2kWaveletTransform,
geometry: J2kReferencedTileGeometry,
) -> Self {
Self {
tile_index,
decoded_rect,
destination_rect,
payload_records,
classic_payloads,
classic_ranges,
wavelet_transform,
geometry,
}
}
#[must_use]
pub const fn tile_index(&self) -> usize {
self.tile_index
}
#[must_use]
pub const fn decoded_rect(&self) -> J2kRect {
self.decoded_rect
}
#[must_use]
pub const fn destination_rect(&self) -> J2kRect {
self.destination_rect
}
#[must_use]
pub const fn payload_records(&self) -> J2kReferencedPayloadRecordSpan {
self.payload_records
}
#[must_use]
pub fn classic_payloads(&self) -> &[J2kClassicCodeBlockPayload] {
&self.classic_payloads
}
#[must_use]
pub fn classic_ranges(&self) -> &[J2kCodestreamRange] {
&self.classic_ranges
}
#[must_use]
pub const fn grayscale_geometry(&self) -> Option<&J2kDirectGrayscalePlan> {
match &self.geometry {
J2kReferencedTileGeometry::Grayscale(value) => Some(value),
J2kReferencedTileGeometry::Color(_) | J2kReferencedTileGeometry::Rgba(_) => None,
}
}
#[must_use]
pub const fn color_geometry(&self) -> Option<&J2kDirectColorPlan> {
match &self.geometry {
J2kReferencedTileGeometry::Color(value) => Some(value),
J2kReferencedTileGeometry::Grayscale(_) | J2kReferencedTileGeometry::Rgba(_) => None,
}
}
#[must_use]
pub const fn rgba_geometry(&self) -> Option<&J2kDirectRgbaPlan> {
match &self.geometry {
J2kReferencedTileGeometry::Rgba(value) => Some(value),
J2kReferencedTileGeometry::Grayscale(_) | J2kReferencedTileGeometry::Color(_) => None,
}
}
#[must_use]
pub const fn wavelet_transform(&self) -> J2kWaveletTransform {
self.wavelet_transform
}
}
#[derive(Debug, Clone, Copy)]
pub struct J2kReferencedImageGeometry<'a> {
tiles: &'a [J2kReferencedTilePlan],
full_dimensions: (u32, u32),
output_rect: J2kRect,
}
impl<'a> J2kReferencedImageGeometry<'a> {
const fn new(
tiles: &'a [J2kReferencedTilePlan],
full_dimensions: (u32, u32),
output_rect: J2kRect,
) -> Self {
Self {
tiles,
full_dimensions,
output_rect,
}
}
#[must_use]
pub const fn tiles(self) -> &'a [J2kReferencedTilePlan] {
self.tiles
}
#[must_use]
pub const fn is_empty(self) -> bool {
self.tiles.is_empty()
}
#[must_use]
pub const fn full_dimensions(self) -> (u32, u32) {
self.full_dimensions
}
#[must_use]
pub const fn output_rect(self) -> J2kRect {
self.output_rect
}
#[must_use]
pub fn is_grayscale(self) -> bool {
!self.is_empty()
&& self
.tiles
.iter()
.all(|tile| tile.grayscale_geometry().is_some())
}
#[must_use]
pub fn is_color(self) -> bool {
!self.is_empty()
&& self
.tiles
.iter()
.all(|tile| tile.color_geometry().is_some())
}
#[must_use]
pub fn is_rgba(self) -> bool {
!self.is_empty() && self.tiles.iter().all(|tile| tile.rgba_geometry().is_some())
}
#[must_use]
pub fn grayscale_geometry(self) -> Option<&'a J2kDirectGrayscalePlan> {
(self.tiles.len() == 1)
.then(|| self.tiles[0].grayscale_geometry())
.flatten()
}
#[must_use]
pub fn color_geometry(self) -> Option<&'a J2kDirectColorPlan> {
(self.tiles.len() == 1)
.then(|| self.tiles[0].color_geometry())
.flatten()
}
#[must_use]
pub fn rgba_geometry(self) -> Option<&'a J2kDirectRgbaPlan> {
(self.tiles.len() == 1)
.then(|| self.tiles[0].rgba_geometry())
.flatten()
}
#[must_use]
pub fn uniform_wavelet_transform(self) -> Option<J2kWaveletTransform> {
let first = self.tiles.first()?.wavelet_transform();
self.tiles
.iter()
.all(|tile| tile.wavelet_transform() == first)
.then_some(first)
}
}
#[derive(Debug)]
pub enum J2kReferencedClassicPlan {
Grayscale {
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<J2kClassicCodeBlockPayload>,
ranges: Vec<J2kCodestreamRange>,
},
Color {
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<J2kClassicCodeBlockPayload>,
ranges: Vec<J2kCodestreamRange>,
},
Rgba {
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<J2kClassicCodeBlockPayload>,
ranges: Vec<J2kCodestreamRange>,
},
}
#[derive(Debug)]
pub enum J2kReferencedHtj2kPlan {
Grayscale {
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<HtCodeBlockPayloadRanges>,
},
Color {
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<HtCodeBlockPayloadRanges>,
},
Rgba {
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<HtCodeBlockPayloadRanges>,
},
}
macro_rules! shared_plan_methods {
($name:ident) => {
impl $name {
#[must_use]
pub fn grayscale_geometry(&self) -> Option<&J2kDirectGrayscalePlan> {
self.image_geometry().grayscale_geometry()
}
#[must_use]
pub fn color_geometry(&self) -> Option<&J2kDirectColorPlan> {
self.image_geometry().color_geometry()
}
#[must_use]
pub fn rgba_geometry(&self) -> Option<&J2kDirectRgbaPlan> {
self.image_geometry().rgba_geometry()
}
#[must_use]
pub const fn image_geometry(&self) -> J2kReferencedImageGeometry<'_> {
match self {
Self::Grayscale {
tiles,
full_dimensions,
output_rect,
..
}
| Self::Color {
tiles,
full_dimensions,
output_rect,
..
}
| Self::Rgba {
tiles,
full_dimensions,
output_rect,
..
} => J2kReferencedImageGeometry::new(
tiles.as_slice(),
*full_dimensions,
*output_rect,
),
}
}
#[must_use]
pub fn tiles(&self) -> &[J2kReferencedTilePlan] {
self.image_geometry().tiles()
}
#[must_use]
pub const fn full_dimensions(&self) -> (u32, u32) {
self.image_geometry().full_dimensions()
}
#[must_use]
pub const fn output_rect(&self) -> J2kRect {
self.image_geometry().output_rect()
}
}
};
}
shared_plan_methods!(J2kReferencedClassicPlan);
shared_plan_methods!(J2kReferencedHtj2kPlan);
impl J2kReferencedClassicPlan {
#[must_use]
pub fn payloads(&self) -> &[J2kClassicCodeBlockPayload] {
match self {
Self::Grayscale { payloads, .. }
| Self::Color { payloads, .. }
| Self::Rgba { payloads, .. } => payloads,
}
}
#[must_use]
pub fn ranges(&self) -> &[J2kCodestreamRange] {
match self {
Self::Grayscale { ranges, .. }
| Self::Color { ranges, .. }
| Self::Rgba { ranges, .. } => ranges,
}
}
#[must_use]
pub fn grayscale(
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<J2kClassicCodeBlockPayload>,
ranges: Vec<J2kCodestreamRange>,
) -> Self {
Self::Grayscale {
tiles,
full_dimensions,
output_rect,
payloads,
ranges,
}
}
#[must_use]
pub fn color(
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<J2kClassicCodeBlockPayload>,
ranges: Vec<J2kCodestreamRange>,
) -> Self {
Self::Color {
tiles,
full_dimensions,
output_rect,
payloads,
ranges,
}
}
#[must_use]
pub fn rgba(
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<J2kClassicCodeBlockPayload>,
ranges: Vec<J2kCodestreamRange>,
) -> Self {
Self::Rgba {
tiles,
full_dimensions,
output_rect,
payloads,
ranges,
}
}
}
impl J2kReferencedHtj2kPlan {
#[must_use]
pub fn payloads(&self) -> &[HtCodeBlockPayloadRanges] {
match self {
Self::Grayscale { payloads, .. }
| Self::Color { payloads, .. }
| Self::Rgba { payloads, .. } => payloads,
}
}
#[must_use]
pub fn grayscale(
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<HtCodeBlockPayloadRanges>,
) -> Self {
Self::Grayscale {
tiles,
full_dimensions,
output_rect,
payloads,
}
}
#[must_use]
pub fn color(
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<HtCodeBlockPayloadRanges>,
) -> Self {
Self::Color {
tiles,
full_dimensions,
output_rect,
payloads,
}
}
#[must_use]
pub fn rgba(
tiles: Vec<J2kReferencedTilePlan>,
full_dimensions: (u32, u32),
output_rect: J2kRect,
payloads: Vec<HtCodeBlockPayloadRanges>,
) -> Self {
Self::Rgba {
tiles,
full_dimensions,
output_rect,
payloads,
}
}
}