Skip to main content

agg_rust/
scanline_bin.rs

1//! Binary scanline container (ScanlineBin).
2//!
3//! Port of `agg_scanline_bin.h` — simplest scanline type with no coverage
4//! data. Each span just records X and length. Used for non-anti-aliased
5//! rendering or as a clipping mask.
6
7use crate::rasterizer_scanline_aa::Scanline;
8
9// ============================================================================
10// BinSpan — a span in a binary scanline
11// ============================================================================
12
13/// A horizontal span in a binary scanline (no coverage data).
14#[derive(Debug, Clone, Copy, Default)]
15pub struct BinSpan {
16    pub x: i32,
17    pub len: i32,
18}
19
20// ============================================================================
21// ScanlineBin — binary scanline (no coverage data)
22// ============================================================================
23
24/// Binary scanline container — no per-pixel coverage, just on/off spans.
25///
26/// Port of C++ `scanline_bin`. Useful for non-AA rendering or clipping masks.
27pub struct ScanlineBin {
28    last_x: i32,
29    y_val: i32,
30    spans: Vec<BinSpan>,
31    cur_span: usize,
32}
33
34impl ScanlineBin {
35    pub fn new() -> Self {
36        Self {
37            last_x: 0x7FFF_FFF0,
38            y_val: 0,
39            spans: Vec::new(),
40            cur_span: 0,
41        }
42    }
43
44    /// Prepare for a new scanline with the given X range.
45    pub fn reset(&mut self, _min_x: i32, max_x: i32) {
46        let max_len = (max_x + 3) as usize;
47        if max_len > self.spans.len() {
48            self.spans.resize(max_len, BinSpan::default());
49        }
50        self.last_x = 0x7FFF_FFF0;
51        self.cur_span = 0;
52    }
53
54    /// Get the slice of active spans (for renderer iteration).
55    pub fn begin(&self) -> &[BinSpan] {
56        &self.spans[1..=self.cur_span]
57    }
58}
59
60impl Scanline for ScanlineBin {
61    fn reset_spans(&mut self) {
62        self.last_x = 0x7FFF_FFF0;
63        self.cur_span = 0;
64    }
65
66    fn add_cell(&mut self, x: i32, _cover: u32) {
67        if x == self.last_x + 1 {
68            self.spans[self.cur_span].len += 1;
69        } else {
70            self.cur_span += 1;
71            self.spans[self.cur_span].x = x;
72            self.spans[self.cur_span].len = 1;
73        }
74        self.last_x = x;
75    }
76
77    fn add_span(&mut self, x: i32, len: u32, _cover: u32) {
78        if x == self.last_x + 1 {
79            self.spans[self.cur_span].len += len as i32;
80        } else {
81            self.cur_span += 1;
82            self.spans[self.cur_span].x = x;
83            self.spans[self.cur_span].len = len as i32;
84        }
85        self.last_x = x + len as i32 - 1;
86    }
87
88    fn finalize(&mut self, y: i32) {
89        self.y_val = y;
90    }
91
92    fn num_spans(&self) -> u32 {
93        self.cur_span as u32
94    }
95
96    fn y(&self) -> i32 {
97        self.y_val
98    }
99}
100
101impl Default for ScanlineBin {
102    fn default() -> Self {
103        Self::new()
104    }
105}
106
107// ============================================================================
108// Tests
109// ============================================================================
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_new() {
117        let sl = ScanlineBin::new();
118        assert_eq!(sl.num_spans(), 0);
119    }
120
121    #[test]
122    fn test_add_cell() {
123        let mut sl = ScanlineBin::new();
124        sl.reset(0, 100);
125        sl.add_cell(10, 255);
126        assert_eq!(sl.num_spans(), 1);
127        let spans = sl.begin();
128        assert_eq!(spans[0].x, 10);
129        assert_eq!(spans[0].len, 1);
130    }
131
132    #[test]
133    fn test_adjacent_cells_merge() {
134        let mut sl = ScanlineBin::new();
135        sl.reset(0, 100);
136        sl.add_cell(10, 255);
137        sl.add_cell(11, 128);
138        sl.add_cell(12, 64);
139        assert_eq!(sl.num_spans(), 1);
140        let spans = sl.begin();
141        assert_eq!(spans[0].x, 10);
142        assert_eq!(spans[0].len, 3);
143    }
144
145    #[test]
146    fn test_add_span() {
147        let mut sl = ScanlineBin::new();
148        sl.reset(0, 100);
149        sl.add_span(5, 10, 255);
150        assert_eq!(sl.num_spans(), 1);
151        let spans = sl.begin();
152        assert_eq!(spans[0].x, 5);
153        assert_eq!(spans[0].len, 10);
154    }
155
156    #[test]
157    fn test_separate_spans() {
158        let mut sl = ScanlineBin::new();
159        sl.reset(0, 100);
160        sl.add_cell(10, 255);
161        sl.add_cell(20, 255);
162        assert_eq!(sl.num_spans(), 2);
163    }
164
165    #[test]
166    fn test_reset_spans() {
167        let mut sl = ScanlineBin::new();
168        sl.reset(0, 100);
169        sl.add_cell(10, 255);
170        sl.reset_spans();
171        assert_eq!(sl.num_spans(), 0);
172    }
173}