use std::marker::PhantomData;
use std::ops::{Index, IndexMut};
use crate::image::tiles::{SubView, SubViewMut};
use crate::image::{ImageView, ImageViewMut, RasterImage, RasterImageMut};
use crate::{Coordinate, Rectangle, Size, internal};
#[inline]
fn strided_checked_index_or_panic(
size: &Size,
stride: usize,
offset: usize,
x: usize,
y: usize,
) -> usize {
assert!(
x < size.width && y < size.height,
"pixel index out of bounds: ({x}, {y}) is not inside {}x{}",
size.width,
size.height
);
y.checked_mul(stride)
.and_then(|row_off| offset.checked_add(row_off))
.and_then(|row_start| row_start.checked_add(x))
.unwrap_or_else(|| {
panic!(
"pixel index arithmetic overflowed usize: ({x}, {y}) on \
strided view (stride={stride}, offset={offset})"
)
})
}
#[inline]
fn strided_checked_row_start_or_panic(
size: &Size,
stride: usize,
offset: usize,
y: usize,
) -> usize {
assert!(
y < size.height,
"row index out of bounds: {y} is not less than height {}",
size.height
);
y.checked_mul(stride)
.and_then(|row_off| offset.checked_add(row_off))
.unwrap_or_else(|| {
panic!(
"row index arithmetic overflowed usize: y={y} on strided view \
(stride={stride}, offset={offset})"
)
})
}
use std::borrow::Cow;
use std::fmt;
use crate::error::Error;
use crate::pixel::{OriginInvariantPixel, PlainChannel, PlainPixel, ZeroablePixel};
trait AssertByteAligned: PlainPixel {
const _ASSERT: () = assert!(
std::mem::align_of::<Self>() == 1,
"from_raw_bytes requires a pixel type with ALIGN == 1; \
use from_bytes_copy for aligned pixel types"
);
}
impl<T: PlainPixel> AssertByteAligned for T {}
pub(crate) mod private {
use std::ops::{Index, IndexMut};
pub trait _Array2D {
type Pixel: Copy;
type Array: Index<usize, Output = Self::Pixel>
+ IndexMut<usize, Output = Self::Pixel>
+ AsRef<[Self::Pixel]>
+ AsMut<[Self::Pixel]>
+ Sized;
}
pub struct Dim<T, const W: usize, const H: usize> {
_marker: std::marker::PhantomData<T>,
}
macro_rules! define_array_2D {
($x:literal, $y:literal) => {
impl<T: Copy> _Array2D for Dim<T, $x, $y> {
type Pixel = T;
type Array = [T; $x * $y];
}
};
}
macro_rules! define_row {
($w:literal; $($h:literal),+ $(,)?) => {
$(
define_array_2D!($w, $h);
)+
};
}
macro_rules! define_all_array_2D {
([$w:literal], [$($h:literal),+ $(,)?]) => {
define_row!($w; $($h),+);
};
([$w:literal, $($rest:literal),+ $(,)?], [$($h:literal),+ $(,)?]) => {
define_row!($w; $($h),+);
define_all_array_2D!([$($rest),+], [$($h),+]);
};
}
define_all_array_2D!(
[
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
],
[
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46,
47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64
]
);
}
pub(crate) mod contiguous_sealed {
pub trait Sealed {}
}
pub trait ContiguousImage: RasterImage + contiguous_sealed::Sealed {
fn as_slice(&self) -> &[Self::Pixel];
}
pub trait ContiguousImageMut: ContiguousImage + RasterImageMut {
fn as_mut_slice(&mut self) -> &mut [Self::Pixel];
}
pub trait PlainImage: ContiguousImage
where
Self::Pixel: PlainPixel,
{
fn as_bytes(&self) -> &[u8];
fn as_bytes_le(&self) -> Cow<'_, [u8]>;
fn as_bytes_be(&self) -> Cow<'_, [u8]>;
}
pub trait PlainImageMut: PlainImage + ContiguousImageMut
where
Self::Pixel: PlainPixel,
{
fn as_mut_bytes(&mut self) -> &mut [u8];
}
pub struct ImageArray<T, const W: usize, const H: usize>
where
private::Dim<T, W, H>: private::_Array2D,
{
data: <private::Dim<T, W, H> as private::_Array2D>::Array,
}
impl<T: Copy, const W: usize, const H: usize> ImageArray<T, W, H>
where
private::Dim<T, W, H>: private::_Array2D,
{
pub fn new(data: <private::Dim<T, W, H> as private::_Array2D>::Array) -> Self {
Self { data }
}
pub fn generate(f: impl Fn(usize, usize) -> <ImageArray<T, W, H> as ImageView>::Pixel) -> Self {
let mut uninit_data: std::mem::MaybeUninit<
<private::Dim<T, W, H> as private::_Array2D>::Array,
> = std::mem::MaybeUninit::uninit();
let data_ptr = uninit_data.as_mut_ptr() as *mut <ImageArray<T, W, H> as ImageView>::Pixel;
for y in 0..H {
for x in 0..W {
unsafe {
data_ptr.add(x + y * W).write(f(x, y));
}
}
}
let data = unsafe { uninit_data.assume_init() };
Self { data }
}
}
impl<T: Copy, const W: usize, const H: usize> ImageView for ImageArray<T, W, H>
where
private::Dim<T, W, H>: private::_Array2D,
{
type Pixel = <private::Dim<T, W, H> as private::_Array2D>::Pixel;
fn size(&self) -> Size {
Size::new(W, H)
}
fn width(&self) -> usize {
W
}
fn height(&self) -> usize {
H
}
#[inline(always)]
fn get(&self, x: usize, y: usize) -> Option<Self::Pixel> {
if x < W && y < H {
Some(self.data[x + y * W])
} else {
None
}
}
#[inline(always)]
fn pixel_at(&self, x: usize, y: usize) -> Self::Pixel {
self.data[x + y * W]
}
}
impl<T: Copy, const W: usize, const H: usize> ImageViewMut for ImageArray<T, W, H>
where
private::Dim<T, W, H>: private::_Array2D,
{
#[inline(always)]
fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut Self::Pixel> {
if x < W && y < H {
Some(&mut self.data[x + y * W])
} else {
None
}
}
#[inline(always)]
fn pixel_at_mut(&mut self, x: usize, y: usize) -> &mut Self::Pixel {
&mut self.data[x + y * W]
}
}
impl<T: OriginInvariantPixel, const W: usize, const H: usize> SubView for ImageArray<T, W, H>
where
private::Dim<T, W, H>: private::_Array2D,
{
type Sub<'a>
= ImageRef<'a, Self::Pixel>
where
Self: 'a;
fn roi(&self, rect: Rectangle) -> Option<Self::Sub<'_>> {
let right = rect.checked_right()?;
let bottom = rect.checked_bottom()?;
if right <= W && bottom <= H {
let offset = rect.top().checked_mul(W)?.checked_add(rect.left())?;
ImageRef::strided(rect.size, W, offset, self.as_slice())
} else {
None
}
}
}
impl<T: OriginInvariantPixel, const W: usize, const H: usize> SubViewMut for ImageArray<T, W, H>
where
private::Dim<T, W, H>: private::_Array2D,
{
type SubMut<'a>
= ImageRefMut<'a, Self::Pixel>
where
Self: 'a;
fn roi_mut(&mut self, rect: Rectangle) -> Option<Self::SubMut<'_>> {
let right = rect.checked_right()?;
let bottom = rect.checked_bottom()?;
if right <= W && bottom <= H {
let stride = W;
let offset = rect.top().checked_mul(W)?.checked_add(rect.left())?;
let slice = self.as_mut_slice();
let len = slice.len();
let ptr = slice.as_mut_ptr();
Some(unsafe { ImageRefMut::strided(rect.size, stride, offset, ptr, len) })
} else {
None
}
}
}
impl<T: Copy, const W: usize, const H: usize> RasterImage for ImageArray<T, W, H>
where
private::Dim<T, W, H>: private::_Array2D,
{
#[inline(always)]
fn row(&self, y: usize) -> &[Self::Pixel] {
&self.data.as_ref()[y * W..y * W + W]
}
}
impl<T: Copy, const W: usize, const H: usize> RasterImageMut for ImageArray<T, W, H>
where
private::Dim<T, W, H>: private::_Array2D,
{
#[inline(always)]
fn row_mut(&mut self, y: usize) -> &mut [Self::Pixel] {
&mut self.data.as_mut()[y * W..y * W + W]
}
}
impl<T: Copy, const W: usize, const H: usize> contiguous_sealed::Sealed for ImageArray<T, W, H> where
private::Dim<T, W, H>: private::_Array2D
{
}
impl<T: Copy, const W: usize, const H: usize> ContiguousImage for ImageArray<T, W, H>
where
private::Dim<T, W, H>: private::_Array2D,
{
fn as_slice(&self) -> &[Self::Pixel] {
self.data.as_ref()
}
}
impl<T: Copy, const W: usize, const H: usize> ContiguousImageMut for ImageArray<T, W, H>
where
private::Dim<T, W, H>: private::_Array2D,
{
fn as_mut_slice(&mut self) -> &mut [Self::Pixel] {
self.data.as_mut()
}
}
impl<T: Copy, const W: usize, const H: usize> PlainImage for ImageArray<T, W, H>
where
T: PlainPixel,
private::Dim<T, W, H>: private::_Array2D<Pixel = T>,
{
fn as_bytes(&self) -> &[u8] {
unsafe { internal::as_bytes(self.as_slice()) }
}
fn as_bytes_le(&self) -> Cow<'_, [u8]> {
unsafe { internal::as_bytes_le(self.as_slice()) }
}
fn as_bytes_be(&self) -> Cow<'_, [u8]> {
unsafe { internal::as_bytes_be(self.as_slice()) }
}
}
impl<T: Copy, const W: usize, const H: usize> PlainImageMut for ImageArray<T, W, H>
where
T: PlainPixel,
private::Dim<T, W, H>: private::_Array2D<Pixel = T>,
{
fn as_mut_bytes(&mut self) -> &mut [u8] {
unsafe { internal::as_mut_bytes(self.as_mut_slice()) }
}
}
impl<T: Copy, C: Into<Coordinate>, const W: usize, const H: usize> Index<C> for ImageArray<T, W, H>
where
private::Dim<T, W, H>: private::_Array2D<Pixel = T>,
{
type Output = T;
#[inline(always)]
fn index(&self, index: C) -> &Self::Output {
let Coordinate { x, y } = index.into();
&self.data[x + y * W]
}
}
impl<T: Copy, C: Into<Coordinate>, const W: usize, const H: usize> IndexMut<C>
for ImageArray<T, W, H>
where
private::Dim<T, W, H>: private::_Array2D<Pixel = T>,
{
#[inline(always)]
fn index_mut(&mut self, index: C) -> &mut Self::Output {
let Coordinate { x, y } = index.into();
self.pixel_at_mut(x, y)
}
}
#[derive(Copy, Clone, Debug)]
pub struct ImageRef<'a, T> {
size: Size,
stride: usize,
offset: usize,
data: &'a [T],
}
impl<'a, T> ImageRef<'a, T> {
pub fn new(width: usize, height: usize, data: &'a [T]) -> Result<Self, Error> {
let size = Size::new(width, height);
let expected = size.checked_area().ok_or(Error::LengthMismatch {
expected: usize::MAX,
actual: data.len(),
})?;
if data.len() == expected {
Ok(Self {
size,
stride: width,
offset: 0,
data,
})
} else {
Err(Error::LengthMismatch {
expected,
actual: data.len(),
})
}
}
pub(crate) fn strided(size: Size, stride: usize, offset: usize, data: &'a [T]) -> Option<Self> {
if size.height == 0 || size.width == 0 {
return Some(Self {
size,
stride,
offset,
data,
});
}
let last_row = size.height - 1;
let last_col = size.width - 1;
let last_index = last_row
.checked_mul(stride)
.and_then(|r| r.checked_add(offset))
.and_then(|s| s.checked_add(last_col))?;
if last_index < data.len() {
Some(Self {
size,
stride,
offset,
data,
})
} else {
None
}
}
#[inline]
pub fn is_contiguous(&self) -> bool {
self.stride == self.size.width && self.offset == 0
}
}
impl<T: Copy> ImageView for ImageRef<'_, T> {
type Pixel = T;
fn size(&self) -> Size {
self.size
}
fn width(&self) -> usize {
self.size.width
}
fn height(&self) -> usize {
self.size.height
}
#[inline(always)]
fn get(&self, x: usize, y: usize) -> Option<Self::Pixel> {
if x < self.size.width && y < self.size.height {
Some(self.data[self.offset + y * self.stride + x])
} else {
None
}
}
#[inline(always)]
fn pixel_at(&self, x: usize, y: usize) -> Self::Pixel {
self.data[strided_checked_index_or_panic(&self.size, self.stride, self.offset, x, y)]
}
}
impl<T: Copy> RasterImage for ImageRef<'_, T> {
#[inline(always)]
fn row(&self, y: usize) -> &[Self::Pixel] {
let start = strided_checked_row_start_or_panic(&self.size, self.stride, self.offset, y);
&self.data[start..start + self.size.width]
}
}
impl<T: OriginInvariantPixel> SubView for ImageRef<'_, T> {
type Sub<'b>
= ImageRef<'b, T>
where
Self: 'b;
fn roi(&self, rect: Rectangle) -> Option<ImageRef<'_, T>> {
let right = rect.checked_right()?;
let bottom = rect.checked_bottom()?;
if right <= self.size.width && bottom <= self.size.height {
let offset = self
.offset
.checked_add(rect.top().checked_mul(self.stride)?)?
.checked_add(rect.left())?;
ImageRef::strided(rect.size, self.stride, offset, self.data)
} else {
None
}
}
}
pub struct ImageRefMut<'a, T> {
size: Size,
stride: usize,
offset: usize,
data: *mut T,
data_len: usize,
_marker: PhantomData<&'a mut T>,
}
unsafe impl<T: Send> Send for ImageRefMut<'_, T> {}
unsafe impl<T: Sync> Sync for ImageRefMut<'_, T> {}
impl<'a, T> ImageRefMut<'a, T> {
pub fn new(width: usize, height: usize, data: &'a mut [T]) -> Result<Self, Error> {
let size = Size::new(width, height);
let expected = size.checked_area().ok_or(Error::LengthMismatch {
expected: usize::MAX,
actual: data.len(),
})?;
if data.len() == expected {
let len = data.len();
let ptr = data.as_mut_ptr();
Ok(Self {
size,
stride: width,
offset: 0,
data: ptr,
data_len: len,
_marker: PhantomData,
})
} else {
Err(Error::LengthMismatch {
expected,
actual: data.len(),
})
}
}
pub(crate) unsafe fn strided(
size: Size,
stride: usize,
offset: usize,
data: *mut T,
data_len: usize,
) -> Self {
Self {
size,
stride,
offset,
data,
data_len,
_marker: PhantomData,
}
}
#[inline]
pub fn is_contiguous(&self) -> bool {
self.stride == self.size.width && self.offset == 0
}
}
impl<T> ImageRefMut<'_, T> {
#[inline(always)]
fn checked_elem_offset(&self, x: usize, y: usize) -> Option<usize> {
let row_off = y.checked_mul(self.stride)?;
let idx = self.offset.checked_add(row_off)?.checked_add(x)?;
if idx < self.data_len { Some(idx) } else { None }
}
#[inline(always)]
fn checked_row_range(&self, y: usize) -> Option<usize> {
let row_off = y.checked_mul(self.stride)?;
let start = self.offset.checked_add(row_off)?;
let end = start.checked_add(self.size.width)?;
if end <= self.data_len {
Some(start)
} else {
None
}
}
}
impl<T: Copy> ImageView for ImageRefMut<'_, T> {
type Pixel = T;
fn size(&self) -> Size {
self.size
}
fn width(&self) -> usize {
self.size.width
}
fn height(&self) -> usize {
self.size.height
}
#[inline(always)]
fn get(&self, x: usize, y: usize) -> Option<Self::Pixel> {
if x < self.size.width && y < self.size.height {
let idx = self
.checked_elem_offset(x, y)
.expect("ImageRefMut::get: element offset overflowed or escaped allocation");
Some(unsafe { self.data.add(idx).read() })
} else {
None
}
}
#[inline(always)]
fn pixel_at(&self, x: usize, y: usize) -> Self::Pixel {
assert!(
x < self.size.width && y < self.size.height,
"ImageRefMut::pixel_at: ({x}, {y}) out of bounds for size {:?}",
self.size
);
let idx = self
.checked_elem_offset(x, y)
.expect("ImageRefMut::pixel_at: element offset overflowed or escaped allocation");
unsafe { self.data.add(idx).read() }
}
}
impl<T: Copy> RasterImage for ImageRefMut<'_, T> {
#[inline(always)]
fn row(&self, y: usize) -> &[Self::Pixel] {
assert!(
y < self.size.height,
"ImageRefMut::row: y={y} out of bounds for height {}",
self.size.height
);
let start = self
.checked_row_range(y)
.expect("ImageRefMut::row: row range overflowed or escaped allocation");
unsafe { std::slice::from_raw_parts(self.data.add(start), self.size.width) }
}
}
impl<T: Copy> RasterImageMut for ImageRefMut<'_, T> {
#[inline(always)]
fn row_mut(&mut self, y: usize) -> &mut [Self::Pixel] {
assert!(
y < self.size.height,
"ImageRefMut::row_mut: y={y} out of bounds for height {}",
self.size.height
);
let start = self
.checked_row_range(y)
.expect("ImageRefMut::row_mut: row range overflowed or escaped allocation");
unsafe { std::slice::from_raw_parts_mut(self.data.add(start), self.size.width) }
}
}
impl<T: Copy> ImageViewMut for ImageRefMut<'_, T> {
fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut Self::Pixel> {
if x < self.size.width && y < self.size.height {
let idx = self
.checked_elem_offset(x, y)
.expect("ImageRefMut::get_mut: element offset overflowed or escaped allocation");
Some(unsafe { &mut *self.data.add(idx) })
} else {
None
}
}
fn pixel_at_mut(&mut self, x: usize, y: usize) -> &mut Self::Pixel {
assert!(
x < self.size.width && y < self.size.height,
"ImageRefMut::pixel_at_mut: ({x}, {y}) out of bounds for size {:?}",
self.size
);
let idx = self
.checked_elem_offset(x, y)
.expect("ImageRefMut::pixel_at_mut: element offset overflowed or escaped allocation");
unsafe { &mut *self.data.add(idx) }
}
}
impl<T: OriginInvariantPixel> SubViewMut for ImageRefMut<'_, T> {
type SubMut<'b>
= ImageRefMut<'b, T>
where
Self: 'b;
fn roi_mut(&mut self, rect: Rectangle) -> Option<ImageRefMut<'_, T>> {
let right = rect.checked_right()?;
let bottom = rect.checked_bottom()?;
if right <= self.size.width && bottom <= self.size.height {
let offset = self
.offset
.checked_add(rect.top().checked_mul(self.stride)?)?
.checked_add(rect.left())?;
Some(unsafe {
ImageRefMut::strided(rect.size, self.stride, offset, self.data, self.data_len)
})
} else {
None
}
}
}
impl<T: OriginInvariantPixel> SubView for ImageRefMut<'_, T> {
type Sub<'b>
= ImageRef<'b, T>
where
Self: 'b;
fn roi(&self, rect: Rectangle) -> Option<ImageRef<'_, T>> {
let right = rect.checked_right()?;
let bottom = rect.checked_bottom()?;
if right <= self.size.width && bottom <= self.size.height {
let offset = self
.offset
.checked_add(rect.top().checked_mul(self.stride)?)?
.checked_add(rect.left())?;
let slice = unsafe { std::slice::from_raw_parts(self.data, self.data_len) };
ImageRef::strided(rect.size, self.stride, offset, slice)
} else {
None
}
}
}
#[derive(Clone)]
pub struct Image<T> {
size: Size,
data: Box<[T]>,
}
impl<T> Image<T> {
pub fn from_vec(width: usize, height: usize, data: Vec<T>) -> Result<Self, Error> {
let size = Size::new(width, height);
let expected = size.checked_area().ok_or(Error::LengthMismatch {
expected: usize::MAX,
actual: data.len(),
})?;
if data.len() == expected {
Ok(Self {
size,
data: data.into_boxed_slice(),
})
} else {
Err(Error::LengthMismatch {
expected,
actual: data.len(),
})
}
}
pub fn from_raw_bytes(width: usize, height: usize, bytes: Vec<u8>) -> Result<Self, Error>
where
T: PlainPixel,
{
let () = <T as AssertByteAligned>::_ASSERT;
let () = <T as PlainChannel>::_ASSERT_SIZE;
let size = Size::new(width, height);
let expected = width
.saturating_mul(height)
.saturating_mul(<T as PlainChannel>::SIZE);
if bytes.len() != expected {
return Err(Error::LengthMismatch {
expected,
actual: bytes.len(),
});
}
let boxed_bytes: Box<[u8]> = bytes.into_boxed_slice();
let count = size.area();
let raw_ptr = Box::into_raw(boxed_bytes) as *mut u8;
let data =
unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(raw_ptr as *mut T, count)) };
Ok(Self { size, data })
}
pub fn from_bytes_copy(width: usize, height: usize, bytes: &[u8]) -> Result<Self, Error>
where
T: PlainPixel,
{
let size = Size::new(width, height);
let expected = width
.saturating_mul(height)
.saturating_mul(<T as PlainChannel>::SIZE);
if bytes.len() != expected {
return Err(Error::LengthMismatch {
expected,
actual: bytes.len(),
});
}
let count = size.area();
let mut data = Vec::with_capacity(count);
for chunk in bytes.chunks_exact(<T as PlainChannel>::SIZE) {
data.push(
<T as PlainChannel>::from_bytes(chunk)
.expect("chunk size guaranteed by chunks_exact"),
);
}
Ok(Self {
size,
data: data.into_boxed_slice(),
})
}
pub fn generate(width: usize, height: usize, f: impl Fn(usize, usize) -> T) -> Self {
let size = Size::new(width, height);
let mut data = Vec::with_capacity(size.area());
for y in 0..height {
for x in 0..width {
data.push(f(x, y));
}
}
Self {
size,
data: data.into_boxed_slice(),
}
}
}
impl<T: ZeroablePixel> Image<T> {
pub fn zero(width: usize, height: usize) -> Self {
let size = Size::new(width, height);
let data = vec![T::zero(); size.area()];
Self {
size,
data: data.into_boxed_slice(),
}
}
}
impl<T: Clone> Image<T> {
pub fn fill(width: usize, height: usize, value: T) -> Self {
let size = Size::new(width, height);
let data = vec![value; size.area()];
Self {
size,
data: data.into_boxed_slice(),
}
}
}
impl<T> fmt::Debug for Image<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let pixel_ty = std::any::type_name::<T>();
let pixel_short = pixel_ty.rsplit("::").next().unwrap_or(pixel_ty);
f.debug_struct("Image")
.field("width", &self.size.width)
.field("height", &self.size.height)
.field("pixel", &pixel_short)
.finish()
}
}
impl<T: PartialEq> PartialEq for Image<T> {
fn eq(&self, other: &Self) -> bool {
self.size == other.size && self.data[..] == other.data[..]
}
}
impl<T: Eq> Eq for Image<T> {}
impl<T: Copy> ImageView for Image<T> {
type Pixel = T;
fn size(&self) -> Size {
self.size
}
fn width(&self) -> usize {
self.size.width
}
fn height(&self) -> usize {
self.size.height
}
#[inline(always)]
fn get(&self, x: usize, y: usize) -> Option<Self::Pixel> {
if x < self.size.width && y < self.size.height {
Some(self.data[internal::index(&self.size, x, y)])
} else {
None
}
}
#[inline(always)]
fn pixel_at(&self, x: usize, y: usize) -> Self::Pixel {
self.data[internal::checked_index_or_panic(&self.size, x, y)]
}
}
impl<T: Copy> ImageViewMut for Image<T> {
#[inline(always)]
fn pixel_at_mut(&mut self, x: usize, y: usize) -> &mut Self::Pixel {
let idx = internal::checked_index_or_panic(&self.size, x, y);
&mut self.data[idx]
}
}
impl<T: OriginInvariantPixel> SubView for Image<T> {
type Sub<'a>
= ImageRef<'a, T>
where
Self: 'a;
fn roi(&self, rect: Rectangle) -> Option<ImageRef<'_, T>> {
let right = rect.checked_right()?;
let bottom = rect.checked_bottom()?;
if right <= self.width() && bottom <= self.height() {
let width = self.width();
let offset = rect.top().checked_mul(width)?.checked_add(rect.left())?;
ImageRef::strided(rect.size, width, offset, self.as_slice())
} else {
None
}
}
}
impl<T: OriginInvariantPixel> SubViewMut for Image<T> {
type SubMut<'a>
= ImageRefMut<'a, T>
where
Self: 'a;
fn roi_mut(&mut self, rect: Rectangle) -> Option<ImageRefMut<'_, T>> {
let right = rect.checked_right()?;
let bottom = rect.checked_bottom()?;
if right <= self.width() && bottom <= self.height() {
let stride = self.width();
let offset = rect.top().checked_mul(stride)?.checked_add(rect.left())?;
let slice = self.as_mut_slice();
let len = slice.len();
let ptr = slice.as_mut_ptr();
Some(unsafe { ImageRefMut::strided(rect.size, stride, offset, ptr, len) })
} else {
None
}
}
}
impl<T: Copy> ImageView for &Image<T> {
type Pixel = T;
fn size(&self) -> Size {
(**self).size()
}
fn width(&self) -> usize {
(**self).width()
}
fn height(&self) -> usize {
(**self).height()
}
#[inline(always)]
fn get(&self, x: usize, y: usize) -> Option<Self::Pixel> {
(**self).get(x, y)
}
#[inline(always)]
fn pixel_at(&self, x: usize, y: usize) -> Self::Pixel {
(**self).pixel_at(x, y)
}
}
impl<T: Copy> RasterImage for &Image<T> {
#[inline(always)]
fn row(&self, y: usize) -> &[Self::Pixel] {
(**self).row(y)
}
}
impl<T: Copy> ImageView for &mut Image<T> {
type Pixel = T;
fn size(&self) -> Size {
(**self).size()
}
fn width(&self) -> usize {
(**self).width()
}
fn height(&self) -> usize {
(**self).height()
}
#[inline(always)]
fn get(&self, x: usize, y: usize) -> Option<Self::Pixel> {
(**self).get(x, y)
}
#[inline(always)]
fn pixel_at(&self, x: usize, y: usize) -> Self::Pixel {
(**self).pixel_at(x, y)
}
}
impl<T: Copy> ImageViewMut for &mut Image<T> {
#[inline(always)]
fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut Self::Pixel> {
(*self).get_mut(x, y)
}
#[inline(always)]
fn pixel_at_mut(&mut self, x: usize, y: usize) -> &mut Self::Pixel {
(*self).pixel_at_mut(x, y)
}
}
impl<T: Copy> RasterImage for &mut Image<T> {
#[inline(always)]
fn row(&self, y: usize) -> &[Self::Pixel] {
(**self).row(y)
}
}
impl<T: Copy> RasterImageMut for &mut Image<T> {
#[inline(always)]
fn row_mut(&mut self, y: usize) -> &mut [Self::Pixel] {
(*self).row_mut(y)
}
}
impl<T: Copy> RasterImage for Image<T> {
#[inline(always)]
fn row(&self, y: usize) -> &[Self::Pixel] {
let (start, end) = internal::checked_row_range_or_panic(&self.size, y);
&self.data[start..end]
}
}
impl<T: Copy> RasterImageMut for Image<T> {
#[inline(always)]
fn row_mut(&mut self, y: usize) -> &mut [Self::Pixel] {
let (start, end) = internal::checked_row_range_or_panic(&self.size, y);
&mut self.data[start..end]
}
}
impl<T: Copy> contiguous_sealed::Sealed for Image<T> {}
impl<T: Copy> ContiguousImage for Image<T> {
fn as_slice(&self) -> &[Self::Pixel] {
&self.data
}
}
impl<T: Copy> ContiguousImageMut for Image<T> {
fn as_mut_slice(&mut self) -> &mut [Self::Pixel] {
&mut self.data
}
}
impl<T: PlainPixel> PlainImage for Image<T> {
fn as_bytes(&self) -> &[u8] {
unsafe { internal::as_bytes(self.as_slice()) }
}
fn as_bytes_le(&self) -> Cow<'_, [u8]> {
unsafe { internal::as_bytes_le(self.as_slice()) }
}
fn as_bytes_be(&self) -> Cow<'_, [u8]> {
unsafe { internal::as_bytes_be(self.as_slice()) }
}
}
impl<T: PlainPixel> PlainImageMut for Image<T> {
fn as_mut_bytes(&mut self) -> &mut [u8] {
unsafe { internal::as_mut_bytes(self.as_mut_slice()) }
}
}
impl<P: Copy, C: Into<Coordinate>> Index<C> for Image<P> {
type Output = P;
#[inline(always)]
fn index(&self, index: C) -> &Self::Output {
let Coordinate { x, y } = index.into();
&self.data[internal::checked_index_or_panic(&self.size, x, y)]
}
}
impl<P: Copy, C: Into<Coordinate>> IndexMut<C> for Image<P> {
#[inline(always)]
fn index_mut(&mut self, index: C) -> &mut Self::Output {
let Coordinate { x, y } = index.into();
self.pixel_at_mut(x, y)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::image::SubView;
use crate::pixel::{Mono8, Mono10, Mono16, MonoA8, Rgb8, Rgb10, Rgb16, RgbF32, Rgba8};
#[test]
fn test_image_array() {
let img: ImageArray<u8, 2, 2> = ImageArray::new([0; 4]);
assert_eq!(img.width(), 2);
assert_eq!(img.height(), 2);
}
#[test]
fn test_image_array_get() {
let mut img = ImageArray::<u8, 2, 2> { data: [1, 2, 3, 4] };
assert_eq!(img.get(0, 0), Some(1));
assert_eq!(img.get(1, 0), Some(2));
assert_eq!(img.get(0, 1), Some(3));
assert_eq!(img[(1, 1)], 4);
assert_eq!(img.get(2, 2), None);
*img.get_mut(0, 0).unwrap() = 10;
assert_eq!(img.get(0, 0), Some(10));
}
#[test]
fn test_compile_time_size_check() {
let _img: ImageArray<u8, 3, 3> = ImageArray::new([0; 9]);
}
#[test]
fn test_rgb_image_to_bytes() {
let img: ImageArray<Rgb8, 2, 2> = ImageArray::new([
Rgb8::new(10, 20, 30),
Rgb8::new(40, 50, 60),
Rgb8::new(70, 80, 90),
Rgb8::new(100, 110, 120),
]);
let bytes = img.as_bytes();
assert_eq!(bytes.len(), 2 * 2 * 3);
assert_eq!(bytes, &[10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]);
let bytes_be = img.as_bytes_be();
assert_eq!(&*bytes_be, bytes);
assert_eq!(bytes, &[10, 20, 30, 40, 50, 60, 70, 80, 90, 100, 110, 120]);
}
#[test]
fn test_mono10_image_to_bytes() {
let img: ImageArray<Mono10, 2, 2> = ImageArray::new([
Mono10::new(512),
Mono10::new(1023),
Mono10::new(256),
Mono10::new(0),
]);
let bytes = img.as_bytes();
assert_eq!(bytes.len(), 2 * 2 * 2);
assert_eq!(bytes, &[0, 2, 255, 3, 0, 1, 0, 0]);
assert_eq!(&*img.as_bytes_le(), bytes);
let bytes_be = img.as_bytes_be();
assert_eq!(&*bytes_be, &[2, 0, 3, 255, 1, 0, 0, 0]);
}
#[test]
fn test_image2d() {
let mut img = Image::<u8>::zero(2, 3);
assert_eq!(img.width(), 2);
assert_eq!(img.height(), 3);
assert_eq!(img.get(0, 0), Some(0));
assert_eq!(img.get(1, 2), Some(0));
assert_eq!(img.get(2, 2), None);
*img.get_mut(1, 1).unwrap() = 42;
assert_eq!(img.get(1, 1), Some(42));
let bytes = img.as_bytes();
assert_eq!(bytes.len(), 2 * 3);
assert_eq!(bytes, &[0, 0, 0, 42, 0, 0]);
}
#[test]
fn test_rgb10_image2d() {
let mut img: Image<Rgb10> = Image::fill(2, 2, Rgb10::new(1, 2, 3));
assert_eq!(img.width(), 2);
assert_eq!(img.height(), 2);
assert_eq!(img.get(0, 0), Some(Rgb10::new(1, 2, 3)));
assert_eq!(img.get(1, 1), Some(Rgb10::new(1, 2, 3)));
*img.get_mut(1, 0).unwrap() = Rgb10::new(10, 20, 30);
assert_eq!(img.get(1, 0), Some(Rgb10::new(10, 20, 30)));
let bytes = img.as_bytes();
assert_eq!(bytes.len(), 2 * 2 * 3 * 2);
assert_eq!(
bytes,
&[
1, 0, 2, 0, 3, 0, 10, 0, 20, 0, 30, 0, 1, 0, 2, 0, 3, 0, 1, 0, 2, 0, 3, 0
]
);
let bytes_be = img.as_bytes_be();
assert_eq!(
&*bytes_be,
&[
0, 1, 0, 2, 0, 3, 0, 10, 0, 20, 0, 30, 0, 1, 0, 2, 0, 3, 0, 1, 0, 2, 0, 3
]
);
}
#[test]
fn test_generate_image_array() {
let img: ImageArray<Rgb16, 3, 4> =
ImageArray::generate(|x, y| Rgb16::new(x as u16, y as u16, (x + y) as u16));
assert_eq!(img.size(), Size::new(3, 4));
assert_eq!(img.get(0, 0), Some(Rgb16::new(0, 0, 0)));
assert_eq!(img.get(1, 2), Some(Rgb16::new(1, 2, 3)));
assert_eq!(img.get(2, 3), Some(Rgb16::new(2, 3, 5)));
}
#[test]
fn test_generate_image2d() {
let img: Image<Rgb16> =
Image::generate(3, 4, |x, y| Rgb16::new(x as u16, y as u16, (x + y) as u16));
assert_eq!(img.size(), Size::new(3, 4));
assert_eq!(img.get(0, 0), Some(Rgb16::new(0, 0, 0)));
assert_eq!(img.get(1, 2), Some(Rgb16::new(1, 2, 3)));
assert_eq!(img.get(2, 3), Some(Rgb16::new(2, 3, 5)));
let img: Image<RgbF32> =
Image::generate(2, 2, |x, y| RgbF32::new(x as f32, y as f32, (x + y) as f32));
assert_eq!(img.size(), Size::new(2, 2));
assert_eq!(img.get(0, 0), Some(RgbF32::new(0.0, 0.0, 0.0)));
assert_eq!(img.get(1, 0), Some(RgbF32::new(1.0, 0.0, 1.0)));
assert_eq!(img.get(0, 1), Some(RgbF32::new(0.0, 1.0, 1.0)));
assert_eq!(img.get(1, 1), Some(RgbF32::new(1.0, 1.0, 2.0)));
}
#[test]
fn test_roi() {
let img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8 + 1));
let roi = img.roi(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.size(), Size::new(2, 2));
assert_eq!(roi.get(0, 0), Some(Mono8::new(6)));
assert_eq!(roi.get(1, 0), Some(Mono8::new(7)));
assert_eq!(roi.get(0, 1), Some(Mono8::new(10)));
assert_eq!(roi.get(1, 1), Some(Mono8::new(11)));
}
#[test]
fn test_roi_image2d() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8 + 1));
let roi = img.roi(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.size(), Size::new(2, 2));
assert_eq!(roi.get(0, 0), Some(Mono8::new(6)));
assert_eq!(roi.get(1, 0), Some(Mono8::new(7)));
assert_eq!(roi.get(0, 1), Some(Mono8::new(10)));
assert_eq!(roi.get(1, 1), Some(Mono8::new(11)));
}
#[test]
fn test_tiles_iter() {
let img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8 + 1));
let img = &img;
let mut iter = img.tiles(Size::new(2, 2));
let roi = iter.next().unwrap();
assert_eq!(roi.size(), Size::new(2, 2));
assert_eq!(roi.get(0, 0), Some(Mono8::new(1)));
assert_eq!(roi.get(1, 0), Some(Mono8::new(2)));
assert_eq!(roi.get(0, 1), Some(Mono8::new(5)));
assert_eq!(roi.get(1, 1), Some(Mono8::new(6)));
}
#[test]
fn test_roi_out_of_bounds() {
let img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8 + 1));
let roi = img.roi(Rectangle::new((3, 3), (2, 2)));
assert!(roi.is_none());
}
#[test]
fn test_roi_mut_out_of_bounds() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8 + 1));
let roi = img.roi_mut(Rectangle::new((3, 3), (2, 2)));
assert!(roi.is_none());
}
#[test]
fn test_roi_mut() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8 + 1));
let mut roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.size(), Size::new(2, 2));
assert_eq!(roi.get(0, 0), Some(Mono8::new(6)));
*roi.get_mut(0, 0).unwrap() = Mono8::new(100);
assert_eq!(roi.get(0, 0), Some(Mono8::new(100)));
}
#[test]
fn test_roi_get_out_of_bounds() {
let img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8 + 1));
let roi = img.roi(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.get(2, 0), None);
assert_eq!(roi.get(0, 2), None);
assert_eq!(roi.get(3, 3), None);
}
#[test]
fn test_image_from_vec() {
let data = vec![1u8, 2, 3, 4, 5, 6];
let img = Image::from_vec(3, 2, data);
assert!(img.is_ok());
let img = img.unwrap();
assert_eq!(img.width(), 3);
assert_eq!(img.height(), 2);
}
#[test]
fn test_image_from_vec_wrong_size() {
let data = vec![1u8, 2, 3, 4, 5];
let img = Image::from_vec(3, 2, data);
assert!(img.is_err());
}
#[test]
fn test_image_from_raw_bytes_mono8() {
let raw = vec![10u8, 20, 30, 40, 50, 60];
let img: Image<Mono8> = Image::from_raw_bytes(3, 2, raw).unwrap();
assert_eq!(img.width(), 3);
assert_eq!(img.height(), 2);
assert_eq!(img.get(0, 0), Some(Mono8::new(10)));
assert_eq!(img.get(2, 1), Some(Mono8::new(60)));
}
#[test]
fn test_image_from_raw_bytes_rgb8() {
use crate::pixel::Rgb8;
let raw = vec![10, 20, 30, 40, 50, 60];
let img: Image<Rgb8> = Image::from_raw_bytes(2, 1, raw).unwrap();
assert_eq!(img.get(0, 0), Some(Rgb8::new(10, 20, 30)));
assert_eq!(img.get(1, 0), Some(Rgb8::new(40, 50, 60)));
}
#[test]
fn test_image_from_raw_bytes_wrong_size() {
let raw = vec![10u8, 20, 30, 40, 50];
assert!(Image::<Mono8>::from_raw_bytes(3, 2, raw).is_err());
}
#[test]
fn test_image_fill() {
let img: Image<u8> = Image::fill(3, 3, 42);
for y in 0..3 {
for x in 0..3 {
assert_eq!(img.get(x, y), Some(42));
}
}
}
#[test]
fn test_image_indexing() {
let mut img: Image<u8> = Image::generate(3, 3, |x, y| (x + y * 3) as u8);
assert_eq!(img[(0, 0)], 0);
assert_eq!(img[(1, 0)], 1);
assert_eq!(img[(2, 1)], 5);
img[(1, 1)] = 99;
assert_eq!(img[(1, 1)], 99);
}
#[test]
fn test_image_array_indexing() {
let mut img: ImageArray<u8, 3, 3> = ImageArray::generate(|x, y| (x + y * 3) as u8);
assert_eq!(img[(0, 0)], 0);
assert_eq!(img[(1, 0)], 1);
assert_eq!(img[(2, 1)], 5);
img[(1, 1)] = 99;
assert_eq!(img[(1, 1)], 99);
}
#[test]
fn test_image_array_as_slice() {
let img: ImageArray<u8, 2, 2> = ImageArray::new([1, 2, 3, 4]);
let slice = img.as_slice();
assert_eq!(slice, &[1, 2, 3, 4]);
}
#[test]
fn test_image_array_as_mut_slice() {
let mut img: ImageArray<u8, 2, 2> = ImageArray::new([1, 2, 3, 4]);
{
let slice = img.as_mut_slice();
slice[0] = 10;
slice[3] = 40;
}
assert_eq!(img.get(0, 0), Some(10));
assert_eq!(img.get(1, 1), Some(40));
}
#[test]
fn test_image_as_slice() {
let img: Image<u8> = Image::generate(2, 2, |x, y| (x + y * 2) as u8 + 1);
let slice = img.as_slice();
assert_eq!(slice, &[1, 2, 3, 4]);
}
#[test]
fn test_image_as_mut_slice() {
let mut img: Image<u8> = Image::generate(2, 2, |x, y| (x + y * 2) as u8 + 1);
{
let slice = img.as_mut_slice();
slice[0] = 10;
slice[3] = 40;
}
assert_eq!(img.get(0, 0), Some(10));
assert_eq!(img.get(1, 1), Some(40));
}
#[test]
fn test_image_array_as_mut_bytes() {
let mut img: ImageArray<u8, 2, 2> = ImageArray::new([1, 2, 3, 4]);
{
let bytes = img.as_mut_bytes();
bytes[0] = 10;
bytes[3] = 40;
}
assert_eq!(img.get(0, 0), Some(10));
assert_eq!(img.get(1, 1), Some(40));
}
#[test]
fn test_image_as_mut_bytes() {
let mut img: Image<u8> = Image::generate(2, 2, |x, y| (x + y * 2) as u8 + 1);
{
let bytes = img.as_mut_bytes();
bytes[0] = 10;
bytes[3] = 40;
}
assert_eq!(img.get(0, 0), Some(10));
assert_eq!(img.get(1, 1), Some(40));
}
#[test]
fn test_image_ref_imageview() {
let img: Image<u8> = Image::generate(3, 4, |x, y| (x + y * 3) as u8 + 1);
let img_ref = &img;
assert_eq!(img_ref.width(), 3);
assert_eq!(img_ref.height(), 4);
assert_eq!(img_ref.size(), Size::new(3, 4));
assert_eq!(img_ref.get(1, 2), Some(8));
assert_eq!(img_ref.pixel_at(2, 3), 12);
fn check_view<V: ImageView<Pixel = u8>>(v: V) {
assert_eq!(v.size(), Size::new(3, 4));
assert_eq!(v.width(), 3);
assert_eq!(v.height(), 4);
assert_eq!(v.get(1, 2), Some(8));
assert_eq!(v.get(99, 99), None);
assert_eq!(v.pixel_at(2, 3), 12);
}
check_view(&img);
}
#[test]
fn test_image_mut_ref_imageview() {
let mut img: Image<u8> = Image::generate(3, 4, |x, y| (x + y * 3) as u8 + 1);
{
let img_ref = &mut img;
assert_eq!(img_ref.width(), 3);
assert_eq!(img_ref.height(), 4);
assert_eq!(img_ref.size(), Size::new(3, 4));
assert_eq!(img_ref.get(1, 2), Some(8));
assert_eq!(img_ref.pixel_at(2, 3), 12);
}
fn check_view<V: ImageView<Pixel = u8>>(v: V) {
assert_eq!(v.size(), Size::new(3, 4));
assert_eq!(v.width(), 3);
assert_eq!(v.height(), 4);
assert_eq!(v.get(1, 2), Some(8));
assert_eq!(v.get(99, 99), None);
assert_eq!(v.pixel_at(2, 3), 12);
}
check_view(&mut img);
}
#[test]
fn test_image_mut_ref_imageviewmut() {
let mut img: Image<u8> = Image::generate(3, 4, |x, y| (x + y * 3) as u8 + 1);
{
let img_ref = &mut img;
*img_ref.get_mut(1, 2).unwrap() = 100;
assert_eq!(img_ref.get(1, 2), Some(100));
let pixel = img_ref.pixel_at_mut(2, 3);
*pixel = 200;
assert_eq!(img_ref.get(2, 3), Some(200));
}
fn check_view_mut<V: ImageViewMut<Pixel = u8>>(mut v: V) {
*v.pixel_at_mut(0, 0) = 42;
assert_eq!(v.get_mut(0, 0), Some(&mut 42));
assert_eq!(v.get_mut(99, 99), None);
}
check_view_mut(&mut img);
}
#[test]
fn test_roi_width_height() {
let img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8 + 1));
let roi = img.roi(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.width(), 2);
assert_eq!(roi.height(), 2);
}
#[test]
fn test_roi_mut_width_height() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8 + 1));
let roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.width(), 2);
assert_eq!(roi.height(), 2);
}
#[test]
fn test_roi_pixel_at() {
let img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8 + 1));
let roi = img.roi(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.pixel_at(0, 0), Mono8::new(6));
assert_eq!(roi.pixel_at(1, 1), Mono8::new(11));
}
#[test]
fn test_roi_mut_pixel_at() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8 + 1));
let roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.pixel_at(0, 0), Mono8::new(6));
assert_eq!(roi.pixel_at(1, 1), Mono8::new(11));
}
#[test]
fn test_roi_mut_pixel_at_mut() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8 + 1));
let mut roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
let pixel = roi.pixel_at_mut(0, 0);
*pixel = Mono8::new(200);
assert_eq!(roi.get(0, 0), Some(Mono8::new(200)));
}
#[test]
fn test_roi_mut_get_mut_out_of_bounds() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8 + 1));
let mut roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.get_mut(2, 0), None);
assert_eq!(roi.get_mut(0, 2), None);
}
#[test]
fn test_image_array_with_different_sizes() {
let img1: ImageArray<u8, 1, 1> = ImageArray::new([42]);
assert_eq!(img1[(0, 0)], 42);
let img2: ImageArray<u8, 5, 5> = ImageArray::generate(|x, y| (x + y) as u8);
assert_eq!(img2[(4, 4)], 8);
let img3: ImageArray<u8, 10, 1> = ImageArray::generate(|x, _| x as u8);
assert_eq!(img3[(9, 0)], 9);
let img4: ImageArray<u8, 1, 10> = ImageArray::generate(|_, y| y as u8);
assert_eq!(img4[(0, 9)], 9);
}
#[test]
fn test_image_array_64x64() {
let img: ImageArray<u8, 64, 64> = ImageArray::generate(|x, y| ((x + y) % 256) as u8);
assert_eq!(img.width(), 64);
assert_eq!(img.height(), 64);
assert_eq!(img.size(), Size::new(64, 64));
assert_eq!(img[(0, 0)], 0);
assert_eq!(img[(63, 63)], (63 + 63) as u8);
assert_eq!(img[(32, 16)], (32 + 16) as u8);
assert_eq!(img.get(64, 0), None); assert_eq!(img.as_slice().len(), 64 * 64);
}
#[test]
fn test_image_array_32x64() {
let img: ImageArray<u8, 32, 64> = ImageArray::generate(|x, y| ((x * 2 + y) % 256) as u8);
assert_eq!(img.width(), 32);
assert_eq!(img.height(), 64);
assert_eq!(img[(31, 63)], (31 * 2 + 63) as u8);
}
#[test]
fn test_image_array_64x1() {
let img: ImageArray<u8, 64, 1> = ImageArray::generate(|x, _| x as u8);
assert_eq!(img.width(), 64);
assert_eq!(img.height(), 1);
assert_eq!(img[(63, 0)], 63);
}
#[test]
fn test_image_array_1x64() {
let img: ImageArray<u8, 1, 64> = ImageArray::generate(|_, y| y as u8);
assert_eq!(img.width(), 1);
assert_eq!(img.height(), 64);
assert_eq!(img[(0, 63)], 63);
}
#[test]
fn test_image_array_large_dimensions_beyond_13() {
let img14: ImageArray<u8, 14, 14> = ImageArray::generate(|x, y| (x + y) as u8);
assert_eq!(img14[(13, 13)], 26);
let img20: ImageArray<u8, 20, 20> = ImageArray::generate(|x, y| (x + y) as u8);
assert_eq!(img20[(19, 19)], 38);
let img50: ImageArray<u8, 50, 50> = ImageArray::generate(|x, y| ((x + y) % 256) as u8);
assert_eq!(img50[(49, 49)], (49 + 49) as u8);
}
#[test]
fn test_size_is_copy() {
let s1 = Size::new(640, 480);
let s2 = s1; assert_eq!(s1, s2); assert_eq!(s1.width, 640);
assert_eq!(s2.height, 480);
}
#[test]
fn test_rectangle_is_copy() {
let r1 = Rectangle::new((10, 20), (100, 200));
let r2 = r1; assert_eq!(r1, r2); assert_eq!(r1.offset.x, 10);
assert_eq!(r2.size.width, 100);
}
#[test]
fn test_size_copy_in_function_call() {
fn takes_size(s: Size) -> usize {
s.area()
}
let s = Size::new(3, 4);
assert_eq!(takes_size(s), 12);
assert_eq!(takes_size(s), 12);
}
#[test]
fn test_image_array_pixel_at_name() {
let img: ImageArray<u8, 3, 3> = ImageArray::generate(|x, y| (x + y * 3) as u8);
assert_eq!(img.pixel_at(0, 0), 0);
assert_eq!(img.pixel_at(2, 2), 8);
}
#[test]
fn test_image_pixel_at_name() {
let img: Image<u8> = Image::generate(3, 3, |x, y| (x + y * 3) as u8);
assert_eq!(img.pixel_at(0, 0), 0);
assert_eq!(img.pixel_at(2, 2), 8);
}
#[test]
fn test_image_pixel_at_mut_name() {
let mut img: Image<u8> = Image::zero(2, 2);
*img.pixel_at_mut(0, 0) = 42;
*img.pixel_at_mut(1, 1) = 99;
assert_eq!(img.pixel_at(0, 0), 42);
assert_eq!(img.pixel_at(1, 1), 99);
}
#[test]
fn test_coordinate_indexing() {
let mut img: Image<u8> = Image::generate(3, 3, |x, y| (x + y * 3) as u8);
let coord = Coordinate::new(1, 2);
assert_eq!(img[coord], 7);
img[coord] = 99;
assert_eq!(img[coord], 99);
}
#[test]
fn test_image_zero_with_zero_size() {
let img: Image<u8> = Image::zero(0, 0);
assert_eq!(img.width(), 0);
assert_eq!(img.height(), 0);
assert_eq!(img.size().area(), 0);
}
#[test]
fn test_image_generate_1x1() {
let img: Image<u8> = Image::generate(1, 1, |_, _| 42);
assert_eq!(img.width(), 1);
assert_eq!(img.height(), 1);
assert_eq!(img.get(0, 0), Some(42));
}
#[test]
fn test_image_array_roi_at_edges() {
let img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8 + 1));
let roi = img.roi(Rectangle::new((0, 0), (2, 2))).unwrap();
assert_eq!(roi.get(0, 0), Some(Mono8::new(1)));
let roi = img.roi(Rectangle::new((2, 2), (1, 1))).unwrap();
assert_eq!(roi.get(0, 0), Some(Mono8::new(11)));
}
#[test]
fn test_image_from_vec_exact_size() {
let data = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9];
let img = Image::from_vec(3, 3, data).unwrap();
assert_eq!(img.get(0, 0), Some(1));
assert_eq!(img.get(2, 2), Some(9));
}
#[test]
fn test_roi_1x1_size() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8 + 1));
let roi = img.roi(Rectangle::new((2, 2), (1, 1))).unwrap();
assert_eq!(roi.width(), 1);
assert_eq!(roi.height(), 1);
assert_eq!(roi.get(0, 0), Some(Mono8::new(11)));
}
#[test]
fn test_contiguous_image_trait() {
let mut img: Image<u8> = Image::zero(2, 2);
let slice = img.as_slice();
assert_eq!(slice.len(), 4);
let mut_slice = img.as_mut_slice();
mut_slice[0] = 100;
assert_eq!(img.get(0, 0), Some(100));
}
#[test]
fn test_image_array_width_height() {
let img: ImageArray<u8, 7, 9> = ImageArray::generate(|x, y| (x + y) as u8);
assert_eq!(img.width(), 7);
assert_eq!(img.height(), 9);
}
#[test]
fn test_image_get_out_of_bounds() {
let img: Image<u8> = Image::zero(3, 3);
assert_eq!(img.get(3, 0), None);
assert_eq!(img.get(0, 3), None);
assert_eq!(img.get(10, 10), None);
}
#[test]
fn test_image_array_get_out_of_bounds() {
let img: ImageArray<u8, 3, 3> = ImageArray::generate(|x, y| (x + y) as u8);
assert_eq!(img.get(3, 0), None);
assert_eq!(img.get(0, 3), None);
assert_eq!(img.get(10, 10), None);
}
#[test]
fn test_roi_mut_modify_through_roi() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8 + 1));
{
let mut roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
let pixel = roi.pixel_at_mut(1, 1);
*pixel = Mono8::new(255);
}
assert_eq!(img.get(2, 2), Some(Mono8::new(255)));
}
#[test]
fn test_image_array_get_mut_out_of_bounds() {
let mut img: ImageArray<u8, 3, 3> = ImageArray::generate(|x, y| (x + y) as u8);
assert!(img.get_mut(3, 0).is_none());
assert!(img.get_mut(0, 3).is_none());
assert!(img.get_mut(10, 10).is_none());
assert!(img.get_mut(0, 0).is_some());
}
#[test]
fn test_image_array_roi_mut_valid() {
let mut img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8));
{
let mut roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.width(), 2);
assert_eq!(roi.height(), 2);
assert_eq!(roi.pixel_at(0, 0), Mono8::new(5)); *roi.pixel_at_mut(0, 0) = Mono8::new(255);
}
assert_eq!(img.pixel_at(1, 1), Mono8::new(255));
}
#[test]
fn test_image_array_roi_mut_out_of_bounds() {
let mut img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8));
let roi = img.roi_mut(Rectangle::new((3, 3), (2, 2)));
assert!(roi.is_none());
}
#[test]
fn test_image_ref_mut_get() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.get(0, 0), Some(Mono8::new(5))); assert_eq!(roi.get(1, 0), Some(Mono8::new(6))); assert_eq!(roi.get(0, 1), Some(Mono8::new(9))); assert_eq!(roi.get(2, 0), None);
assert_eq!(roi.get(0, 2), None);
}
#[test]
fn test_image_ref_mut_edge_bounds() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let roi = img.roi_mut(Rectangle::new((2, 2), (2, 2))).unwrap();
assert_eq!(roi.size(), Size::new(2, 2));
assert_eq!(roi.get(0, 0), Some(Mono8::new(10))); assert_eq!(roi.get(1, 1), Some(Mono8::new(15))); assert_eq!(roi.get(2, 0), None);
assert_eq!(roi.get(0, 2), None);
}
#[test]
fn test_image_ref_mut_mutation_reflected_in_image() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
{
let mut roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
*roi.pixel_at_mut(0, 0) = Mono8::new(200);
*roi.pixel_at_mut(1, 1) = Mono8::new(201);
}
assert_eq!(img.get(1, 1), Some(Mono8::new(200)));
assert_eq!(img.get(2, 2), Some(Mono8::new(201)));
}
#[test]
fn test_image_ref_mut_send_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<ImageRefMut<'_, u8>>();
assert_sync::<ImageRefMut<'_, u8>>();
}
#[test]
fn test_image_ref_mut_full_image_roi() {
let mut img: Image<Mono8> = Image::generate(3, 3, |x, y| Mono8::new((x + y * 3) as u8));
let roi = img.roi_mut(Rectangle::new((0, 0), (3, 3))).unwrap();
assert_eq!(roi.size(), Size::new(3, 3));
assert_eq!(roi.get(0, 0), Some(Mono8::new(0)));
assert_eq!(roi.get(2, 2), Some(Mono8::new(8)));
}
#[test]
fn test_image_ref_mut_1x1_roi() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let mut roi = img.roi_mut(Rectangle::new((2, 3), (1, 1))).unwrap();
assert_eq!(roi.size(), Size::new(1, 1));
assert_eq!(roi.get(0, 0), Some(Mono8::new(14))); *roi.pixel_at_mut(0, 0) = Mono8::new(99);
assert_eq!(roi.get(0, 0), Some(Mono8::new(99)));
}
#[test]
fn test_image_array_roi_mut_send_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
let mut img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8));
let roi = img.roi_mut(Rectangle::new((0, 0), (2, 2))).unwrap();
assert_eq!(roi.get(0, 0), Some(Mono8::new(0)));
assert_send::<ImageRefMut<'_, Mono8>>();
assert_sync::<ImageRefMut<'_, Mono8>>();
}
#[test]
fn test_image_roi_out_of_bounds() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let roi = img.roi(Rectangle::new((3, 3), (2, 2)));
assert!(roi.is_none());
}
#[test]
fn test_mut_ref_image2d_imageview_methods() {
let mut img: Image<u8> = Image::generate(3, 3, |x, y| (x + y * 3) as u8);
let img_ref: &mut Image<u8> = &mut img;
assert_eq!(img_ref.size(), Size::new(3, 3));
assert_eq!(img_ref.width(), 3);
assert_eq!(img_ref.height(), 3);
assert_eq!(img_ref.get(0, 0), Some(0));
assert_eq!(img_ref.get(1, 0), Some(1));
assert_eq!(img_ref.get(3, 0), None);
assert_eq!(img_ref.pixel_at(2, 2), 8);
}
#[test]
fn test_mut_ref_image2d_imageviewmut_methods() {
let mut img: Image<u8> = Image::generate(3, 3, |x, y| (x + y * 3) as u8);
let img_ref: &mut Image<u8> = &mut img;
assert_eq!(img_ref.get_mut(0, 0), Some(&mut 0));
assert_eq!(img_ref.get_mut(3, 0), None);
*img_ref.pixel_at_mut(1, 1) = 99;
assert_eq!(img_ref.pixel_at(1, 1), 99);
}
#[test]
fn test_image_as_bytes_le() {
let img: Image<u8> = Image::generate(2, 2, |x, y| (x + y * 2) as u8);
let bytes_le = img.as_bytes_le();
assert_eq!(&*bytes_le, &[0, 1, 2, 3]);
}
#[test]
fn test_image_as_bytes_be() {
let img: Image<u8> = Image::generate(2, 2, |x, y| (x + y * 2) as u8);
let bytes_be = img.as_bytes_be();
assert_eq!(&*bytes_be, &[0, 1, 2, 3]);
}
#[test]
fn test_image_as_bytes_le_u16() {
let img: Image<Rgb16> = Image::generate(1, 1, |_, _| Rgb16::new(0x0102, 0x0304, 0x0506));
let bytes_le = img.as_bytes_le();
assert_eq!(&*bytes_le, &[0x02, 0x01, 0x04, 0x03, 0x06, 0x05]);
}
#[test]
fn test_image_as_bytes_be_u16() {
let img: Image<Rgb16> = Image::generate(1, 1, |_, _| Rgb16::new(0x0102, 0x0304, 0x0506));
let bytes_be = img.as_bytes_be();
assert_eq!(&*bytes_be, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]);
}
#[test]
fn test_mut_ref_image2d_imageview_via_trait_object() {
fn read_via_view<V: ImageView<Pixel = u8>>(v: V) -> (usize, usize, Option<u8>, u8) {
let w = v.width();
let h = v.height();
let oob = v.get(100, 100);
let val = v.pixel_at(0, 0);
(w, h, oob, val)
}
let mut img: Image<u8> = Image::generate(3, 2, |x, y| (x + y * 3) as u8);
let r: &mut Image<u8> = &mut img;
let (w, h, oob, val) = read_via_view(r);
assert_eq!(w, 3);
assert_eq!(h, 2);
assert_eq!(oob, None);
assert_eq!(val, 0);
}
#[test]
fn test_mut_ref_image2d_imageviewmut_via_trait_object() {
fn write_via_view<V: ImageView<Pixel = u8> + ImageViewMut>(mut v: V) {
if let Some(px) = v.get_mut(0, 0) {
*px = 77;
}
assert!(v.get_mut(100, 100).is_none());
*v.pixel_at_mut(1, 0) = 88;
}
let mut img: Image<u8> = Image::zero(3, 2);
write_via_view(&mut img);
assert_eq!(img.pixel_at(0, 0), 77);
assert_eq!(img.pixel_at(1, 0), 88);
}
#[test]
fn test_mut_ref_image2d_size() {
let mut img: Image<u8> = Image::zero(5, 7);
let r: &mut Image<u8> = &mut img;
assert_eq!(r.size(), Size::new(5, 7));
}
#[test]
fn test_image_get_none_direct() {
let img: Image<Mono8> = Image::fill(2, 2, Mono8::new(1));
assert!(img.get(2, 0).is_none());
assert!(img.get(0, 2).is_none());
assert!(img.get(999, 999).is_none());
assert!(img.get(0, 0).is_some());
}
#[test]
fn test_image_get_mut_none() {
let mut img: Image<u8> = Image::zero(3, 3);
assert!(img.get_mut(3, 0).is_none());
assert!(img.get_mut(0, 3).is_none());
assert!(img.get_mut(0, 0).is_some());
}
#[test]
fn test_image_roi_none_x() {
let img: Image<Mono8> = Image::zero(4, 4);
assert!(img.roi(Rectangle::new((3, 0), (2, 1))).is_none());
}
#[test]
fn test_image_roi_none_y() {
let img: Image<Mono8> = Image::zero(4, 4);
assert!(img.roi(Rectangle::new((0, 3), (1, 2))).is_none());
}
#[test]
fn test_image_roi_mut_none_x() {
let mut img: Image<Mono8> = Image::zero(4, 4);
assert!(img.roi_mut(Rectangle::new((3, 0), (2, 1))).is_none());
}
#[test]
fn test_image_roi_mut_none_y() {
let mut img: Image<Mono8> = Image::zero(4, 4);
assert!(img.roi_mut(Rectangle::new((0, 3), (1, 2))).is_none());
}
#[test]
fn test_from_raw_bytes_size_mismatch() {
let bytes = vec![0u8; 7];
let result: Result<Image<Mono8>, Error> = Image::from_raw_bytes(2, 2, bytes);
assert!(result.is_err()); }
#[test]
fn test_from_raw_bytes_srgb_mono8() {
use crate::pixel::SrgbMono8;
let raw = vec![10u8, 20, 30, 40, 50, 60];
let img: Image<SrgbMono8> = Image::from_raw_bytes(3, 2, raw).unwrap();
assert_eq!(img.width(), 3);
assert_eq!(img.height(), 2);
assert_eq!(img.pixel_at(0, 0), SrgbMono8::new(10));
assert_eq!(img.pixel_at(2, 1), SrgbMono8::new(60));
}
#[test]
fn test_from_raw_bytes_rgba8() {
let raw = vec![1, 2, 3, 4, 5, 6, 7, 8];
let img: Image<Rgba8> = Image::from_raw_bytes(2, 1, raw).unwrap();
assert_eq!(img.pixel_at(0, 0), Rgba8::new(1, 2, 3, 4));
assert_eq!(img.pixel_at(1, 0), Rgba8::new(5, 6, 7, 8));
}
#[test]
fn test_from_raw_bytes_monoa8() {
let raw = vec![10, 255, 20, 128];
let img: Image<MonoA8> = Image::from_raw_bytes(2, 1, raw).unwrap();
assert_eq!(img.pixel_at(0, 0), MonoA8::new(10, 255));
assert_eq!(img.pixel_at(1, 0), MonoA8::new(20, 128));
}
#[test]
fn test_image_array_plain_data_mut_bytes_rgb8() {
let mut img: ImageArray<Rgb8, 2, 1> =
ImageArray::new([Rgb8::new(10, 20, 30), Rgb8::new(40, 50, 60)]);
let bytes = img.as_mut_bytes();
assert_eq!(bytes, &[10, 20, 30, 40, 50, 60]);
bytes[0] = 99;
assert_eq!(img.pixel_at(0, 0).r.0, 99);
}
#[test]
fn test_image_array_plain_data_le_be_mono8() {
let img: ImageArray<Mono8, 3, 1> =
ImageArray::new([Mono8::new(1), Mono8::new(2), Mono8::new(3)]);
let le = img.as_bytes_le();
let be = img.as_bytes_be();
assert_eq!(&*le, &[1, 2, 3]);
assert_eq!(&*be, &[1, 2, 3]);
}
#[test]
fn test_image_array_contiguous_image_mono8() {
let mut img: ImageArray<Mono8, 2, 2> =
ImageArray::generate(|x, y| Mono8::new((x + y) as u8));
let slice = img.as_slice();
assert_eq!(slice.len(), 4);
assert_eq!(slice[0], Mono8::new(0));
let mslice = img.as_mut_slice();
mslice[0] = Mono8::new(42);
assert_eq!(img.pixel_at(0, 0), Mono8::new(42));
}
#[test]
fn test_image_array_coordinate_indexing() {
let mut img: ImageArray<Mono8, 3, 3> =
ImageArray::generate(|x, y| Mono8::new((x + y * 3) as u8));
let coord = Coordinate::new(2, 1);
assert_eq!(img[coord], Mono8::new(5));
img[coord] = Mono8::new(99);
assert_eq!(img[coord], Mono8::new(99));
}
#[test]
fn test_image_array_tuple_indexing() {
let mut img: ImageArray<u8, 2, 3> = ImageArray::generate(|x, y| (x + y * 2) as u8);
assert_eq!(img[(0, 0)], 0);
assert_eq!(img[(1, 2)], 5);
img[(1, 2)] = 77;
assert_eq!(img[(1, 2)], 77);
}
#[test]
fn test_image_ref_properties_via_roi() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let rect = Rectangle::new((1, 2), (2, 1));
let roi = img.roi(rect).unwrap();
assert_eq!(roi.width(), 2);
assert_eq!(roi.height(), 1);
assert_eq!(roi.size(), Size::new(2, 1));
}
#[test]
fn test_image_ref_width_height_pixel_at() {
let img: ImageArray<Mono8, 5, 5> =
ImageArray::generate(|x, y| Mono8::new((x + y * 5) as u8));
let roi = img.roi(Rectangle::new((1, 1), (3, 2))).unwrap();
assert_eq!(roi.width(), 3);
assert_eq!(roi.height(), 2);
assert_eq!(roi.pixel_at(0, 0), Mono8::new(6)); assert_eq!(roi.pixel_at(2, 1), Mono8::new(13)); }
#[test]
fn test_image_ref_get_out_of_bounds_imagearray() {
let img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8));
let roi = img.roi(Rectangle::new((0, 0), (2, 2))).unwrap();
assert_eq!(roi.get(0, 0), Some(Mono8::new(0)));
assert_eq!(roi.get(1, 1), Some(Mono8::new(5)));
assert_eq!(roi.get(2, 0), None);
assert_eq!(roi.get(0, 2), None);
}
#[test]
fn test_image_ref_mut_get_mut_write_read() {
let mut img: Image<Mono8> = Image::fill(3, 3, Mono8::new(0));
let mut roi = img.roi_mut(Rectangle::new((0, 0), (3, 3))).unwrap();
for y in 0..3 {
for x in 0..3 {
*roi.get_mut(x, y).unwrap() = Mono8::new((x + y * 3) as u8);
}
}
for y in 0..3 {
for x in 0..3 {
assert_eq!(roi.get(x, y), Some(Mono8::new((x + y * 3) as u8)));
}
}
}
#[test]
fn test_image_ref_mut_width_height() {
let mut img: Image<Mono8> = Image::generate(6, 4, |x, y| Mono8::new((x + y) as u8));
let roi = img.roi_mut(Rectangle::new((2, 1), (3, 2))).unwrap();
assert_eq!(roi.width(), 3);
assert_eq!(roi.height(), 2);
assert_eq!(roi.size(), Size::new(3, 2));
}
#[test]
fn test_image_coordinate_indexing_mono8() {
let mut img: Image<Mono8> = Image::generate(3, 3, |x, y| Mono8::new((x + y * 3) as u8));
let coord = Coordinate::new(2, 2);
assert_eq!(img[coord], Mono8::new(8));
img[coord] = Mono8::new(42);
assert_eq!(img[coord], Mono8::new(42));
}
#[test]
fn test_ref_image_imageview_delegation() {
let img: Image<u8> = Image::generate(3, 2, |x, y| (x + y * 3) as u8);
let r: &Image<u8> = &img;
assert_eq!(r.size(), Size::new(3, 2));
assert_eq!(r.width(), 3);
assert_eq!(r.height(), 2);
assert_eq!(r.get(0, 0), Some(0));
assert_eq!(r.get(2, 1), Some(5));
assert_eq!(r.get(3, 0), None);
assert_eq!(r.pixel_at(1, 0), 1);
}
#[test]
fn test_image_plain_data_mut_bytes() {
let mut img: Image<Mono8> = Image::fill(2, 2, Mono8::new(10));
let bytes = img.as_mut_bytes();
assert_eq!(bytes, &[10, 10, 10, 10]);
bytes[0] = 99;
assert_eq!(img.pixel_at(0, 0), Mono8::new(99));
}
#[test]
fn test_image_contiguous_image_mono8() {
let mut img: Image<Mono8> = Image::fill(2, 2, Mono8::new(5));
assert_eq!(img.as_slice().len(), 4);
assert_eq!(img.as_slice()[0], Mono8::new(5));
img.as_mut_slice()[0] = Mono8::new(42);
assert_eq!(img.pixel_at(0, 0), Mono8::new(42));
}
#[test]
fn test_imagearray_tiling_exercises_image_ref() {
let img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8));
let tiles: Vec<_> = img.tiles(Size::new(2, 2)).collect();
assert_eq!(tiles.len(), 4);
let t = &tiles[0];
assert_eq!(t.size(), Size::new(2, 2));
assert_eq!(t.width(), 2);
assert_eq!(t.height(), 2);
assert_eq!(t.get(0, 0), Some(Mono8::new(0)));
assert_eq!(t.get(1, 1), Some(Mono8::new(5)));
assert_eq!(t.get(2, 0), None);
assert_eq!(t.pixel_at(0, 0), Mono8::new(0));
}
#[test]
fn test_imagearray_tiling_partial_exercises_image_ref() {
let img: ImageArray<Mono8, 5, 3> =
ImageArray::generate(|x, y| Mono8::new((x + y * 5) as u8));
let tiles: Vec<_> = img.tiles(Size::new(3, 2)).collect();
assert_eq!(tiles.len(), 4); assert_eq!(tiles[1].size(), Size::new(2, 2));
assert_eq!(tiles[1].get(0, 0), Some(Mono8::new(3)));
assert_eq!(tiles[1].pixel_at(1, 0), Mono8::new(4));
assert_eq!(tiles[2].size(), Size::new(3, 1));
assert_eq!(tiles[2].get(0, 0), Some(Mono8::new(10)));
}
#[test]
fn test_from_raw_bytes_zero_size() {
let raw: Vec<u8> = vec![];
let img: Result<Image<Mono8>, Error> = Image::from_raw_bytes(0, 0, raw);
assert!(img.is_ok());
let img = img.unwrap();
assert_eq!(img.width(), 0);
assert_eq!(img.height(), 0);
}
#[test]
fn test_image_ref_new_success() {
let data = [1u8, 2, 3, 4, 5, 6];
let view = ImageRef::new(3, 2, &data).unwrap();
assert_eq!(view.width(), 3);
assert_eq!(view.height(), 2);
assert_eq!(view.size(), Size::new(3, 2));
assert!(view.is_contiguous());
}
#[test]
fn test_image_ref_new_size_mismatch() {
let data = [1u8, 2, 3, 4, 5];
assert!(ImageRef::new(3, 2, &data).is_err());
}
#[test]
fn test_image_ref_pixel_at() {
let data: Vec<u8> = (0..12).collect();
let view = ImageRef::new(4, 3, &data).unwrap();
assert_eq!(view.pixel_at(0, 0), 0);
assert_eq!(view.pixel_at(3, 0), 3);
assert_eq!(view.pixel_at(0, 1), 4);
assert_eq!(view.pixel_at(3, 2), 11);
}
#[test]
fn test_image_ref_get_out_of_bounds() {
let data = [1u8, 2, 3, 4];
let view = ImageRef::new(2, 2, &data).unwrap();
assert_eq!(view.get(0, 0), Some(1));
assert_eq!(view.get(1, 1), Some(4));
assert_eq!(view.get(2, 0), None);
assert_eq!(view.get(0, 2), None);
}
#[test]
fn test_image_ref_is_contiguous() {
let data = [1u8, 2, 3, 4, 5, 6];
let view = ImageRef::new(3, 2, &data).unwrap();
assert!(view.is_contiguous());
let strided = ImageRef::strided(Size::new(2, 2), 3, 0, &data).unwrap();
assert!(!strided.is_contiguous());
}
#[test]
fn test_image_ref_strided_pixel_at() {
let data: Vec<u8> = (0..12).collect();
let view = ImageRef::strided(Size::new(2, 2), 4, 5, &data).unwrap();
assert_eq!(view.pixel_at(0, 0), 5); assert_eq!(view.pixel_at(1, 0), 6); assert_eq!(view.pixel_at(0, 1), 9); assert_eq!(view.pixel_at(1, 1), 10); }
#[test]
fn test_image_ref_roi_contiguous() {
let data: Vec<Mono8> = (0u8..16).map(Mono8::new).collect();
let view = ImageRef::new(4, 4, &data).unwrap();
let sub = view.roi(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(sub.size(), Size::new(2, 2));
assert_eq!(sub.pixel_at(0, 0), Mono8::new(5));
assert_eq!(sub.pixel_at(1, 0), Mono8::new(6));
assert_eq!(sub.pixel_at(0, 1), Mono8::new(9));
assert_eq!(sub.pixel_at(1, 1), Mono8::new(10));
}
#[test]
fn test_image_ref_roi_out_of_bounds() {
let data: Vec<Mono8> = (0u8..16).map(Mono8::new).collect();
let view = ImageRef::new(4, 4, &data).unwrap();
assert!(view.roi(Rectangle::new((3, 3), (2, 2))).is_none());
}
#[test]
fn test_image_ref_nested_roi() {
let data: Vec<Mono8> = (0u8..36).map(Mono8::new).collect();
let view = ImageRef::new(6, 6, &data).unwrap();
let sub1 = view.roi(Rectangle::new((1, 1), (4, 4))).unwrap();
let sub2 = sub1.roi(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(sub2.pixel_at(0, 0), Mono8::new(14)); assert_eq!(sub2.pixel_at(1, 0), Mono8::new(15)); assert_eq!(sub2.pixel_at(0, 1), Mono8::new(20)); assert_eq!(sub2.pixel_at(1, 1), Mono8::new(21)); }
#[test]
fn test_image_ref_copy() {
let data = [1u8, 2, 3, 4];
let view = ImageRef::new(2, 2, &data).unwrap();
let copy = view; assert_eq!(view.pixel_at(0, 0), copy.pixel_at(0, 0));
}
#[test]
fn test_image_ref_zero_size() {
let data: [u8; 0] = [];
let view = ImageRef::new(0, 0, &data).unwrap();
assert_eq!(view.width(), 0);
assert_eq!(view.height(), 0);
assert!(view.is_contiguous());
}
#[test]
fn test_image_ref_mut_new_success() {
let mut data = [1u8, 2, 3, 4];
let view = ImageRefMut::new(2, 2, &mut data).unwrap();
assert_eq!(view.width(), 2);
assert_eq!(view.height(), 2);
assert!(view.is_contiguous());
}
#[test]
fn test_image_ref_mut_new_size_mismatch() {
let mut data = [1u8, 2, 3];
assert!(ImageRefMut::new(2, 2, &mut data).is_err());
}
#[test]
fn test_image_ref_mut_pixel_at_mut() {
let mut data = [0u8; 4];
let mut view = ImageRefMut::new(2, 2, &mut data).unwrap();
*view.pixel_at_mut(1, 1) = 42;
assert_eq!(view.pixel_at(1, 1), 42);
}
#[test]
fn test_image_ref_mut_mutation_visible_in_parent() {
let mut img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
{
let mut roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
*roi.pixel_at_mut(0, 0) = Mono8::new(200);
}
assert_eq!(img.get(1, 1), Some(Mono8::new(200)));
}
#[test]
fn test_image_ref_mut_send_sync_standalone() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<ImageRefMut<'_, u8>>();
assert_sync::<ImageRefMut<'_, u8>>();
}
#[test]
fn test_image_ref_mut_get_mut_out_of_bounds() {
let mut data = [0u8; 4];
let mut view = ImageRefMut::new(2, 2, &mut data).unwrap();
assert!(view.get_mut(2, 0).is_none());
assert!(view.get_mut(0, 2).is_none());
assert!(view.get_mut(0, 0).is_some());
}
#[test]
fn test_image_ref_mut_roi_mut() {
let mut data: Vec<Mono8> = (0u8..16).map(Mono8::new).collect();
let mut view = ImageRefMut::new(4, 4, &mut data).unwrap();
let mut sub = view.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(sub.pixel_at(0, 0), Mono8::new(5));
*sub.pixel_at_mut(0, 0) = Mono8::new(99);
assert_eq!(sub.pixel_at(0, 0), Mono8::new(99));
}
#[test]
fn test_image_ref_mut_roi_read_only() {
let mut data: Vec<Mono8> = (0u8..16).map(Mono8::new).collect();
let view = ImageRefMut::new(4, 4, &mut data).unwrap();
let sub = view.roi(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(sub.pixel_at(0, 0), Mono8::new(5));
}
#[test]
fn test_from_bytes_copy_mono8() {
let bytes = vec![10u8, 20, 30, 40, 50, 60];
let img: Image<Mono8> = Image::from_bytes_copy(3, 2, &bytes).unwrap();
assert_eq!(img.width(), 3);
assert_eq!(img.height(), 2);
assert_eq!(img.pixel_at(0, 0), Mono8::new(10));
assert_eq!(img.pixel_at(2, 0), Mono8::new(30));
assert_eq!(img.pixel_at(0, 1), Mono8::new(40));
assert_eq!(img.pixel_at(2, 1), Mono8::new(60));
}
#[test]
fn test_from_bytes_copy_mono16() {
let bytes: Vec<u8> = vec![0x00, 0x01, 0x02, 0x03];
let img: Image<Mono16> = Image::from_bytes_copy(2, 1, &bytes).unwrap();
assert_eq!(img.width(), 2);
assert_eq!(img.height(), 1);
let expected_0 = u16::from_ne_bytes([0x00, 0x01]);
let expected_1 = u16::from_ne_bytes([0x02, 0x03]);
assert_eq!(img.pixel_at(0, 0).value(), expected_0);
assert_eq!(img.pixel_at(1, 0).value(), expected_1);
}
#[test]
fn test_from_bytes_copy_rgb8() {
let bytes = vec![10, 20, 30, 40, 50, 60];
let img: Image<Rgb8> = Image::from_bytes_copy(2, 1, &bytes).unwrap();
assert_eq!(img.width(), 2);
assert_eq!(img.height(), 1);
assert_eq!(img.pixel_at(0, 0), Rgb8::new(10, 20, 30));
assert_eq!(img.pixel_at(1, 0), Rgb8::new(40, 50, 60));
}
#[test]
fn test_from_bytes_copy_wrong_size() {
let bytes = vec![1, 2, 3]; let result = Image::<Mono8>::from_bytes_copy(2, 2, &bytes);
assert!(result.is_err());
match result {
Err(Error::LengthMismatch { expected, actual }) => {
assert_eq!(expected, 4);
assert_eq!(actual, 3);
}
Err(other) => panic!("expected LengthMismatch, got {:?}", other),
Ok(_) => panic!("expected Err, got Ok"),
}
}
#[test]
fn test_from_bytes_copy_zero_size() {
let bytes: Vec<u8> = vec![];
let img: Image<Mono8> = Image::from_bytes_copy(0, 0, &bytes).unwrap();
assert_eq!(img.width(), 0);
assert_eq!(img.height(), 0);
}
#[test]
fn test_from_bytes_copy_roundtrip() {
let original = Image::from_vec(
2,
2,
vec![
Mono16::new(100),
Mono16::new(200),
Mono16::new(300),
Mono16::new(400),
],
)
.unwrap();
let bytes_data: Vec<u8> = original.as_bytes().to_vec();
let reconstructed: Image<Mono16> = Image::from_bytes_copy(2, 2, &bytes_data).unwrap();
assert_eq!(reconstructed.pixel_at(0, 0).value(), 100);
assert_eq!(reconstructed.pixel_at(1, 0).value(), 200);
assert_eq!(reconstructed.pixel_at(0, 1).value(), 300);
assert_eq!(reconstructed.pixel_at(1, 1).value(), 400);
}
#[test]
fn test_raster_image_row_basic() {
let img = Image::generate(4, 3, |x, y| (y * 4 + x) as u8);
assert_eq!(img.row(0), &[0, 1, 2, 3]);
assert_eq!(img.row(1), &[4, 5, 6, 7]);
assert_eq!(img.row(2), &[8, 9, 10, 11]);
}
#[test]
fn test_raster_image_row_1x1() {
let img = Image::fill(1, 1, 42u8);
assert_eq!(img.row(0), &[42]);
}
#[test]
fn test_raster_image_row_wide() {
let img = Image::generate(10, 1, |x, _| x as u8);
assert_eq!(img.row(0), &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
}
#[test]
fn test_raster_image_row_tall() {
let img = Image::generate(1, 5, |_, y| y as u8);
for y in 0..5 {
assert_eq!(img.row(y), &[y as u8]);
}
}
#[test]
#[should_panic]
fn test_raster_image_row_out_of_bounds() {
let img = Image::fill(3, 3, 0u8);
let _ = img.row(3);
}
#[test]
fn test_raster_image_mut_row_mut_basic() {
let mut img = Image::generate(4, 3, |x, y| (y * 4 + x) as u8);
img.row_mut(1).fill(99);
assert_eq!(img.row(0), &[0, 1, 2, 3]);
assert_eq!(img.row(1), &[99, 99, 99, 99]);
assert_eq!(img.row(2), &[8, 9, 10, 11]);
}
#[test]
fn test_raster_image_mut_row_mut_individual() {
let mut img = Image::fill(3, 2, 0u8);
img.row_mut(0)[1] = 42;
assert_eq!(img.row(0), &[0, 42, 0]);
assert_eq!(img.row(1), &[0, 0, 0]);
}
#[test]
#[should_panic]
fn test_raster_image_mut_row_mut_out_of_bounds() {
let mut img = Image::fill(3, 3, 0u8);
let _ = img.row_mut(3);
}
#[test]
fn test_raster_image_row_len_equals_width() {
let img = Image::fill(7, 5, 0u8);
for y in 0..img.height() {
assert_eq!(img.row(y).len(), 7);
}
}
#[test]
fn test_raster_image_row_consistent_with_pixel_at() {
let img = Image::generate(5, 4, |x, y| (y * 10 + x) as u8);
for y in 0..img.height() {
let row = img.row(y);
for (x, &pixel) in row.iter().enumerate() {
assert_eq!(pixel, img.pixel_at(x, y));
}
}
}
#[test]
fn test_raster_image_mut_row_mut_consistent_with_pixel_at_mut() {
let mut img = Image::generate(4, 3, |x, y| (y * 4 + x) as u8);
for y in 0..img.height() {
for x in 0..img.width() {
img.row_mut(y)[x] += 100;
}
}
for y in 0..img.height() {
for x in 0..img.width() {
assert_eq!(img.pixel_at(x, y), (y * 4 + x) as u8 + 100);
}
}
}
#[test]
fn test_raster_image_array_row_basic() {
let img: ImageArray<u8, 3, 2> = ImageArray::generate(|x, y| (y * 3 + x) as u8);
assert_eq!(img.row(0), &[0, 1, 2]);
assert_eq!(img.row(1), &[3, 4, 5]);
}
#[test]
fn test_raster_image_array_row_1x1() {
let img: ImageArray<u8, 1, 1> = ImageArray::new([7]);
assert_eq!(img.row(0), &[7]);
}
#[test]
#[should_panic]
fn test_raster_image_array_row_out_of_bounds() {
let img: ImageArray<u8, 3, 2> = ImageArray::generate(|_, _| 0);
let _ = img.row(2);
}
#[test]
fn test_raster_image_array_row_mut_basic() {
let mut img: ImageArray<u8, 3, 2> = ImageArray::generate(|x, y| (y * 3 + x) as u8);
img.row_mut(0).fill(99);
assert_eq!(img.row(0), &[99, 99, 99]);
assert_eq!(img.row(1), &[3, 4, 5]);
}
#[test]
fn test_raster_image_array_row_consistent_with_pixel_at() {
let img: ImageArray<u8, 4, 4> = ImageArray::generate(|x, y| (y * 4 + x) as u8);
for y in 0..img.height() {
let row = img.row(y);
for (x, &pixel) in row.iter().enumerate() {
assert_eq!(pixel, img.pixel_at(x, y));
}
}
}
#[test]
fn test_raster_image_ref_row_contiguous() {
let data: Vec<u8> = (0..12).collect();
let img = ImageRef::new(4, 3, &data).unwrap();
assert_eq!(img.row(0), &[0, 1, 2, 3]);
assert_eq!(img.row(1), &[4, 5, 6, 7]);
assert_eq!(img.row(2), &[8, 9, 10, 11]);
}
#[test]
fn test_raster_image_ref_row_consistent_with_pixel_at() {
let data: Vec<u8> = (0..20).collect();
let img = ImageRef::new(5, 4, &data).unwrap();
for y in 0..img.height() {
let row = img.row(y);
for (x, &pixel) in row.iter().enumerate() {
assert_eq!(pixel, img.pixel_at(x, y));
}
}
}
#[test]
#[should_panic]
fn test_raster_image_ref_row_out_of_bounds() {
let data: Vec<u8> = (0..6).collect();
let img = ImageRef::new(3, 2, &data).unwrap();
let _ = img.row(2);
}
#[test]
fn test_raster_image_ref_strided_row() {
let img = Image::generate(4, 4, |x, y| Mono8::new((y * 4 + x) as u8));
let roi = img.roi(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.row(0), &[Mono8::new(5), Mono8::new(6)]);
assert_eq!(roi.row(1), &[Mono8::new(9), Mono8::new(10)]);
}
#[test]
fn test_raster_image_ref_strided_row_consistent_with_pixel_at() {
let img = Image::generate(6, 6, |x, y| Mono8::new((y * 6 + x) as u8));
let roi = img.roi(Rectangle::new((2, 1), (3, 4))).unwrap();
for y in 0..roi.height() {
let row = roi.row(y);
for (x, &pixel) in row.iter().enumerate() {
assert_eq!(pixel, roi.pixel_at(x, y));
}
}
}
#[test]
fn test_raster_image_ref_strided_row_len_equals_roi_width() {
let img = Image::generate(8, 8, |x, y| Mono8::new((y * 8 + x) as u8));
let roi = img.roi(Rectangle::new((1, 1), (5, 3))).unwrap();
for y in 0..roi.height() {
assert_eq!(roi.row(y).len(), 5);
}
}
#[test]
fn test_raster_image_ref_nested_roi_row() {
let img = Image::generate(8, 8, |x, y| Mono8::new((y * 8 + x) as u8));
let roi1 = img.roi(Rectangle::new((1, 1), (6, 6))).unwrap();
let roi2 = roi1.roi(Rectangle::new((1, 1), (3, 3))).unwrap();
assert_eq!(
roi2.row(0),
&[Mono8::new(18), Mono8::new(19), Mono8::new(20)]
);
assert_eq!(
roi2.row(1),
&[Mono8::new(26), Mono8::new(27), Mono8::new(28)]
);
assert_eq!(
roi2.row(2),
&[Mono8::new(34), Mono8::new(35), Mono8::new(36)]
);
}
#[test]
fn test_raster_image_ref_mut_row() {
let mut data: Vec<u8> = (0..12).collect();
let view = ImageRefMut::new(4, 3, &mut data).unwrap();
assert_eq!(view.row(0), &[0, 1, 2, 3]);
assert_eq!(view.row(1), &[4, 5, 6, 7]);
assert_eq!(view.row(2), &[8, 9, 10, 11]);
}
#[test]
fn test_raster_image_ref_mut_row_mut() {
let mut data: Vec<u8> = (0..12).collect();
let mut view = ImageRefMut::new(4, 3, &mut data).unwrap();
view.row_mut(1).fill(42);
assert_eq!(view.row(0), &[0, 1, 2, 3]);
assert_eq!(view.row(1), &[42, 42, 42, 42]);
assert_eq!(view.row(2), &[8, 9, 10, 11]);
}
#[test]
fn test_raster_image_ref_mut_row_mut_reflected_in_data() {
let mut data: Vec<u8> = (0..6).collect();
let mut view = ImageRefMut::new(3, 2, &mut data).unwrap();
view.row_mut(0)[1] = 99;
#[allow(clippy::drop_non_drop)]
drop(view); assert_eq!(data, vec![0, 99, 2, 3, 4, 5]);
}
#[test]
fn test_raster_image_ref_mut_strided_row() {
let mut img = Image::generate(4, 4, |x, y| Mono8::new((y * 4 + x) as u8));
let roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
assert_eq!(roi.row(0), &[Mono8::new(5), Mono8::new(6)]);
assert_eq!(roi.row(1), &[Mono8::new(9), Mono8::new(10)]);
}
#[test]
fn test_raster_image_ref_mut_strided_row_mut() {
let mut img = Image::generate(4, 4, |x, y| Mono8::new((y * 4 + x) as u8));
{
let mut roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
roi.row_mut(0).fill(Mono8::new(99));
}
assert_eq!(img.pixel_at(0, 1), Mono8::new(4)); assert_eq!(img.pixel_at(1, 1), Mono8::new(99)); assert_eq!(img.pixel_at(2, 1), Mono8::new(99)); assert_eq!(img.pixel_at(3, 1), Mono8::new(7)); }
#[test]
fn test_raster_image_ref_mut_row_consistent_with_pixel_at() {
let mut data: Vec<u8> = (0..20).collect();
let view = ImageRefMut::new(5, 4, &mut data).unwrap();
for y in 0..view.height() {
let row = view.row(y);
for (x, &pixel) in row.iter().enumerate() {
assert_eq!(pixel, view.pixel_at(x, y));
}
}
}
#[test]
#[should_panic]
fn test_raster_image_ref_mut_row_out_of_bounds() {
let mut data: Vec<u8> = (0..6).collect();
let view = ImageRefMut::new(3, 2, &mut data).unwrap();
let _ = view.row(2);
}
#[test]
#[should_panic]
fn test_raster_image_ref_mut_row_mut_out_of_bounds() {
let mut data: Vec<u8> = (0..6).collect();
let mut view = ImageRefMut::new(3, 2, &mut data).unwrap();
let _ = view.row_mut(2);
}
#[test]
fn test_raster_image_ref_to_image_row() {
let img = Image::generate(3, 2, |x, y| (y * 3 + x) as u8);
let r: &Image<u8> = &img;
assert_eq!(r.row(0), &[0, 1, 2]);
assert_eq!(r.row(1), &[3, 4, 5]);
}
#[test]
fn test_raster_image_mut_ref_to_image_row_mut() {
let mut img = Image::generate(3, 2, |x, y| (y * 3 + x) as u8);
let r: &mut Image<u8> = &mut img;
r.row_mut(1).fill(42);
assert_eq!(r.row(0), &[0, 1, 2]);
assert_eq!(r.row(1), &[42, 42, 42]);
}
#[test]
fn test_raster_image_via_trait() {
fn read_row(img: &dyn RasterImage<Pixel = u8>, y: usize) -> Vec<u8> {
img.row(y).to_vec()
}
let img = Image::generate(3, 2, |x, y| (y * 3 + x) as u8);
assert_eq!(read_row(&img, 0), vec![0, 1, 2]);
assert_eq!(read_row(&img, 1), vec![3, 4, 5]);
}
#[test]
fn test_raster_image_mut_via_trait() {
fn fill_row(img: &mut dyn RasterImageMut<Pixel = u8>, y: usize, val: u8) {
img.row_mut(y).fill(val);
}
let mut img = Image::generate(3, 2, |x, _| x as u8);
fill_row(&mut img, 0, 99);
assert_eq!(img.row(0), &[99, 99, 99]);
assert_eq!(img.row(1), &[0, 1, 2]);
}
#[test]
fn test_raster_image_row_rgb8() {
let img = Image::generate(2, 2, |x, y| Rgb8::new((x * 10) as u8, (y * 10) as u8, 0));
let row0 = img.row(0);
assert_eq!(row0.len(), 2);
assert_eq!(row0[0], Rgb8::new(0, 0, 0));
assert_eq!(row0[1], Rgb8::new(10, 0, 0));
let row1 = img.row(1);
assert_eq!(row1[0], Rgb8::new(0, 10, 0));
assert_eq!(row1[1], Rgb8::new(10, 10, 0));
}
#[test]
fn test_contiguous_image_implies_raster_image() {
fn check_contiguous_has_row<I: ContiguousImage>(img: &I) -> usize {
img.row(0).len()
}
let img = Image::fill(5, 3, 0u8);
assert_eq!(check_contiguous_has_row(&img), 5);
let arr: ImageArray<u8, 4, 2> = ImageArray::generate(|_, _| 0);
assert_eq!(check_contiguous_has_row(&arr), 4);
}
#[test]
fn test_contiguous_image_mut_implies_raster_image_mut() {
fn fill_via_row<I: ContiguousImageMut>(img: &mut I, val: I::Pixel)
where
I::Pixel: Copy,
{
for y in 0..img.height() {
img.row_mut(y).fill(val);
}
}
let mut img = Image::fill(3, 2, 0u8);
fill_via_row(&mut img, 42);
assert_eq!(img.as_slice(), &[42, 42, 42, 42, 42, 42]);
}
#[test]
fn test_raster_image_zero_width() {
let img = Image::<u8>::zero(0, 3);
assert_eq!(img.row(0), &[] as &[u8]);
}
#[test]
fn test_raster_image_zero_height() {
let img = Image::<u8>::zero(3, 0);
assert_eq!(img.height(), 0);
}
#[test]
#[should_panic(expected = "pixel_at")]
fn imageref_mut_pixel_at_out_of_bounds_panics() {
let mut buf = [0u8; 4];
let v = ImageRefMut::new(2, 2, &mut buf).unwrap();
let _ = v.pixel_at(5, 5);
}
#[test]
#[should_panic(expected = "pixel_at_mut")]
fn imageref_mut_pixel_at_mut_out_of_bounds_panics() {
let mut buf = [0u8; 4];
let mut v = ImageRefMut::new(2, 2, &mut buf).unwrap();
let _ = v.pixel_at_mut(5, 5);
}
#[test]
#[should_panic(expected = "row")]
fn imageref_mut_row_out_of_bounds_panics() {
let mut buf = [0u8; 4];
let v = ImageRefMut::new(2, 2, &mut buf).unwrap();
let _ = v.row(5);
}
#[test]
#[should_panic(expected = "row_mut")]
fn imageref_mut_row_mut_out_of_bounds_panics() {
let mut buf = [0u8; 4];
let mut v = ImageRefMut::new(2, 2, &mut buf).unwrap();
let _ = v.row_mut(5);
}
#[test]
fn imageref_mut_get_out_of_bounds_returns_none() {
let mut buf = [1u8, 2, 3, 4];
let v = ImageRefMut::new(2, 2, &mut buf).unwrap();
assert!(v.get(5, 5).is_none());
assert_eq!(v.get(1, 1), Some(4));
}
#[test]
fn image_from_vec_rejects_overflowing_dimensions() {
let data = vec![0u8; 4];
let res = Image::from_vec(usize::MAX, 2, data);
assert!(matches!(res, Err(Error::LengthMismatch { .. })));
}
#[test]
fn imageref_new_rejects_overflowing_dimensions() {
let data = [0u8; 4];
let res = ImageRef::new(usize::MAX, 2, &data);
assert!(matches!(res, Err(Error::LengthMismatch { .. })));
}
#[test]
fn imageref_mut_new_rejects_overflowing_dimensions() {
let mut data = [0u8; 4];
let res = ImageRefMut::new(usize::MAX, 2, &mut data);
assert!(matches!(res, Err(Error::LengthMismatch { .. })));
}
#[test]
fn image_roi_rejects_overflowing_rectangle() {
let img = Image::<Mono8>::zero(4, 4);
let rect = Rectangle::new((usize::MAX - 1, 0), (10, 1));
assert!(img.roi(rect).is_none());
}
#[test]
fn image_roi_mut_rejects_overflowing_rectangle() {
let mut img = Image::<Mono8>::zero(4, 4);
let rect = Rectangle::new((0, usize::MAX - 1), (1, 10));
assert!(img.roi_mut(rect).is_none());
}
const OVERFLOW_Y: usize = 1usize << (usize::BITS as usize - 2);
#[test]
#[should_panic]
fn pixel_at_overflow_panics_instead_of_returning_wrong_pixel() {
let img: Image<u8> = Image::fill(4, 4, 7);
let _ = img.pixel_at(0, OVERFLOW_Y);
}
#[test]
#[should_panic]
fn pixel_at_mut_overflow_panics_instead_of_returning_wrong_pixel() {
let mut img: Image<u8> = Image::fill(4, 4, 7);
let _ = img.pixel_at_mut(0, OVERFLOW_Y);
}
#[test]
#[should_panic]
fn row_overflow_panics_instead_of_returning_wrong_row() {
let img: Image<u8> = Image::fill(4, 4, 7);
let _ = img.row(OVERFLOW_Y);
}
#[test]
#[should_panic]
fn row_mut_overflow_panics_instead_of_returning_wrong_row() {
let mut img: Image<u8> = Image::fill(4, 4, 7);
let _ = img.row_mut(OVERFLOW_Y);
}
#[test]
#[should_panic]
fn index_overflow_panics_instead_of_returning_wrong_pixel() {
let img: Image<u8> = Image::fill(4, 4, 7);
let _ = img[(0usize, OVERFLOW_Y)];
}
#[test]
fn debug_image_is_compact_and_contains_dimensions() {
let img: Image<Mono8> = Image::fill(1920, 1080, Mono8::new(0));
let s = format!("{:?}", img);
assert!(s.contains("1920"), "missing width: {s}");
assert!(s.contains("1080"), "missing height: {s}");
assert!(s.contains("Mono8"), "missing pixel type: {s}");
assert!(
s.len() < 256,
"Debug output unexpectedly large ({} bytes)",
s.len()
);
}
#[test]
fn debug_image_works_without_t_debug_bound() {
#[derive(Copy, Clone)]
struct OpaquePixel(#[allow(dead_code)] u32);
let img: Image<OpaquePixel> = Image::fill(2, 3, OpaquePixel(0));
let s = format!("{:?}", img);
assert!(s.contains("OpaquePixel"), "got: {s}");
}
#[test]
fn image_partial_eq_reflexive_and_symmetric() {
let a: Image<Mono8> = Image::fill(4, 3, Mono8::new(5));
let b: Image<Mono8> = Image::fill(4, 3, Mono8::new(5));
let c: Image<Mono8> = Image::fill(4, 3, Mono8::new(6));
let d: Image<Mono8> = Image::fill(5, 3, Mono8::new(5));
assert_eq!(a, a); assert_eq!(a, b);
assert_eq!(b, a); assert_ne!(a, c); assert_ne!(a, d); }
#[test]
fn fill_works_with_clone_only_type() {
let img: Image<String> = Image::fill(2, 2, "hello".to_string());
assert_eq!(img.data.len(), 4);
assert_eq!(&img.data[0], "hello");
assert_eq!(&img.data[3], "hello");
}
}
#[cfg(test)]
mod origin_invariant_gate_tests {
use crate::image::{BinaryImage, Image, ImageView, ImageViewMut, SubView, SubViewMut};
use crate::pixel::Mono8;
use crate::{Rectangle, Size};
#[test]
fn binary_image_roi_matches_parent() {
let img = BinaryImage::generate(5, 5, |x, y| (x + y) % 2 == 0);
let roi = img.roi(Rectangle::new((1, 1), (3, 3))).unwrap();
assert_eq!(roi.size(), Size::new(3, 3));
for y in 0..3 {
for x in 0..3 {
assert_eq!(roi.pixel_at(x, y), img.pixel_at(x + 1, y + 1));
}
}
}
#[test]
fn binary_image_roi_mut_writes_through() {
let mut img = BinaryImage::fill(4, 4, false);
{
let mut roi = img.roi_mut(Rectangle::new((1, 1), (2, 2))).unwrap();
*roi.pixel_at_mut(0, 0) = true;
}
assert!(img.pixel_at(1, 1)); assert!(!img.pixel_at(0, 0)); }
#[test]
fn binary_image_tiles_and_sliding_windows() {
let img = BinaryImage::generate(4, 4, |x, _| x >= 2);
assert_eq!(img.tiles(Size::new(2, 2)).count(), 4);
assert_eq!(img.sliding_windows(Size::new(2, 2)).count(), 9);
}
#[test]
fn mono8_gated_apis_round_trip() {
let img = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
assert_eq!(
img.roi(Rectangle::new((1, 1), (2, 2)))
.unwrap()
.pixel_at(0, 0),
Mono8::new(5),
);
assert_eq!(img.tiles(Size::new(2, 2)).count(), 4);
assert_eq!(img.sliding_windows(Size::new(2, 2)).count(), 9);
}
}