use std::error::Error;
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct MismatchedSize {
pub expected: usize,
pub received: usize,
}
#[derive(Clone, Debug)]
pub enum BlurError {
ZeroBaseSize,
MinimumSliceSizeMismatch(MismatchedSize),
MinimumStrideSizeMismatch(MismatchedSize),
OddKernel(usize),
KernelSizeMismatch(MismatchedSize),
ImagesMustMatch,
StrideIsNotSupported,
FftChannelsNotSupported,
ExceedingPointerSize,
NegativeOrZeroSigma,
InvalidArguments,
FftError(String),
}
impl Error for BlurError {}
impl std::fmt::Display for BlurError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
BlurError::MinimumSliceSizeMismatch(size) => f.write_fmt(format_args!(
"Minimum image slice size mismatch: expected={}, received={}",
size.expected, size.received
)),
BlurError::MinimumStrideSizeMismatch(size) => f.write_fmt(format_args!(
"Minimum stride must have size at least {} but it is {}",
size.expected, size.received
)),
BlurError::ZeroBaseSize => f.write_str("Image size must not be zero"),
BlurError::OddKernel(size) => {
f.write_fmt(format_args!("Kernel size must be odd, but received {size}",))
}
BlurError::KernelSizeMismatch(size) => f.write_fmt(format_args!(
"Kernel size mismatch: expected={}, received={}",
size.expected, size.received
)),
BlurError::ImagesMustMatch => {
f.write_str("Source and destination images must match in their dimensions")
}
BlurError::StrideIsNotSupported => f.write_str("Stride is not supported"),
BlurError::FftChannelsNotSupported => f.write_str("Fft supports only planar images"),
BlurError::ExceedingPointerSize => {
f.write_str("Image bounds and blurring kernel/radius exceeds pointer capacity")
}
BlurError::NegativeOrZeroSigma => {
f.write_str("Negative or zero sigma is not supported")
}
BlurError::InvalidArguments => f.write_str("Invalid arguments"),
BlurError::FftError(msg) => f.write_str(msg),
}
}
}
pub(crate) fn check_slice_size<T>(
arr: &[T],
stride: usize,
width: usize,
height: usize,
cn: usize,
) -> Result<(), BlurError> {
if width == 0 || height == 0 {
return Err(BlurError::ZeroBaseSize);
}
if arr.len() < stride * (height - 1) + width * cn {
return Err(BlurError::MinimumSliceSizeMismatch(MismatchedSize {
expected: stride * height,
received: arr.len(),
}));
}
if (stride) < (width * cn) {
return Err(BlurError::MinimumStrideSizeMismatch(MismatchedSize {
expected: width * cn,
received: stride,
}));
}
Ok(())
}
pub(crate) enum ScratchBuffer<T, const N: usize> {
Stack([T; N], usize),
Heap(Vec<T>),
}
impl<T: Default + Copy, const N: usize> ScratchBuffer<T, N> {
pub(crate) fn new(size: usize) -> Self {
if size <= N {
Self::Stack([T::default(); N], size)
} else {
Self::Heap(vec![T::default(); size])
}
}
pub(crate) fn as_mut_slice(&mut self) -> &mut [T] {
match self {
Self::Stack(buf, len) => &mut buf[..*len],
Self::Heap(buf) => buf.as_mut_slice(),
}
}
}
use std::fmt::Debug;
pub(crate) enum CowScratch<T: Copy + Debug + Default, const N: usize> {
Uninit,
Stack([T; N], usize),
Heap(Vec<T>),
}
impl<T: Copy + Debug + Default, const N: usize> CowScratch<T, N> {
pub(crate) const fn new() -> Self {
Self::Uninit
}
pub(crate) fn is_empty(&self) -> bool {
matches!(self, Self::Uninit)
}
pub(crate) fn get_or_init(&mut self, size: usize) -> &mut [T] {
if self.is_empty() {
*self = if size <= N {
Self::Stack([T::default(); N], size)
} else {
Self::Heap(vec![T::default(); size])
};
}
self.borrow_mut()
}
pub(crate) fn borrow_mut(&mut self) -> &mut [T] {
match self {
Self::Uninit => &mut [],
Self::Stack(buf, len) => &mut buf[..*len],
Self::Heap(v) => v,
}
}
pub(crate) fn get_or_unwrap_mut(&mut self) -> &mut [T] {
match self {
Self::Uninit => panic!("CowScratch is uninitialized"),
Self::Stack(buf, len) => &mut buf[..*len],
Self::Heap(v) => v,
}
}
}