Skip to main content

agg_rust/
scanline_u.rs

1//! Unpacked scanline container (ScanlineU8).
2//!
3//! Port of `agg_scanline_u.h` — stores per-pixel coverage values in a flat
4//! array, with spans referencing into it. This is the most commonly used
5//! scanline type for anti-aliased rendering.
6
7use crate::rasterizer_scanline_aa::Scanline;
8
9// ============================================================================
10// ScanlineSpan — a horizontal run within a scanline
11// ============================================================================
12
13/// A horizontal span within a scanline, referencing coverage data.
14///
15/// For `ScanlineU8`: `len` is always positive, `cover_offset` indexes into
16/// the covers array.
17#[derive(Debug, Clone, Copy, Default)]
18pub struct ScanlineSpan {
19    pub x: i32,
20    pub len: i32,
21    pub cover_offset: usize,
22}
23
24// ============================================================================
25// ScanlineU8 — unpacked scanline with per-pixel coverage
26// ============================================================================
27
28/// Unpacked scanline container with per-pixel u8 coverage values.
29///
30/// Port of C++ `scanline_u8`. Each pixel in a span has its own coverage
31/// byte, stored in a flat array indexed by `x - min_x`.
32///
33/// Usage protocol:
34/// 1. `reset(min_x, max_x)` — allocate covers and spans arrays
35/// 2. `add_cell()` / `add_span()` — accumulate span data (X must be monotonically increasing)
36/// 3. `finalize(y)` — set the Y coordinate
37/// 4. Iterate with `begin()` and `covers()` for rendering
38/// 5. `reset_spans()` — prepare for next scanline
39pub struct ScanlineU8 {
40    min_x: i32,
41    last_x: i32,
42    y_val: i32,
43    covers: Vec<u8>,
44    spans: Vec<ScanlineSpan>,
45    cur_span: usize, // index of current span (0 = sentinel, spans start at 1)
46}
47
48impl ScanlineU8 {
49    pub fn new() -> Self {
50        Self {
51            min_x: 0,
52            last_x: 0x7FFF_FFF0,
53            y_val: 0,
54            covers: Vec::new(),
55            spans: Vec::new(),
56            cur_span: 0,
57        }
58    }
59
60    /// Prepare for a new scanline with the given X range.
61    pub fn reset(&mut self, min_x: i32, max_x: i32) {
62        let max_len = (max_x - min_x + 2) as usize;
63        if max_len > self.spans.len() {
64            self.spans.resize(max_len, ScanlineSpan::default());
65            self.covers.resize(max_len, 0);
66        }
67        self.last_x = 0x7FFF_FFF0;
68        self.min_x = min_x;
69        self.cur_span = 0;
70    }
71
72    /// Get the slice of active spans (for renderer iteration).
73    /// Spans are 1-indexed; index 0 is a sentinel.
74    pub fn begin(&self) -> &[ScanlineSpan] {
75        &self.spans[1..=self.cur_span]
76    }
77
78    /// Get the full covers array (spans reference into this via `cover_offset`).
79    pub fn covers(&self) -> &[u8] {
80        &self.covers
81    }
82}
83
84impl Scanline for ScanlineU8 {
85    fn reset_spans(&mut self) {
86        self.last_x = 0x7FFF_FFF0;
87        self.cur_span = 0;
88    }
89
90    fn add_cell(&mut self, x: i32, cover: u32) {
91        let xi = (x - self.min_x) as usize;
92        self.covers[xi] = cover as u8;
93        if xi as i32 == self.last_x + 1 {
94            self.spans[self.cur_span].len += 1;
95        } else {
96            self.cur_span += 1;
97            self.spans[self.cur_span].x = x;
98            self.spans[self.cur_span].len = 1;
99            self.spans[self.cur_span].cover_offset = xi;
100        }
101        self.last_x = xi as i32;
102    }
103
104    fn add_span(&mut self, x: i32, len: u32, cover: u32) {
105        let xi = (x - self.min_x) as usize;
106        // Fill covers with the same value
107        for i in 0..len as usize {
108            self.covers[xi + i] = cover as u8;
109        }
110        if xi as i32 == self.last_x + 1 {
111            self.spans[self.cur_span].len += len as i32;
112        } else {
113            self.cur_span += 1;
114            self.spans[self.cur_span].x = x;
115            self.spans[self.cur_span].len = len as i32;
116            self.spans[self.cur_span].cover_offset = xi;
117        }
118        self.last_x = xi as i32 + len as i32 - 1;
119    }
120
121    fn finalize(&mut self, y: i32) {
122        self.y_val = y;
123    }
124
125    fn num_spans(&self) -> u32 {
126        self.cur_span as u32
127    }
128
129    fn y(&self) -> i32 {
130        self.y_val
131    }
132}
133
134impl Default for ScanlineU8 {
135    fn default() -> Self {
136        Self::new()
137    }
138}
139
140// ============================================================================
141// Tests
142// ============================================================================
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    #[test]
149    fn test_new() {
150        let sl = ScanlineU8::new();
151        assert_eq!(sl.num_spans(), 0);
152        assert_eq!(sl.y(), 0);
153    }
154
155    #[test]
156    fn test_reset_and_add_cell() {
157        let mut sl = ScanlineU8::new();
158        sl.reset(0, 100);
159        sl.add_cell(10, 128);
160        assert_eq!(sl.num_spans(), 1);
161        let spans = sl.begin();
162        assert_eq!(spans[0].x, 10);
163        assert_eq!(spans[0].len, 1);
164        assert_eq!(sl.covers()[spans[0].cover_offset], 128);
165    }
166
167    #[test]
168    fn test_adjacent_cells_merge() {
169        let mut sl = ScanlineU8::new();
170        sl.reset(0, 100);
171        sl.add_cell(10, 100);
172        sl.add_cell(11, 200);
173        sl.add_cell(12, 150);
174        // Adjacent cells should form a single span
175        assert_eq!(sl.num_spans(), 1);
176        let spans = sl.begin();
177        assert_eq!(spans[0].x, 10);
178        assert_eq!(spans[0].len, 3);
179        assert_eq!(sl.covers()[spans[0].cover_offset], 100);
180        assert_eq!(sl.covers()[spans[0].cover_offset + 1], 200);
181        assert_eq!(sl.covers()[spans[0].cover_offset + 2], 150);
182    }
183
184    #[test]
185    fn test_non_adjacent_cells_separate_spans() {
186        let mut sl = ScanlineU8::new();
187        sl.reset(0, 100);
188        sl.add_cell(10, 100);
189        sl.add_cell(20, 200);
190        assert_eq!(sl.num_spans(), 2);
191        let spans = sl.begin();
192        assert_eq!(spans[0].x, 10);
193        assert_eq!(spans[1].x, 20);
194    }
195
196    #[test]
197    fn test_add_span() {
198        let mut sl = ScanlineU8::new();
199        sl.reset(0, 100);
200        sl.add_span(5, 10, 255);
201        assert_eq!(sl.num_spans(), 1);
202        let spans = sl.begin();
203        assert_eq!(spans[0].x, 5);
204        assert_eq!(spans[0].len, 10);
205        // All covers should be 255
206        for i in 0..10 {
207            assert_eq!(sl.covers()[spans[0].cover_offset + i], 255);
208        }
209    }
210
211    #[test]
212    fn test_finalize() {
213        let mut sl = ScanlineU8::new();
214        sl.reset(0, 100);
215        sl.add_cell(10, 128);
216        sl.finalize(42);
217        assert_eq!(sl.y(), 42);
218    }
219
220    #[test]
221    fn test_reset_spans() {
222        let mut sl = ScanlineU8::new();
223        sl.reset(0, 100);
224        sl.add_cell(10, 128);
225        assert_eq!(sl.num_spans(), 1);
226        sl.reset_spans();
227        assert_eq!(sl.num_spans(), 0);
228    }
229
230    #[test]
231    fn test_span_then_adjacent_cell() {
232        let mut sl = ScanlineU8::new();
233        sl.reset(0, 100);
234        sl.add_span(5, 3, 200);
235        sl.add_cell(8, 100); // adjacent to span end (5+3-1=7, next=8)
236        assert_eq!(sl.num_spans(), 1);
237        let spans = sl.begin();
238        assert_eq!(spans[0].len, 4);
239    }
240
241    #[test]
242    fn test_with_min_x_offset() {
243        let mut sl = ScanlineU8::new();
244        sl.reset(50, 150);
245        sl.add_cell(60, 128);
246        sl.add_cell(61, 64);
247        assert_eq!(sl.num_spans(), 1);
248        let spans = sl.begin();
249        assert_eq!(spans[0].x, 60);
250        assert_eq!(spans[0].len, 2);
251        assert_eq!(sl.covers()[spans[0].cover_offset], 128);
252        assert_eq!(sl.covers()[spans[0].cover_offset + 1], 64);
253    }
254}