use alloc::vec::Vec;
use j2k_core::BackendKind;
use crate::J2kEncodeDispatchReport;
pub(super) use j2k_types::{
MAX_JPEG2000_PART1_COMPONENTS,
MAX_JPEG2000_PART1_SAMPLE_BIT_DEPTH as MAX_PART1_SAMPLE_BIT_DEPTH,
};
pub(super) const MAX_RAW_PIXEL_ENCODE_BIT_DEPTH: u8 = 24;
pub(super) const MAX_CLASSIC_REVERSIBLE_MARKER_BITPLANES: u16 = 37;
pub(super) const MAX_HTJ2K_ENCODE_BITPLANES: u16 = 31;
macro_rules! define_encoded_j2k {
(
$(#[$attr:meta])*
pub struct $name:ident {
$($extra_fields:tt)*
}
) => {
$(#[$attr])*
pub struct $name {
/// Raw JPEG 2000 codestream bytes.
pub codestream: Vec<u8>,
pub backend: BackendKind,
pub dispatch_report: J2kEncodeDispatchReport,
pub width: u32,
pub height: u32,
pub components: u16,
pub bit_depth: u8,
pub signed: bool,
$($extra_fields)*
}
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum EncodeBackendPreference {
#[default]
Auto,
CpuOnly,
RequireDevice,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum J2kProgressionOrder {
#[default]
Lrcp,
Rlcp,
Rpcl,
Pcrl,
Cprl,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum J2kBlockCodingMode {
#[default]
Classic,
HighThroughput,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum ReversibleTransform {
#[default]
Rct53,
None53,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum J2kEncodeValidation {
#[default]
CpuRoundTrip,
External,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[expect(
clippy::struct_excessive_bools,
reason = "the public options type exposes independent compatibility switches"
)]
#[non_exhaustive]
pub struct J2kLosslessEncodeOptions {
pub backend: EncodeBackendPreference,
pub block_coding_mode: J2kBlockCodingMode,
pub progression: J2kProgressionOrder,
pub max_decomposition_levels: Option<u8>,
pub tile_size: Option<(u32, u32)>,
pub tile_part_packet_limit: Option<u16>,
pub quality_layers: u8,
pub write_tlm: bool,
pub write_plt: bool,
pub write_plm: bool,
pub write_ppm: bool,
pub write_ppt: bool,
pub write_sop: bool,
pub write_eph: bool,
pub reversible_transform: ReversibleTransform,
pub validation: J2kEncodeValidation,
}
impl Default for J2kLosslessEncodeOptions {
fn default() -> Self {
Self {
backend: EncodeBackendPreference::Auto,
block_coding_mode: J2kBlockCodingMode::Classic,
progression: J2kProgressionOrder::Lrcp,
max_decomposition_levels: None,
tile_size: None,
tile_part_packet_limit: None,
quality_layers: 1,
write_tlm: false,
write_plt: false,
write_plm: false,
write_ppm: false,
write_ppt: false,
write_sop: false,
write_eph: false,
reversible_transform: ReversibleTransform::Rct53,
validation: J2kEncodeValidation::CpuRoundTrip,
}
}
}
impl J2kLosslessEncodeOptions {
pub const fn new(
backend: EncodeBackendPreference,
block_coding_mode: J2kBlockCodingMode,
progression: J2kProgressionOrder,
max_decomposition_levels: Option<u8>,
reversible_transform: ReversibleTransform,
validation: J2kEncodeValidation,
) -> Self {
Self {
backend,
block_coding_mode,
progression,
max_decomposition_levels,
tile_size: None,
tile_part_packet_limit: None,
quality_layers: 1,
write_tlm: false,
write_plt: false,
write_plm: false,
write_ppm: false,
write_ppt: false,
write_sop: false,
write_eph: false,
reversible_transform,
validation,
}
}
#[must_use]
pub const fn with_backend(mut self, backend: EncodeBackendPreference) -> Self {
self.backend = backend;
self
}
#[must_use]
pub const fn with_accelerated_backend(self) -> Self {
self.with_backend(EncodeBackendPreference::Auto)
}
#[must_use]
pub const fn with_cpu_only_backend(self) -> Self {
self.with_backend(EncodeBackendPreference::CpuOnly)
}
#[must_use]
pub const fn with_strict_device_backend(self) -> Self {
self.with_backend(EncodeBackendPreference::RequireDevice)
}
#[must_use]
pub const fn with_block_coding_mode(mut self, block_coding_mode: J2kBlockCodingMode) -> Self {
self.block_coding_mode = block_coding_mode;
self
}
#[must_use]
pub const fn with_progression(mut self, progression: J2kProgressionOrder) -> Self {
self.progression = progression;
self
}
#[must_use]
pub const fn with_max_decomposition_levels(
mut self,
max_decomposition_levels: Option<u8>,
) -> Self {
self.max_decomposition_levels = max_decomposition_levels;
self
}
#[must_use]
pub const fn with_tile_size(mut self, tile_size: Option<(u32, u32)>) -> Self {
self.tile_size = tile_size;
self
}
#[must_use]
pub const fn with_tile_part_packet_limit(
mut self,
tile_part_packet_limit: Option<u16>,
) -> Self {
self.tile_part_packet_limit = tile_part_packet_limit;
self
}
#[must_use]
pub const fn with_quality_layers(mut self, quality_layers: u8) -> Self {
self.quality_layers = quality_layers;
self
}
#[must_use]
pub fn with_marker_segments(mut self, marker_segments: &[J2kMarkerSegment]) -> Self {
self.write_tlm = false;
self.write_plt = false;
self.write_plm = false;
self.write_ppm = false;
self.write_ppt = false;
self.write_sop = false;
self.write_eph = false;
for marker in marker_segments {
match marker {
J2kMarkerSegment::Sop => self.write_sop = true,
J2kMarkerSegment::Eph => self.write_eph = true,
J2kMarkerSegment::Tlm => self.write_tlm = true,
J2kMarkerSegment::Plt => self.write_plt = true,
J2kMarkerSegment::Plm => self.write_plm = true,
J2kMarkerSegment::Ppm => self.write_ppm = true,
J2kMarkerSegment::Ppt => self.write_ppt = true,
}
}
self
}
#[must_use]
pub const fn with_reversible_transform(
mut self,
reversible_transform: ReversibleTransform,
) -> Self {
self.reversible_transform = reversible_transform;
self
}
#[must_use]
pub const fn with_validation(mut self, validation: J2kEncodeValidation) -> Self {
self.validation = validation;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum J2kRateTarget {
BitsPerPixel(f64),
Bytes(u64),
PsnrDb(f64),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct J2kQualityLayer {
pub target: J2kRateTarget,
}
impl J2kQualityLayer {
#[must_use]
pub const fn new(target: J2kRateTarget) -> Self {
Self { target }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum J2kMarkerSegment {
Sop,
Eph,
Tlm,
Plt,
Plm,
Ppm,
Ppt,
}
#[derive(Debug, Clone, PartialEq)]
#[non_exhaustive]
pub struct J2kLossyEncodeOptions {
pub backend: EncodeBackendPreference,
pub block_coding_mode: J2kBlockCodingMode,
pub progression: J2kProgressionOrder,
pub max_decomposition_levels: Option<u8>,
pub rate_target: Option<J2kRateTarget>,
pub quality_layers: Vec<J2kQualityLayer>,
pub tile_size: Option<(u32, u32)>,
pub tile_part_packet_limit: Option<u16>,
pub precinct_exponents: Vec<(u8, u8)>,
pub marker_segments: Vec<J2kMarkerSegment>,
pub psnr_tolerance_db: f64,
pub psnr_iteration_budget: u8,
pub validation: J2kEncodeValidation,
}
impl Default for J2kLossyEncodeOptions {
fn default() -> Self {
Self {
backend: EncodeBackendPreference::Auto,
block_coding_mode: J2kBlockCodingMode::Classic,
progression: J2kProgressionOrder::Lrcp,
max_decomposition_levels: None,
rate_target: None,
quality_layers: Vec::new(),
tile_size: None,
tile_part_packet_limit: None,
precinct_exponents: Vec::new(),
marker_segments: Vec::new(),
psnr_tolerance_db: 0.25,
psnr_iteration_budget: 8,
validation: J2kEncodeValidation::CpuRoundTrip,
}
}
}
impl J2kLossyEncodeOptions {
#[must_use]
pub fn with_backend(mut self, backend: EncodeBackendPreference) -> Self {
self.backend = backend;
self
}
#[must_use]
pub fn with_accelerated_backend(self) -> Self {
self.with_backend(EncodeBackendPreference::Auto)
}
#[must_use]
pub fn with_cpu_only_backend(self) -> Self {
self.with_backend(EncodeBackendPreference::CpuOnly)
}
#[must_use]
pub fn with_strict_device_backend(self) -> Self {
self.with_backend(EncodeBackendPreference::RequireDevice)
}
#[must_use]
pub fn with_block_coding_mode(mut self, block_coding_mode: J2kBlockCodingMode) -> Self {
self.block_coding_mode = block_coding_mode;
self
}
#[must_use]
pub fn with_progression(mut self, progression: J2kProgressionOrder) -> Self {
self.progression = progression;
self
}
#[must_use]
pub fn with_max_decomposition_levels(mut self, max_decomposition_levels: Option<u8>) -> Self {
self.max_decomposition_levels = max_decomposition_levels;
self
}
#[must_use]
pub fn with_rate_target(mut self, rate_target: Option<J2kRateTarget>) -> Self {
self.rate_target = rate_target;
self
}
#[must_use]
pub fn with_quality_layers(mut self, quality_layers: Vec<J2kQualityLayer>) -> Self {
self.quality_layers = quality_layers;
self
}
#[must_use]
pub fn with_tile_size(mut self, tile_size: Option<(u32, u32)>) -> Self {
self.tile_size = tile_size;
self
}
#[must_use]
pub fn with_tile_part_packet_limit(mut self, tile_part_packet_limit: Option<u16>) -> Self {
self.tile_part_packet_limit = tile_part_packet_limit;
self
}
#[must_use]
pub fn with_marker_segments(mut self, marker_segments: Vec<J2kMarkerSegment>) -> Self {
self.marker_segments = marker_segments;
self
}
#[must_use]
pub fn with_validation(mut self, validation: J2kEncodeValidation) -> Self {
self.validation = validation;
self
}
}
define_encoded_j2k! {
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EncodedJ2k {
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct J2kLossyEncodeReport {
pub target: Option<J2kRateTarget>,
pub quality_layers: u16,
pub quantization_scale: f32,
pub actual_bytes: u64,
pub actual_bits_per_pixel: f64,
pub psnr_db: Option<f64>,
pub ht_rate_granularity_bytes: Option<u64>,
}
define_encoded_j2k! {
#[derive(Debug, Clone, PartialEq)]
pub struct EncodedLossyJ2k {
pub report: J2kLossyEncodeReport,
}
}