use core::fmt;
use core::marker::PhantomData;
use j2k_core::{BackendKind, DeviceMemoryRange};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResidentHandoffError {
EmptyRange,
OffsetOverflow,
RangeExceedsAllocation,
BackendMismatch {
expected: BackendKind,
actual: BackendKind,
},
ZeroDimension,
ZeroSampling,
InvalidBitDepth,
ZeroByteStride,
LayoutExceedsBuffer,
CodestreamExceedsCapacity,
}
impl fmt::Display for ResidentHandoffError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::EmptyRange => f.write_str("resident buffer range is empty"),
Self::OffsetOverflow => f.write_str("resident buffer range offset overflows"),
Self::RangeExceedsAllocation => {
f.write_str("resident buffer range exceeds allocation length")
}
Self::BackendMismatch { expected, actual } => write!(
f,
"resident buffer backend mismatch: expected {expected:?}, got {actual:?}"
),
Self::ZeroDimension => f.write_str("resident component dimensions must be nonzero"),
Self::ZeroSampling => f.write_str("resident component sampling must be nonzero"),
Self::InvalidBitDepth => f.write_str("resident sample bit depth must be 1..=32"),
Self::ZeroByteStride => f.write_str("resident byte stride must be nonzero"),
Self::LayoutExceedsBuffer => f.write_str("resident row layout exceeds buffer range"),
Self::CodestreamExceedsCapacity => {
f.write_str("resident codestream byte length exceeds buffer capacity")
}
}
}
}
impl std::error::Error for ResidentHandoffError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResidentBufferRef<'a> {
memory: DeviceMemoryRange,
_lifetime: PhantomData<&'a ()>,
}
impl ResidentBufferRef<'_> {
pub fn new(memory: DeviceMemoryRange) -> Result<Self, ResidentHandoffError> {
if memory.len == 0 {
return Err(ResidentHandoffError::EmptyRange);
}
memory
.offset
.checked_add(memory.len)
.ok_or(ResidentHandoffError::OffsetOverflow)?;
Ok(Self {
memory,
_lifetime: PhantomData,
})
}
pub fn with_allocation_len(
memory: DeviceMemoryRange,
allocation_len: usize,
) -> Result<Self, ResidentHandoffError> {
let buffer = Self::new(memory)?;
let end = memory
.offset
.checked_add(memory.len)
.ok_or(ResidentHandoffError::OffsetOverflow)?;
if end > allocation_len {
return Err(ResidentHandoffError::RangeExceedsAllocation);
}
Ok(buffer)
}
pub const fn memory_range(&self) -> DeviceMemoryRange {
self.memory
}
pub const fn backend(&self) -> BackendKind {
self.memory.backend
}
pub const fn byte_len(&self) -> usize {
self.memory.len
}
fn require_backend(self, backend: BackendKind) -> Result<Self, ResidentHandoffError> {
if self.memory.backend == backend {
Ok(self)
} else {
Err(ResidentHandoffError::BackendMismatch {
expected: backend,
actual: self.memory.backend,
})
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResidentSampling {
pub x_rsiz: u8,
pub y_rsiz: u8,
}
impl ResidentSampling {
pub const fn new(x_rsiz: u8, y_rsiz: u8) -> Result<Self, ResidentHandoffError> {
if x_rsiz == 0 || y_rsiz == 0 {
return Err(ResidentHandoffError::ZeroSampling);
}
Ok(Self { x_rsiz, y_rsiz })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResidentSampleInfo {
pub bit_depth: u8,
pub signed: bool,
}
impl ResidentSampleInfo {
pub const fn new(bit_depth: u8, signed: bool) -> Result<Self, ResidentHandoffError> {
if bit_depth == 0 || bit_depth > 32 {
return Err(ResidentHandoffError::InvalidBitDepth);
}
Ok(Self { bit_depth, signed })
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResidentColorModel {
Unknown,
Grayscale,
Rgb,
YCbCr,
Rgba,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResidentComponentGeometry {
pub component_index: usize,
pub width: u32,
pub height: u32,
pub sampling: ResidentSampling,
}
impl ResidentComponentGeometry {
pub const fn new(
component_index: usize,
width: u32,
height: u32,
sampling: ResidentSampling,
) -> Result<Self, ResidentHandoffError> {
if width == 0 || height == 0 {
return Err(ResidentHandoffError::ZeroDimension);
}
Ok(Self {
component_index,
width,
height,
sampling,
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResidentDctCoefficientOrder {
Natural,
ZigZag,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResidentJpegDctGrid<'a> {
pub buffer: ResidentBufferRef<'a>,
pub component: ResidentComponentGeometry,
pub sample: ResidentSampleInfo,
pub color: ResidentColorModel,
pub block_cols: u32,
pub block_rows: u32,
pub row_pitch_bytes: usize,
pub bytes_per_coefficient: usize,
pub coefficient_order: ResidentDctCoefficientOrder,
}
impl<'a> ResidentJpegDctGrid<'a> {
pub fn new(
buffer: ResidentBufferRef<'a>,
component: ResidentComponentGeometry,
sample: ResidentSampleInfo,
color: ResidentColorModel,
layout: ResidentDctGridLayout,
) -> Result<Self, ResidentHandoffError> {
if layout.block_cols == 0 || layout.block_rows == 0 {
return Err(ResidentHandoffError::ZeroDimension);
}
if layout.row_pitch_bytes == 0 || layout.bytes_per_coefficient == 0 {
return Err(ResidentHandoffError::ZeroByteStride);
}
let row_coefficients = usize::try_from(layout.block_cols)
.ok()
.and_then(|cols| cols.checked_mul(64))
.ok_or(ResidentHandoffError::OffsetOverflow)?;
validate_row_layout_fits_buffer(
buffer,
row_coefficients,
layout.block_rows,
layout.row_pitch_bytes,
layout.bytes_per_coefficient,
)?;
Ok(Self {
buffer,
component,
sample,
color,
block_cols: layout.block_cols,
block_rows: layout.block_rows,
row_pitch_bytes: layout.row_pitch_bytes,
bytes_per_coefficient: layout.bytes_per_coefficient,
coefficient_order: layout.coefficient_order,
})
}
pub fn require_backend(self, backend: BackendKind) -> Result<Self, ResidentHandoffError> {
self.buffer.require_backend(backend)?;
Ok(self)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResidentDctGridLayout {
pub block_cols: u32,
pub block_rows: u32,
pub row_pitch_bytes: usize,
pub bytes_per_coefficient: usize,
pub coefficient_order: ResidentDctCoefficientOrder,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResidentDwtSubbandKind {
LowLow,
HighLow,
LowHigh,
HighHigh,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResidentDwtSubband<'a> {
pub buffer: ResidentBufferRef<'a>,
pub component: ResidentComponentGeometry,
pub sample: ResidentSampleInfo,
pub color: ResidentColorModel,
pub level: u8,
pub subband: ResidentDwtSubbandKind,
pub width: u32,
pub height: u32,
pub row_pitch_bytes: usize,
pub bytes_per_coefficient: usize,
}
impl<'a> ResidentDwtSubband<'a> {
pub fn new(
buffer: ResidentBufferRef<'a>,
component: ResidentComponentGeometry,
sample: ResidentSampleInfo,
color: ResidentColorModel,
layout: ResidentDwtSubbandLayout,
) -> Result<Self, ResidentHandoffError> {
if layout.width == 0 || layout.height == 0 {
return Err(ResidentHandoffError::ZeroDimension);
}
if layout.row_pitch_bytes == 0 || layout.bytes_per_coefficient == 0 {
return Err(ResidentHandoffError::ZeroByteStride);
}
validate_row_layout_fits_buffer(
buffer,
usize::try_from(layout.width).map_err(|_| ResidentHandoffError::OffsetOverflow)?,
layout.height,
layout.row_pitch_bytes,
layout.bytes_per_coefficient,
)?;
Ok(Self {
buffer,
component,
sample,
color,
level: layout.level,
subband: layout.subband,
width: layout.width,
height: layout.height,
row_pitch_bytes: layout.row_pitch_bytes,
bytes_per_coefficient: layout.bytes_per_coefficient,
})
}
pub fn require_backend(self, backend: BackendKind) -> Result<Self, ResidentHandoffError> {
self.buffer.require_backend(backend)?;
Ok(self)
}
}
fn validate_row_layout_fits_buffer(
buffer: ResidentBufferRef<'_>,
row_values: usize,
rows: u32,
row_pitch_bytes: usize,
bytes_per_value: usize,
) -> Result<(), ResidentHandoffError> {
let row_bytes = row_values
.checked_mul(bytes_per_value)
.ok_or(ResidentHandoffError::OffsetOverflow)?;
if row_pitch_bytes < row_bytes {
return Err(ResidentHandoffError::LayoutExceedsBuffer);
}
let rows = usize::try_from(rows).map_err(|_| ResidentHandoffError::OffsetOverflow)?;
let last_row_offset = rows
.saturating_sub(1)
.checked_mul(row_pitch_bytes)
.ok_or(ResidentHandoffError::OffsetOverflow)?;
let required_len = last_row_offset
.checked_add(row_bytes)
.ok_or(ResidentHandoffError::OffsetOverflow)?;
if required_len > buffer.byte_len() {
return Err(ResidentHandoffError::LayoutExceedsBuffer);
}
Ok(())
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResidentDwtSubbandLayout {
pub level: u8,
pub subband: ResidentDwtSubbandKind,
pub width: u32,
pub height: u32,
pub row_pitch_bytes: usize,
pub bytes_per_coefficient: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ResidentCodestreamBuffer<'a> {
pub buffer: ResidentBufferRef<'a>,
pub byte_len: usize,
pub capacity: usize,
}
impl<'a> ResidentCodestreamBuffer<'a> {
pub fn new(
buffer: ResidentBufferRef<'a>,
byte_len: usize,
capacity: usize,
) -> Result<Self, ResidentHandoffError> {
if byte_len > capacity || capacity > buffer.byte_len() {
return Err(ResidentHandoffError::CodestreamExceedsCapacity);
}
Ok(Self {
buffer,
byte_len,
capacity,
})
}
pub fn require_backend(self, backend: BackendKind) -> Result<Self, ResidentHandoffError> {
self.buffer.require_backend(backend)?;
Ok(self)
}
}