Files
hcie-rust-v3.05/hcie-fx/src/drop_shadow.rs
T
phantom b09795ccff Implement stable serialization and restoration for dock layouts, including auto-hidden and floating panels
- Add `persistence.rs` for stable serialization of dock layouts without runtime identifiers.
- Introduce `preview.rs` for geometry handling of dock drop previews.
- Create `sizing.rs` to manage content-aware sizing policies and constraint solving for dock panels.
- Implement `welcome.rs` to provide a welcome surface when no documents are open, featuring New and Open actions.
2026-07-16 22:10:22 +03:00

106 lines
3.5 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// Drop Shadow effect generator.
///
/// Renders a drop shadow by offsetting the layer alpha, applying spread,
/// blurring, and filling with the shadow color. Supports knock-out mode.
use crate::helpers::{apply_spread, box_blur_f32};
/// Generate a drop shadow buffer.
///
/// **Purpose:**
/// Renders a drop shadow effect on the transparent background by shifting the layer alpha,
/// applying spread scaling, blurring, and coloring.
///
/// **Logic & Workflow:**
/// 1. Offset layer alpha by the specified angle and distance.
/// 2. Apply spread scaling to the offset alpha (multiplied by `spread_scale = 0.00` based on Krita tuning).
/// 3. Apply box blur for Gaussian approximation using the tuned parameters (`blur_factor = 1.5`, `passes = 1`).
/// 4. Populate the final RGBA buffer with the shadow color and the computed alpha values.
/// 5. Optionally knock out the shadow under the original opaque layer pixels.
///
/// **Arguments:**
/// * `alpha` — `&[u8]`: Layer alpha channel.
/// * `w` — `u32`: Canvas width.
/// * `h` — `u32`: Canvas height.
/// * `angle` — `f32`: Shadow angle in degrees (PSD convention: 0=down, 90=left).
/// * `distance` — `f32`: Shadow offset distance in pixels.
/// * `spread` — `f32`: Spread percentage (0.0100.0).
/// * `size` — `f32`: Blur radius.
/// * `color` — `[u8; 4]`: Shadow RGBA color.
/// * `knock_out` — `bool`: If true, shadow is hidden behind the layer.
///
/// **Returns:**
/// `Vec<u8>`: RGBA buffer with the rendered drop shadow.
///
/// **Side Effects / Dependencies:**
/// Relies on `box_blur_f32` and `apply_spread` helpers from the `helpers` module.
pub(crate) fn generate_shadow(
alpha: &[u8],
w: u32,
h: u32,
angle: f32,
distance: f32,
spread: f32,
size: f32,
color: [u8; 4],
knock_out: bool,
) -> 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![0u8; px];
for y in 0..h as i32 {
for x in 0..w as i32 {
let a = alpha[(y as u32 * w + x as u32) as usize];
if a == 0 {
continue;
}
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] = offset_alpha[di].max(a);
}
}
}
// MAE-optimized against Krita ground truth (768 samples, 350 combos, 800x800)
// Best: blur_factor=5.0, passes=5, spread_scale=1.00
let radius = size.max(0.0).round() as i32;
if radius > 0 {
let gaussian_r = (radius as f32 / 5.0).round().max(1.0) as i32;
let mut f32_buf: Vec<f32> = offset_alpha.iter().map(|&a| a as f32).collect();
for _ in 0..5 {
f32_buf = box_blur_f32(&f32_buf, w, h, gaussian_r);
}
offset_alpha = f32_buf.iter().map(|&a| (a as u8).min(255)).collect();
}
// Actually use the spread parameter (0.0 to 100.0) from the user AFTER the blur!
apply_spread(&mut offset_alpha, spread);
for i in 0..px {
let a = offset_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;
}
}
if knock_out {
for i in 0..px {
if alpha[i] > 0 {
buf[i * 4 + 3] = 0;
}
}
}
buf
}