Skip to main content

agg_rust/
scanline_storage_bin.rs

1//! Binary (non-AA) scanline storage.
2//!
3//! Port of `agg_scanline_storage_bin.h`.
4//! Stores rasterized scanlines without coverage data, for boolean
5//! operations on binary (non-anti-aliased) shapes.
6
7use crate::rasterizer_scanline_aa::Scanline;
8
9/// Stored span data: x and len only (no coverage).
10#[derive(Debug, Clone, Copy, Default)]
11struct SpanData {
12    x: i32,
13    len: i32,
14}
15
16/// Per-scanline metadata.
17#[derive(Debug, Clone, Copy, Default)]
18struct ScanlineData {
19    y: i32,
20    num_spans: u32,
21    start_span: usize,
22}
23
24/// Binary (non-AA) scanline storage.
25///
26/// Port of C++ `scanline_storage_bin`.
27/// Like `ScanlineStorageAa` but stores only span extents, no coverage.
28pub struct ScanlineStorageBin {
29    spans: Vec<SpanData>,
30    scanlines: Vec<ScanlineData>,
31    min_x: i32,
32    min_y: i32,
33    max_x: i32,
34    max_y: i32,
35    cur_scanline: usize,
36}
37
38impl ScanlineStorageBin {
39    pub fn new() -> Self {
40        Self {
41            spans: Vec::new(),
42            scanlines: Vec::new(),
43            min_x: i32::MAX,
44            min_y: i32::MAX,
45            max_x: i32::MIN,
46            max_y: i32::MIN,
47            cur_scanline: 0,
48        }
49    }
50
51    /// Prepare for new rendering (clear all stored data).
52    pub fn prepare(&mut self) {
53        self.spans.clear();
54        self.scanlines.clear();
55        self.min_x = i32::MAX;
56        self.min_y = i32::MAX;
57        self.max_x = i32::MIN;
58        self.max_y = i32::MIN;
59        self.cur_scanline = 0;
60    }
61
62    /// Store a scanline from a ScanlineBin.
63    pub fn render_scanline_bin(&mut self, sl: &crate::scanline_bin::ScanlineBin) {
64        let y = sl.y();
65        if y < self.min_y {
66            self.min_y = y;
67        }
68        if y > self.max_y {
69            self.max_y = y;
70        }
71
72        let start_span = self.spans.len();
73        let mut num_spans = 0u32;
74
75        let spans = sl.begin();
76        for sp in spans {
77            let x = sp.x;
78            let len = sp.len;
79            let xe = x + len - 1;
80            if x < self.min_x {
81                self.min_x = x;
82            }
83            if xe > self.max_x {
84                self.max_x = xe;
85            }
86            self.spans.push(SpanData { x, len });
87            num_spans += 1;
88        }
89
90        self.scanlines.push(ScanlineData {
91            y,
92            num_spans,
93            start_span,
94        });
95    }
96
97    /// Store a scanline from any Scanline type (uses add_span with cover_full).
98    pub fn render_scanline_u8(&mut self, sl: &crate::scanline_u::ScanlineU8) {
99        let y = sl.y();
100        if y < self.min_y {
101            self.min_y = y;
102        }
103        if y > self.max_y {
104            self.max_y = y;
105        }
106
107        let start_span = self.spans.len();
108        let mut num_spans = 0u32;
109
110        let spans = sl.begin();
111        for sp in spans {
112            let x = sp.x;
113            let len = sp.len;
114            let xe = x + len - 1;
115            if x < self.min_x {
116                self.min_x = x;
117            }
118            if xe > self.max_x {
119                self.max_x = xe;
120            }
121            self.spans.push(SpanData { x, len });
122            num_spans += 1;
123        }
124
125        self.scanlines.push(ScanlineData {
126            y,
127            num_spans,
128            start_span,
129        });
130    }
131
132    /// Reset the scanline iteration pointer.
133    pub fn rewind_scanlines(&mut self) -> bool {
134        self.cur_scanline = 0;
135        !self.scanlines.is_empty()
136    }
137
138    /// Sweep the next stored scanline into an output scanline container.
139    pub fn sweep_scanline<SL: Scanline>(&mut self, sl: &mut SL) -> bool {
140        sl.reset_spans();
141        loop {
142            if self.cur_scanline >= self.scanlines.len() {
143                return false;
144            }
145            let sld = self.scanlines[self.cur_scanline];
146            let num_spans = sld.num_spans;
147            self.cur_scanline += 1;
148
149            if num_spans == 0 {
150                continue;
151            }
152
153            for i in 0..num_spans as usize {
154                let sp = self.spans[sld.start_span + i];
155                sl.add_span(sp.x, sp.len as u32, 255); // cover_full for binary
156            }
157            sl.finalize(sld.y);
158            return true;
159        }
160    }
161
162    pub fn min_x(&self) -> i32 {
163        self.min_x
164    }
165    pub fn min_y(&self) -> i32 {
166        self.min_y
167    }
168    pub fn max_x(&self) -> i32 {
169        self.max_x
170    }
171    pub fn max_y(&self) -> i32 {
172        self.max_y
173    }
174
175    pub fn num_scanlines(&self) -> usize {
176        self.scanlines.len()
177    }
178
179    /// Get the Y coordinate for a stored scanline by index.
180    pub fn scanline_y(&self, idx: usize) -> i32 {
181        self.scanlines[idx].y
182    }
183
184    /// Get the number of spans for a stored scanline.
185    pub fn scanline_num_spans(&self, idx: usize) -> u32 {
186        self.scanlines[idx].num_spans
187    }
188
189    /// Iterate over embedded spans for boolean algebra.
190    pub fn embedded_spans(&self, sl_idx: usize) -> impl Iterator<Item = EmbeddedBinSpan> + '_ {
191        let sld = &self.scanlines[sl_idx];
192        let spans = &self.spans[sld.start_span..sld.start_span + sld.num_spans as usize];
193        spans.iter().map(|sp| EmbeddedBinSpan {
194            x: sp.x,
195            len: sp.len,
196        })
197    }
198}
199
200impl Default for ScanlineStorageBin {
201    fn default() -> Self {
202        Self::new()
203    }
204}
205
206/// A span from embedded binary scanline iteration.
207#[derive(Debug, Clone, Copy)]
208pub struct EmbeddedBinSpan {
209    pub x: i32,
210    pub len: i32,
211}
212
213impl EmbeddedBinSpan {
214    /// Get end X (exclusive).
215    pub fn x_end(&self) -> i32 {
216        self.x + self.len
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223    use crate::scanline_bin::ScanlineBin;
224
225    #[test]
226    fn test_empty_storage() {
227        let storage = ScanlineStorageBin::new();
228        assert_eq!(storage.num_scanlines(), 0);
229    }
230
231    #[test]
232    fn test_store_and_replay() {
233        let mut storage = ScanlineStorageBin::new();
234        storage.prepare();
235
236        let mut sl = ScanlineBin::new();
237        sl.reset(0, 100);
238        sl.add_span(10, 5, 255);
239        sl.finalize(3);
240        storage.render_scanline_bin(&sl);
241
242        assert_eq!(storage.num_scanlines(), 1);
243        assert_eq!(storage.min_x(), 10);
244        assert_eq!(storage.max_x(), 14);
245
246        assert!(storage.rewind_scanlines());
247        let mut sl2 = ScanlineBin::new();
248        sl2.reset(0, 100);
249        assert!(storage.sweep_scanline(&mut sl2));
250        assert_eq!(sl2.y(), 3);
251        assert_eq!(sl2.num_spans(), 1);
252        let spans = sl2.begin();
253        assert_eq!(spans[0].x, 10);
254        assert_eq!(spans[0].len, 5);
255    }
256
257    #[test]
258    fn test_multiple_scanlines() {
259        let mut storage = ScanlineStorageBin::new();
260        storage.prepare();
261
262        let mut sl = ScanlineBin::new();
263        sl.reset(0, 100);
264        sl.add_span(0, 10, 255);
265        sl.finalize(0);
266        storage.render_scanline_bin(&sl);
267
268        sl.reset_spans();
269        sl.add_span(5, 15, 255);
270        sl.finalize(1);
271        storage.render_scanline_bin(&sl);
272
273        assert_eq!(storage.num_scanlines(), 2);
274        assert_eq!(storage.min_x(), 0);
275        assert_eq!(storage.max_x(), 19);
276        assert_eq!(storage.min_y(), 0);
277        assert_eq!(storage.max_y(), 1);
278    }
279
280    #[test]
281    fn test_embedded_spans() {
282        let mut storage = ScanlineStorageBin::new();
283        storage.prepare();
284
285        let mut sl = ScanlineBin::new();
286        sl.reset(0, 100);
287        sl.add_span(5, 10, 255);
288        sl.add_span(30, 5, 255);
289        sl.finalize(0);
290        storage.render_scanline_bin(&sl);
291
292        let spans: Vec<_> = storage.embedded_spans(0).collect();
293        assert_eq!(spans.len(), 2);
294        assert_eq!(spans[0].x, 5);
295        assert_eq!(spans[0].len, 10);
296        assert_eq!(spans[1].x, 30);
297        assert_eq!(spans[1].len, 5);
298    }
299}