use std::marker::PhantomData;
use crate::image::ImageRefMut;
use crate::image::sequential::ContiguousImageMut;
use crate::image::{ImageView, ImageViewMut};
use crate::{Coordinate, Rectangle, Size, Stride};
pub trait SubView: ImageView {
type Sub<'a>: ImageView<Pixel = Self::Pixel>
where
Self: 'a;
fn roi(&self, rect: Rectangle) -> Option<Self::Sub<'_>>;
fn tiles(&self, size: Size) -> TileIter<'_, Self>
where
Self: Sized,
{
TileIter::new(self, size)
}
fn sliding_windows(&self, size: Size) -> SlidingWindowIter<'_, Self>
where
Self: Sized,
{
SlidingWindowIter::new(self, size, Stride::one())
}
}
pub trait SubViewMut: SubView + ImageViewMut {
type SubMut<'a>: ImageViewMut<Pixel = Self::Pixel>
where
Self: 'a;
fn roi_mut(&mut self, rect: Rectangle) -> Option<Self::SubMut<'_>>;
}
#[derive(Clone, Debug)]
pub struct TileIter<'a, T: SubView> {
size: Size,
current: crate::Coordinate,
img: &'a T,
}
impl<'a, T> TileIter<'a, T>
where
T: SubView,
{
pub(crate) fn new(img: &'a T, size: Size) -> TileIter<'a, T> {
assert!(
size.width > 0 && size.height > 0,
"TileIter: tile size must be non-zero in both dimensions, got {size:?}"
);
TileIter {
size,
current: crate::Coordinate::new(0, 0),
img,
}
}
}
impl<'a, T> Iterator for TileIter<'a, T>
where
T: SubView,
{
type Item = <T as SubView>::Sub<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.current.y >= self.img.height() {
return None;
}
let clamped_width = self.size.width.min(self.img.width() - self.current.x);
let clamped_height = self.size.height.min(self.img.height() - self.current.y);
let clamped_size = Size::new(clamped_width, clamped_height);
let roi = self.img.roi(Rectangle::new(self.current, clamped_size));
self.current = Coordinate {
x: self.current.x + self.size.width,
y: self.current.y,
};
if self.current.x >= self.img.width() {
self.current = Coordinate {
x: 0,
y: self.current.y + self.size.height,
};
}
roi
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct SlidingWindow {
window_size: Size,
stride: Stride,
}
impl SlidingWindow {
pub fn new(window_size: Size) -> Self {
Self {
window_size,
stride: Stride::one(),
}
}
pub fn stride(mut self, stride: Stride) -> Self {
self.stride = stride;
self
}
pub fn iter<'a, I>(&self, image: &'a I) -> SlidingWindowIter<'a, I>
where
I: SubView,
{
SlidingWindowIter::new(image, self.window_size, self.stride)
}
}
#[derive(Clone, Debug)]
pub struct SlidingWindowIter<'a, T: SubView> {
window_size: Size,
stride: Stride,
current: Coordinate,
img: &'a T,
cols: usize,
rows: usize,
}
impl<'a, T> SlidingWindowIter<'a, T>
where
T: SubView,
{
pub(crate) fn new(img: &'a T, window_size: Size, stride: Stride) -> Self {
assert!(
stride.horizontal() > 0 && stride.vertical() > 0,
"SlidingWindowIter: stride must be non-zero in both dimensions, got {stride:?}"
);
let (cols, rows) = if window_size.width > img.width()
|| window_size.height > img.height()
|| window_size.width == 0
|| window_size.height == 0
{
(0, 0)
} else {
let max_x = img.width() - window_size.width; let max_y = img.height() - window_size.height;
let cols = max_x / stride.horizontal() + 1;
let rows = max_y / stride.vertical() + 1;
(cols, rows)
};
Self {
window_size,
stride,
current: Coordinate::new(0, 0),
img,
cols,
rows,
}
}
pub fn enumerate_positions(self) -> EnumeratePositions<'a, T> {
EnumeratePositions {
inner: self,
col: 0,
row: 0,
}
}
}
impl<'a, T> Iterator for SlidingWindowIter<'a, T>
where
T: SubView,
{
type Item = <T as SubView>::Sub<'a>;
fn next(&mut self) -> Option<Self::Item> {
if self.current.y >= self.rows {
return None;
}
let px = self.current.x * self.stride.horizontal();
let py = self.current.y * self.stride.vertical();
let roi = self
.img
.roi(Rectangle::new(Coordinate::new(px, py), self.window_size));
self.current.x += 1;
if self.current.x >= self.cols {
self.current.x = 0;
self.current.y += 1;
}
roi
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = if self.current.y >= self.rows {
0
} else {
let remaining_in_row = self.cols - self.current.x;
let remaining_full_rows = self.rows - self.current.y - 1;
remaining_in_row + remaining_full_rows * self.cols
};
(remaining, Some(remaining))
}
}
impl<'a, T> ExactSizeIterator for SlidingWindowIter<'a, T> where T: SubView {}
#[derive(Clone, Debug)]
pub struct EnumeratePositions<'a, T: SubView> {
inner: SlidingWindowIter<'a, T>,
col: usize,
row: usize,
}
impl<'a, T> Iterator for EnumeratePositions<'a, T>
where
T: SubView,
{
type Item = ((usize, usize), <T as SubView>::Sub<'a>);
fn next(&mut self) -> Option<Self::Item> {
let window = self.inner.next()?;
let pos = (self.col, self.row);
self.col += 1;
if self.col >= self.inner.cols {
self.col = 0;
self.row += 1;
}
Some((pos, window))
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<'a, T> ExactSizeIterator for EnumeratePositions<'a, T> where T: SubView {}
pub struct TileIterMut<'a, T> {
data: *mut T,
len: usize,
image_size: Size,
tile_size: Size,
current: Coordinate,
_marker: PhantomData<&'a mut T>,
}
unsafe impl<T: Send> Send for TileIterMut<'_, T> {}
unsafe impl<T: Sync> Sync for TileIterMut<'_, T> {}
impl<'a, T> TileIterMut<'a, T> {
pub(crate) unsafe fn new(data: *mut T, len: usize, image_size: Size, tile_size: Size) -> Self {
assert!(
tile_size.width > 0 && tile_size.height > 0,
"TileIterMut: tile size must be non-zero in both dimensions, got {tile_size:?}"
);
let expected = image_size
.checked_area()
.expect("TileIterMut::new: image_size area overflows usize");
assert_eq!(
expected, len,
"TileIterMut::new: ContiguousImageMut reported size {image_size:?} \
whose area ({expected}) does not match the underlying slice length ({len})",
);
Self {
data,
len,
image_size,
tile_size,
current: Coordinate::new(0, 0),
_marker: PhantomData,
}
}
}
impl<'a, T> Iterator for TileIterMut<'a, T> {
type Item = ImageRefMut<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
if self.current.y >= self.image_size.height {
return None;
}
let clamped_width = self
.tile_size
.width
.min(self.image_size.width - self.current.x);
let clamped_height = self
.tile_size
.height
.min(self.image_size.height - self.current.y);
let clamped_size = Size::new(clamped_width, clamped_height);
let rect = Rectangle::new(self.current, clamped_size);
let stride = self.image_size.width;
let offset = rect.top() * stride + rect.left();
let roi = unsafe { ImageRefMut::strided(rect.size, stride, offset, self.data, self.len) };
self.current = Coordinate {
x: self.current.x + self.tile_size.width,
y: self.current.y,
};
if self.current.x >= self.image_size.width {
self.current = Coordinate {
x: 0,
y: self.current.y + self.tile_size.height,
};
}
Some(roi)
}
}
mod sealed {
pub trait Sealed {}
}
pub trait IntoTilesMut<'a>: sealed::Sealed {
type Pixel;
type TileMut<'b>: ImageViewMut<Pixel = Self::Pixel>
where
Self: 'b;
type TilesIterMut<'b>: Iterator<Item = Self::TileMut<'b>>
where
Self: 'b;
fn into_tiles_mut(self, size: Size) -> Self::TilesIterMut<'a>;
}
impl<I: ContiguousImageMut> sealed::Sealed for &mut I {}
impl<'a, I> IntoTilesMut<'a> for &'a mut I
where
I: ContiguousImageMut,
{
type Pixel = <I as ImageView>::Pixel;
type TileMut<'b>
= ImageRefMut<'b, Self::Pixel>
where
Self: 'b;
type TilesIterMut<'b>
= TileIterMut<'b, Self::Pixel>
where
Self: 'b;
fn into_tiles_mut(self, size: Size) -> Self::TilesIterMut<'a> {
let image_size = self.size();
let slice = self.as_mut_slice();
let len = slice.len();
let ptr = slice.as_mut_ptr();
unsafe { TileIterMut::new(ptr, len, image_size, size) }
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::image::ImageView;
use crate::image::sequential::{
ContiguousImage, ContiguousImageMut, Image, ImageArray, ImageRefMut,
};
use crate::pixel::Mono8;
#[test]
fn test_sliding_window_stride1_count() {
let img: Image<Mono8> = Image::generate(6, 6, |x, y| Mono8::new((x + y * 6) as u8));
assert_eq!(img.sliding_windows(Size::new(3, 3)).count(), 16);
}
#[test]
fn test_sliding_window_stride1_all_same_size() {
let img: Image<Mono8> = Image::generate(5, 5, |x, y| Mono8::new((x + y * 5) as u8));
for w in img.sliding_windows(Size::new(3, 3)) {
assert_eq!(w.size(), Size::new(3, 3));
}
}
#[test]
fn test_sliding_window_stride1_data() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let windows: Vec<_> = img.sliding_windows(Size::new(2, 2)).collect();
assert_eq!(windows.len(), 9);
assert_eq!(windows[0].get(0, 0), Some(Mono8::new(0)));
assert_eq!(windows[0].get(1, 0), Some(Mono8::new(1)));
assert_eq!(windows[0].get(0, 1), Some(Mono8::new(4)));
assert_eq!(windows[0].get(1, 1), Some(Mono8::new(5)));
assert_eq!(windows[1].get(0, 0), Some(Mono8::new(1)));
assert_eq!(windows[1].get(1, 1), Some(Mono8::new(6)));
assert_eq!(windows[3].get(0, 0), Some(Mono8::new(4)));
assert_eq!(windows[8].get(0, 0), Some(Mono8::new(10)));
assert_eq!(windows[8].get(1, 1), Some(Mono8::new(15)));
}
#[test]
fn test_sliding_window_stride2() {
let img: Image<Mono8> = Image::generate(8, 8, |x, y| Mono8::new((x + y * 8) as u8));
let windows: Vec<_> = SlidingWindow::new(Size::new(3, 3))
.stride(Stride::new(2, 2))
.iter(&img)
.collect();
assert_eq!(windows.len(), 9);
assert_eq!(windows[0].get(0, 0), Some(Mono8::new(0)));
assert_eq!(windows[1].get(0, 0), Some(Mono8::new(2)));
assert_eq!(windows[2].get(0, 0), Some(Mono8::new(4)));
assert_eq!(windows[3].get(0, 0), Some(Mono8::new(16)));
}
#[test]
fn test_sliding_window_non_square_stride() {
let img: Image<Mono8> = Image::generate(10, 8, |x, y| Mono8::new((x + y * 10) as u8));
let windows: Vec<_> = SlidingWindow::new(Size::new(3, 3))
.stride(Stride::new(3, 2))
.iter(&img)
.collect();
assert_eq!(windows.len(), 9);
assert_eq!(windows[0].get(0, 0), Some(Mono8::new(0))); assert_eq!(windows[1].get(0, 0), Some(Mono8::new(3))); assert_eq!(windows[2].get(0, 0), Some(Mono8::new(6))); assert_eq!(windows[3].get(0, 0), Some(Mono8::new(20))); assert_eq!(windows[6].get(0, 0), Some(Mono8::new(40))); }
#[test]
fn test_sliding_window_window_equals_image() {
let img: Image<Mono8> = Image::generate(3, 3, |x, y| Mono8::new((x + y * 3) as u8));
let windows: Vec<_> = img.sliding_windows(Size::new(3, 3)).collect();
assert_eq!(windows.len(), 1);
assert_eq!(windows[0].size(), Size::new(3, 3));
assert_eq!(windows[0].get(0, 0), Some(Mono8::new(0)));
assert_eq!(windows[0].get(2, 2), Some(Mono8::new(8)));
}
#[test]
fn test_sliding_window_larger_than_image() {
let img: Image<Mono8> = Image::generate(3, 3, |x, y| Mono8::new((x + y * 3) as u8));
let windows: Vec<_> = img.sliding_windows(Size::new(4, 4)).collect();
assert_eq!(windows.len(), 0);
}
#[test]
fn test_sliding_window_1x1() {
let img: Image<Mono8> = Image::generate(3, 4, |x, y| Mono8::new((x + y * 3) as u8));
let windows: Vec<_> = img.sliding_windows(Size::new(1, 1)).collect();
assert_eq!(windows.len(), 12); for w in &windows {
assert_eq!(w.size(), Size::new(1, 1));
}
assert_eq!(windows[0].get(0, 0), Some(Mono8::new(0)));
assert_eq!(windows[11].get(0, 0), Some(Mono8::new(11)));
}
#[test]
fn test_sliding_window_non_square_window() {
let img: Image<Mono8> = Image::generate(6, 4, |x, y| Mono8::new((x + y * 6) as u8));
let windows: Vec<_> = img.sliding_windows(Size::new(2, 3)).collect();
assert_eq!(windows.len(), 10);
for w in &windows {
assert_eq!(w.size(), Size::new(2, 3));
}
}
#[test]
fn test_sliding_window_stride_larger_than_window() {
let img: Image<Mono8> = Image::generate(10, 10, |x, y| Mono8::new((x + y * 10) as u8));
let windows: Vec<_> = SlidingWindow::new(Size::new(2, 2))
.stride(Stride::new(4, 4))
.iter(&img)
.collect();
assert_eq!(windows.len(), 9);
}
#[test]
fn test_sliding_window_stride_skips_last_position() {
let img: Image<Mono8> = Image::generate(7, 7, |x, y| Mono8::new((x + y * 7) as u8));
let windows: Vec<_> = SlidingWindow::new(Size::new(3, 3))
.stride(Stride::new(3, 3))
.iter(&img)
.collect();
assert_eq!(windows.len(), 4); assert_eq!(windows[0].get(0, 0), Some(Mono8::new(0))); assert_eq!(windows[1].get(0, 0), Some(Mono8::new(3))); assert_eq!(windows[2].get(0, 0), Some(Mono8::new(21))); assert_eq!(windows[3].get(0, 0), Some(Mono8::new(24))); }
#[test]
fn test_sliding_window_exact_size_iterator() {
let img: Image<Mono8> = Image::generate(6, 6, |x, y| Mono8::new((x + y * 6) as u8));
let mut iter = img.sliding_windows(Size::new(3, 3));
assert_eq!(iter.len(), 16);
iter.next();
assert_eq!(iter.len(), 15);
for _ in &mut iter {}
assert_eq!(iter.len(), 0);
}
#[test]
fn test_sliding_window_exhaustion() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let mut iter = img.sliding_windows(Size::new(3, 3));
let mut count = 0;
while iter.next().is_some() {
count += 1;
}
assert_eq!(count, 4);
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
#[test]
fn test_sliding_window_enumerate_positions() {
let img: Image<Mono8> = Image::generate(5, 5, |x, y| Mono8::new((x + y * 5) as u8));
let items: Vec<_> = img
.sliding_windows(Size::new(3, 3))
.enumerate_positions()
.collect();
assert_eq!(items.len(), 9);
assert_eq!(items[0].0, (0, 0));
assert_eq!(items[1].0, (1, 0));
assert_eq!(items[2].0, (2, 0));
assert_eq!(items[3].0, (0, 1));
assert_eq!(items[8].0, (2, 2));
assert_eq!(items[7].0, (1, 2));
assert_eq!(items[7].1.get(0, 0), Some(Mono8::new(11))); }
#[test]
fn test_sliding_window_enumerate_positions_with_stride() {
let img: Image<Mono8> = Image::generate(8, 8, |x, y| Mono8::new((x + y * 8) as u8));
let items: Vec<_> = SlidingWindow::new(Size::new(3, 3))
.stride(Stride::new(2, 2))
.iter(&img)
.enumerate_positions()
.collect();
assert_eq!(items.len(), 9); assert_eq!(items[4].0, (1, 1));
assert_eq!(items[4].1.get(0, 0), Some(Mono8::new(18))); }
#[test]
fn test_sliding_window_enumerate_positions_exact_size() {
let img: Image<Mono8> = Image::generate(5, 5, |x, y| Mono8::new((x + y * 5) as u8));
let mut iter = img.sliding_windows(Size::new(3, 3)).enumerate_positions();
assert_eq!(iter.len(), 9);
iter.next();
assert_eq!(iter.len(), 8);
}
#[test]
fn test_sliding_window_imagearray() {
let img: ImageArray<Mono8, 6, 6> =
ImageArray::generate(|x, y| Mono8::new((x + y * 6) as u8));
let windows: Vec<_> = img.sliding_windows(Size::new(3, 3)).collect();
assert_eq!(windows.len(), 16);
assert_eq!(windows[0].get(0, 0), Some(Mono8::new(0)));
assert_eq!(windows[15].get(0, 0), Some(Mono8::new(21)));
}
#[test]
fn test_sliding_window_does_not_consume() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let w1: Vec<_> = img.sliding_windows(Size::new(2, 2)).collect();
let w2: Vec<_> = img.sliding_windows(Size::new(2, 2)).collect();
assert_eq!(w1.len(), w2.len());
assert_eq!(img.get(0, 0), Some(Mono8::new(0)));
}
#[test]
fn test_sliding_window_builder_default_stride() {
let img: Image<Mono8> = Image::generate(5, 5, |x, y| Mono8::new((x + y * 5) as u8));
let from_method: Vec<_> = img.sliding_windows(Size::new(3, 3)).collect();
let from_builder: Vec<_> = SlidingWindow::new(Size::new(3, 3)).iter(&img).collect();
assert_eq!(from_method.len(), from_builder.len());
for (a, b) in from_method.iter().zip(from_builder.iter()) {
assert_eq!(a.get(0, 0), b.get(0, 0));
}
}
#[test]
fn test_sliding_window_row_major_order() {
let img: Image<Mono8> = Image::generate(5, 5, |x, y| Mono8::new((x + y * 5) as u8));
let windows: Vec<_> = img.sliding_windows(Size::new(2, 2)).collect();
let origins: Vec<Mono8> = windows.iter().map(|w| w.pixel_at(0, 0)).collect();
let expected: Vec<Mono8> = (0..4)
.flat_map(|y| (0..4).map(move |x| Mono8::new((x + y * 5) as u8)))
.collect();
assert_eq!(origins, expected);
}
#[test]
fn test_tiles_iter_basic() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8 + 1));
let tiles: Vec<_> = img.tiles(Size::new(2, 2)).collect();
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[0].get(0, 0), Some(Mono8::new(1)));
assert_eq!(tiles[0].size(), Size::new(2, 2));
}
#[test]
fn test_tiles_iter_partial_edges() {
let img: Image<Mono8> = Image::generate(10, 10, |x, y| Mono8::new((x + y * 10) as u8));
let tiles: Vec<_> = img.tiles(Size::new(3, 3)).collect();
assert_eq!(tiles.len(), 16);
assert_eq!(tiles[0].size(), Size::new(3, 3));
assert_eq!(tiles[3].size(), Size::new(1, 3));
assert_eq!(tiles[12].size(), Size::new(3, 1));
assert_eq!(tiles[15].size(), Size::new(1, 1));
}
#[test]
fn test_tiles_iter_partial_edges_data() {
let img: Image<Mono8> = Image::generate(5, 5, |x, y| Mono8::new((x + y * 5) as u8));
let tiles: Vec<_> = img.tiles(Size::new(3, 3)).collect();
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[1].size(), Size::new(2, 3));
assert_eq!(tiles[1].get(0, 0), Some(Mono8::new(3))); assert_eq!(tiles[1].get(1, 0), Some(Mono8::new(4)));
assert_eq!(tiles[2].size(), Size::new(3, 2));
assert_eq!(tiles[2].get(0, 0), Some(Mono8::new(15)));
assert_eq!(tiles[3].size(), Size::new(2, 2));
assert_eq!(tiles[3].get(0, 0), Some(Mono8::new(18))); assert_eq!(tiles[3].get(1, 1), Some(Mono8::new(24))); }
#[test]
fn test_tiles_iter_larger_than_image() {
let img: Image<Mono8> = Image::generate(2, 2, |x, y| Mono8::new((x + y * 2) as u8));
let tiles: Vec<_> = img.tiles(Size::new(5, 5)).collect();
assert_eq!(tiles.len(), 1);
assert_eq!(tiles[0].size(), Size::new(2, 2));
}
#[test]
fn test_tiles_iter_with_imagearray() {
let img: ImageArray<Mono8, 8, 8> =
ImageArray::generate(|x, y| Mono8::new((x + y * 8) as u8));
let tiles: Vec<_> = img.tiles(Size::new(4, 4)).collect();
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[0].size(), Size::new(4, 4));
}
#[test]
fn test_tiles_iter_imagearray_partial() {
let img: ImageArray<Mono8, 7, 7> =
ImageArray::generate(|x, y| Mono8::new((x + y * 7) as u8));
let tiles: Vec<_> = img.tiles(Size::new(3, 3)).collect();
assert_eq!(tiles.len(), 9);
assert_eq!(tiles[2].size(), Size::new(1, 3));
assert_eq!(tiles[8].size(), Size::new(1, 1));
}
#[test]
fn test_sub_view_trait() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let roi = img.roi(Rectangle::new((1, 1), (2, 2)));
assert!(roi.is_some());
let roi = roi.unwrap();
assert_eq!(roi.size(), Size::new(2, 2));
}
#[test]
fn test_sub_view_mut_trait() {
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)));
assert!(roi.is_some());
let roi = roi.unwrap();
assert_eq!(roi.size(), Size::new(2, 2));
}
#[test]
fn test_tiles_iter_exhaustion() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let mut iter = img.tiles(Size::new(2, 2));
let mut count = 0;
while iter.next().is_some() {
count += 1;
}
assert_eq!(count, 4);
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
#[test]
fn test_tiles_single_pixel() {
let img: Image<Mono8> = Image::generate(3, 3, |x, y| Mono8::new((x + y * 3) as u8));
let tiles: Vec<_> = img.tiles(Size::new(1, 1)).collect();
assert_eq!(tiles.len(), 9);
for tile in &tiles {
assert_eq!(tile.size(), Size::new(1, 1));
}
}
#[test]
fn test_tiles_single_row() {
let img: Image<Mono8> = Image::generate(7, 4, |x, y| Mono8::new((x + y * 7) as u8));
let tiles: Vec<_> = img.tiles(Size::new(3, 4)).collect();
assert_eq!(tiles.len(), 3);
assert_eq!(tiles[0].size(), Size::new(3, 4));
assert_eq!(tiles[1].size(), Size::new(3, 4));
assert_eq!(tiles[2].size(), Size::new(1, 4)); }
#[test]
fn test_tiles_single_column() {
let img: Image<Mono8> = Image::generate(4, 7, |x, y| Mono8::new((x + y * 4) as u8));
let tiles: Vec<_> = img.tiles(Size::new(4, 3)).collect();
assert_eq!(tiles.len(), 3);
assert_eq!(tiles[0].size(), Size::new(4, 3));
assert_eq!(tiles[1].size(), Size::new(4, 3));
assert_eq!(tiles[2].size(), Size::new(4, 1)); }
#[test]
fn test_tiles_iter_imagearray_mono8() {
use crate::pixel::Mono8;
let img: ImageArray<Mono8, 6, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 6) as u8));
let tiles: Vec<_> = img.tiles(Size::new(3, 2)).collect();
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[0].size(), Size::new(3, 2));
assert_eq!(tiles[0].get(0, 0), Some(Mono8::new(0)));
assert_eq!(tiles[0].get(2, 1), Some(Mono8::new(8)));
assert_eq!(tiles[0].pixel_at(1, 0), Mono8::new(1));
assert_eq!(tiles[0].width(), 3);
assert_eq!(tiles[0].height(), 2);
assert_eq!(tiles[1].get(0, 0), Some(Mono8::new(3)));
assert_eq!(tiles[2].get(0, 0), Some(Mono8::new(12)));
assert_eq!(tiles[3].get(0, 0), Some(Mono8::new(15)));
assert_eq!(tiles[0].get(3, 0), None);
assert_eq!(tiles[0].get(0, 2), None);
}
#[test]
fn test_sub_view_imagearray_mono8() {
use crate::pixel::Mono8;
let img: ImageArray<Mono8, 4, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 4) as u8));
let roi = img.roi(Rectangle::new((1, 1), (2, 2)));
assert!(roi.is_some());
let roi = roi.unwrap();
assert_eq!(roi.size(), Size::new(2, 2));
assert_eq!(roi.width(), 2);
assert_eq!(roi.height(), 2);
assert_eq!(roi.get(0, 0), Some(Mono8::new(5)));
assert_eq!(roi.pixel_at(1, 1), Mono8::new(10));
assert_eq!(roi.get(2, 0), None);
}
#[test]
fn test_sub_view_mut_imagearray_mono8() {
use crate::pixel::Mono8;
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.size(), Size::new(2, 2));
assert_eq!(roi.get(0, 0), Some(Mono8::new(5)));
*roi.pixel_at_mut(0, 0) = Mono8::new(99);
assert_eq!(roi.get(0, 0), Some(Mono8::new(99)));
assert_eq!(roi.get_mut(2, 0), None);
}
fn tiles_mut<T: Copy>(img: &mut Image<T>, tile_size: Size) -> TileIterMut<'_, T> {
let image_size = img.size();
let slice = img.as_mut_slice();
let len = slice.len();
let ptr = slice.as_mut_ptr();
unsafe { TileIterMut::new(ptr, len, image_size, tile_size) }
}
#[test]
fn test_tiles_mut_basic() {
let mut img: Image<u8> = Image::generate(4, 4, |x, y| (x + y * 4) as u8 + 1);
let tiles: Vec<_> = tiles_mut(&mut img, Size::new(2, 2)).collect();
assert_eq!(tiles.len(), 4);
for tile in &tiles {
assert_eq!(tile.size(), Size::new(2, 2));
}
assert_eq!(tiles[0].get(0, 0), Some(1));
assert_eq!(tiles[0].get(1, 0), Some(2));
assert_eq!(tiles[0].get(0, 1), Some(5));
assert_eq!(tiles[0].get(1, 1), Some(6));
assert_eq!(tiles[1].get(0, 0), Some(3));
assert_eq!(tiles[1].get(1, 0), Some(4));
assert_eq!(tiles[2].get(0, 0), Some(9));
assert_eq!(tiles[3].get(0, 0), Some(11));
assert_eq!(tiles[3].get(1, 1), Some(16));
}
#[test]
fn test_tiles_mut_write_and_verify() {
let mut img: Image<u8> = Image::fill(4, 4, 0u8);
{
let mut tiles: Vec<_> = tiles_mut(&mut img, Size::new(2, 2)).collect();
for (i, tile) in tiles.iter_mut().enumerate() {
let val = (i as u8 + 1) * 10;
for y in 0..tile.height() {
for x in 0..tile.width() {
*tile.pixel_at_mut(x, y) = val;
}
}
}
}
assert_eq!(img.get(0, 0), Some(10));
assert_eq!(img.get(1, 1), Some(10));
assert_eq!(img.get(2, 0), Some(20));
assert_eq!(img.get(3, 1), Some(20));
assert_eq!(img.get(0, 2), Some(30));
assert_eq!(img.get(1, 3), Some(30));
assert_eq!(img.get(2, 2), Some(40));
assert_eq!(img.get(3, 3), Some(40));
}
#[test]
fn test_tiles_mut_partial_edges() {
let mut img: Image<u8> = Image::generate(10, 10, |x, y| (x + y * 10) as u8);
let tiles: Vec<_> = tiles_mut(&mut img, Size::new(3, 3)).collect();
assert_eq!(tiles.len(), 16);
assert_eq!(tiles[0].size(), Size::new(3, 3));
assert_eq!(tiles[3].size(), Size::new(1, 3));
assert_eq!(tiles[12].size(), Size::new(3, 1));
assert_eq!(tiles[15].size(), Size::new(1, 1));
}
#[test]
fn test_tiles_mut_matches_immutable_sizes() {
let mut img: Image<Mono8> = Image::generate(7, 5, |x, y| Mono8::new((x + y * 7) as u8));
let tile_size = Size::new(3, 2);
let immut_sizes: Vec<Size> = img.tiles(tile_size).map(|t| t.size()).collect();
let mut_sizes: Vec<Size> = tiles_mut(&mut img, tile_size).map(|t| t.size()).collect();
assert_eq!(immut_sizes, mut_sizes);
}
#[test]
fn test_tiles_mut_larger_than_image() {
let mut img: Image<u8> = Image::generate(2, 2, |x, y| (x + y * 2) as u8);
let tiles: Vec<_> = tiles_mut(&mut img, Size::new(5, 5)).collect();
assert_eq!(tiles.len(), 1);
assert_eq!(tiles[0].size(), Size::new(2, 2));
}
#[test]
fn test_tiles_mut_1x1_tiles() {
let mut img: Image<u8> = Image::generate(3, 3, |x, y| (x + y * 3) as u8);
let tiles: Vec<_> = tiles_mut(&mut img, Size::new(1, 1)).collect();
assert_eq!(tiles.len(), 9);
for tile in &tiles {
assert_eq!(tile.size(), Size::new(1, 1));
}
assert_eq!(tiles[0].get(0, 0), Some(0)); assert_eq!(tiles[1].get(0, 0), Some(1)); assert_eq!(tiles[4].get(0, 0), Some(4)); assert_eq!(tiles[8].get(0, 0), Some(8)); }
#[test]
fn test_tiles_mut_disjointness() {
let mut img: Image<u8> = Image::fill(6, 4, 0u8);
{
let mut counter: u8 = 1;
let mut tiles: Vec<_> = tiles_mut(&mut img, Size::new(3, 2)).collect();
for tile in tiles.iter_mut() {
for y in 0..tile.height() {
for x in 0..tile.width() {
*tile.pixel_at_mut(x, y) = counter;
counter += 1;
}
}
}
}
let slice = img.as_slice();
assert_eq!(slice.len(), 24);
let mut seen = std::collections::HashSet::new();
for &v in slice {
assert_ne!(v, 0, "pixel was not written");
assert!(seen.insert(v), "pixel value {} written twice", v);
}
assert_eq!(seen.len(), 24);
}
#[test]
fn test_tiles_mut_mutation_roundtrip() {
let mut img: Image<u8> = Image::generate(5, 5, |x, y| (x + y * 5) as u8);
let expected: Vec<u8> = (0..25).map(|v: u8| v + 1).collect();
{
let mut tiles: Vec<_> = tiles_mut(&mut img, Size::new(2, 3)).collect();
for tile in tiles.iter_mut() {
for y in 0..tile.height() {
for x in 0..tile.width() {
let px = tile.pixel_at_mut(x, y);
*px += 1;
}
}
}
}
assert_eq!(img.as_slice(), &expected[..]);
}
#[test]
fn test_tiles_mut_exhaustion() {
let mut img: Image<u8> = Image::generate(4, 4, |x, y| (x + y * 4) as u8);
let mut iter = tiles_mut(&mut img, Size::new(2, 2));
let mut count = 0;
while iter.next().is_some() {
count += 1;
}
assert_eq!(count, 4);
assert!(iter.next().is_none());
assert!(iter.next().is_none());
}
#[test]
fn test_tiles_mut_send_sync() {
fn assert_send<T: Send>() {}
fn assert_sync<T: Sync>() {}
assert_send::<TileIterMut<'_, u8>>();
assert_sync::<TileIterMut<'_, u8>>();
assert_send::<ImageRefMut<'_, u8>>();
assert_sync::<ImageRefMut<'_, u8>>();
}
#[test]
fn test_tiles_mut_imagearray() {
let mut img: ImageArray<u8, 8, 8> = ImageArray::generate(|x, y| (x + y * 8) as u8);
let slice = img.as_mut_slice();
let len = slice.len();
let ptr = slice.as_mut_ptr();
let tiles: Vec<_> =
unsafe { TileIterMut::new(ptr, len, Size::new(8, 8), Size::new(4, 4)) }.collect();
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[0].size(), Size::new(4, 4));
assert_eq!(tiles[0].get(0, 0), Some(0));
}
#[test]
fn test_tiles_mut_partial_edges_data() {
let mut img: Image<u8> = Image::generate(5, 5, |x, y| (x + y * 5) as u8);
let tiles: Vec<_> = tiles_mut(&mut img, Size::new(3, 3)).collect();
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[1].size(), Size::new(2, 3));
assert_eq!(tiles[1].get(0, 0), Some(3));
assert_eq!(tiles[1].get(1, 0), Some(4));
assert_eq!(tiles[2].size(), Size::new(3, 2));
assert_eq!(tiles[2].get(0, 0), Some(15));
assert_eq!(tiles[3].size(), Size::new(2, 2));
assert_eq!(tiles[3].get(0, 0), Some(18));
assert_eq!(tiles[3].get(1, 1), Some(24));
}
#[test]
fn test_tiles_mut_single_row_tiles() {
let mut img: Image<u8> = Image::generate(7, 4, |x, y| (x + y * 7) as u8);
let tiles: Vec<_> = tiles_mut(&mut img, Size::new(3, 4)).collect();
assert_eq!(tiles.len(), 3);
assert_eq!(tiles[0].size(), Size::new(3, 4));
assert_eq!(tiles[1].size(), Size::new(3, 4));
assert_eq!(tiles[2].size(), Size::new(1, 4)); }
#[test]
fn test_tiles_mut_single_column_tiles() {
let mut img: Image<u8> = Image::generate(4, 7, |x, y| (x + y * 4) as u8);
let tiles: Vec<_> = tiles_mut(&mut img, Size::new(4, 3)).collect();
assert_eq!(tiles.len(), 3);
assert_eq!(tiles[0].size(), Size::new(4, 3));
assert_eq!(tiles[1].size(), Size::new(4, 3));
assert_eq!(tiles[2].size(), Size::new(4, 1)); }
#[test]
fn test_into_tiles_mut_symmetry_with_into_tiles() {
let mut img: Image<Mono8> = Image::generate(7, 5, |x, y| Mono8::new((x + y * 7) as u8));
let tile_size = Size::new(3, 2);
let immut_sizes: Vec<Size> = img.tiles(tile_size).map(|t| t.size()).collect();
let immut_values: Vec<Vec<Mono8>> = img
.tiles(tile_size)
.map(|t| {
let mut vals = Vec::new();
for y in 0..t.height() {
for x in 0..t.width() {
vals.push(t.pixel_at(x, y));
}
}
vals
})
.collect();
let mut_tiles: Vec<_> = (&mut img).into_tiles_mut(tile_size).collect();
let mut_sizes: Vec<Size> = mut_tiles.iter().map(|t| t.size()).collect();
let mut_values: Vec<Vec<Mono8>> = mut_tiles
.iter()
.map(|t| {
let mut vals = Vec::new();
for y in 0..t.height() {
for x in 0..t.width() {
vals.push(t.pixel_at(x, y));
}
}
vals
})
.collect();
assert_eq!(immut_sizes, mut_sizes);
assert_eq!(immut_values, mut_values);
}
#[test]
fn test_into_tiles_mut_image_6x4_3x2() {
let mut img: Image<u8> = Image::fill(6, 4, 0u8);
{
let mut tiles: Vec<_> = (&mut img).into_tiles_mut(Size::new(3, 2)).collect();
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[0].size(), Size::new(3, 2));
assert_eq!(tiles[1].size(), Size::new(3, 2));
assert_eq!(tiles[2].size(), Size::new(3, 2));
assert_eq!(tiles[3].size(), Size::new(3, 2));
for (i, tile) in tiles.iter_mut().enumerate() {
let val = (i as u8 + 1) * 10;
for y in 0..tile.height() {
for x in 0..tile.width() {
*tile.pixel_at_mut(x, y) = val;
}
}
}
}
assert_eq!(img.get(0, 0), Some(10));
assert_eq!(img.get(2, 1), Some(10));
assert_eq!(img.get(3, 0), Some(20));
assert_eq!(img.get(5, 1), Some(20));
assert_eq!(img.get(0, 2), Some(30));
assert_eq!(img.get(2, 3), Some(30));
assert_eq!(img.get(3, 2), Some(40));
assert_eq!(img.get(5, 3), Some(40));
}
#[test]
fn test_into_tiles_mut_imagearray() {
let mut img: ImageArray<Mono8, 6, 4> =
ImageArray::generate(|x, y| Mono8::new((x + y * 6) as u8));
let tile_size = Size::new(3, 2);
let immut_sizes: Vec<Size> = img.tiles(tile_size).map(|t| t.size()).collect();
let tiles: Vec<_> = (&mut img).into_tiles_mut(tile_size).collect();
let mut_sizes: Vec<Size> = tiles.iter().map(|t| t.size()).collect();
assert_eq!(immut_sizes, mut_sizes);
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[0].get(0, 0), Some(Mono8::new(0)));
assert_eq!(tiles[1].get(0, 0), Some(Mono8::new(3)));
assert_eq!(tiles[2].get(0, 0), Some(Mono8::new(12)));
assert_eq!(tiles[3].get(0, 0), Some(Mono8::new(15)));
}
#[test]
fn test_into_tiles_mut_imagearray_write_and_verify() {
let mut img: ImageArray<u8, 6, 4> = ImageArray::generate(|_, _| 0u8);
{
let mut tiles: Vec<_> = (&mut img).into_tiles_mut(Size::new(3, 2)).collect();
for (i, tile) in tiles.iter_mut().enumerate() {
let val = (i as u8 + 1) * 10;
for y in 0..tile.height() {
for x in 0..tile.width() {
*tile.pixel_at_mut(x, y) = val;
}
}
}
}
assert_eq!(img.get(0, 0), Some(10));
assert_eq!(img.get(3, 0), Some(20));
assert_eq!(img.get(0, 2), Some(30));
assert_eq!(img.get(3, 2), Some(40));
}
#[test]
fn test_into_tiles_mut_parallel_ready_simulation() {
let mut img: Image<u8> = Image::fill(8, 6, 0u8);
{
let mut tiles: Vec<_> = (&mut img).into_tiles_mut(Size::new(4, 3)).collect();
assert_eq!(tiles.len(), 4);
for (i, tile) in tiles.iter_mut().enumerate() {
let val = (i as u8) + 1;
for y in 0..tile.height() {
for x in 0..tile.width() {
*tile.pixel_at_mut(x, y) = val;
}
}
}
}
for y in 0..6 {
for x in 0..8 {
let expected = match (x < 4, y < 3) {
(true, true) => 1,
(false, true) => 2,
(true, false) => 3,
(false, false) => 4,
};
assert_eq!(img.get(x, y), Some(expected), "mismatch at ({}, {})", x, y);
}
}
}
#[test]
fn test_into_tiles_mut_image_partial_edges() {
let mut img: Image<u8> = Image::generate(10, 10, |x, y| (x + y * 10) as u8);
let tiles: Vec<_> = (&mut img).into_tiles_mut(Size::new(3, 3)).collect();
assert_eq!(tiles.len(), 16);
assert_eq!(tiles[0].size(), Size::new(3, 3));
assert_eq!(tiles[3].size(), Size::new(1, 3)); assert_eq!(tiles[12].size(), Size::new(3, 1)); assert_eq!(tiles[15].size(), Size::new(1, 1)); }
#[test]
fn test_into_tiles_mut_imagearray_partial_edges() {
let mut img: ImageArray<u8, 7, 7> = ImageArray::generate(|x, y| (x + y * 7) as u8);
let tiles: Vec<_> = (&mut img).into_tiles_mut(Size::new(3, 3)).collect();
assert_eq!(tiles.len(), 9);
assert_eq!(tiles[2].size(), Size::new(1, 3));
assert_eq!(tiles[8].size(), Size::new(1, 1));
}
#[test]
fn test_into_tiles_mut_larger_than_image() {
let mut img: Image<u8> = Image::generate(2, 2, |x, y| (x + y * 2) as u8);
let tiles: Vec<_> = (&mut img).into_tiles_mut(Size::new(10, 10)).collect();
assert_eq!(tiles.len(), 1);
assert_eq!(tiles[0].size(), Size::new(2, 2));
}
#[test]
fn test_into_tiles_mut_disjointness_via_trait() {
let mut img: Image<u8> = Image::fill(5, 5, 0u8);
{
let mut counter: u8 = 1;
let mut tiles: Vec<_> = (&mut img).into_tiles_mut(Size::new(2, 3)).collect();
for tile in tiles.iter_mut() {
for y in 0..tile.height() {
for x in 0..tile.width() {
*tile.pixel_at_mut(x, y) = counter;
counter += 1;
}
}
}
}
let slice = img.as_slice();
assert_eq!(slice.len(), 25);
let mut seen = std::collections::HashSet::new();
for &v in slice {
assert_ne!(v, 0, "pixel was not written");
assert!(seen.insert(v), "pixel value {} written twice", v);
}
assert_eq!(seen.len(), 25);
}
#[test]
fn test_into_tiles_mut_mutation_roundtrip_via_trait() {
let mut img: Image<u8> = Image::generate(6, 4, |x, y| (x + y * 6) as u8);
let expected: Vec<u8> = (0..24).map(|v: u8| v + 1).collect();
{
let mut tiles: Vec<_> = (&mut img).into_tiles_mut(Size::new(3, 2)).collect();
for tile in tiles.iter_mut() {
for y in 0..tile.height() {
for x in 0..tile.width() {
let px = tile.pixel_at_mut(x, y);
*px += 1;
}
}
}
}
assert_eq!(img.as_slice(), &expected[..]);
}
#[test]
fn test_subview_into_tiles_called_on_owned_image() {
let img: Image<Mono8> = Image::generate(6, 4, |x, y| Mono8::new((x + y * 6) as u8));
let tiles: Vec<_> = img.tiles(Size::new(3, 2)).collect();
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[0].size(), Size::new(3, 2));
assert_eq!(tiles[0].get(0, 0), Some(Mono8::new(0)));
assert_eq!(tiles[1].get(0, 0), Some(Mono8::new(3)));
assert_eq!(tiles[2].get(0, 0), Some(Mono8::new(12)));
assert_eq!(tiles[3].get(0, 0), Some(Mono8::new(15)));
}
#[test]
fn test_subview_into_tiles_called_on_ref() {
let img: Image<Mono8> = Image::generate(6, 4, |x, y| Mono8::new((x + y * 6) as u8));
let img_ref = &img;
let tiles: Vec<_> = img_ref.tiles(Size::new(3, 2)).collect();
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[0].size(), Size::new(3, 2));
assert_eq!(tiles[0].get(0, 0), Some(Mono8::new(0)));
}
#[test]
fn test_subview_into_tiles_does_not_consume() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let tiles1: Vec<_> = img.tiles(Size::new(2, 2)).collect();
let tiles2: Vec<_> = img.tiles(Size::new(2, 2)).collect();
assert_eq!(tiles1.len(), tiles2.len());
for (a, b) in tiles1.iter().zip(tiles2.iter()) {
assert_eq!(a.size(), b.size());
assert_eq!(a.get(0, 0), b.get(0, 0));
}
assert_eq!(img.get(0, 0), Some(Mono8::new(0)));
}
#[test]
fn test_subview_into_tiles_imagearray() {
let img: ImageArray<Mono8, 8, 6> =
ImageArray::generate(|x, y| Mono8::new((x + y * 8) as u8));
let tiles: Vec<_> = img.tiles(Size::new(4, 3)).collect();
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[0].size(), Size::new(4, 3));
assert_eq!(tiles[1].size(), Size::new(4, 3));
assert_eq!(tiles[2].size(), Size::new(4, 3));
assert_eq!(tiles[3].size(), Size::new(4, 3));
assert_eq!(tiles[0].get(0, 0), Some(Mono8::new(0))); assert_eq!(tiles[1].get(0, 0), Some(Mono8::new(4))); assert_eq!(tiles[2].get(0, 0), Some(Mono8::new(24))); assert_eq!(tiles[3].get(0, 0), Some(Mono8::new(28))); }
#[test]
fn test_subview_into_tiles_partial_edges() {
let img: Image<Mono8> = Image::generate(5, 5, |x, y| Mono8::new((x + y * 5) as u8));
let tiles: Vec<_> = img.tiles(Size::new(3, 3)).collect();
assert_eq!(tiles.len(), 4);
assert_eq!(tiles[0].size(), Size::new(3, 3)); assert_eq!(tiles[1].size(), Size::new(2, 3)); assert_eq!(tiles[2].size(), Size::new(3, 2)); assert_eq!(tiles[3].size(), Size::new(2, 2)); }
#[test]
fn test_subview_into_tiles_multiple_borrows_simultaneously() {
let img: Image<Mono8> = Image::generate(4, 4, |x, y| Mono8::new((x + y * 4) as u8));
let tiles_2x2: Vec<_> = img.tiles(Size::new(2, 2)).collect();
let tiles_4x4: Vec<_> = img.tiles(Size::new(4, 4)).collect();
assert_eq!(tiles_2x2.len(), 4);
assert_eq!(tiles_4x4.len(), 1);
assert_eq!(tiles_2x2[0].get(0, 0), tiles_4x4[0].get(0, 0));
}
#[test]
fn test_subview_into_tiles_symmetry_with_into_tiles_mut() {
let mut img: Image<Mono8> = Image::generate(7, 5, |x, y| Mono8::new((x + y * 7) as u8));
let tile_size = Size::new(3, 2);
let immut_sizes: Vec<Size> = img.tiles(tile_size).map(|t| t.size()).collect();
let immut_values: Vec<Vec<Mono8>> = img
.tiles(tile_size)
.map(|t| {
let mut vals = Vec::new();
for y in 0..t.height() {
for x in 0..t.width() {
vals.push(t.pixel_at(x, y));
}
}
vals
})
.collect();
let mut_tiles: Vec<_> = (&mut img).into_tiles_mut(tile_size).collect();
let mut_sizes: Vec<Size> = mut_tiles.iter().map(|t| t.size()).collect();
let mut_values: Vec<Vec<Mono8>> = mut_tiles
.iter()
.map(|t| {
let mut vals = Vec::new();
for y in 0..t.height() {
for x in 0..t.width() {
vals.push(t.pixel_at(x, y));
}
}
vals
})
.collect();
assert_eq!(immut_sizes, mut_sizes);
assert_eq!(immut_values, mut_values);
}
#[test]
#[should_panic(expected = "tile size must be non-zero")]
fn tile_iter_rejects_zero_width() {
let img = Image::<Mono8>::zero(4, 4);
let _ = img.tiles(Size::new(0, 2)).count();
}
#[test]
#[should_panic(expected = "tile size must be non-zero")]
fn tile_iter_rejects_zero_height() {
let img = Image::<Mono8>::zero(4, 4);
let _ = img.tiles(Size::new(2, 0)).count();
}
#[test]
#[should_panic(expected = "stride must be non-zero")]
fn sliding_window_iter_rejects_zero_horizontal_stride() {
let img = Image::<Mono8>::zero(4, 4);
let _ = SlidingWindow::new(Size::new(2, 2))
.stride(Stride::new(0, 1))
.iter(&img);
}
#[test]
#[should_panic(expected = "stride must be non-zero")]
fn sliding_window_iter_rejects_zero_vertical_stride() {
let img = Image::<Mono8>::zero(4, 4);
let _ = SlidingWindow::new(Size::new(2, 2))
.stride(Stride::new(1, 0))
.iter(&img);
}
#[test]
#[should_panic(expected = "tile size must be non-zero")]
fn tile_iter_mut_rejects_zero_tile_size() {
let mut img = Image::<u8>::zero(4, 4);
let _ = (&mut img).into_tiles_mut(Size::new(0, 2)).count();
}
}