1use crate::clip::{PathDataRef, intersect};
7use crate::fearless_simd::Level;
8use crate::flatten::{FlattenCtx, Line};
9use crate::kurbo::{Affine, PathEl, Stroke};
10use crate::peniko::Fill;
11use crate::strip::Strip;
12use crate::tile::Tiles;
13use crate::{flatten, strip};
14use alloc::vec::Vec;
15use peniko::kurbo::StrokeCtx;
16
17#[derive(Debug, Default, PartialEq, Eq)]
19pub struct StripStorage {
20 pub strips: Vec<Strip>,
22 pub alphas: Vec<u8>,
24 generation_mode: GenerationMode,
25}
26
27#[derive(Debug, Default, Copy, Clone, PartialEq, Eq)]
29pub enum GenerationMode {
30 #[default]
31 Replace,
33 Append,
35}
36
37impl StripStorage {
38 pub fn clear(&mut self) {
40 self.strips.clear();
41 self.alphas.clear();
42 }
43
44 pub fn set_generation_mode(&mut self, mode: GenerationMode) {
46 self.generation_mode = mode;
47 }
48
49 pub fn is_empty(&self) -> bool {
51 self.strips.is_empty() && self.alphas.is_empty()
52 }
53
54 pub fn extend(&mut self, other: &Self) {
56 self.strips.extend(&other.strips);
57 self.alphas.extend(&other.alphas);
58 }
59}
60
61#[derive(Debug)]
63pub struct StripGenerator {
64 pub(crate) level: Level,
65 line_buf: Vec<Line>,
66 flatten_ctx: FlattenCtx,
67 stroke_ctx: StrokeCtx,
68 temp_storage: StripStorage,
69 tiles: Tiles,
70 width: u16,
71 height: u16,
72}
73
74impl StripGenerator {
75 pub fn new(width: u16, height: u16, level: Level) -> Self {
77 Self {
78 level,
79 line_buf: Vec::new(),
80 tiles: Tiles::new(level),
81 flatten_ctx: FlattenCtx::default(),
82 stroke_ctx: StrokeCtx::default(),
83 temp_storage: StripStorage::default(),
84 width,
85 height,
86 }
87 }
88
89 pub fn generate_filled_path(
91 &mut self,
92 path: impl IntoIterator<Item = PathEl>,
93 fill_rule: Fill,
94 transform: Affine,
95 aliasing_threshold: Option<u8>,
96 strip_storage: &mut StripStorage,
97 clip_path: Option<PathDataRef<'_>>,
98 ) {
99 flatten::fill(
100 self.level,
101 path,
102 transform,
103 &mut self.line_buf,
104 &mut self.flatten_ctx,
105 );
106
107 self.generate_with_clip(aliasing_threshold, strip_storage, fill_rule, clip_path);
108 }
109
110 pub fn generate_stroked_path(
112 &mut self,
113 path: impl IntoIterator<Item = PathEl>,
114 stroke: &Stroke,
115 transform: Affine,
116 aliasing_threshold: Option<u8>,
117 strip_storage: &mut StripStorage,
118 clip_path: Option<PathDataRef<'_>>,
119 ) {
120 flatten::stroke(
121 self.level,
122 path,
123 stroke,
124 transform,
125 &mut self.line_buf,
126 &mut self.flatten_ctx,
127 &mut self.stroke_ctx,
128 );
129 self.generate_with_clip(aliasing_threshold, strip_storage, Fill::NonZero, clip_path);
130 }
131
132 fn generate_with_clip(
133 &mut self,
134 aliasing_threshold: Option<u8>,
135 strip_storage: &mut StripStorage,
136 fill_rule: Fill,
137 clip_path: Option<PathDataRef<'_>>,
138 ) {
139 if strip_storage.generation_mode == GenerationMode::Replace {
140 strip_storage.strips.clear();
141 }
142
143 self.tiles
144 .make_tiles_analytic_aa(&self.line_buf, self.width, self.height);
145 self.tiles.sort_tiles();
146
147 if let Some(clip_path) = clip_path {
148 self.temp_storage.clear();
149
150 strip::render(
151 self.level,
152 &self.tiles,
153 &mut self.temp_storage.strips,
154 &mut self.temp_storage.alphas,
155 fill_rule,
156 aliasing_threshold,
157 &self.line_buf,
158 );
159 let path_data = PathDataRef {
160 strips: &self.temp_storage.strips,
161 alphas: &self.temp_storage.alphas,
162 };
163
164 intersect(self.level, clip_path, path_data, strip_storage);
165 } else {
166 strip::render(
167 self.level,
168 &self.tiles,
169 &mut strip_storage.strips,
170 &mut strip_storage.alphas,
171 fill_rule,
172 aliasing_threshold,
173 &self.line_buf,
174 );
175 }
176 }
177
178 pub fn reset(&mut self) {
180 self.line_buf.clear();
181 self.tiles.reset();
182 self.temp_storage.clear();
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use crate::fearless_simd::Level;
189 use crate::kurbo::{Affine, Rect, Shape};
190 use crate::peniko::Fill;
191 use crate::strip_generator::{StripGenerator, StripStorage};
192
193 #[test]
194 fn reset() {
195 let mut generator = StripGenerator::new(100, 100, Level::fallback());
196 let mut storage = StripStorage::default();
197 let rect = Rect::new(0.0, 0.0, 100.0, 100.0);
198
199 generator.generate_filled_path(
200 rect.to_path(0.1),
201 Fill::NonZero,
202 Affine::IDENTITY,
203 None,
204 &mut storage,
205 None,
206 );
207
208 assert!(!generator.line_buf.is_empty());
209 assert!(!storage.is_empty());
210
211 generator.reset();
212 storage.clear();
213
214 assert!(generator.line_buf.is_empty());
215 assert!(storage.is_empty());
216 }
217}