use core::fmt;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum UnsupportedOperation {
RowLevelEncode,
PullEncode,
AnimationEncode,
DecodeInto,
RowLevelDecode,
AnimationDecode,
PixelFormat,
MultiImageDecode,
}
impl UnsupportedOperation {
pub const fn name(self) -> &'static str {
match self {
Self::RowLevelEncode => "row_level_encode",
Self::PullEncode => "pull_encode",
Self::AnimationEncode => "animation_encode",
Self::DecodeInto => "decode_into",
Self::RowLevelDecode => "row_level_decode",
Self::AnimationDecode => "animation_decode",
Self::PixelFormat => "pixel_format",
Self::MultiImageDecode => "multi_image_decode",
}
}
}
impl fmt::Display for UnsupportedOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unsupported operation: {}", self.name())
}
}
impl core::error::Error for UnsupportedOperation {}
#[derive(Clone, PartialEq)]
#[non_exhaustive]
pub struct EncodeCapabilities {
icc: bool,
exif: bool,
xmp: bool,
cicp: bool,
stop: bool,
animation: bool,
push_rows: bool,
encode_from: bool,
lossy: bool,
lossless: bool,
hdr: bool,
gain_map: bool,
native_gray: bool,
native_16bit: bool,
native_f32: bool,
native_alpha: bool,
enforces_max_pixels: bool,
enforces_max_memory: bool,
effort_range: Option<[i32; 2]>,
quality_range: Option<[f32; 2]>,
threads_supported_range: (u16, u16),
}
impl Default for EncodeCapabilities {
fn default() -> Self {
Self::new()
}
}
impl EncodeCapabilities {
pub const EMPTY: Self = Self::new();
pub const fn new() -> Self {
Self {
icc: false,
exif: false,
xmp: false,
cicp: false,
stop: false,
animation: false,
push_rows: false,
encode_from: false,
lossy: false,
lossless: false,
hdr: false,
gain_map: false,
native_gray: false,
native_16bit: false,
native_f32: false,
native_alpha: false,
enforces_max_pixels: false,
enforces_max_memory: false,
effort_range: None,
quality_range: None,
threads_supported_range: (1, 1),
}
}
pub const fn icc(&self) -> bool {
self.icc
}
pub const fn exif(&self) -> bool {
self.exif
}
pub const fn xmp(&self) -> bool {
self.xmp
}
pub const fn cicp(&self) -> bool {
self.cicp
}
pub const fn stop(&self) -> bool {
self.stop
}
pub const fn animation(&self) -> bool {
self.animation
}
pub const fn push_rows(&self) -> bool {
self.push_rows
}
pub const fn encode_from(&self) -> bool {
self.encode_from
}
pub const fn lossy(&self) -> bool {
self.lossy
}
pub const fn lossless(&self) -> bool {
self.lossless
}
pub const fn hdr(&self) -> bool {
self.hdr
}
pub const fn gain_map(&self) -> bool {
self.gain_map
}
pub const fn native_gray(&self) -> bool {
self.native_gray
}
pub const fn native_16bit(&self) -> bool {
self.native_16bit
}
pub const fn native_f32(&self) -> bool {
self.native_f32
}
pub const fn native_alpha(&self) -> bool {
self.native_alpha
}
pub const fn enforces_max_pixels(&self) -> bool {
self.enforces_max_pixels
}
pub const fn enforces_max_memory(&self) -> bool {
self.enforces_max_memory
}
pub const fn effort_range(&self) -> Option<[i32; 2]> {
self.effort_range
}
pub const fn quality_range(&self) -> Option<[f32; 2]> {
self.quality_range
}
pub const fn threads_supported_range(&self) -> (u16, u16) {
self.threads_supported_range
}
pub const fn supports(&self, op: UnsupportedOperation) -> bool {
match op {
UnsupportedOperation::RowLevelEncode => self.push_rows,
UnsupportedOperation::PullEncode => self.encode_from,
UnsupportedOperation::AnimationEncode => self.animation,
UnsupportedOperation::DecodeInto
| UnsupportedOperation::RowLevelDecode
| UnsupportedOperation::AnimationDecode
| UnsupportedOperation::MultiImageDecode
| UnsupportedOperation::PixelFormat => false,
}
}
pub const fn with_icc(mut self, v: bool) -> Self {
self.icc = v;
self
}
pub const fn with_exif(mut self, v: bool) -> Self {
self.exif = v;
self
}
pub const fn with_xmp(mut self, v: bool) -> Self {
self.xmp = v;
self
}
pub const fn with_cicp(mut self, v: bool) -> Self {
self.cicp = v;
self
}
pub const fn with_stop(mut self, v: bool) -> Self {
self.stop = v;
self
}
pub const fn with_animation(mut self, v: bool) -> Self {
self.animation = v;
self
}
pub const fn with_push_rows(mut self, v: bool) -> Self {
self.push_rows = v;
self
}
pub const fn with_encode_from(mut self, v: bool) -> Self {
self.encode_from = v;
self
}
pub const fn with_lossy(mut self, v: bool) -> Self {
self.lossy = v;
self
}
pub const fn with_lossless(mut self, v: bool) -> Self {
self.lossless = v;
self
}
pub const fn with_hdr(mut self, v: bool) -> Self {
self.hdr = v;
self
}
pub const fn with_gain_map(mut self, v: bool) -> Self {
self.gain_map = v;
self
}
pub const fn with_native_gray(mut self, v: bool) -> Self {
self.native_gray = v;
self
}
pub const fn with_native_16bit(mut self, v: bool) -> Self {
self.native_16bit = v;
self
}
pub const fn with_native_f32(mut self, v: bool) -> Self {
self.native_f32 = v;
self
}
pub const fn with_native_alpha(mut self, v: bool) -> Self {
self.native_alpha = v;
self
}
pub const fn with_enforces_max_pixels(mut self, v: bool) -> Self {
self.enforces_max_pixels = v;
self
}
pub const fn with_enforces_max_memory(mut self, v: bool) -> Self {
self.enforces_max_memory = v;
self
}
pub const fn with_effort_range(mut self, min: i32, max: i32) -> Self {
assert!(min <= max, "effort range: min must be <= max");
self.effort_range = Some([min, max]);
self
}
pub const fn with_quality_range(mut self, min: f32, max: f32) -> Self {
assert!(min <= max, "quality range: min must be <= max");
self.quality_range = Some([min, max]);
self
}
pub const fn with_threads_supported_range(mut self, min: u16, max: u16) -> Self {
assert!(min >= 1, "threads range: min must be >= 1");
assert!(min <= max, "threads range: min must be <= max");
self.threads_supported_range = (min, max);
self
}
}
impl fmt::Debug for EncodeCapabilities {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("EncodeCapabilities");
s.field("icc", &self.icc)
.field("exif", &self.exif)
.field("xmp", &self.xmp)
.field("cicp", &self.cicp)
.field("stop", &self.stop)
.field("animation", &self.animation)
.field("lossy", &self.lossy)
.field("lossless", &self.lossless)
.field("hdr", &self.hdr)
.field("gain_map", &self.gain_map)
.field("native_gray", &self.native_gray)
.field("native_16bit", &self.native_16bit)
.field("native_f32", &self.native_f32)
.field("native_alpha", &self.native_alpha)
.field("push_rows", &self.push_rows)
.field("encode_from", &self.encode_from)
.field("enforces_max_pixels", &self.enforces_max_pixels)
.field("enforces_max_memory", &self.enforces_max_memory)
.field("threads_supported_range", &self.threads_supported_range);
if let Some(range) = &self.effort_range {
s.field("effort_range", range);
}
if let Some(range) = &self.quality_range {
s.field("quality_range", range);
}
s.finish()
}
}
#[derive(Clone, PartialEq, Eq)]
#[non_exhaustive]
pub struct DecodeCapabilities {
icc: bool,
exif: bool,
xmp: bool,
cicp: bool,
stop: bool,
animation: bool,
multi_image: bool,
cheap_probe: bool,
decode_into: bool,
streaming: bool,
hdr: bool,
gain_map: bool,
native_gray: bool,
native_16bit: bool,
native_f32: bool,
native_alpha: bool,
enforces_max_pixels: bool,
enforces_max_memory: bool,
enforces_max_input_bytes: bool,
threads_supported_range: (u16, u16),
}
impl Default for DecodeCapabilities {
fn default() -> Self {
Self::new()
}
}
impl DecodeCapabilities {
pub const EMPTY: Self = Self::new();
pub const fn new() -> Self {
Self {
icc: false,
exif: false,
xmp: false,
cicp: false,
stop: false,
animation: false,
multi_image: false,
cheap_probe: false,
decode_into: false,
streaming: false,
hdr: false,
gain_map: false,
native_gray: false,
native_16bit: false,
native_f32: false,
native_alpha: false,
enforces_max_pixels: false,
enforces_max_memory: false,
enforces_max_input_bytes: false,
threads_supported_range: (1, 1),
}
}
pub const fn icc(&self) -> bool {
self.icc
}
pub const fn exif(&self) -> bool {
self.exif
}
pub const fn xmp(&self) -> bool {
self.xmp
}
pub const fn cicp(&self) -> bool {
self.cicp
}
pub const fn stop(&self) -> bool {
self.stop
}
pub const fn animation(&self) -> bool {
self.animation
}
pub const fn multi_image(&self) -> bool {
self.multi_image
}
pub const fn cheap_probe(&self) -> bool {
self.cheap_probe
}
pub const fn decode_into(&self) -> bool {
self.decode_into
}
pub const fn streaming(&self) -> bool {
self.streaming
}
pub const fn hdr(&self) -> bool {
self.hdr
}
pub const fn gain_map(&self) -> bool {
self.gain_map
}
pub const fn native_gray(&self) -> bool {
self.native_gray
}
pub const fn native_16bit(&self) -> bool {
self.native_16bit
}
pub const fn native_f32(&self) -> bool {
self.native_f32
}
pub const fn native_alpha(&self) -> bool {
self.native_alpha
}
pub const fn enforces_max_pixels(&self) -> bool {
self.enforces_max_pixels
}
pub const fn enforces_max_memory(&self) -> bool {
self.enforces_max_memory
}
pub const fn enforces_max_input_bytes(&self) -> bool {
self.enforces_max_input_bytes
}
pub const fn threads_supported_range(&self) -> (u16, u16) {
self.threads_supported_range
}
pub const fn supports(&self, op: UnsupportedOperation) -> bool {
match op {
UnsupportedOperation::DecodeInto => self.decode_into,
UnsupportedOperation::RowLevelDecode => self.streaming,
UnsupportedOperation::AnimationDecode => self.animation,
UnsupportedOperation::MultiImageDecode => self.multi_image,
UnsupportedOperation::RowLevelEncode
| UnsupportedOperation::PullEncode
| UnsupportedOperation::AnimationEncode
| UnsupportedOperation::PixelFormat => false,
}
}
pub const fn with_icc(mut self, v: bool) -> Self {
self.icc = v;
self
}
pub const fn with_exif(mut self, v: bool) -> Self {
self.exif = v;
self
}
pub const fn with_xmp(mut self, v: bool) -> Self {
self.xmp = v;
self
}
pub const fn with_cicp(mut self, v: bool) -> Self {
self.cicp = v;
self
}
pub const fn with_stop(mut self, v: bool) -> Self {
self.stop = v;
self
}
pub const fn with_animation(mut self, v: bool) -> Self {
self.animation = v;
self
}
pub const fn with_multi_image(mut self, v: bool) -> Self {
self.multi_image = v;
self
}
pub const fn with_cheap_probe(mut self, v: bool) -> Self {
self.cheap_probe = v;
self
}
pub const fn with_decode_into(mut self, v: bool) -> Self {
self.decode_into = v;
self
}
pub const fn with_streaming(mut self, v: bool) -> Self {
self.streaming = v;
self
}
pub const fn with_hdr(mut self, v: bool) -> Self {
self.hdr = v;
self
}
pub const fn with_gain_map(mut self, v: bool) -> Self {
self.gain_map = v;
self
}
pub const fn with_native_gray(mut self, v: bool) -> Self {
self.native_gray = v;
self
}
pub const fn with_native_16bit(mut self, v: bool) -> Self {
self.native_16bit = v;
self
}
pub const fn with_native_f32(mut self, v: bool) -> Self {
self.native_f32 = v;
self
}
pub const fn with_native_alpha(mut self, v: bool) -> Self {
self.native_alpha = v;
self
}
pub const fn with_enforces_max_pixels(mut self, v: bool) -> Self {
self.enforces_max_pixels = v;
self
}
pub const fn with_enforces_max_memory(mut self, v: bool) -> Self {
self.enforces_max_memory = v;
self
}
pub const fn with_enforces_max_input_bytes(mut self, v: bool) -> Self {
self.enforces_max_input_bytes = v;
self
}
pub const fn with_threads_supported_range(mut self, min: u16, max: u16) -> Self {
assert!(min >= 1, "threads range: min must be >= 1");
assert!(min <= max, "threads range: min must be <= max");
self.threads_supported_range = (min, max);
self
}
}
impl fmt::Debug for DecodeCapabilities {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = f.debug_struct("DecodeCapabilities");
s.field("icc", &self.icc)
.field("exif", &self.exif)
.field("xmp", &self.xmp)
.field("cicp", &self.cicp)
.field("stop", &self.stop)
.field("animation", &self.animation)
.field("multi_image", &self.multi_image)
.field("cheap_probe", &self.cheap_probe)
.field("decode_into", &self.decode_into)
.field("streaming", &self.streaming)
.field("hdr", &self.hdr)
.field("gain_map", &self.gain_map)
.field("native_gray", &self.native_gray)
.field("native_16bit", &self.native_16bit)
.field("native_f32", &self.native_f32)
.field("native_alpha", &self.native_alpha)
.field("enforces_max_pixels", &self.enforces_max_pixels)
.field("enforces_max_memory", &self.enforces_max_memory)
.field("enforces_max_input_bytes", &self.enforces_max_input_bytes)
.field("threads_supported_range", &self.threads_supported_range);
s.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn encode_default_all_false() {
let caps = EncodeCapabilities::new();
assert!(!caps.icc());
assert!(!caps.exif());
assert!(!caps.xmp());
assert!(!caps.cicp());
assert!(!caps.stop());
assert!(!caps.animation());
assert!(!caps.push_rows());
assert!(!caps.encode_from());
assert!(!caps.lossy());
assert!(!caps.lossless());
assert!(!caps.hdr());
assert!(!caps.native_gray());
assert!(!caps.native_16bit());
assert!(!caps.native_f32());
assert!(!caps.native_alpha());
assert!(!caps.enforces_max_pixels());
assert!(!caps.enforces_max_memory());
assert!(caps.effort_range().is_none());
assert!(caps.quality_range().is_none());
assert_eq!(caps.threads_supported_range(), (1, 1));
}
#[test]
fn decode_default_all_false() {
let caps = DecodeCapabilities::new();
assert!(!caps.icc());
assert!(!caps.exif());
assert!(!caps.xmp());
assert!(!caps.cicp());
assert!(!caps.stop());
assert!(!caps.animation());
assert!(!caps.cheap_probe());
assert!(!caps.decode_into());
assert!(!caps.streaming());
assert!(!caps.hdr());
assert!(!caps.native_gray());
assert!(!caps.native_16bit());
assert!(!caps.native_f32());
assert!(!caps.native_alpha());
assert!(!caps.enforces_max_pixels());
assert!(!caps.enforces_max_memory());
assert!(!caps.enforces_max_input_bytes());
assert_eq!(caps.threads_supported_range(), (1, 1));
}
#[test]
fn encode_builder() {
let caps = EncodeCapabilities::new()
.with_icc(true)
.with_stop(true)
.with_native_gray(true)
.with_animation(true)
.with_native_16bit(true)
.with_hdr(true)
.with_threads_supported_range(1, 8);
assert!(caps.icc());
assert!(!caps.exif());
assert!(caps.stop());
assert!(caps.native_gray());
assert!(caps.animation());
assert!(caps.native_16bit());
assert!(!caps.lossless());
assert!(caps.hdr());
assert_eq!(caps.threads_supported_range(), (1, 8));
}
#[test]
fn decode_builder() {
let caps = DecodeCapabilities::new()
.with_icc(true)
.with_cheap_probe(true)
.with_stop(true)
.with_animation(true)
.with_enforces_max_input_bytes(true)
.with_threads_supported_range(1, 4);
assert!(caps.icc());
assert!(caps.cheap_probe());
assert!(caps.stop());
assert!(caps.animation());
assert!(caps.enforces_max_input_bytes());
assert_eq!(caps.threads_supported_range(), (1, 4));
}
#[test]
fn encode_static_construction() {
static CAPS: EncodeCapabilities = EncodeCapabilities::new()
.with_icc(true)
.with_exif(true)
.with_xmp(true)
.with_stop(true)
.with_animation(true)
.with_lossless(true)
.with_cicp(true)
.with_effort_range(0, 100)
.with_quality_range(0.0, 100.0)
.with_threads_supported_range(1, 16);
assert!(CAPS.icc());
assert!(CAPS.stop());
assert!(!CAPS.native_gray());
assert!(CAPS.animation());
assert!(CAPS.lossless());
assert!(CAPS.cicp());
assert_eq!(CAPS.effort_range(), Some([0, 100]));
assert_eq!(CAPS.quality_range(), Some([0.0, 100.0]));
assert_eq!(CAPS.threads_supported_range(), (1, 16));
}
#[test]
fn decode_static_construction() {
static CAPS: DecodeCapabilities = DecodeCapabilities::new()
.with_icc(true)
.with_cheap_probe(true)
.with_stop(true)
.with_animation(true)
.with_enforces_max_pixels(true)
.with_enforces_max_input_bytes(true);
assert!(CAPS.icc());
assert!(CAPS.cheap_probe());
assert!(CAPS.enforces_max_pixels());
assert!(!CAPS.enforces_max_memory());
assert!(CAPS.enforces_max_input_bytes());
}
#[test]
fn encode_effort_quality_ranges() {
let caps = EncodeCapabilities::new()
.with_effort_range(0, 10)
.with_quality_range(0.0, 100.0);
assert_eq!(caps.effort_range(), Some([0i32, 10]));
assert_eq!(caps.quality_range(), Some([0.0, 100.0]));
}
#[test]
fn encode_supports() {
let caps = EncodeCapabilities::new()
.with_push_rows(true)
.with_encode_from(true)
.with_animation(true);
assert!(caps.supports(UnsupportedOperation::RowLevelEncode));
assert!(caps.supports(UnsupportedOperation::PullEncode));
assert!(caps.supports(UnsupportedOperation::AnimationEncode));
assert!(!caps.supports(UnsupportedOperation::DecodeInto));
assert!(!caps.supports(UnsupportedOperation::RowLevelDecode));
assert!(!caps.supports(UnsupportedOperation::AnimationDecode));
assert!(!caps.supports(UnsupportedOperation::PixelFormat));
}
#[test]
fn decode_supports() {
let caps = DecodeCapabilities::new()
.with_decode_into(true)
.with_streaming(true)
.with_animation(true);
assert!(caps.supports(UnsupportedOperation::DecodeInto));
assert!(caps.supports(UnsupportedOperation::RowLevelDecode));
assert!(caps.supports(UnsupportedOperation::AnimationDecode));
assert!(!caps.supports(UnsupportedOperation::RowLevelEncode));
assert!(!caps.supports(UnsupportedOperation::PullEncode));
assert!(!caps.supports(UnsupportedOperation::AnimationEncode));
assert!(!caps.supports(UnsupportedOperation::PixelFormat));
}
#[test]
fn supports_empty_all_false() {
let enc = EncodeCapabilities::new();
let dec = DecodeCapabilities::new();
for op in [
UnsupportedOperation::RowLevelEncode,
UnsupportedOperation::PullEncode,
UnsupportedOperation::AnimationEncode,
UnsupportedOperation::DecodeInto,
UnsupportedOperation::RowLevelDecode,
UnsupportedOperation::AnimationDecode,
UnsupportedOperation::PixelFormat,
] {
assert!(
!enc.supports(op),
"EncodeCapabilities::EMPTY.supports({op:?})"
);
assert!(
!dec.supports(op),
"DecodeCapabilities::EMPTY.supports({op:?})"
);
}
}
#[test]
fn unsupported_operation_display() {
assert_eq!(
alloc::format!("{}", UnsupportedOperation::RowLevelEncode),
"unsupported operation: row_level_encode"
);
assert_eq!(
alloc::format!("{}", UnsupportedOperation::DecodeInto),
"unsupported operation: decode_into"
);
assert_eq!(
alloc::format!("{}", UnsupportedOperation::PixelFormat),
"unsupported operation: pixel_format"
);
}
#[test]
fn unsupported_operation_name() {
assert_eq!(UnsupportedOperation::PullEncode.name(), "pull_encode");
assert_eq!(
UnsupportedOperation::AnimationEncode.name(),
"animation_encode"
);
assert_eq!(
UnsupportedOperation::RowLevelDecode.name(),
"row_level_decode"
);
assert_eq!(
UnsupportedOperation::AnimationDecode.name(),
"animation_decode"
);
}
#[test]
fn unsupported_operation_is_error() {
let op = UnsupportedOperation::DecodeInto;
let err: &dyn core::error::Error = &op;
assert!(err.source().is_none());
}
#[test]
fn unsupported_operation_eq_hash() {
assert_eq!(
UnsupportedOperation::DecodeInto,
UnsupportedOperation::DecodeInto
);
assert_ne!(
UnsupportedOperation::DecodeInto,
UnsupportedOperation::PullEncode
);
}
}