use crate::{BackendRequest, BandPresence, JxrError, LimitKind, PixelFormat, Rect};
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
pub enum DecodeScale {
#[default]
Full,
Quarter,
Sixteenth,
}
impl DecodeScale {
#[must_use]
pub const fn denominator(self) -> u32 {
match self {
Self::Full => 1,
Self::Quarter => 4,
Self::Sixteenth => 16,
}
}
#[must_use]
pub const fn retained_bands(self, available: BandPresence) -> BandPresence {
match self {
Self::Full => available,
Self::Quarter if available.has_low_pass() => BandPresence::NoHighPass,
Self::Quarter | Self::Sixteenth => BandPresence::DcOnly,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum AlphaHandling {
#[default]
Preserve,
Drop,
Premultiply,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct DecodeLimits {
pub max_width: u32,
pub max_height: u32,
pub max_pixels: u64,
pub max_components: u16,
pub max_tiles: u32,
pub max_compressed_bytes: u64,
pub max_coefficient_bytes: u64,
pub max_host_allocation_bytes: u64,
}
impl Default for DecodeLimits {
fn default() -> Self {
Self {
max_width: 1 << 20,
max_height: 1 << 20,
max_pixels: 1 << 30,
max_components: 64,
max_tiles: 1 << 20,
max_compressed_bytes: 1 << 30,
max_coefficient_bytes: 1 << 32,
max_host_allocation_bytes: 1 << 32,
}
}
}
impl DecodeLimits {
pub fn check_dimensions(self, width: u32, height: u32) -> Result<(), JxrError> {
check_limit(
"image width",
LimitKind::Width,
u64::from(width),
u64::from(self.max_width),
)?;
check_limit(
"image height",
LimitKind::Height,
u64::from(height),
u64::from(self.max_height),
)?;
let pixels = u64::from(width)
.checked_mul(u64::from(height))
.ok_or_else(|| JxrError::arithmetic("image pixel count"))?;
check_limit(
"image pixel count",
LimitKind::Pixels,
pixels,
self.max_pixels,
)
}
pub fn check_components(self, components: u16) -> Result<(), JxrError> {
check_limit(
"component count",
LimitKind::Components,
u64::from(components),
u64::from(self.max_components),
)
}
pub fn check_tiles(self, tiles: u32) -> Result<(), JxrError> {
check_limit(
"tile count",
LimitKind::Tiles,
u64::from(tiles),
u64::from(self.max_tiles),
)
}
pub fn check_compressed_bytes(self, bytes: usize) -> Result<(), JxrError> {
let requested =
u64::try_from(bytes).map_err(|_| JxrError::arithmetic("compressed input size"))?;
check_limit(
"compressed input size",
LimitKind::CompressedBytes,
requested,
self.max_compressed_bytes,
)
}
pub fn check_coefficient_bytes(self, bytes: usize) -> Result<(), JxrError> {
let requested =
u64::try_from(bytes).map_err(|_| JxrError::arithmetic("coefficient arena size"))?;
check_limit(
"coefficient arena size",
LimitKind::CoefficientBytes,
requested,
self.max_coefficient_bytes,
)
}
pub fn check_host_allocation_bytes(self, bytes: usize) -> Result<(), JxrError> {
let requested =
u64::try_from(bytes).map_err(|_| JxrError::arithmetic("host output size"))?;
check_limit(
"host output size",
LimitKind::HostAllocationBytes,
requested,
self.max_host_allocation_bytes,
)
}
}
fn check_limit(
operation: &'static str,
limit: LimitKind,
requested: u64,
maximum: u64,
) -> Result<(), JxrError> {
if requested > maximum {
Err(JxrError::limit(operation, limit, requested, maximum))
} else {
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct DecodeRequest {
pub region: Option<Rect>,
pub scale: DecodeScale,
pub output: PixelFormat,
pub alpha: AlphaHandling,
pub backend: BackendRequest,
pub limits: DecodeLimits,
}
impl DecodeRequest {
#[must_use]
pub fn new(output: PixelFormat) -> Self {
Self {
region: None,
scale: DecodeScale::Full,
output,
alpha: AlphaHandling::Preserve,
backend: BackendRequest::Auto,
limits: DecodeLimits::default(),
}
}
#[must_use]
pub const fn with_region(mut self, region: Rect) -> Self {
self.region = Some(region);
self
}
#[must_use]
pub const fn with_scale(mut self, scale: DecodeScale) -> Self {
self.scale = scale;
self
}
#[must_use]
pub const fn with_alpha(mut self, alpha: AlphaHandling) -> Self {
self.alpha = alpha;
self
}
#[must_use]
pub const fn with_backend(mut self, backend: BackendRequest) -> Self {
self.backend = backend;
self
}
#[must_use]
pub const fn with_limits(mut self, limits: DecodeLimits) -> Self {
self.limits = limits;
self
}
}