///|
/// Box blur using per-channel integral images (O(1) per pixel).
/// Border windows are shrunk to the valid area.
pub fn box_blur(img : Image, radius : Int) -> Image {
if radius <= 0 || img.is_empty() {
return img.clone()
}
let h = img.h
let w = img.w
let out = Image::new(h, w)
let w1 = w + 1
for c = 0; c < 3; c = c + 1 {
let integ = Array::make((h + 1) * w1, 0)
for y = 0; y < h; y = y + 1 {
let mut rowsum = 0
for x = 0; x < w; x = x + 1 {
rowsum = rowsum + img.data[img.offset(y, x) + c].to_int()
integ[(y + 1) * w1 + (x + 1)] = integ[y * w1 + (x + 1)] + rowsum
}
}
for y = 0; y < h; y = y + 1 {
let y0 = clampi(y - radius, 0, h - 1)
let y1 = clampi(y + radius, 0, h - 1)
for x = 0; x < w; x = x + 1 {
let x0 = clampi(x - radius, 0, w - 1)
let x1 = clampi(x + radius, 0, w - 1)
let area = (y1 - y0 + 1) * (x1 - x0 + 1)
let s = integ[(y1 + 1) * w1 + (x1 + 1)] -
integ[y0 * w1 + (x1 + 1)] -
integ[(y1 + 1) * w1 + x0] +
integ[y0 * w1 + x0]
out.data[out.offset(y, x) + c] = clamp_byte(s / area)
}
}
}
let n = h * w
for i = 0; i < n; i = i + 1 {
out.data[i * 4 + 3] = img.data[i * 4 + 3]
}
out
}