use crate::{BlurError, FastBlurChannels, ImageSize, MismatchedSize};
use std::fmt::Debug;
pub(crate) fn check_image_size_overflow_with_stride(
width: u32,
height: u32,
stride: u32,
chan: usize,
t_size: isize,
) -> bool {
let Ok(w) = isize::try_from(width) else {
return true;
};
let Ok(h) = isize::try_from(height) else {
return true;
};
let Ok(n) = isize::try_from(chan) else {
return true;
};
let Ok(stride) = isize::try_from(stride) else {
return true;
};
let Some(h_minus_1) = h.checked_sub(1) else {
return true;
};
let Some(lhs) = stride.checked_mul(h_minus_1) else {
return true;
};
let Some(rhs) = w.checked_mul(n) else {
return true;
};
lhs.checked_add(rhs)
.and_then(|x| x.checked_mul(t_size))
.is_none()
}
#[derive(Debug)]
pub enum BufferStore<'a, T: Copy + Debug> {
Borrowed(&'a mut [T]),
Owned(Vec<T>),
}
impl<T: Copy + Debug> BufferStore<'_, T> {
#[allow(clippy::should_implement_trait)]
pub fn borrow(&self) -> &[T] {
match self {
Self::Borrowed(p_ref) => p_ref,
Self::Owned(vec) => vec,
}
}
#[allow(clippy::should_implement_trait)]
pub fn borrow_mut(&mut self) -> &mut [T] {
match self {
Self::Borrowed(p_ref) => p_ref,
Self::Owned(vec) => vec,
}
}
#[allow(clippy::should_implement_trait)]
pub(crate) fn resize(&mut self, new_size: usize, value: T) {
match self {
Self::Borrowed(_) => {}
Self::Owned(vec) => vec.resize(new_size, value),
}
}
}
pub struct BlurImage<'a, T: Clone + Copy + Default + Debug> {
pub data: std::borrow::Cow<'a, [T]>,
pub width: u32,
pub height: u32,
pub stride: u32,
pub channels: FastBlurChannels,
}
pub struct BlurImageMut<'a, T: Clone + Copy + Default + Debug> {
pub data: BufferStore<'a, T>,
pub width: u32,
pub height: u32,
pub stride: u32,
pub channels: FastBlurChannels,
}
impl<T: Clone + Copy + Default + Debug> Default for BlurImageMut<'_, T> {
fn default() -> Self {
BlurImageMut {
data: BufferStore::Owned(Vec::new()),
width: 0,
height: 0,
stride: 0,
channels: FastBlurChannels::Plane,
}
}
}
impl<'a, T: Clone + Copy + Default + Debug> BlurImage<'a, T> {
pub fn alloc(width: u32, height: u32, channels: FastBlurChannels) -> Self {
Self {
data: std::borrow::Cow::Owned(vec![
T::default();
width as usize
* height as usize
* channels.channels()
]),
width,
height,
stride: width * channels.channels() as u32,
channels,
}
}
pub fn borrow(arr: &'a [T], width: u32, height: u32, channels: FastBlurChannels) -> Self {
Self {
data: std::borrow::Cow::Borrowed(arr),
width,
height,
stride: width * channels.channels() as u32,
channels,
}
}
pub fn copy_to_mut(&self, dst: &mut BlurImageMut<T>) -> Result<(), BlurError> {
self.check_layout()?;
dst.check_layout(Some(self))?;
self.size_matches_mut(dst)?;
let src_data = self.projected();
let dst_stride = dst.row_stride() as usize;
let dst_data = dst.projected();
for (src, dst) in src_data
.chunks(self.row_stride() as usize)
.zip(dst_data.chunks_mut(dst_stride))
{
let src = &src[..self.width as usize * self.channels.channels()];
let dst = &mut dst[..self.width as usize * self.channels.channels()];
for (src, dst) in src.iter().zip(dst.iter_mut()) {
*dst = *src;
}
}
Ok(())
}
pub fn projected(&self) -> &[T] {
let cn = self.channels.channels();
let total_size =
self.row_stride() as usize * (self.height as usize - 1) + self.width as usize * cn;
&self.data.as_ref()[..total_size]
}
#[inline]
pub fn size_matches(&self, other: &BlurImage<'_, T>) -> Result<(), BlurError> {
if self.width == other.width
&& self.height == other.height
&& self.channels == other.channels
{
return Ok(());
}
Err(BlurError::ImagesMustMatch)
}
#[inline]
pub fn size(&self) -> ImageSize {
ImageSize::new(self.width as usize, self.height as usize)
}
#[inline]
pub fn size_matches_mut(&self, other: &BlurImageMut<'_, T>) -> Result<(), BlurError> {
if self.width == other.width
&& self.height == other.height
&& self.channels == other.channels
{
return Ok(());
}
Err(BlurError::ImagesMustMatch)
}
#[inline]
pub fn only_size_matches_mut(&self, other: &BlurImageMut<'_, T>) -> Result<(), BlurError> {
if self.width == other.width && self.height == other.height {
return Ok(());
}
Err(BlurError::ImagesMustMatch)
}
pub fn check_layout_channels(&self, cn: usize) -> Result<(), BlurError> {
if self.width == 0 || self.height == 0 {
return Err(BlurError::ZeroBaseSize);
}
let data_len = self.data.as_ref().len();
if data_len
< self.row_stride() as usize * (self.height as usize - 1) + self.width as usize * cn
{
return Err(BlurError::MinimumSliceSizeMismatch(MismatchedSize {
expected: self.row_stride() as usize * self.height as usize,
received: data_len,
}));
}
if (self.row_stride() as usize) < (self.width as usize * cn) {
return Err(BlurError::MinimumStrideSizeMismatch(MismatchedSize {
expected: self.width as usize * cn,
received: self.row_stride() as usize,
}));
}
Ok(())
}
#[inline]
pub fn row_stride(&self) -> u32 {
if self.stride == 0 {
self.width * self.channels.channels() as u32
} else {
self.stride
}
}
pub fn check_layout(&self) -> Result<(), BlurError> {
if self.width == 0 || self.height == 0 {
return Err(BlurError::ZeroBaseSize);
}
let cn = self.channels.channels();
if check_image_size_overflow_with_stride(
self.width,
self.height,
self.row_stride(),
cn,
size_of::<T>() as isize,
) {
return Err(BlurError::ExceedingPointerSize);
}
if self.data.len()
< self.row_stride() as usize * (self.height as usize - 1) + self.width as usize * cn
{
return Err(BlurError::MinimumSliceSizeMismatch(MismatchedSize {
expected: self.row_stride() as usize * self.height as usize,
received: self.data.len(),
}));
}
if (self.row_stride() as usize) < (self.width as usize * cn) {
return Err(BlurError::MinimumStrideSizeMismatch(MismatchedSize {
expected: self.width as usize * cn,
received: self.row_stride() as usize,
}));
}
Ok(())
}
pub fn clone_as_mut<'f>(&self) -> BlurImageMut<'f, T> {
BlurImageMut {
data: BufferStore::Owned(self.data.to_vec()),
width: self.width,
height: self.height,
stride: self.stride,
channels: self.channels,
}
}
}
impl<'a, T: Clone + Copy + Default + Debug> BlurImageMut<'a, T> {
pub fn alloc(width: u32, height: u32, channels: FastBlurChannels) -> Self {
Self {
data: BufferStore::Owned(vec![
T::default();
width as usize * height as usize * channels.channels()
]),
width,
height,
stride: width * channels.channels() as u32,
channels,
}
}
pub fn borrow(arr: &'a mut [T], width: u32, height: u32, channels: FastBlurChannels) -> Self {
Self {
data: BufferStore::Borrowed(arr),
width,
height,
stride: width * channels.channels() as u32,
channels,
}
}
#[inline]
pub fn row_stride(&self) -> u32 {
if self.stride == 0 {
self.width * self.channels.channels() as u32
} else {
self.stride
}
}
pub fn projected(&mut self) -> &mut [T] {
let cn = self.channels.channels();
let total_size =
self.row_stride() as usize * (self.height as usize - 1) + self.width as usize * cn;
&mut self.data.borrow_mut()[..total_size]
}
pub fn layout_test(&self) -> Result<(), BlurError> {
if self.width == 0 || self.height == 0 {
return Err(BlurError::ZeroBaseSize);
}
let cn = self.channels.channels();
if check_image_size_overflow_with_stride(
self.width,
self.height,
self.row_stride(),
cn,
size_of::<T>() as isize,
) {
return Err(BlurError::ExceedingPointerSize);
}
let data_len = self.data.borrow().len();
if data_len
< self.row_stride() as usize * (self.height as usize - 1) + self.width as usize * cn
{
return Err(BlurError::MinimumSliceSizeMismatch(MismatchedSize {
expected: self.row_stride() as usize * self.height as usize,
received: data_len,
}));
}
if (self.stride as usize) < (self.width as usize * cn) {
return Err(BlurError::MinimumStrideSizeMismatch(MismatchedSize {
expected: self.width as usize * cn,
received: self.stride as usize,
}));
}
Ok(())
}
#[inline]
pub fn check_layout(&mut self, other: Option<&BlurImage<'_, T>>) -> Result<(), BlurError> {
if let Some(other) = other
&& matches!(self.data, BufferStore::Owned(_))
{
self.resize(other.width, other.height, other.channels);
return Ok(());
}
if self.width == 0 || self.height == 0 {
return Err(BlurError::ZeroBaseSize);
}
let cn = self.channels.channels();
if check_image_size_overflow_with_stride(
self.width,
self.height,
self.row_stride(),
cn,
size_of::<T>() as isize,
) {
return Err(BlurError::ExceedingPointerSize);
}
let data_len = self.data.borrow().len();
if data_len
< self.row_stride() as usize * (self.height as usize - 1) + self.width as usize * cn
{
return Err(BlurError::MinimumSliceSizeMismatch(MismatchedSize {
expected: self.row_stride() as usize * self.height as usize,
received: data_len,
}));
}
if (self.row_stride() as usize) < (self.width as usize * cn) {
return Err(BlurError::MinimumStrideSizeMismatch(MismatchedSize {
expected: self.width as usize * cn,
received: self.row_stride() as usize,
}));
}
Ok(())
}
pub fn check_layout_channels(
&mut self,
cn: usize,
other: Option<&BlurImage<'_, T>>,
) -> Result<(), BlurError> {
if let Some(other) = other
&& matches!(self.data, BufferStore::Owned(_))
{
self.resize_arbitrary(other.width, other.height, cn);
return Ok(());
}
if self.width == 0 || self.height == 0 {
return Err(BlurError::ZeroBaseSize);
}
if check_image_size_overflow_with_stride(
self.width,
self.height,
self.row_stride(),
cn,
size_of::<T>() as isize,
) {
return Err(BlurError::ExceedingPointerSize);
}
let data_len = self.data.borrow().len();
if data_len
< self.row_stride() as usize * (self.height as usize - 1) + self.width as usize * cn
{
return Err(BlurError::MinimumSliceSizeMismatch(MismatchedSize {
expected: self.row_stride() as usize * self.height as usize,
received: data_len,
}));
}
if (self.row_stride() as usize) < (self.width as usize * cn) {
return Err(BlurError::MinimumStrideSizeMismatch(MismatchedSize {
expected: self.width as usize * cn,
received: self.row_stride() as usize,
}));
}
Ok(())
}
#[inline]
pub fn size_matches(&self, other: &BlurImage<'_, T>) -> Result<(), BlurError> {
if self.width == other.width
&& self.height == other.height
&& self.channels == other.channels
{
return Ok(());
}
Err(BlurError::ImagesMustMatch)
}
#[inline]
pub fn size_matches_mut(&self, other: &BlurImageMut<'_, T>) -> Result<(), BlurError> {
if self.width == other.width
&& self.height == other.height
&& self.channels == other.channels
{
return Ok(());
}
Err(BlurError::ImagesMustMatch)
}
#[inline]
pub fn to_immutable_ref(&self) -> BlurImage<'_, T> {
BlurImage {
data: std::borrow::Cow::Borrowed(self.data.borrow()),
stride: self.row_stride(),
width: self.width,
height: self.height,
channels: self.channels,
}
}
#[inline]
#[allow(clippy::should_implement_trait)]
pub fn resize(&mut self, width: u32, height: u32, channels: FastBlurChannels) {
self.height = height;
self.width = width;
self.channels = channels;
self.stride = self.width * self.channels.channels() as u32;
self.data.resize(
self.row_stride() as usize * self.height as usize,
T::default(),
);
}
#[inline]
#[allow(clippy::should_implement_trait)]
pub fn resize_arbitrary(&mut self, width: u32, height: u32, cn: usize) {
self.height = height;
self.width = width;
self.channels = if cn == 4 {
FastBlurChannels::Channels4
} else if cn == 3 {
FastBlurChannels::Channels3
} else {
FastBlurChannels::Plane
};
self.stride = self.width * cn as u32;
self.data.resize(
self.row_stride() as usize * self.height as usize,
T::default(),
);
}
}