use crate::ImageSize;
use std::error::Error;
use std::fmt::Display;
#[derive(Copy, Clone, Debug)]
pub struct PicScaleBufferMismatch {
pub expected: usize,
pub width: usize,
pub height: usize,
pub channels: usize,
pub slice_len: usize,
}
#[derive(Debug)]
pub enum PicScaleError {
ZeroImageDimensions,
SourceImageIsTooLarge,
DestinationImageIsTooLarge,
BufferMismatch(PicScaleBufferMismatch),
InvalidStride(usize, usize),
UnsupportedBitDepth(usize),
UnknownResizingFilter,
OutOfMemory(usize),
Generic(String),
InvalidScratchSize {
expected: usize,
size: usize,
},
InvalidSourceSize {
expected: ImageSize,
size: ImageSize,
},
InvalidDestinationSize {
expected: ImageSize,
size: ImageSize,
},
EmptyPlan,
CropOutOfBounds {
x: usize,
y: usize,
width: usize,
height: usize,
image_width: usize,
image_height: usize,
},
}
impl PicScaleError {
#[inline]
pub fn code(&self) -> usize {
match self {
PicScaleError::ZeroImageDimensions => 1,
PicScaleError::SourceImageIsTooLarge => 2,
PicScaleError::DestinationImageIsTooLarge => 3,
PicScaleError::BufferMismatch(_) => 4,
PicScaleError::InvalidStride(_, _) => 5,
PicScaleError::UnsupportedBitDepth(_) => 6,
PicScaleError::UnknownResizingFilter => 7,
PicScaleError::OutOfMemory(_) => 8,
PicScaleError::InvalidScratchSize { .. } => 9,
PicScaleError::InvalidSourceSize { .. } => 10,
PicScaleError::InvalidDestinationSize { .. } => 11,
PicScaleError::EmptyPlan => 12,
PicScaleError::Generic(_) => 13,
PicScaleError::CropOutOfBounds { .. } => 14,
}
}
}
impl Display for PicScaleError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PicScaleError::InvalidStride(min_stride, real_stride) => f.write_fmt(format_args!(
"Stride must be at least {min_stride}, but received {real_stride}",
)),
PicScaleError::ZeroImageDimensions => {
f.write_str("One of image dimensions is 0, this should not happen")
}
PicScaleError::SourceImageIsTooLarge => {
f.write_str("Input image larger than memory capabilities")
}
PicScaleError::DestinationImageIsTooLarge => {
f.write_str("Destination image larger than memory capabilities")
}
PicScaleError::BufferMismatch(buffer_mismatch) => f.write_fmt(format_args!(
"Image buffer len expected to be {} [w({})*h({})*channels({})] but received {}",
buffer_mismatch.expected,
buffer_mismatch.width,
buffer_mismatch.height,
buffer_mismatch.channels,
buffer_mismatch.slice_len,
)),
PicScaleError::UnsupportedBitDepth(depth) => {
f.write_fmt(format_args!("Bit-depth must be in [1, 16] but got {depth}",))
}
PicScaleError::UnknownResizingFilter => {
f.write_str("Unknown resizing filter was requested")
}
PicScaleError::OutOfMemory(capacity) => f.write_fmt(format_args!(
"There is no enough memory to allocate {capacity} bytes"
)),
PicScaleError::InvalidScratchSize { expected, size } => f.write_fmt(format_args!(
"Scratch size must be at least {expected} bytes, but received {size}",
)),
PicScaleError::InvalidSourceSize { expected, size } => f.write_fmt(format_args!(
"Source size must be at least {:?} bytes, but received {:?}",
expected, size
)),
PicScaleError::InvalidDestinationSize { expected, size } => f.write_fmt(format_args!(
"Destination size must be at least {:?} bytes, but received {:?}",
expected, size
)),
PicScaleError::EmptyPlan => {
f.write_str("Multi-step scaling appeared to be an empty one")
}
PicScaleError::Generic(x) => f.write_str(x),
PicScaleError::CropOutOfBounds {
x,
y,
width,
height,
image_width,
image_height,
} => write!(
f,
"Crop region ({x}, {y}, {width}x{height}) exceeds image bounds ({image_width}x{image_height})"
),
}
}
}
impl Error for PicScaleError {}
macro_rules! try_vec {
() => {
Vec::new()
};
($elem:expr; $n:expr) => {{
let mut v = Vec::new();
v.try_reserve_exact($n)
.map_err(|_| crate::validation::PicScaleError::OutOfMemory($n))?;
v.resize($n, $elem);
v
}};
}
pub(crate) use try_vec;
macro_rules! validate_scratch {
($scratch: expr, $required_size: expr) => {{
if $scratch.len() < $required_size {
return Err(PicScaleError::InvalidScratchSize {
expected: $required_size,
size: $scratch.len(),
});
}
&mut $scratch[..$required_size]
}};
}
pub(crate) use validate_scratch;
macro_rules! validate_sizes {
($store: expr, $into: expr, $src_size: expr, $dst_size: expr) => {{
$into.validate()?;
$store.validate()?;
if $store.width != $src_size.width && $store.height != $src_size.height {
return Err(PicScaleError::InvalidSourceSize {
expected: $src_size,
size: $store.size(),
});
}
if $into.width != $dst_size.width && $into.height != $dst_size.height {
return Err(PicScaleError::InvalidDestinationSize {
expected: $dst_size,
size: $into.size(),
});
}
if $store.width == $into.width && $store.height == $into.height {
$store.copied_to_mut($into);
return Ok(());
}
}};
}
pub(crate) use validate_sizes;