vello_common/filter/drop_shadow.rs
1// Copyright 2026 the Vello Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! The drop shadow filter.
5
6use crate::color::{AlphaColor, Srgb};
7use crate::filter::gaussian_blur::{MAX_KERNEL_SIZE, plan_decimated_blur, transform_blur_params};
8use crate::filter::transform_offset_params;
9use crate::filter_effects::EdgeMode;
10use crate::kurbo::Affine;
11
12/// A drop shadow filter.
13#[derive(Debug)]
14pub struct DropShadow {
15 /// The x-offset of the shadow.
16 pub dx: f32,
17 /// The y-offset of the shadow.
18 pub dy: f32,
19 /// The color of the shadow.
20 pub color: AlphaColor<Srgb>,
21 /// Standard deviation for the blur (for reference/debugging).
22 pub std_deviation: f32,
23 /// Edge mode for blur sampling.
24 pub edge_mode: EdgeMode,
25 /// Whether to composite the original input over the colored shadow.
26 pub composite_original: bool,
27 /// Number of 2x2 decimation levels to use (0 means direct convolution).
28 pub n_decimations: usize,
29 /// Pre-computed Gaussian kernel weights for the reduced blur.
30 /// Only the first `kernel_size` elements are valid.
31 pub kernel: [f32; MAX_KERNEL_SIZE],
32 /// Actual length of the kernel (kernel is padded to `MAX_KERNEL_SIZE`).
33 pub kernel_size: u8,
34}
35
36impl DropShadow {
37 /// Create a new drop shadow filter with the specified parameters.
38 ///
39 /// This precomputes the blur decimation plan and kernel for optimal performance.
40 pub fn new(
41 dx: f32,
42 dy: f32,
43 std_deviation: f32,
44 edge_mode: EdgeMode,
45 color: AlphaColor<Srgb>,
46 ) -> Self {
47 Self::new_impl(dx, dy, std_deviation, edge_mode, color, true)
48 }
49
50 /// Create a new shadow-only drop shadow with the specified parameters.
51 pub fn new_shadow_only(
52 dx: f32,
53 dy: f32,
54 std_deviation: f32,
55 edge_mode: EdgeMode,
56 color: AlphaColor<Srgb>,
57 ) -> Self {
58 Self::new_impl(dx, dy, std_deviation, edge_mode, color, false)
59 }
60
61 fn new_impl(
62 dx: f32,
63 dy: f32,
64 std_deviation: f32,
65 edge_mode: EdgeMode,
66 color: AlphaColor<Srgb>,
67 composite_original: bool,
68 ) -> Self {
69 // Precompute blur plan (same logic as GaussianBlur::new)
70 let (n_decimations, kernel, kernel_size) = plan_decimated_blur(std_deviation);
71
72 Self {
73 dx,
74 dy,
75 color,
76 std_deviation,
77 edge_mode,
78 composite_original,
79 n_decimations,
80 kernel,
81 kernel_size,
82 }
83 }
84}
85
86/// Transform a drop shadow's offset and standard deviation using the affine transformation.
87///
88/// Applies the full linear transformation (rotation, scale, and shear) to the offset vector,
89/// and scales the blur standard deviation uniformly.
90///
91/// # Arguments
92/// * `dx` - Horizontal offset in user space
93/// * `dy` - Vertical offset in user space
94/// * `std_deviation` - Blur standard deviation in user space
95/// * `transform` - The transformation matrix to apply
96///
97/// # Returns
98/// A tuple of (`scaled_dx`, `scaled_dy`, `scaled_std_dev`) in device space
99pub(crate) fn transform_shadow_params(
100 dx: f32,
101 dy: f32,
102 std_deviation: f32,
103 transform: &Affine,
104) -> (f32, f32, f32) {
105 let (scaled_dx, scaled_dy) = transform_offset_params(dx, dy, transform);
106
107 // Scale the blur radius uniformly
108 let scaled_std_dev = transform_blur_params(std_deviation, transform);
109
110 (scaled_dx, scaled_dy, scaled_std_dev)
111}