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 | |