Skip to main content

vello_cpu/filter/
flood.rs

1// Copyright 2025 the Vello Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! Flood filter implementation.
5
6use super::FilterEffect;
7use crate::layer_manager::LayerManager;
8use vello_common::color::{AlphaColor, Srgb};
9use vello_common::peniko::color::PremulRgba8;
10use vello_common::pixmap::Pixmap;
11
12pub(crate) struct Flood {
13    pub color: AlphaColor<Srgb>,
14}
15
16impl Flood {
17    /// Create a new flood filter with the specified color.
18    pub(crate) fn new(color: AlphaColor<Srgb>) -> Self {
19        Self { color }
20    }
21}
22
23impl FilterEffect for Flood {
24    fn execute_lowp(&self, pixmap: &mut Pixmap, _layer_manager: &mut LayerManager) {
25        // Convert AlphaColor to u8 [0-255] range
26        let flood_r = (self.color.components[0] * 255.0) as u8;
27        let flood_g = (self.color.components[1] * 255.0) as u8;
28        let flood_b = (self.color.components[2] * 255.0) as u8;
29        let flood_a = (self.color.components[3] * 255.0) as u8;
30
31        // Premultiply RGB by alpha for PremulRgba8
32        let premul_r = ((flood_r as u16 * flood_a as u16) / 255) as u8;
33        let premul_g = ((flood_g as u16 * flood_a as u16) / 255) as u8;
34        let premul_b = ((flood_b as u16 * flood_a as u16) / 255) as u8;
35
36        let flood_color = PremulRgba8 {
37            r: premul_r,
38            g: premul_g,
39            b: premul_b,
40            a: flood_a,
41        };
42
43        // Fill ALL pixels with flood color (entire subregion)
44        pixmap.data_mut().fill(flood_color);
45    }
46
47    fn execute_highp(&self, pixmap: &mut Pixmap, _layer_manager: &mut LayerManager) {
48        let flood_r = self.color.components[0];
49        let flood_g = self.color.components[1];
50        let flood_b = self.color.components[2];
51        let flood_a = self.color.components[3];
52
53        // Premultiply RGB by alpha for PremulRgba8
54        let flood_color = PremulRgba8 {
55            r: (flood_r * flood_a * 255.0) as u8,
56            g: (flood_g * flood_a * 255.0) as u8,
57            b: (flood_b * flood_a * 255.0) as u8,
58            a: (flood_a * 255.0) as u8,
59        };
60
61        // Fill ALL pixels with flood color (entire subregion)
62        pixmap.data_mut().fill(flood_color);
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use crate::layer_manager::LayerManager;
70    use vello_common::color::Srgb;
71
72    /// Test flood with semi-transparent color - verifies correct premultiplication.
73    #[test]
74    fn test_flood_semi_transparent_lowp() {
75        let mut pixmap = Pixmap::new(2, 2);
76        let mut layer_manager = LayerManager::new();
77
78        // Semi-transparent white (50% alpha)
79        let color = AlphaColor {
80            components: [1.0, 1.0, 1.0, 0.5],
81            cs: std::marker::PhantomData::<Srgb>,
82        };
83        let flood = Flood::new(color);
84        flood.execute_lowp(&mut pixmap, &mut layer_manager);
85
86        // RGB should be premultiplied by alpha: 255 * 0.5 = 127-128
87        for y in 0..2 {
88            for x in 0..2 {
89                let pixel = pixmap.sample(x, y);
90                assert_eq!(
91                    pixel,
92                    PremulRgba8 {
93                        r: 127,
94                        g: 127,
95                        b: 127,
96                        a: 127
97                    }
98                );
99            }
100        }
101    }
102
103    /// Test flood highp with semi-transparent color - verifies correct premultiplication.
104    #[test]
105    fn test_flood_semi_transparent_highp() {
106        let mut pixmap = Pixmap::new(2, 2);
107        let mut layer_manager = LayerManager::new();
108
109        // Semi-transparent white (50% alpha)
110        let color = AlphaColor {
111            components: [1.0, 1.0, 1.0, 0.5],
112            cs: std::marker::PhantomData::<Srgb>,
113        };
114        let flood = Flood::new(color);
115        flood.execute_highp(&mut pixmap, &mut layer_manager);
116
117        // RGB should be premultiplied by alpha: 255 * 0.5 = 127-128
118        for y in 0..2 {
119            for x in 0..2 {
120                let pixel = pixmap.sample(x, y);
121                assert_eq!(
122                    pixel,
123                    PremulRgba8 {
124                        r: 127,
125                        g: 127,
126                        b: 127,
127                        a: 127
128                    }
129                );
130            }
131        }
132    }
133}