b09795ccff
- 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.
106 lines
3.5 KiB
Rust
106 lines
3.5 KiB
Rust
/// 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.0–100.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
|
||
}
|