vello_common/
strip_generator.rs

1// Copyright 2025 the Vello Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! Abstraction for generating strips from paths.
5
6use crate::fearless_simd::Level;
7use crate::flatten::{FlattenCtx, Line};
8use crate::kurbo::{Affine, PathEl, Stroke};
9use crate::peniko::Fill;
10use crate::strip::Strip;
11use crate::tile::Tiles;
12use crate::{flatten, strip};
13use alloc::vec::Vec;
14
15/// An object for easily generating strips for a filled/stroked path.
16#[derive(Debug)]
17pub struct StripGenerator {
18    level: Level,
19    alphas: Vec<u8>,
20    line_buf: Vec<Line>,
21    flatten_ctx: FlattenCtx,
22    tiles: Tiles,
23    strip_buf: Vec<Strip>,
24    width: u16,
25    height: u16,
26}
27
28impl StripGenerator {
29    /// Create a new strip generator.
30    pub fn new(width: u16, height: u16, level: Level) -> Self {
31        Self {
32            alphas: Vec::new(),
33            level,
34            line_buf: Vec::new(),
35            tiles: Tiles::new(level),
36            strip_buf: Vec::new(),
37            flatten_ctx: FlattenCtx::default(),
38            width,
39            height,
40        }
41    }
42
43    /// Generate the strips for a filled path.
44    pub fn generate_filled_path<'a>(
45        &'a mut self,
46        path: impl IntoIterator<Item = PathEl>,
47        fill_rule: Fill,
48        transform: Affine,
49        anti_alias: bool,
50        func: impl FnOnce(&'a [Strip]),
51    ) {
52        flatten::fill(
53            self.level,
54            path,
55            transform,
56            &mut self.line_buf,
57            &mut self.flatten_ctx,
58        );
59        self.make_strips(fill_rule, anti_alias);
60        func(&mut self.strip_buf);
61    }
62
63    /// Generate the strips for a stroked path.
64    pub fn generate_stroked_path<'a>(
65        &'a mut self,
66        path: impl IntoIterator<Item = PathEl>,
67        stroke: &Stroke,
68        transform: Affine,
69        anti_alias: bool,
70        func: impl FnOnce(&'a [Strip]),
71    ) {
72        flatten::stroke(
73            self.level,
74            path,
75            stroke,
76            transform,
77            &mut self.line_buf,
78            &mut self.flatten_ctx,
79        );
80        self.make_strips(Fill::NonZero, anti_alias);
81        func(&mut self.strip_buf);
82    }
83
84    /// Return a reference to the current alpha buffer of the strip generator.
85    pub fn alpha_buf(&self) -> &[u8] {
86        &self.alphas
87    }
88
89    /// Extend the alpha buffer with the given alphas.
90    pub fn extend_alpha_buf(&mut self, alphas: &[u8]) {
91        self.alphas.extend_from_slice(alphas);
92    }
93
94    /// Set the alpha buffer.
95    pub fn set_alpha_buf(&mut self, alpha_buf: Vec<u8>) {
96        self.alphas = alpha_buf;
97    }
98
99    /// Take the alpha buffer and set it to an empty one.
100    pub fn take_alpha_buf(&mut self) -> Vec<u8> {
101        core::mem::take(&mut self.alphas)
102    }
103
104    /// Swap the alpha buffer with the given one.
105    pub fn replace_alpha_buf(&mut self, alphas: Vec<u8>) -> Vec<u8> {
106        core::mem::replace(&mut self.alphas, alphas)
107    }
108
109    /// Reset the strip generator.
110    pub fn reset(&mut self) {
111        self.line_buf.clear();
112        self.tiles.reset();
113        self.alphas.clear();
114        self.strip_buf.clear();
115    }
116
117    fn make_strips(&mut self, fill_rule: Fill, anti_alias: bool) {
118        self.tiles
119            .make_tiles(&self.line_buf, self.width, self.height);
120        self.tiles.sort_tiles();
121        strip::render(
122            self.level,
123            &self.tiles,
124            &mut self.strip_buf,
125            &mut self.alphas,
126            fill_rule,
127            anti_alias,
128            &self.line_buf,
129        );
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use crate::fearless_simd::Level;
136    use crate::kurbo::{Affine, Rect, Shape};
137    use crate::peniko::Fill;
138    use crate::strip_generator::StripGenerator;
139
140    #[test]
141    fn reset_strip_generator() {
142        let mut generator = StripGenerator::new(100, 100, Level::fallback());
143        let rect = Rect::new(0.0, 0.0, 100.0, 100.0);
144
145        generator.generate_filled_path(
146            rect.to_path(0.1),
147            Fill::NonZero,
148            Affine::IDENTITY,
149            true,
150            |_| {},
151        );
152
153        assert!(!generator.line_buf.is_empty());
154        assert!(!generator.strip_buf.is_empty());
155        assert!(!generator.alphas.is_empty());
156
157        generator.reset();
158
159        assert!(generator.line_buf.is_empty());
160        assert!(generator.strip_buf.is_empty());
161        assert!(generator.alphas.is_empty());
162    }
163}