1#![doc = include_str!("../README.md")]
2
3use std::ops::{Add, Sub, RangeBounds};
4pub use imgref::ImgRef;
5
6pub struct SummedArea<SumType> {
12 stride: usize,
13 sums: Vec<SumType>,
14}
15
16impl<SumType> SummedArea<SumType> where SumType: Default + Copy + Add<Output=SumType> + Sub<Output=SumType> {
17 #[inline]
21 #[must_use]
22 pub fn new_slice<InputType>(input: &[InputType], width: usize) -> Self where SumType: From<InputType>, InputType: Copy {
23 let height = input.len()/width;
24 Self::new(ImgRef::new(&input, width, height))
25 }
26
27 #[must_use]
29 pub fn new<InputType>(input: ImgRef<InputType>) -> Self where SumType: From<InputType>, InputType: Copy {
30 let out_width = input.width()+1;
31 let out_height = input.height()+1;
32 let area = out_width.checked_mul(out_height).unwrap();
33
34 let mut sums: Vec<SumType> = Vec::with_capacity(area);
35 sums.resize_with(out_width, SumType::default); let out = &mut sums.spare_capacity_mut()[..area-out_width];
38
39 let mut rows = input.rows().zip(out.chunks_exact_mut(out_width));
40 if let Some((in_row, out_row)) = rows.next() {
41 let (first, out_row) = out_row.split_first_mut().unwrap();
42 debug_assert_eq!(out_row.len(), input.width());
43 first.write(SumType::default()); let mut row_sum = SumType::default();
45 in_row.iter().copied().zip(out_row.iter_mut()).for_each(|(curr, out_col)| {
46 let curr = SumType::from(curr);
47 row_sum = row_sum + curr;
48 out_col.write(row_sum);
49 });
50 let mut prev_out_row = out_row;
51 rows.for_each(|(in_row, out_row)| {
52 let (first, out_row) = out_row.split_first_mut().unwrap();
53 debug_assert_eq!(out_row.len(), input.width());
54 first.write(SumType::default()); let mut up_left = SumType::default();
56 let mut left = SumType::default();
57 let cols = in_row.iter().copied().zip(out_row.iter_mut().zip(prev_out_row.iter_mut()));
58 cols.for_each(|(curr, (out_col, prev_out_px))| {
59 let up = unsafe { prev_out_px.assume_init_read() };
61 let curr = SumType::from(curr);
62 let curr_out = curr + up + left - up_left;
63 out_col.write(curr_out);
64 left = curr_out;
65 up_left = up;
66 });
67 prev_out_row = out_row;
68 });
69 }
70 unsafe {
72 sums.set_len(area);
73 }
74 debug_assert_eq!(sums.len()/out_width, out_height);
75 Self {
76 stride: out_width,
77 sums,
78 }
79 }
80
81 #[inline]
83 #[must_use]
84 pub fn width(&self) -> usize {
85 self.stride-1
86 }
87
88 #[inline]
90 #[must_use]
91 pub fn height(&self) -> usize {
92 self.sums.len() / self.stride - 1
93 }
94
95 #[inline(always)]
99 #[track_caller]
100 #[must_use]
101 pub fn sum_rect(&self, x: usize, y: usize, width: usize, height: usize) -> SumType {
102 self.try_sum_rect(x, y, width, height).expect("oob")
103 }
104
105 #[inline]
109 #[must_use]
110 pub fn try_sum_rect(&self, x1: usize, y1: usize, width: usize, height: usize) -> Option<SumType> {
111 self.try_sum_bounds(x1, y1, x1+width, y1+height)
112 }
113
114 #[inline(always)]
118 #[track_caller]
119 #[must_use]
120 pub fn sum_range(&self, horizontal: impl RangeBounds<usize>, vertical: impl RangeBounds<usize>) -> SumType {
121 self.try_sum_range(horizontal, vertical).expect("oob")
122 }
123
124 #[inline]
128 #[must_use]
129 pub fn try_sum_range(&self, horizontal: impl RangeBounds<usize>, vertical: impl RangeBounds<usize>) -> Option<SumType> {
130 let (x1, x2) = bounds(horizontal, self.stride);
131 let (y1, y2) = bounds(vertical, self.height());
132 self.try_sum_bounds(x1, y1, x2, y2)
133 }
134
135 #[inline]
136 fn try_sum_bounds(&self, x1: usize, y1: usize, x2: usize, y2: usize) -> Option<SumType> {
137 let x1y1 = x1 + y1 * self.stride;
138 let x1y2 = x1 + y2 * self.stride;
139 let x2y1 = x2 + y1 * self.stride;
140 let x2y2 = x2 + y2 * self.stride;
141 let tl = *self.sums.get(x1y1)?;
143 let tr = *self.sums.get(x2y1)?;
144 let bl = *self.sums.get(x1y2)?;
145 let br = *self.sums.get(x2y2)?;
146 Some(tl + br - tr - bl)
147 }
148}
149
150fn bounds(range: impl RangeBounds<usize>, max: usize) -> (usize, usize) {
151 let start = match range.start_bound() {
152 std::ops::Bound::Included(x) => *x,
153 std::ops::Bound::Unbounded => 0,
154 std::ops::Bound::Excluded(x) => *x+1,
155 };
156 let end = match range.end_bound() {
157 std::ops::Bound::Included(x) => *x+1,
158 std::ops::Bound::Excluded(x) => *x,
159 std::ops::Bound::Unbounded => max,
160 };
161 (start, end)
162}
163
164#[test]
165fn bounds_test() {
166 assert_eq!((0, 99), bounds(0.., 99));
167 assert_eq!((0, 0), bounds(0..0, 99));
168 assert_eq!((0, 1), bounds(0..1, 99));
169 assert_eq!((0, 2), bounds(0..=1, 99));
170 assert_eq!((0, 1), bounds(0..=0, 99));
171 assert_eq!((0, 50), bounds(..50, 99));
172 assert_eq!((0, 50), bounds(..=49, 99));
173}
174
175#[test]
176fn wiki() {
177 let s = SummedArea::new_slice(&[
178 31.,2.,4.,33.,5.,36.,
179 12.,26.,9.,10.,29.,25.,
180 13.,17.,21.,22.,20.,18.,
181 24.,23.,15.,16.,14.,19.,
182 30.,8.,28.,27.,11.,7.,
183 1.,35.,34.,3.,32.,6.,
184 ], 6);
185 assert_eq!(s.sums.len(), 7*7);
186 assert_eq!(s.width(), 6);
187 assert_eq!(s.height(), 6);
188 assert_eq!([
189 0., 0.,0.,0.,0.,0.,0.,
190 0., 31.,33.,37.,70.,75.,111.,
191 0., 43.,71.,84.,127.,161.,222.,
192 0., 56.,101.,135.,200.,254.,333.,
193 0., 80.,148.,197.,278.,346.,444.,
194 0., 110.,186.,263.,371.,450.,555.,
195 0., 111.,222.,333.,444.,555.,666.,
196 ], s.sums.as_slice());
197
198 assert_eq!(111., s.sum_rect(2, 3, 3, 2));
199 assert_eq!(111., s.try_sum_range(2..=4, 3..5).unwrap());
200}
201
202#[test]
203fn pixels() {
204 let s = SummedArea::<rgb::RGB<u16>>::new_slice(&[
205 rgb::RGB::<u8>::new(1,2,3),
206 rgb::RGB::<u8>::new(4,5,6),
207 ], 1);
208 assert_eq!(2, s.height());
209 assert_eq!(rgb::RGB::<u16>::new(1,2,3), s.sum_range(0..1, 0..1));
210 assert_eq!(rgb::RGB::new(4,5,6), s.sum_range(0..1, 1..2));
211 assert_eq!(rgb::RGB::new(5,7,9), s.sum_range(0..1, 0..2));
212}
213
214#[test]
215fn ones() {
216 let _ = SummedArea::<i64>::new_slice(&[1i32], 1);
217
218 let s = SummedArea::new_slice(&[
219 1.,1.,1.,
220 1.,1.,1.,
221 1.,1.,1.,
222 ], 3);
223 assert_eq!(s.height(), 3);
224 assert_eq!([
225 0.0, 0.0, 0.0, 0.0,
226 0.0, 1.0, 2.0, 3.0,
227 0.0, 2.0, 4.0, 6.0,
228 0.0, 3.0, 6.0, 9.0,
229 ], s.sums.as_slice());
230
231 assert_eq!(0., s.sum_rect(0,0,0,0));
232 assert_eq!(0., s.sum_rect(2,2,0,0));
233 assert_eq!(0., s.sum_rect(0,0,1,0));
234 assert_eq!(0., s.sum_rect(0,0,0,1));
235 assert_eq!(1., s.sum_rect(0,0,1,1));
236 assert_eq!(1., s.sum_rect(1,1,1,1));
237 assert_eq!(2., s.sum_rect(0,0,1,2));
238 assert_eq!(2., s.sum_rect(0,0,2,1));
239 assert_eq!(4., s.sum_rect(0,0,2,2));
240 assert_eq!(4., s.sum_rect(1,1,2,2));
241 assert_eq!(6., s.sum_rect(0,0,2,3));
242 assert_eq!(6., s.sum_rect(0,0,3,2));
243 assert_eq!(6., s.sum_rect(0,1,3,2));
244 assert_eq!(9., s.sum_rect(0,0,3,3));
245 assert_eq!(1., s.sum_rect(2,2,1,1));
246}