1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
//! Row-parallel iteration over a pixel buffer.
//!
//! Single entry point [`par_rows`]: takes a contiguous `width × height` `u32` pixel buffer, a
//! row range (clip-aware), and a closure invoked per row. With the `rayon` feature on, rows are
//! dispatched across the rayon thread pool; off, the same iteration runs sequentially. The
//! closure signature is identical either way — rasterizers and boundary passes don't branch on
//! the feature gate, they just call this wrapper and the right thing happens.
//!
//! ## Why `Send + Sync` on the closure
//!
//! When rayon is on, rows run on different worker threads and the closure must be safe to call
//! from multiple threads concurrently — that's `Send + Sync`. Rasterizer closures are tiny
//! captures of small `f32` / `u32` / packed-pixel parameters with no interior mutability, so
//! the bound is satisfied trivially. We require it unconditionally (even in sequential builds)
//! so the call-site code is identical across feature combos — no `#[cfg]` per call.
/// Iterate `pixels` row-by-row over `row_start..row_end`, calling `f(row_index, &mut row_slice)`
/// for each row. Each `row_slice` is exactly `width` `u32`s. With `rayon` enabled, rows are
/// dispatched in parallel; without it, sequentially.
///
/// Pre-conditions: `pixels.len() >= row_end * width` and `row_start <= row_end`. No internal
/// clamping — the caller (a clipped rasterizer or a boundary pass) is responsible for shaping
/// the range to fit the buffer.
/// Iterate a flat `pixels` slice in equal-sized chunks of `chunk_len` (handles trailing tail of
/// any length). Closure receives `(chunk_offset_in_pixels, &mut chunk_slice)`. Same Rayon-vs-
/// sequential dispatch as [`par_rows`] — used for whole-buffer passes (finalize, flatten,
/// Op::Under) where row geometry doesn't matter and chunking by a multiple of cache-line size
/// gives the best throughput.