long_screen/kernels/reveal/
stats.rs1use super::{luma, CanvasView, Footprint, Frame, CELL};
10
11pub trait CellRef {
13 fn cell(&self, cx: i64, cy: i64) -> Option<[u8; 3]>;
15 fn cells(&self) -> (i64, i64, i64, i64);
17}
18
19pub struct CellMap {
21 pub cx0: i64,
22 pub cy0: i64,
23 pub cw: usize,
24 pub ch: usize,
25 pub rgbk: Vec<[u8; 4]>,
27}
28
29impl CellMap {
30 pub fn covering(x: i64, y: i64, width: usize, height: usize) -> Self {
32 let c = CELL as i64;
33 let (cx0, cy0) = (x.div_euclid(c), y.div_euclid(c));
34 let (cx1, cy1) = (
35 (x + width as i64 + c - 1).div_euclid(c),
36 (y + height as i64 + c - 1).div_euclid(c),
37 );
38 let (cw, ch) = ((cx1 - cx0).max(0) as usize, (cy1 - cy0).max(0) as usize);
39 Self {
40 cx0,
41 cy0,
42 cw,
43 ch,
44 rgbk: vec![[0u8; 4]; cw * ch],
45 }
46 }
47
48 pub fn ingest(&mut self, view: &CanvasView<'_>) {
51 let c = CELL as i64;
52 let x0 = ((view.x + c - 1).div_euclid(c)).max(self.cx0);
53 let y0 = ((view.y + c - 1).div_euclid(c)).max(self.cy0);
54 let x1 = ((view.x + view.width as i64).div_euclid(c)).min(self.cx0 + self.cw as i64);
55 let y1 = ((view.y + view.height as i64).div_euclid(c)).min(self.cy0 + self.ch as i64);
56 for cy in y0..y1 {
57 for cx in x0..x1 {
58 let (wx, wy) = (cx * c, cy * c);
59 let mut s = [0u32; 3];
60 let mut ok = true;
61 'px: for dy in 0..c {
62 for dx in 0..c {
63 match view.covered(wx + dx, wy + dy) {
64 Some(i) => {
65 for (k, v) in s.iter_mut().enumerate() {
66 *v += view.rgba[i * 4 + k] as u32;
67 }
68 }
69 None => {
70 ok = false;
71 break 'px;
72 }
73 }
74 }
75 }
76 if ok {
77 let q = (CELL * CELL) as u32;
78 self.rgbk[(cy - self.cy0) as usize * self.cw + (cx - self.cx0) as usize] = [
79 ((s[0] + q / 2) / q) as u8,
80 ((s[1] + q / 2) / q) as u8,
81 ((s[2] + q / 2) / q) as u8,
82 1,
83 ];
84 }
85 }
86 }
87 }
88
89 pub fn from_canvas(view: &CanvasView<'_>) -> Self {
90 let mut m = Self::covering(view.x, view.y, view.width, view.height);
91 m.ingest(view);
92 m
93 }
94}
95
96impl CellRef for CellMap {
97 #[inline]
98 fn cell(&self, cx: i64, cy: i64) -> Option<[u8; 3]> {
99 let (x, y) = (cx - self.cx0, cy - self.cy0);
100 if x < 0 || y < 0 || x >= self.cw as i64 || y >= self.ch as i64 {
101 return None;
102 }
103 let v = self.rgbk[y as usize * self.cw + x as usize];
104 (v[3] != 0).then_some([v[0], v[1], v[2]])
105 }
106 fn cells(&self) -> (i64, i64, i64, i64) {
107 (
108 self.cx0,
109 self.cy0,
110 self.cx0 + self.cw as i64,
111 self.cy0 + self.ch as i64,
112 )
113 }
114}
115
116#[derive(Clone, Copy, Default)]
118pub struct Acc {
119 pub n: u16,
121 pub bad: u16,
122 pub sr: u32,
124 pub so: u32,
125 pub srr: u32,
126 pub sro: u32,
127 pub soo: u32,
128 pub br: u32,
130 pub bg: u32,
131 pub bb: u32,
132 pub bl: u32,
133 pub bll: u32,
134}
135
136impl Acc {
137 #[inline]
138 fn add(&mut self, o: [u32; 3], r: [u32; 3], tau: i32) {
139 if self.n == u16::MAX {
140 return;
141 }
142 let lo = luma(o[0] as u8, o[1] as u8, o[2] as u8);
143 let lr = luma(r[0] as u8, r[1] as u8, r[2] as u8);
144 let d = (o[0] as i32 - r[0] as i32)
145 .abs()
146 .max((o[1] as i32 - r[1] as i32).abs())
147 .max((o[2] as i32 - r[2] as i32).abs());
148 self.n += 1;
149 self.sr += lr;
150 self.so += lo;
151 self.srr += lr * lr;
152 self.sro += lr * lo;
153 self.soo += lo * lo;
154 if d > tau {
155 self.bad += 1;
156 self.br += o[0];
157 self.bg += o[1];
158 self.bb += o[2];
159 self.bl += lo;
160 self.bll += lo * lo;
161 }
162 }
163}
164
165#[derive(Clone)]
166pub struct LayerStats {
167 pub x: i64,
169 pub y: i64,
170 pub cw: usize,
171 pub ch: usize,
172 pub cells: Vec<Acc>,
173}
174
175impl LayerStats {
176 pub fn new(x: i64, y: i64, width: usize, height: usize) -> Self {
177 let (cw, ch) = (width.div_ceil(CELL), height.div_ceil(CELL));
178 Self {
179 x,
180 y,
181 cw,
182 ch,
183 cells: vec![Acc::default(); cw * ch],
184 }
185 }
186
187 pub fn field(&self, f: impl Fn(&Acc) -> u32) -> Vec<u32> {
189 self.cells.iter().map(f).collect()
190 }
191
192 pub fn accumulate_cells<R: CellRef + Sync + ?Sized>(
198 &mut self,
199 frame: &Frame<'_>,
200 footprint: &Footprint<'_>,
201 reference: &R,
202 pose: (i64, i64),
203 tau: i32,
204 ) -> usize {
205 let (px, py) = pose;
206 let c = CELL as i64;
207 let (rx0, ry0, rx1, ry1) = reference.cells();
208 let wx0 = (footprint.x + px).div_euclid(c).max(rx0);
209 let wy0 = (footprint.y + py).div_euclid(c).max(ry0);
210 let wx1 = (footprint.x + footprint.width as i64 + px + c - 1)
211 .div_euclid(c)
212 .min(rx1);
213 let wy1 = (footprint.y + footprint.height as i64 + py + c - 1)
214 .div_euclid(c)
215 .min(ry1);
216 if wx1 <= wx0 || wy1 <= wy0 {
217 return 0;
218 }
219 let rows = (wy1 - wy0) as usize;
220 let chunks = crate::kernels::pool::chunks_for(rows * (wx1 - wx0) as usize, 8192);
221 let mut counts = vec![0usize; chunks];
222 let cells = crate::kernels::pool::SyncPtr(self.cells.as_mut_ptr());
223 let counted = crate::kernels::pool::SyncPtr(counts.as_mut_ptr());
224 let (lx0, ly0, cw, ch) = (self.x, self.y, self.cw as i64, self.ch as i64);
225 let (w, h) = (frame.width as i64, frame.height as i64);
226 let (fx0, fy0, fw, fh) = (
227 footprint.x,
228 footprint.y,
229 footprint.width as i64,
230 footprint.height as i64,
231 );
232 crate::kernels::pool::par_for(chunks, |k| {
233 let (a, b) = (
234 crate::kernels::pool::split(rows, chunks, k),
235 crate::kernels::pool::split(rows, chunks, k + 1),
236 );
237 let mut compared = 0usize;
238 for r in a..b {
239 let cy = wy0 + r as i64;
240 let sy = cy * c - py;
241 let (ly, my) = ((sy - ly0).div_euclid(c), sy - fy0);
242 if sy < 0 || sy + c > h || ly < 0 || ly >= ch || my < 0 || my + c > fh {
243 continue;
244 }
245 let (m0, m1) = (
246 &footprint.mask[(my * fw) as usize..((my + 1) * fw) as usize],
247 &footprint.mask[((my + 1) * fw) as usize..((my + 2) * fw) as usize],
248 );
249 let (f0, f1) = (
250 &frame.rgba[(sy * w * 4) as usize..((sy + 1) * w * 4) as usize],
251 &frame.rgba[((sy + 1) * w * 4) as usize..((sy + 2) * w * 4) as usize],
252 );
253 let k = (px + lx0 + 1).div_euclid(c);
256 let lo = [wx0, (px + 1).div_euclid(c), k, (fx0 + px + 1).div_euclid(c)];
257 let hi = [
258 wx1,
259 (w - c + px).div_euclid(c) + 1,
260 cw + k,
261 (fx0 + fw - c + px).div_euclid(c) + 1,
262 ];
263 let (cx_lo, cx_hi) = (lo.into_iter().max().unwrap(), hi.into_iter().min().unwrap());
264 for cx in cx_lo..cx_hi.max(cx_lo) {
265 let sx = cx * c - px;
266 let (lx, mx) = (cx - k, (sx - fx0) as usize);
267 if m0[mx] == 0 || m0[mx + 1] == 0 || m1[mx] == 0 || m1[mx + 1] == 0 {
268 continue;
269 }
270 let Some(rv) = reference.cell(cx, cy) else {
271 continue;
272 };
273 let i = sx as usize * 4;
274 let mut o = [0u32; 3];
275 for (k, v) in o.iter_mut().enumerate() {
276 *v = (f0[i + k] as u32
277 + f0[i + 4 + k] as u32
278 + f1[i + k] as u32
279 + f1[i + 4 + k] as u32
280 + 2)
281 / 4;
282 }
283 let acc = unsafe { &mut *cells.get().add((ly * cw + lx) as usize) };
285 acc.add(o, [rv[0] as u32, rv[1] as u32, rv[2] as u32], tau);
286 compared += 1;
287 }
288 }
289 unsafe { *counted.get().add(k) = compared };
291 });
292 counts.iter().sum()
293 }
294}