use tiny_skia::Pixmap;
pub(crate) fn apply_gaussian_blur(pixmap: &mut Pixmap, sigma: f32) {
if sigma <= 0.0 {
return;
}
let radius = (sigma * 3.0).ceil() as i32;
let width = pixmap.width() as i32;
let height = pixmap.height() as i32;
if radius < 1 || width == 0 || height == 0 {
return;
}
let inv_two_sigma_sq = 1.0 / (2.0 * sigma * sigma);
let mut kernel = vec![0f32; (2 * radius + 1) as usize];
let mut sum = 0f32;
for (i, k) in kernel.iter_mut().enumerate() {
let x = i as i32 - radius;
let v = (-((x * x) as f32) * inv_two_sigma_sq).exp();
*k = v;
sum += v;
}
for k in &mut kernel {
*k /= sum;
}
let stride = width as usize * 4;
let data = pixmap.data_mut();
for px in data.chunks_exact_mut(4) {
let a = u32::from(px[3]);
px[0] = ((u32::from(px[0]) * a + 127) / 255) as u8;
px[1] = ((u32::from(px[1]) * a + 127) / 255) as u8;
px[2] = ((u32::from(px[2]) * a + 127) / 255) as u8;
}
let mut temp = vec![0u8; data.len()];
for y in 0..height {
let row = y as usize * stride;
for x in 0..width {
let mut acc = [0f32; 4];
for (ki, &kw) in kernel.iter().enumerate() {
let sx = (x + ki as i32 - radius).clamp(0, width - 1) as usize;
let i = row + sx * 4;
for (a, &v) in acc.iter_mut().zip(&data[i..i + 4]) {
*a += kw * f32::from(v);
}
}
let o = row + x as usize * 4;
for (dst, &a) in temp[o..o + 4].iter_mut().zip(&acc) {
*dst = a.round().clamp(0.0, 255.0) as u8;
}
}
}
for x in 0..width {
let col = x as usize * 4;
for y in 0..height {
let mut acc = [0f32; 4];
for (ki, &kw) in kernel.iter().enumerate() {
let sy = (y + ki as i32 - radius).clamp(0, height - 1) as usize;
let i = sy * stride + col;
for (a, &v) in acc.iter_mut().zip(&temp[i..i + 4]) {
*a += kw * f32::from(v);
}
}
let o = y as usize * stride + col;
for (dst, &a) in data[o..o + 4].iter_mut().zip(&acc) {
*dst = a.round().clamp(0.0, 255.0) as u8;
}
}
}
for px in data.chunks_exact_mut(4) {
let a = u32::from(px[3]);
for c in &mut px[0..3] {
*c = (u32::from(*c) * 255).checked_div(a).unwrap_or(0).min(255) as u8;
}
}
}