Skip to main content

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}