use crate::helpers::box_blur_f32; pub fn generate_glow( alpha: &[u8], w: u32, h: u32, spread: f32, size: f32, color: [u8; 4], inner: bool, ) -> Vec { let px = (w * h) as usize; let mut buf = vec![0u8; px * 4]; let radius = size.max(0.0).round() as i32; let glow_alpha = if inner { // MAE-optimized against Krita ground truth (48 samples, 3000 combos, 800x800) // Best: blur_factor=4.0, passes=4, alpha_scale=0.00, glow_boost=2.00, spread_before_blur=false let mut ga = alpha.iter().map(|&a| a as f32).collect::>(); if radius > 0 { let gaussian_r = (radius as f32 / 4.0).round().max(1.0) as i32; for _ in 0..4 { ga = box_blur_f32(&ga, w, h, gaussian_r); } } for i in 0..px { ga[i] = 255.0 - ga[i]; } if spread > 0.0 { let factor = 1.0 / (1.0 - spread / 100.0); for a in ga.iter_mut() { *a = (*a * factor).round().min(255.0); } } for i in 0..px { ga[i] = (ga[i] - alpha[i] as f32 * 0.00).max(0.0) * 2.00 * alpha[i] as f32 / 255.0; } ga.iter().map(|&a| a as u8).collect::>() } else { let mut ga = alpha.iter().map(|&a| a as f32).collect::>(); // BEGIN_GLOW_TUNING_ZONE // MAE-optimized against Krita ground truth (48 samples, 3000 combos, 800x800) // Best: blur_factor=4.0, passes=4, alpha_scale=0.00, glow_boost=2.00, spread_before_blur=false if radius > 0 { let gaussian_r = (radius as f32 / 4.0).round().max(1.0) as i32; for _ in 0..4 { ga = box_blur_f32(&ga, w, h, gaussian_r); } } if spread > 0.0 { let factor = 1.0 / (1.0 - spread / 100.0); for a in ga.iter_mut() { *a = (*a * factor).round().min(255.0); } } for i in 0..px { let mask = 255.0 - alpha[i] as f32; ga[i] = (ga[i] - alpha[i] as f32 * 0.00).max(0.0) * 2.00 * mask / 255.0; } // END_GLOW_TUNING_ZONE ga.iter().map(|&a| a as u8).collect::>() }; for i in 0..px { let a = glow_alpha[i]; if a > 0 { buf[i * 4] = color[0]; buf[i * 4 + 1] = color[1]; buf[i * 4 + 2] = color[2]; buf[i * 4 + 3] = a; } } buf }