| 1 | use core::ops::Deref; |
| 2 | use imgref::ImgRef; |
| 3 | |
| 4 | /// Previous, current, and next pixel or row |
| 5 | #[derive (Copy,Clone,Debug,Eq,PartialEq)] |
| 6 | #[repr (C)] |
| 7 | pub struct Triple<T> { |
| 8 | pub prev: T, |
| 9 | pub curr: T, |
| 10 | pub next: T, |
| 11 | } |
| 12 | |
| 13 | impl<T> Triple<T> { |
| 14 | #[must_use ] |
| 15 | #[inline (always)] |
| 16 | pub fn new(prev: T, curr: T, next: T) -> Self { |
| 17 | Triple { prev, curr, next } |
| 18 | } |
| 19 | } |
| 20 | |
| 21 | impl<T> AsRef<[T]> for Triple<T> { |
| 22 | #[inline (always)] |
| 23 | fn as_ref(&self) -> &[T] { |
| 24 | unsafe { |
| 25 | std::slice::from_raw_parts(self as *const Triple<T> as *const T, len:3) |
| 26 | } |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | impl<T> Deref for Triple<T> { |
| 31 | type Target = [T]; |
| 32 | |
| 33 | #[inline (always)] |
| 34 | fn deref(&self) -> &Self::Target { |
| 35 | self.as_ref() |
| 36 | } |
| 37 | } |
| 38 | |
| 39 | impl<T: Copy> Triple<T> { |
| 40 | /// Add the next item, and shift others (prev is gone, prev = current) |
| 41 | /// If the item is `None`, it'll copy the last one instead. |
| 42 | #[must_use ] |
| 43 | #[inline (always)] |
| 44 | pub fn advance(self, next: Option<T>) -> Self { |
| 45 | Triple { |
| 46 | prev: self.curr, |
| 47 | curr: self.next, |
| 48 | next: next.unwrap_or(self.next), |
| 49 | } |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | /// Loop over 9 neighboring pixels in the image described by [`ImgRef`] (`Img.as_ref()`) |
| 54 | /// |
| 55 | /// The callback is: (`x`, `y`, `previous_row`, `current_row`, `next_row`) |
| 56 | /// |
| 57 | /// This function will never panic, if your callback doesn't panic. |
| 58 | #[inline (always)] |
| 59 | pub fn loop9_img<Pixel, Callback>(img: ImgRef<'_, Pixel>, cb: Callback) |
| 60 | where Pixel: Copy, Callback: FnMut(usize, usize, Triple<Pixel>,Triple<Pixel>,Triple<Pixel>) |
| 61 | { |
| 62 | loop9(img, left:0, top:0, img.width(), img.height(), cb); |
| 63 | } |
| 64 | |
| 65 | /// Loop over 9 neighboring pixels in the left/top/width/height fragment of the image described by [`ImgRef`] (`Img.as_ref()`) |
| 66 | /// |
| 67 | /// The callback is: (`x`, `y`, `previous_row`, `current_row`, `next_row`) |
| 68 | /// |
| 69 | /// This function will never panic, if your callback doesn't panic. |
| 70 | pub fn loop9<Pixel, Callback>(img: ImgRef<'_, Pixel>, left: usize, top: usize, width: usize, height: usize, mut cb: Callback) |
| 71 | where Pixel: Copy, Callback: FnMut(usize, usize, Triple<Pixel>,Triple<Pixel>,Triple<Pixel>) |
| 72 | { |
| 73 | let max_width = img.width(); |
| 74 | let max_height = img.height(); |
| 75 | let stride = img.stride(); |
| 76 | if stride == 0 || max_height == 0 || max_width == 0 { |
| 77 | return; |
| 78 | } |
| 79 | let data = img.buf(); |
| 80 | let t = top.min(max_height-1) * stride; |
| 81 | let start_row = if let Some(r) = data.get(t..t+max_width) { r } else { return }; |
| 82 | if start_row.is_empty() { return; } |
| 83 | let mut row = Triple { |
| 84 | prev: start_row, |
| 85 | curr: start_row, |
| 86 | next: start_row, |
| 87 | }; |
| 88 | for y in top..top+height { |
| 89 | row = row.advance({ |
| 90 | let t = (y+1) * stride; |
| 91 | data.get(t..t+max_width) |
| 92 | }); |
| 93 | if row.prev.is_empty() || row.curr.is_empty() || row.next.is_empty() { |
| 94 | return; |
| 95 | } |
| 96 | let mut tp; |
| 97 | let mut tn = row.prev[left.min(row.prev.len()-1)]; |
| 98 | let mut tc = row.prev[left.saturating_sub(1).min(row.prev.len()-1)]; |
| 99 | let mut mp; |
| 100 | let mut mn = row.curr[left.min(row.curr.len()-1)]; |
| 101 | let mut mc = row.curr[left.saturating_sub(1).min(row.curr.len()-1)]; |
| 102 | let mut bp; |
| 103 | let mut bn = row.next[left.min(row.next.len()-1)]; |
| 104 | let mut bc = row.next[left.saturating_sub(1).min(row.next.len()-1)]; |
| 105 | for x in left..left+width { |
| 106 | tp = tc; |
| 107 | tc = tn; |
| 108 | tn = row.prev.get(x+1).copied().unwrap_or(tc); |
| 109 | mp = mc; |
| 110 | mc = mn; |
| 111 | mn = row.curr.get(x+1).copied().unwrap_or(mc); |
| 112 | bp = bc; |
| 113 | bc = bn; |
| 114 | bn = row.next.get(x+1).copied().unwrap_or(bc); |
| 115 | cb(x-left, y-top, Triple::new(tp, tc, tn), Triple::new(mp, mc, mn), Triple::new(bp, bc, bn)); |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | |
| 121 | #[test ] |
| 122 | fn test_oob() { |
| 123 | use imgref::Img; |
| 124 | let img = Img::new(vec![0; 5*4], 5, 4); |
| 125 | for w in 1..8 { |
| 126 | for h in 1..8 { |
| 127 | for x in 0..8 { |
| 128 | for y in 0..8 { |
| 129 | let mut n = 0; |
| 130 | loop9(img.as_ref(), x,y,w,h, |_x,_y,_top,_mid,_bot| { n += 1 }); |
| 131 | assert_eq!(n, w*h, "{x},{y},{w},{h}" ); |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | #[test ] |
| 139 | fn test_loop9() { |
| 140 | use imgref::Img; |
| 141 | |
| 142 | let src = vec![ |
| 143 | 1, 2, 3, 4, 0, |
| 144 | 5, 6, 7, 8, 0, |
| 145 | 9,10,11,12, 0, |
| 146 | 13,14,15,16, 0, |
| 147 | ]; |
| 148 | let img = Img::new_stride(src.clone(), 4, 4, 5); |
| 149 | assert_eq!(4, img.width()); |
| 150 | assert_eq!(5, img.stride()); |
| 151 | assert_eq!(4, img.height()); |
| 152 | |
| 153 | let check = |l,t,w,h,exp: &[_]|{ |
| 154 | let mut res = Vec::new(); |
| 155 | loop9(img.as_ref(), l,t,w,h, |_x,_y,_top,mid,_bot| res.push(mid.curr)); |
| 156 | assert_eq!(exp, res); |
| 157 | }; |
| 158 | |
| 159 | check(0,0,4,4, &[ |
| 160 | 1, 2, 3, 4, |
| 161 | 5, 6, 7, 8, |
| 162 | 9,10,11,12, |
| 163 | 13,14,15,16, |
| 164 | ]); |
| 165 | |
| 166 | check(0,0,4,1, &[1, 2, 3, 4]); |
| 167 | |
| 168 | check(0,3,4,1, &[13,14,15,16]); |
| 169 | |
| 170 | check(0,0,3,3, &[ |
| 171 | 1, 2, 3, |
| 172 | 5, 6, 7, |
| 173 | 9,10,11, |
| 174 | ]); |
| 175 | |
| 176 | check(0,0,1,1, &[1]); |
| 177 | check(1,0,1,1, &[2]); |
| 178 | check(2,0,1,1, &[3]); |
| 179 | check(3,0,1,1, &[4]); |
| 180 | |
| 181 | check(1,0,3,4,&[ |
| 182 | 2, 3, 4, |
| 183 | 6, 7, 8, |
| 184 | 10,11,12, |
| 185 | 14,15,16, |
| 186 | ]); |
| 187 | } |
| 188 | |