use alloc::sync::Arc;
use core::num::NonZeroUsize;
use j2k_core::{
Colorspace, CompressedPayloadKind, CompressedTransferSyntax, Downscale, PixelFormat,
PixelLayout, Rect, SampleType,
};
use super::{BatchExecutionShape, BatchItemError, PreparedImage};
use crate::{DecodeSettings, DeviceDecodeRequest};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum PreparationDepth {
MetadataOnly,
Htj2kOffsetPlan,
ClassicOffsetPlan,
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum DecodeRequest {
#[default]
Full,
Region {
roi: Rect,
},
Reduced {
scale: Downscale,
},
RegionReduced {
roi: Rect,
scale: Downscale,
},
}
impl DecodeRequest {
pub(super) fn device_request(self) -> DeviceDecodeRequest {
match self {
Self::Full => DeviceDecodeRequest::Full,
Self::Region { roi } => DeviceDecodeRequest::Region { roi },
Self::Reduced { scale } => DeviceDecodeRequest::Scaled { scale },
Self::RegionReduced { roi, scale } => DeviceDecodeRequest::RegionScaled { roi, scale },
}
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum BatchLayout {
#[default]
Nchw,
Nhwc,
}
#[derive(Debug, Clone)]
pub struct EncodedImage {
pub bytes: Arc<[u8]>,
pub request: DecodeRequest,
}
pub(super) struct PrepareJob {
pub(super) source_index: usize,
pub(super) input: Option<EncodedImage>,
}
pub(super) type PrepareImageResult =
Result<(PreparedImage, BatchGroupInfo, BatchExecutionShape), BatchItemError>;
impl EncodedImage {
#[must_use]
pub fn new(bytes: Arc<[u8]>, request: DecodeRequest) -> Self {
Self { bytes, request }
}
#[must_use]
pub fn full(bytes: Arc<[u8]>) -> Self {
Self::new(bytes, DecodeRequest::Full)
}
}
#[derive(Debug, Clone, Copy)]
pub struct BatchDecodeOptions {
pub layout: BatchLayout,
pub settings: DecodeSettings,
pub workers: Option<NonZeroUsize>,
}
impl Default for BatchDecodeOptions {
fn default() -> Self {
Self {
layout: BatchLayout::Nchw,
settings: DecodeSettings::strict(),
workers: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum BatchAlpha {
None,
Straight,
Premultiplied,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum BatchCodecRoute {
Classic,
Htj2k,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum BatchWaveletTransform {
Reversible53,
Irreversible97,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BatchGroupInfo {
pub dimensions: (u32, u32),
pub color: PixelLayout,
pub alpha: BatchAlpha,
pub precision: u8,
pub signed: bool,
pub sample_type: SampleType,
pub layout: BatchLayout,
pub colorspace: Colorspace,
pub route: BatchCodecRoute,
pub transform: BatchWaveletTransform,
pub transfer_syntax: CompressedTransferSyntax,
pub payload_kind: CompressedPayloadKind,
}
impl BatchGroupInfo {
#[must_use]
pub fn samples_per_image(&self) -> Option<usize> {
(self.dimensions.0 as usize)
.checked_mul(self.dimensions.1 as usize)?
.checked_mul(self.color.channels())
}
#[doc(hidden)]
#[must_use]
pub const fn native_pixel_format(&self) -> Option<PixelFormat> {
if !matches!(
(self.sample_type, self.precision, self.signed),
(SampleType::U8, 1..=8, false)
| (SampleType::U16, 9..=16, false)
| (SampleType::I16, 1..=16, true)
) {
return None;
}
match (self.color, self.sample_type) {
(PixelLayout::Gray, SampleType::U8) => Some(PixelFormat::Gray8),
(PixelLayout::Gray, SampleType::U16) => Some(PixelFormat::Gray16),
(PixelLayout::Gray, SampleType::I16) => Some(PixelFormat::GrayI16),
(PixelLayout::Rgb, SampleType::U8) => Some(PixelFormat::Rgb8),
(PixelLayout::Rgb, SampleType::U16) => Some(PixelFormat::Rgb16),
(PixelLayout::Rgb, SampleType::I16) => Some(PixelFormat::RgbI16),
(PixelLayout::Rgba, SampleType::U8) => Some(PixelFormat::Rgba8),
(PixelLayout::Rgba, SampleType::U16) => Some(PixelFormat::Rgba16),
(PixelLayout::Rgba, SampleType::I16) => Some(PixelFormat::RgbaI16),
_ => None,
}
}
}
#[cfg(test)]
mod tests {
use j2k_core::{
Colorspace, CompressedPayloadKind, CompressedTransferSyntax, PixelFormat, PixelLayout,
SampleType,
};
use super::{BatchAlpha, BatchCodecRoute, BatchGroupInfo, BatchLayout, BatchWaveletTransform};
fn group_info(
color: PixelLayout,
sample_type: SampleType,
precision: u8,
signed: bool,
) -> BatchGroupInfo {
BatchGroupInfo {
dimensions: (8, 8),
color,
alpha: if color == PixelLayout::Rgba {
BatchAlpha::Straight
} else {
BatchAlpha::None
},
precision,
signed,
sample_type,
layout: BatchLayout::Nchw,
colorspace: if color == PixelLayout::Gray {
Colorspace::Grayscale
} else {
Colorspace::SRgb
},
route: BatchCodecRoute::Htj2k,
transform: BatchWaveletTransform::Reversible53,
transfer_syntax: CompressedTransferSyntax::HtJpeg2000Lossless,
payload_kind: CompressedPayloadKind::Jpeg2000Codestream,
}
}
#[test]
fn native_pixel_format_maps_every_representable_color_and_sample_type() {
for (color, sample_type, precision, signed, expected) in [
(
PixelLayout::Gray,
SampleType::U8,
8,
false,
PixelFormat::Gray8,
),
(
PixelLayout::Gray,
SampleType::U16,
12,
false,
PixelFormat::Gray16,
),
(
PixelLayout::Gray,
SampleType::I16,
12,
true,
PixelFormat::GrayI16,
),
(
PixelLayout::Rgb,
SampleType::U8,
8,
false,
PixelFormat::Rgb8,
),
(
PixelLayout::Rgb,
SampleType::U16,
12,
false,
PixelFormat::Rgb16,
),
(
PixelLayout::Rgb,
SampleType::I16,
12,
true,
PixelFormat::RgbI16,
),
(
PixelLayout::Rgba,
SampleType::U8,
8,
false,
PixelFormat::Rgba8,
),
(
PixelLayout::Rgba,
SampleType::U16,
12,
false,
PixelFormat::Rgba16,
),
(
PixelLayout::Rgba,
SampleType::I16,
12,
true,
PixelFormat::RgbaI16,
),
] {
assert_eq!(
group_info(color, sample_type, precision, signed).native_pixel_format(),
Some(expected)
);
}
}
#[test]
fn native_pixel_format_rejects_inconsistent_sample_metadata() {
for info in [
group_info(PixelLayout::Gray, SampleType::U8, 0, false),
group_info(PixelLayout::Gray, SampleType::U8, 9, false),
group_info(PixelLayout::Gray, SampleType::U8, 8, true),
group_info(PixelLayout::Rgb, SampleType::U16, 8, false),
group_info(PixelLayout::Rgb, SampleType::U16, 17, false),
group_info(PixelLayout::Rgba, SampleType::I16, 12, false),
group_info(PixelLayout::Rgba, SampleType::I16, 17, true),
] {
assert_eq!(info.native_pixel_format(), None);
}
}
}