Skip to main content

agg_rust/
scanline_p.rs

1//! Packed scanline container (ScanlineP8).
2//!
3//! Port of `agg_scanline_p.h` — stores coverage data in a packed/RLE format.
4//! Solid spans (uniform coverage) use negative `len` with a single cover value,
5//! saving memory for large filled areas.
6
7use crate::rasterizer_scanline_aa::Scanline;
8
9// ============================================================================
10// PackedSpan — a span in a packed scanline
11// ============================================================================
12
13/// A span in a packed scanline.
14///
15/// - `len > 0`: per-pixel covers, `cover_offset` indexes into covers array
16///   for `len` values
17/// - `len < 0`: solid span of `-len` pixels, all with the single cover value
18///   at `cover_offset`
19#[derive(Debug, Clone, Copy, Default)]
20pub struct PackedSpan {
21    pub x: i32,
22    pub len: i32,
23    pub cover_offset: usize,
24}
25
26// ============================================================================
27// ScanlineP8 — packed scanline with RLE for solid spans
28// ============================================================================
29
30/// Packed scanline container with RLE compression for solid (uniform-cover) spans.
31///
32/// Port of C++ `scanline_p8`. Solid spans store a negative `len` and a single
33/// cover value, reducing memory for large filled areas.
34pub struct ScanlineP8 {
35    last_x: i32,
36    y_val: i32,
37    covers: Vec<u8>,
38    cover_ptr: usize,
39    spans: Vec<PackedSpan>,
40    cur_span: usize,
41}
42
43impl ScanlineP8 {
44    pub fn new() -> Self {
45        Self {
46            last_x: 0x7FFF_FFF0,
47            y_val: 0,
48            covers: Vec::new(),
49            cover_ptr: 0,
50            spans: Vec::new(),
51            cur_span: 0,
52        }
53    }
54
55    /// Prepare for a new scanline with the given X range.
56    pub fn reset(&mut self, _min_x: i32, max_x: i32) {
57        let max_len = (max_x + 3) as usize;
58        if max_len > self.spans.len() {
59            self.spans.resize(max_len, PackedSpan::default());
60            self.covers.resize(max_len, 0);
61        }
62        self.last_x = 0x7FFF_FFF0;
63        self.cover_ptr = 0;
64        self.cur_span = 0;
65        self.spans[0].len = 0;
66    }
67
68    /// Get the slice of active spans (for renderer iteration).
69    pub fn begin(&self) -> &[PackedSpan] {
70        &self.spans[1..=self.cur_span]
71    }
72
73    /// Get the full covers array (spans reference into this via `cover_offset`).
74    pub fn covers(&self) -> &[u8] {
75        &self.covers
76    }
77}
78
79impl Scanline for ScanlineP8 {
80    fn reset_spans(&mut self) {
81        self.last_x = 0x7FFF_FFF0;
82        self.cover_ptr = 0;
83        self.cur_span = 0;
84        self.spans[0].len = 0;
85    }
86
87    fn add_cell(&mut self, x: i32, cover: u32) {
88        self.covers[self.cover_ptr] = cover as u8;
89        if x == self.last_x + 1 && self.spans[self.cur_span].len > 0 {
90            self.spans[self.cur_span].len += 1;
91        } else {
92            self.cur_span += 1;
93            self.spans[self.cur_span].cover_offset = self.cover_ptr;
94            self.spans[self.cur_span].x = x;
95            self.spans[self.cur_span].len = 1;
96        }
97        self.last_x = x;
98        self.cover_ptr += 1;
99    }
100
101    fn add_span(&mut self, x: i32, len: u32, cover: u32) {
102        if x == self.last_x + 1
103            && self.spans[self.cur_span].len < 0
104            && cover as u8 == self.covers[self.spans[self.cur_span].cover_offset]
105        {
106            // Extend the existing solid span
107            self.spans[self.cur_span].len -= len as i32;
108        } else {
109            self.covers[self.cover_ptr] = cover as u8;
110            self.cur_span += 1;
111            self.spans[self.cur_span].cover_offset = self.cover_ptr;
112            self.cover_ptr += 1;
113            self.spans[self.cur_span].x = x;
114            self.spans[self.cur_span].len = -(len as i32);
115        }
116        self.last_x = x + len as i32 - 1;
117    }
118
119    fn finalize(&mut self, y: i32) {
120        self.y_val = y;
121    }
122
123    fn num_spans(&self) -> u32 {
124        self.cur_span as u32
125    }
126
127    fn y(&self) -> i32 {
128        self.y_val
129    }
130}
131
132impl Default for ScanlineP8 {
133    fn default() -> Self {
134        Self::new()
135    }
136}
137
138// ============================================================================
139// Tests
140// ============================================================================
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    fn test_new() {
148        let sl = ScanlineP8::new();
149        assert_eq!(sl.num_spans(), 0);
150    }
151
152    #[test]
153    fn test_add_cell() {
154        let mut sl = ScanlineP8::new();
155        sl.reset(0, 100);
156        sl.add_cell(10, 128);
157        assert_eq!(sl.num_spans(), 1);
158        let spans = sl.begin();
159        assert_eq!(spans[0].x, 10);
160        assert_eq!(spans[0].len, 1); // positive = per-pixel
161        assert_eq!(sl.covers()[spans[0].cover_offset], 128);
162    }
163
164    #[test]
165    fn test_adjacent_cells_form_per_pixel_span() {
166        let mut sl = ScanlineP8::new();
167        sl.reset(0, 100);
168        sl.add_cell(10, 100);
169        sl.add_cell(11, 200);
170        sl.add_cell(12, 150);
171        assert_eq!(sl.num_spans(), 1);
172        let spans = sl.begin();
173        assert_eq!(spans[0].len, 3); // positive = per-pixel covers
174    }
175
176    #[test]
177    fn test_add_span_creates_solid_span() {
178        let mut sl = ScanlineP8::new();
179        sl.reset(0, 100);
180        sl.add_span(5, 10, 255);
181        assert_eq!(sl.num_spans(), 1);
182        let spans = sl.begin();
183        assert_eq!(spans[0].x, 5);
184        assert_eq!(spans[0].len, -10); // negative = solid span
185        assert_eq!(sl.covers()[spans[0].cover_offset], 255);
186    }
187
188    #[test]
189    fn test_adjacent_solid_spans_merge() {
190        let mut sl = ScanlineP8::new();
191        sl.reset(0, 100);
192        sl.add_span(5, 10, 255);
193        sl.add_span(15, 5, 255); // adjacent, same cover → merge
194        assert_eq!(sl.num_spans(), 1);
195        let spans = sl.begin();
196        assert_eq!(spans[0].len, -15); // merged solid span
197    }
198
199    #[test]
200    fn test_adjacent_solid_spans_different_cover_no_merge() {
201        let mut sl = ScanlineP8::new();
202        sl.reset(0, 100);
203        sl.add_span(5, 10, 255);
204        sl.add_span(15, 5, 128); // adjacent but different cover
205        assert_eq!(sl.num_spans(), 2);
206    }
207
208    #[test]
209    fn test_cell_after_solid_span_new_span() {
210        let mut sl = ScanlineP8::new();
211        sl.reset(0, 100);
212        sl.add_span(5, 3, 200);
213        sl.add_cell(8, 100); // adjacent to solid span end, but cell creates per-pixel span
214        assert_eq!(sl.num_spans(), 2);
215    }
216
217    #[test]
218    fn test_reset_spans() {
219        let mut sl = ScanlineP8::new();
220        sl.reset(0, 100);
221        sl.add_cell(10, 128);
222        sl.reset_spans();
223        assert_eq!(sl.num_spans(), 0);
224    }
225}