--- /dev/null
+use core::ops::Deref;
+use imgref::*;
+
+/// Previous, current, and next pixel or row
+#[derive(Copy,Clone,Debug,Eq,PartialEq)]
+#[repr(C)]
+pub struct Triple<T> {
+ pub prev: T,
+ pub curr: T,
+ pub next: T,
+}
+
+impl<T> Triple<T> {
+ #[inline(always)]
+ pub fn new(prev: T, curr: T, next: T) -> Self {
+ Triple {prev, curr, next}
+ }
+}
+
+impl<T> AsRef<[T]> for Triple<T> {
+ #[inline(always)]
+ fn as_ref(&self) -> &[T] {
+ unsafe {
+ std::slice::from_raw_parts(&self.prev as *const T, 3)
+ }
+ }
+}
+
+impl<T> Deref for Triple<T> {
+ type Target = [T];
+ #[inline(always)]
+ fn deref(&self) -> &Self::Target {
+ self.as_ref()
+ }
+}
+
+impl<T: Copy> Triple<T> {
+ /// Add next item, and shift others (prev is gone, prev = current)
+ /// If item is None, it'll copy the last one instead.
+ #[inline(always)]
+ pub fn advance(self, next: Option<T>) -> Self {
+ let next = if let Some(next) = next {next} else {self.next};
+ Triple {
+ prev: self.curr,
+ curr: self.next,
+ next,
+ }
+ }
+}
+
+/// Loop over 9 neighboring pixels in the image described by `ImgRef` (`Img.as_ref()`)
+///
+/// The callback is: (x, y, previous_row, current_row, next_row)
+#[inline(always)]
+pub fn loop9_img<Pixel, Callback>(img: ImgRef<'_, Pixel>, cb: Callback)
+ where Pixel: Copy, Callback: FnMut(usize, usize, Triple<Pixel>,Triple<Pixel>,Triple<Pixel>)
+{
+ loop9(img, 0, 0, img.width(), img.height(), cb)
+}
+
+/// Loop over 9 neighboring pixels in the left/top/width/height fragment of the image described by `ImgRef` (`Img.as_ref()`)
+///
+/// The callback is: (x, y, previous_row, current_row, next_row)
+#[inline]
+pub fn loop9<Pixel, Callback>(img: ImgRef<'_, Pixel>, left: usize, top: usize, width: usize, height: usize, mut cb: Callback)
+ where Pixel: Copy, Callback: FnMut(usize, usize, Triple<Pixel>,Triple<Pixel>,Triple<Pixel>)
+{
+ let max_width = img.width();
+ let max_height = img.height();
+ let stride = img.stride();
+ let data = img.buf().as_ref();
+ let t = top * stride;
+ let mut row = Triple {
+ prev: &data[t..t+max_width],
+ curr: &data[t..t+max_width],
+ next: &data[t..t+max_width],
+ };
+ for y in top..top+height {
+ row = row.advance(if y+1 < max_height {let t=(y+1)*stride; Some(&data[t..t+max_width])} else {None});
+ let mut tp;
+ let mut tn = row.prev[left];
+ let mut tc = row.prev[left.saturating_sub(1)];
+ let mut mp;
+ let mut mn = row.curr[left];
+ let mut mc = row.curr[left.saturating_sub(1)];
+ let mut bp;
+ let mut bn = row.next[left];
+ let mut bc = row.next[left.saturating_sub(1)];
+ for x in left..left+width {
+ tp = tc;
+ tc = tn;
+ tn = if x+1 < max_width {row.prev[x+1]} else {tc};
+ mp = mc;
+ mc = mn;
+ mn = if x+1 < max_width {row.curr[x+1]} else {mc};
+ bp = bc;
+ bc = bn;
+ bn = if x+1 < max_width {row.next[x+1]} else {bc};
+ cb(x-left,y-top,Triple::new(tp,tc,tn),Triple::new(mp,mc,mn),Triple::new(bp,bc,bn));
+ }
+ }
+}
+
+
+#[test]
+fn test_loop9() {
+ let src = vec![
+ 1, 2, 3, 4,
+ 5, 6, 7, 8,
+ 9,10,11,12,
+ 13,14,15,16,
+ ];
+ let img = Img::new(src.clone(), 4, 4);
+ assert_eq!(4, img.width());
+ assert_eq!(4, img.stride());
+ assert_eq!(4, img.height());
+
+ let check = |l,t,w,h,exp|{
+ let mut res = Vec::new();
+ loop9(img.as_ref(), l,t,w,h, |_x,_y,_top,mid,_bot| res.push(mid.curr));
+ assert_eq!(exp, res);
+ };
+
+ check(0,0,4,4, src);
+
+ check(0,0,4,1, vec![1, 2, 3, 4]);
+
+ check(0,3,4,1, vec![13,14,15,16]);
+
+ check(0,0,3,3, vec![
+ 1, 2, 3,
+ 5, 6, 7,
+ 9,10,11,
+ ]);
+
+ check(0,0,1,1,vec![1]);
+ check(1,0,1,1,vec![2]);
+ check(2,0,1,1,vec![3]);
+ check(3,0,1,1,vec![4]);
+
+ check(1,0,3,4,vec![
+ 2, 3, 4,
+ 6, 7, 8,
+ 10,11,12,
+ 14,15,16,
+ ]);
+}