Files
hcie-rust-v3.05/hcie-fx/src/inner_shadow.rs
T

90 lines
2.7 KiB
Rust

/// Inner Shadow effect generator.
///
/// Renders an inner shadow by offsetting the inverted layer alpha,
/// blurring, and masking with the original alpha. MAE-optimized
/// parameters for Photoshop-compatible output.
use crate::helpers::{apply_spread, box_blur_f32};
/// Generate an inner shadow buffer.
///
/// **Algorithm:**
/// 1. Invert layer alpha (255 - alpha)
/// 2. Offset inverted alpha by (distance, angle)
/// 3. Box blur 2-pass (blur_factor=10.0)
/// 4. Mask with original alpha (mask_strength=0.50)
/// 5. Fill with shadow color
///
/// **MAE-optimized parameters** (from grid search on 1536 PSD samples):
/// - blur_factor = 10.0
/// - passes = 2
/// - choke_scale = 0.0 (choke disabled)
/// - mask_strength = 0.50
///
/// **Arguments:**
/// * `alpha` — Layer alpha channel
/// * `w`, `h` — Canvas dimensions
/// * `angle` — Shadow angle in degrees
/// * `distance` — Shadow offset distance in pixels
/// * `choke` — Choke amount (currently unused, kept for API compatibility)
/// * `size` — Blur radius
/// * `color` — Shadow RGBA color
///
/// **Returns:**
/// RGBA buffer with the rendered inner shadow.
pub(crate) fn generate_inner_shadow(
alpha: &[u8],
w: u32,
h: u32,
angle: f32,
distance: f32,
choke: f32,
size: f32,
color: [u8; 4],
) -> Vec<u8> {
let px = (w * h) as usize;
let mut buf = vec![0u8; px * 4];
let rad = angle.to_radians();
let dx_raw = (-rad.cos() * distance).round() as i32;
let dy_raw = (rad.sin() * distance).round() as i32;
let mut offset_alpha = vec![255u8; px];
for y in 0..h as i32 {
for x in 0..w as i32 {
let orig_i = (y as u32 * w + x as u32) as usize;
let val = 255 - alpha[orig_i];
let dx = x + dx_raw;
let dy = y + dy_raw;
if dx >= 0 && dx < w as i32 && dy >= 0 && dy < h as i32 {
let di = (dy as u32 * w + dx as u32) as usize;
offset_alpha[di] = val;
}
}
}
apply_spread(&mut offset_alpha, choke);
let radius = size.max(0.0).round() as i32;
if radius > 0 {
let gaussian_r = (radius as f32 / 10.0).round().max(1.0) as i32;
let mut f32_buf: Vec<f32> = offset_alpha.iter().map(|&a| a as f32).collect();
f32_buf = box_blur_f32(&f32_buf, w, h, gaussian_r);
f32_buf = box_blur_f32(&f32_buf, w, h, gaussian_r);
offset_alpha = f32_buf.iter().map(|&a| (a as u8).min(255)).collect();
}
for i in 0..px {
let a = ((offset_alpha[i] as f32 * 0.50) as u32 * alpha[i] as u32 / 255) as u8;
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
}