1use alloc::vec;
15
16use alloc::vec::Vec;
17
18use crate::{px, put, Dst, FxArgs, ERR_CANCEL};
19
20pub const FX_LOCHIST_MAX: i32 = 1024;
21
22#[derive(Clone)]
24pub struct LocHist {
25 pub h: [[u32; 256]; 4],
26 pub blk: [[u32; 16]; 4],
27}
28
29impl LocHist {
30 #[inline(always)]
31 fn add(&mut self, p: u32) {
32 for c in 0..4 {
33 let v = (p >> (8 * c) & 255) as usize;
34 self.h[c][v] += 1;
35 self.blk[c][v >> 4] += 1;
36 }
37 }
38 #[inline(always)]
39 fn sub(&mut self, p: u32) {
40 for c in 0..4 {
41 let v = (p >> (8 * c) & 255) as usize;
42 self.h[c][v] -= 1;
43 self.blk[c][v >> 4] -= 1;
44 }
45 }
46}
47
48pub fn hist_rgba(a: &FxArgs, hist: &mut [[u32; 256]; 4]) -> i64 {
50 *hist = [[0; 256]; 4];
51 let mut count = 0i64;
52 let mut sbuf = [0u8; 256];
53 for y in a.y0..a.y1 {
54 if a.cancelled() {
55 return ERR_CANCEL as i64;
56 }
57 let row = a.src_row(y);
58 let mut x = a.x0;
59 while a.x1 - x > 0 {
60 let n = (a.x1 - x).min(256);
61 let sel = match a.sel {
62 Some(m) => Some(m.row(x, y, n, &mut sbuf).0),
63 None => None,
64 };
65 for i in 0..n as usize {
66 if let Some(s) = sel {
67 if s[i] == 0 {
68 continue;
69 }
70 }
71 let p = px(row, x as usize + i);
72 if p < 0x0100_0000 {
73 continue; }
75 hist[0][(p & 255) as usize] += 1;
76 hist[1][(p >> 8 & 255) as usize] += 1;
77 hist[2][(p >> 16 & 255) as usize] += 1;
78 hist[3][(p >> 24) as usize] += 1;
79 count += 1;
80 }
81 x += n;
82 }
83 }
84 count
85}
86
87pub fn hist_percentile(h: &[u32; 256], total: i64, fraction: f64) -> u32 {
89 let target = total as f64 * fraction;
90 let mut integral = 0i64;
91 for (j, &c) in h.iter().enumerate() {
92 integral += c as i64;
93 if integral as f64 > target {
94 return j as u32;
95 }
96 }
97 0
98}
99
100pub fn hist_mean(h: &[u32; 256]) -> f64 {
102 let (mut s, mut n) = (0i64, 0i64);
103 for (j, &c) in h.iter().enumerate() {
104 n += c as i64;
105 s += c as i64 * j as i64;
106 }
107 if n == 0 {
108 return 0.0;
109 }
110 (s as f32 / n as f32) as f64
111}
112
113pub fn lochist(a: &FxArgs, dst: &mut Dst, rad: i32, mut f: impl FnMut(&LocHist, u32, u32) -> u32) -> i32 {
115 let rad = rad.clamp(0, FX_LOCHIST_MAX);
116 let cutoff = ((2 * rad + 1) * (2 * rad + 1) + 2) >> 2;
117 let (w, h) = (a.src.w, a.src.h);
118 let stride = a.src.stride as usize;
119 let sp = &a.src.px;
120 let at = |x: i32, y: i32| -> u32 { px(&sp[y as usize * stride..], x as usize) };
121 let mut edge = vec![0i32; rad as usize + 1];
123 for v in 0..=rad {
124 let mut e = 0;
125 for u in 0..=rad {
126 if u * u + v * v <= cutoff {
127 e = u;
128 }
129 }
130 edge[v as usize] = e;
131 }
132 let mut hb = alloc::boxed::Box::new(LocHist { h: [[0; 256]; 4], blk: [[0; 16]; 4] });
133 let mut offs: Vec<(usize, usize)> = Vec::with_capacity(2 * rad as usize + 1);
137 let rd = |o: usize| -> u32 {
138 let q: [u8; 4] = sp[o..o + 4].try_into().unwrap();
139 u32::from_le_bytes(q)
140 };
141 for y in a.y0..a.y1 {
142 if a.x1 <= a.x0 {
143 break;
144 }
145 if a.cancelled() {
146 return ERR_CANCEL;
147 }
148 let hb = &mut *hb;
149 hb.h = [[0; 256]; 4];
150 hb.blk = [[0; 16]; 4];
151 let mut area = 0u32;
152 let top = -rad.min(y);
153 let bottom = (h - 1 - y).min(rad);
154 let x0 = a.x0;
155 let mut left = -rad.min(x0);
156 let mut right = (w - 1 - x0).min(rad);
157 for v in top..=bottom {
158 for u in left..=right {
159 if u * u + v * v <= cutoff {
160 hb.add(at(x0 + u, y + v));
161 area += 1;
162 }
163 }
164 }
165 offs.clear();
166 for v in top..=bottom {
167 let e = edge[v.unsigned_abs() as usize] as usize;
168 let ro = (y + v) as usize * stride;
169 offs.push((ro.wrapping_sub(e * 4), ro + (e + 1) * 4));
170 }
171 let drow = dst.row(y);
172 for x in a.x0..a.x1 {
173 put(drow, (x - a.x0) as usize, f(hb, area, at(x, y)));
174 if x >= rad && x <= w - 2 - rad {
175 let b = x as usize * 4;
176 for &(ot, ol) in &offs {
177 let (pt, pl) = (rd(ot.wrapping_add(b)), rd(ol + b));
178 if pt != pl {
179 hb.sub(pt);
180 hb.add(pl);
181 }
182 }
183 continue;
184 }
185 left = -rad.min(x);
186 right = (w - 1 - x).min(rad + 1);
187 let mut v = -1;
189 while v >= top {
190 if -edge[-v as usize] >= left {
191 break;
192 }
193 v -= 1;
194 }
195 while v >= top {
196 hb.sub(at(x - edge[-v as usize], y + v));
197 area -= 1;
198 v -= 1;
199 }
200 let mut v = -1;
202 while v >= top {
203 if edge[-v as usize] + 1 <= right {
204 break;
205 }
206 v -= 1;
207 }
208 while v >= top {
209 hb.add(at(x + edge[-v as usize] + 1, y + v));
210 area += 1;
211 v -= 1;
212 }
213 let mut v = 0;
215 while v <= bottom {
216 if -edge[v as usize] >= left {
217 break;
218 }
219 v += 1;
220 }
221 while v <= bottom {
222 hb.sub(at(x - edge[v as usize], y + v));
223 area -= 1;
224 v += 1;
225 }
226 let mut v = 0;
228 while v <= bottom {
229 if edge[v as usize] + 1 <= right {
230 break;
231 }
232 v += 1;
233 }
234 while v <= bottom {
235 hb.add(at(x + edge[v as usize] + 1, y + v));
236 area += 1;
237 v += 1;
238 }
239 }
240 }
241 0
242}