1use core::convert::TryFrom;
8
9use tiny_skia_path::SaturateCast;
10
11use crate::{FillRule, IntRect, LengthU32, Path, Rect};
12
13use crate::blitter::Blitter;
14use crate::edge::{Edge, LineEdge};
15use crate::edge_builder::{BasicEdgeBuilder, ShiftedIntRect};
16use crate::fixed_point::{fdot16, fdot6, FDot16};
17use crate::geom::{IntRectExt, ScreenIntRect};
18
19#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
20use tiny_skia_path::NoStdFloat;
21
22pub fn fill_path(
23 path: &Path,
24 fill_rule: FillRule,
25 clip: &ScreenIntRect,
26 blitter: &mut dyn Blitter,
27) {
28 let ir = match conservative_round_to_int(&path.bounds()) {
29 Some(v) => v,
30 None => return,
31 };
32
33 let path_contained_in_clip = if let Some(bounds) = ir.to_screen_int_rect() {
34 clip.contains(&bounds)
35 } else {
36 false
39 };
40
41 fill_path_impl(
44 path,
45 fill_rule,
46 clip,
47 ir.y(),
48 ir.bottom(),
49 0,
50 path_contained_in_clip,
51 blitter,
52 );
53}
54
55fn conservative_round_to_int(src: &Rect) -> Option<IntRect> {
62 IntRect::from_ltrb(
64 round_down_to_int(src.left()),
65 round_down_to_int(src.top()),
66 round_up_to_int(src.right()),
67 round_up_to_int(src.bottom()),
68 )
69}
70
71const CONSERVATIVE_ROUND_BIAS: f64 = 0.5 + 1.5 / fdot6::ONE as f64;
80
81fn round_down_to_int(x: f32) -> i32 {
86 let mut xx = x as f64;
87 xx -= CONSERVATIVE_ROUND_BIAS;
88 i32::saturate_from(xx.ceil())
89}
90
91fn round_up_to_int(x: f32) -> i32 {
95 let mut xx = x as f64;
96 xx += CONSERVATIVE_ROUND_BIAS;
97 i32::saturate_from(xx.floor())
98}
99
100pub fn fill_path_impl(
101 path: &Path,
102 fill_rule: FillRule,
103 clip_rect: &ScreenIntRect,
104 mut start_y: i32,
105 mut stop_y: i32,
106 shift_edges_up: i32,
107 path_contained_in_clip: bool,
108 blitter: &mut dyn Blitter,
109) {
110 let shifted_clip = match ShiftedIntRect::new(clip_rect, shift_edges_up) {
111 Some(v) => v,
112 None => return,
113 };
114
115 let clip = if path_contained_in_clip {
116 None
117 } else {
118 Some(&shifted_clip)
119 };
120 let mut edges = match BasicEdgeBuilder::build_edges(path, clip, shift_edges_up) {
121 Some(v) => v,
122 None => return, };
124
125 edges.sort_by(|a, b| {
126 let mut value_a = a.as_line().first_y;
127 let mut value_b = b.as_line().first_y;
128
129 if value_a == value_b {
130 value_a = a.as_line().x;
131 value_b = b.as_line().x;
132 }
133
134 value_a.cmp(&value_b)
135 });
136
137 for i in 0..edges.len() {
138 edges[i].prev = Some(i as u32 + 0);
140 edges[i].next = Some(i as u32 + 2);
141 }
142
143 const EDGE_HEAD_Y: i32 = i32::MIN;
144 const EDGE_TAIL_Y: i32 = i32::MAX;
145
146 edges.insert(
147 0,
148 Edge::Line(LineEdge {
149 prev: None,
150 next: Some(1),
151 x: i32::MIN,
152 first_y: EDGE_HEAD_Y,
153 ..LineEdge::default()
154 }),
155 );
156
157 edges.push(Edge::Line(LineEdge {
158 prev: Some(edges.len() as u32 - 1),
159 next: None,
160 first_y: EDGE_TAIL_Y,
161 ..LineEdge::default()
162 }));
163
164 start_y <<= shift_edges_up;
165 stop_y <<= shift_edges_up;
166
167 let top = shifted_clip.shifted().y() as i32;
168 if !path_contained_in_clip && start_y < top {
169 start_y = top;
170 }
171
172 let bottom = shifted_clip.shifted().bottom() as i32;
173 if !path_contained_in_clip && stop_y > bottom {
174 stop_y = bottom;
175 }
176
177 let start_y = match u32::try_from(start_y) {
178 Ok(v) => v,
179 Err(_) => return,
180 };
181 let stop_y = match u32::try_from(stop_y) {
182 Ok(v) => v,
183 Err(_) => return,
184 };
185
186 walk_edges(
189 fill_rule,
190 start_y,
191 stop_y,
192 shifted_clip.shifted().right(),
193 &mut edges,
194 blitter,
195 );
196}
197
198fn walk_edges(
200 fill_rule: FillRule,
201 start_y: u32,
202 stop_y: u32,
203 right_clip: u32,
204 edges: &mut [Edge],
205 blitter: &mut dyn Blitter,
206) {
207 let mut curr_y = start_y;
208 let winding_mask = if fill_rule == FillRule::EvenOdd {
209 1
210 } else {
211 -1
212 };
213
214 loop {
215 let mut w = 0i32;
216 let mut left = 0u32;
217 let mut prev_x = edges[0].x;
218
219 let mut curr_idx = edges[0].next.unwrap() as usize;
220 while edges[curr_idx].first_y <= curr_y as i32 {
221 debug_assert!(edges[curr_idx].last_y >= curr_y as i32);
222
223 let x = fdot16::round_to_i32(edges[curr_idx].x) as u32; if (w & winding_mask) == 0 {
226 left = x;
228 }
229
230 w += i32::from(edges[curr_idx].winding);
231
232 if (w & winding_mask) == 0 {
233 if let Some(width) = LengthU32::new(x - left) {
235 blitter.blit_h(left, curr_y, width);
236 }
237 }
238
239 let next_idx = edges[curr_idx].next.unwrap();
240 let new_x;
241
242 if edges[curr_idx].last_y == curr_y as i32 {
243 match &mut edges[curr_idx] {
245 Edge::Line(_) => {
246 remove_edge(curr_idx, edges);
247 }
248 Edge::Quadratic(ref mut quad) => {
249 if quad.curve_count > 0 && quad.update() {
250 new_x = quad.line.x;
251
252 if new_x < prev_x {
253 backward_insert_edge_based_on_x(curr_idx, edges);
255 } else {
256 prev_x = new_x;
257 }
258 } else {
259 remove_edge(curr_idx, edges);
260 }
261 }
262 Edge::Cubic(ref mut cubic) => {
263 if cubic.curve_count < 0 && cubic.update() {
264 debug_assert!(cubic.line.first_y == curr_y as i32 + 1);
265
266 new_x = cubic.line.x;
267
268 if new_x < prev_x {
269 backward_insert_edge_based_on_x(curr_idx, edges);
271 } else {
272 prev_x = new_x;
273 }
274 } else {
275 remove_edge(curr_idx, edges);
276 }
277 }
278 }
279 } else {
280 debug_assert!(edges[curr_idx].last_y > curr_y as i32);
281 new_x = edges[curr_idx].x + edges[curr_idx].dx;
282 edges[curr_idx].x = new_x;
283
284 if new_x < prev_x {
285 backward_insert_edge_based_on_x(curr_idx, edges);
287 } else {
288 prev_x = new_x;
289 }
290 }
291
292 curr_idx = next_idx as usize;
293 }
294
295 if (w & winding_mask) != 0 {
296 if let Some(width) = LengthU32::new(right_clip - left) {
298 blitter.blit_h(left, curr_y, width);
299 }
300 }
301
302 curr_y += 1;
303 if curr_y >= stop_y {
304 break;
305 }
306
307 insert_new_edges(curr_idx, curr_y as i32, edges);
309 }
310}
311
312fn remove_edge(curr_idx: usize, edges: &mut [Edge]) {
313 let prev = edges[curr_idx].prev.unwrap();
314 let next = edges[curr_idx].next.unwrap();
315
316 edges[prev as usize].next = Some(next);
317 edges[next as usize].prev = Some(prev);
318}
319
320fn backward_insert_edge_based_on_x(curr_idx: usize, edges: &mut [Edge]) {
321 let x = edges[curr_idx].x;
322 let mut prev_idx = edges[curr_idx].prev.unwrap() as usize;
323 while prev_idx != 0 {
324 if edges[prev_idx].x > x {
325 prev_idx = edges[prev_idx].prev.unwrap() as usize;
326 } else {
327 break;
328 }
329 }
330
331 let next_idx = edges[prev_idx].next.unwrap() as usize;
332 if next_idx != curr_idx {
333 remove_edge(curr_idx, edges);
334 insert_edge_after(curr_idx, prev_idx, edges);
335 }
336}
337
338fn insert_edge_after(curr_idx: usize, after_idx: usize, edges: &mut [Edge]) {
339 edges[curr_idx].prev = Some(after_idx as u32);
340 edges[curr_idx].next = edges[after_idx].next;
341
342 let after_next_idx = edges[after_idx].next.unwrap() as usize;
343 edges[after_next_idx].prev = Some(curr_idx as u32);
344 edges[after_idx].next = Some(curr_idx as u32);
345}
346
347fn backward_insert_start(mut prev_idx: usize, x: FDot16, edges: &mut [Edge]) -> usize {
352 while let Some(prev) = edges[prev_idx].prev {
353 prev_idx = prev as usize;
354 if edges[prev_idx].x <= x {
355 break;
356 }
357 }
358
359 prev_idx
360}
361
362fn insert_new_edges(mut new_idx: usize, curr_y: i32, edges: &mut [Edge]) {
363 if edges[new_idx].first_y != curr_y {
364 return;
365 }
366
367 let prev_idx = edges[new_idx].prev.unwrap() as usize;
368 if edges[prev_idx].x <= edges[new_idx].x {
369 return;
370 }
371
372 let mut start_idx = backward_insert_start(prev_idx, edges[new_idx].x, edges);
374 loop {
376 let next_idx = edges[new_idx].next.unwrap() as usize;
377 let mut keep_edge = false;
378 loop {
379 let after_idx = edges[start_idx].next.unwrap() as usize;
380 if after_idx == new_idx {
381 keep_edge = true;
382 break;
383 }
384
385 if edges[after_idx].x >= edges[new_idx].x {
386 break;
387 }
388
389 start_idx = after_idx;
390 }
391
392 if !keep_edge {
393 remove_edge(new_idx, edges);
394 insert_edge_after(new_idx, start_idx, edges);
395 }
396
397 start_idx = new_idx;
398 new_idx = next_idx;
399
400 if edges[new_idx].first_y != curr_y {
401 break;
402 }
403 }
404}