Skip to main content

agg_rust/
rasterizer_compound_aa.rs

1//! Compound AA rasterizer with per-edge style indices.
2//!
3//! Port of `agg_rasterizer_compound_aa.h`.
4//! Extends the standard rasterizer with left/right style indices per edge,
5//! enabling multi-style rendering (e.g. Flash-style fills where a single
6//! scanline contains pixels from multiple fill styles).
7
8use crate::basics::{
9    is_close, is_move_to, is_stop, is_vertex, iround, FillingRule, VertexSource,
10    POLY_SUBPIXEL_MASK, POLY_SUBPIXEL_SCALE, POLY_SUBPIXEL_SHIFT,
11};
12use crate::rasterizer_scanline_aa::Scanline;
13
14// ============================================================================
15// CellStyleAa — cell with left/right style indices
16// ============================================================================
17
18/// A pixel cell with left/right style indices for compound rendering.
19///
20/// Port of C++ `cell_style_aa`.
21#[derive(Debug, Clone, Copy)]
22pub struct CellStyleAa {
23    pub x: i32,
24    pub y: i32,
25    pub cover: i32,
26    pub area: i32,
27    pub left: i16,
28    pub right: i16,
29}
30
31impl CellStyleAa {
32    #[inline]
33    pub fn initial(&mut self) {
34        self.x = i32::MAX;
35        self.y = i32::MAX;
36        self.cover = 0;
37        self.area = 0;
38        self.left = -1;
39        self.right = -1;
40    }
41
42    #[inline]
43    pub fn style(&mut self, other: &CellStyleAa) {
44        self.left = other.left;
45        self.right = other.right;
46    }
47
48    #[inline]
49    pub fn not_equal(&self, ex: i32, ey: i32, style: &CellStyleAa) -> bool {
50        (ex as u32).wrapping_sub(self.x as u32)
51            | (ey as u32).wrapping_sub(self.y as u32)
52            | (self.left as u32).wrapping_sub(style.left as u32)
53            | (self.right as u32).wrapping_sub(style.right as u32)
54            != 0
55    }
56}
57
58impl Default for CellStyleAa {
59    fn default() -> Self {
60        Self {
61            x: i32::MAX,
62            y: i32::MAX,
63            cover: 0,
64            area: 0,
65            left: -1,
66            right: -1,
67        }
68    }
69}
70
71// ============================================================================
72// LayerOrder — rendering order for styles
73// ============================================================================
74
75/// Layer order for compound rasterizer style rendering.
76///
77/// Port of C++ `layer_order_e`.
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum LayerOrder {
80    Unsorted,
81    Direct,
82    Inverse,
83}
84
85// ============================================================================
86// Internal cells engine for CellStyleAa (matches rasterizer_cells_aa logic)
87// ============================================================================
88
89/// Limit for dx magnitude before recursive subdivision.
90const DX_LIMIT: i64 = 16384 << POLY_SUBPIXEL_SHIFT;
91
92#[derive(Debug, Clone, Copy, Default)]
93struct SortedY {
94    start: u32,
95    num: u32,
96}
97
98/// Self-contained cells engine for `CellStyleAa`.
99/// Port of C++ `rasterizer_cells_aa<cell_style_aa>`.
100struct CellsEngine {
101    cells: Vec<CellStyleAa>,
102    sorted_cells: Vec<u32>,
103    sorted_y: Vec<SortedY>,
104    curr_cell: CellStyleAa,
105    style_cell: CellStyleAa,
106    min_x: i32,
107    min_y: i32,
108    max_x: i32,
109    max_y: i32,
110    sorted: bool,
111}
112
113impl CellsEngine {
114    fn new() -> Self {
115        Self {
116            cells: Vec::new(),
117            sorted_cells: Vec::new(),
118            sorted_y: Vec::new(),
119            curr_cell: CellStyleAa::default(),
120            style_cell: CellStyleAa::default(),
121            min_x: i32::MAX,
122            min_y: i32::MAX,
123            max_x: i32::MIN,
124            max_y: i32::MIN,
125            sorted: false,
126        }
127    }
128
129    fn reset(&mut self) {
130        self.cells.clear();
131        self.sorted_cells.clear();
132        self.sorted_y.clear();
133        self.curr_cell.initial();
134        self.style_cell.initial();
135        self.min_x = i32::MAX;
136        self.min_y = i32::MAX;
137        self.max_x = i32::MIN;
138        self.max_y = i32::MIN;
139        self.sorted = false;
140    }
141
142    #[inline]
143    fn style(&mut self, style_cell: &CellStyleAa) {
144        self.style_cell.style(style_cell);
145    }
146
147    #[inline]
148    fn min_x(&self) -> i32 {
149        self.min_x
150    }
151    #[inline]
152    fn min_y(&self) -> i32 {
153        self.min_y
154    }
155    #[inline]
156    fn max_x(&self) -> i32 {
157        self.max_x
158    }
159    #[inline]
160    fn max_y(&self) -> i32 {
161        self.max_y
162    }
163    #[inline]
164    fn total_cells(&self) -> u32 {
165        self.cells.len() as u32
166    }
167    #[inline]
168    fn sorted(&self) -> bool {
169        self.sorted
170    }
171
172    #[inline]
173    fn scanline_num_cells(&self, y: u32) -> u32 {
174        self.sorted_y[(y as i32 - self.min_y) as usize].num
175    }
176
177    /// Get sorted cell indices for scanline `y`.
178    #[inline]
179    fn scanline_cells(&self, y: u32) -> &[u32] {
180        let sy = &self.sorted_y[(y as i32 - self.min_y) as usize];
181        &self.sorted_cells[sy.start as usize..(sy.start + sy.num) as usize]
182    }
183
184    #[inline]
185    fn cell(&self, idx: u32) -> &CellStyleAa {
186        &self.cells[idx as usize]
187    }
188
189    #[inline]
190    fn add_curr_cell(&mut self) {
191        if self.curr_cell.area | self.curr_cell.cover != 0 {
192            self.cells.push(self.curr_cell);
193        }
194    }
195
196    #[inline]
197    fn set_curr_cell(&mut self, x: i32, y: i32) {
198        if self.curr_cell.not_equal(x, y, &self.style_cell) {
199            self.add_curr_cell();
200            self.curr_cell.style(&self.style_cell);
201            self.curr_cell.x = x;
202            self.curr_cell.y = y;
203            self.curr_cell.cover = 0;
204            self.curr_cell.area = 0;
205        }
206    }
207
208    fn render_hline(&mut self, ey: i32, x1: i32, y1: i32, x2: i32, y2: i32) {
209        let ex1 = x1 >> POLY_SUBPIXEL_SHIFT;
210        let ex2 = x2 >> POLY_SUBPIXEL_SHIFT;
211        let fx1 = x1 & POLY_SUBPIXEL_MASK as i32;
212        let fx2 = x2 & POLY_SUBPIXEL_MASK as i32;
213
214        if y1 == y2 {
215            self.set_curr_cell(ex2, ey);
216            return;
217        }
218
219        if ex1 == ex2 {
220            let delta = y2 - y1;
221            self.curr_cell.cover += delta;
222            self.curr_cell.area += (fx1 + fx2) * delta;
223            return;
224        }
225
226        let mut p = (POLY_SUBPIXEL_SCALE as i64 - fx1 as i64) * (y2 - y1) as i64;
227        let mut first = POLY_SUBPIXEL_SCALE as i32;
228        let mut incr = 1_i32;
229        let mut dx = x2 as i64 - x1 as i64;
230
231        if dx < 0 {
232            p = fx1 as i64 * (y2 - y1) as i64;
233            first = 0;
234            incr = -1;
235            dx = -dx;
236        }
237
238        let mut delta = (p / dx) as i32;
239        let mut modulo = p % dx;
240        if modulo < 0 {
241            delta -= 1;
242            modulo += dx;
243        }
244
245        self.curr_cell.cover += delta;
246        self.curr_cell.area += (fx1 + first) * delta;
247
248        let mut ex1 = ex1 + incr;
249        self.set_curr_cell(ex1, ey);
250        let mut y1 = y1 + delta;
251
252        if ex1 != ex2 {
253            p = POLY_SUBPIXEL_SCALE as i64 * (y2 - y1 + delta) as i64;
254            let mut lift = (p / dx) as i32;
255            let mut rem = p % dx;
256            if rem < 0 {
257                lift -= 1;
258                rem += dx;
259            }
260            modulo -= dx;
261
262            while ex1 != ex2 {
263                delta = lift;
264                modulo += rem;
265                if modulo >= 0 {
266                    modulo -= dx;
267                    delta += 1;
268                }
269                self.curr_cell.cover += delta;
270                self.curr_cell.area += POLY_SUBPIXEL_SCALE as i32 * delta;
271                y1 += delta;
272                ex1 += incr;
273                self.set_curr_cell(ex1, ey);
274            }
275        }
276        delta = y2 - y1;
277        self.curr_cell.cover += delta;
278        self.curr_cell.area += (fx2 + POLY_SUBPIXEL_SCALE as i32 - first) * delta;
279    }
280
281    fn line(&mut self, x1: i32, y1: i32, x2: i32, y2: i32) {
282        let dx = x2 as i64 - x1 as i64;
283
284        if dx >= DX_LIMIT || dx <= -DX_LIMIT {
285            let cx = ((x1 as i64 + x2 as i64) >> 1) as i32;
286            let cy = ((y1 as i64 + y2 as i64) >> 1) as i32;
287            self.line(x1, y1, cx, cy);
288            self.line(cx, cy, x2, y2);
289            return;
290        }
291
292        let dy = y2 as i64 - y1 as i64;
293        let ex1 = x1 >> POLY_SUBPIXEL_SHIFT;
294        let ex2 = x2 >> POLY_SUBPIXEL_SHIFT;
295        let ey1_orig = y1 >> POLY_SUBPIXEL_SHIFT;
296        let ey2 = y2 >> POLY_SUBPIXEL_SHIFT;
297        let fy1 = y1 & POLY_SUBPIXEL_MASK as i32;
298        let fy2 = y2 & POLY_SUBPIXEL_MASK as i32;
299
300        if ex1 < self.min_x { self.min_x = ex1; }
301        if ex1 > self.max_x { self.max_x = ex1; }
302        if ey1_orig < self.min_y { self.min_y = ey1_orig; }
303        if ey1_orig > self.max_y { self.max_y = ey1_orig; }
304        if ex2 < self.min_x { self.min_x = ex2; }
305        if ex2 > self.max_x { self.max_x = ex2; }
306        if ey2 < self.min_y { self.min_y = ey2; }
307        if ey2 > self.max_y { self.max_y = ey2; }
308
309        let mut ey1 = ey1_orig;
310        self.set_curr_cell(ex1, ey1);
311
312        if ey1 == ey2 {
313            self.render_hline(ey1, x1, fy1, x2, fy2);
314            return;
315        }
316
317        let mut incr = 1_i32;
318        if dx == 0 {
319            let ex = x1 >> POLY_SUBPIXEL_SHIFT;
320            let two_fx = (x1 - (ex << POLY_SUBPIXEL_SHIFT)) << 1;
321            let mut first = POLY_SUBPIXEL_SCALE as i32;
322            if dy < 0 {
323                first = 0;
324                incr = -1;
325            }
326            let mut delta = first - fy1;
327            self.curr_cell.cover += delta;
328            self.curr_cell.area += two_fx * delta;
329            ey1 += incr;
330            self.set_curr_cell(ex, ey1);
331
332            delta = first + first - POLY_SUBPIXEL_SCALE as i32;
333            let area = two_fx * delta;
334            while ey1 != ey2 {
335                self.curr_cell.cover = delta;
336                self.curr_cell.area = area;
337                ey1 += incr;
338                self.set_curr_cell(ex, ey1);
339            }
340            delta = fy2 - POLY_SUBPIXEL_SCALE as i32 + first;
341            self.curr_cell.cover += delta;
342            self.curr_cell.area += two_fx * delta;
343            return;
344        }
345
346        let mut p = (POLY_SUBPIXEL_SCALE as i64 - fy1 as i64) * dx;
347        let mut first = POLY_SUBPIXEL_SCALE as i32;
348        let mut dy_abs = dy;
349        if dy < 0 {
350            p = fy1 as i64 * dx;
351            first = 0;
352            incr = -1;
353            dy_abs = -dy;
354        }
355
356        let mut delta = (p / dy_abs) as i32;
357        let mut modulo = p % dy_abs;
358        if modulo < 0 {
359            delta -= 1;
360            modulo += dy_abs;
361        }
362
363        let mut x_from = x1 + delta;
364        self.render_hline(ey1, x1, fy1, x_from, first);
365        ey1 += incr;
366        self.set_curr_cell(x_from >> POLY_SUBPIXEL_SHIFT, ey1);
367
368        if ey1 != ey2 {
369            p = POLY_SUBPIXEL_SCALE as i64 * dx;
370            let mut lift = (p / dy_abs) as i32;
371            let mut rem = p % dy_abs;
372            if rem < 0 {
373                lift -= 1;
374                rem += dy_abs;
375            }
376            modulo -= dy_abs;
377
378            while ey1 != ey2 {
379                delta = lift;
380                modulo += rem;
381                if modulo >= 0 {
382                    modulo -= dy_abs;
383                    delta += 1;
384                }
385                let x_to = x_from + delta;
386                self.render_hline(
387                    ey1,
388                    x_from,
389                    POLY_SUBPIXEL_SCALE as i32 - first,
390                    x_to,
391                    first,
392                );
393                x_from = x_to;
394                ey1 += incr;
395                self.set_curr_cell(x_from >> POLY_SUBPIXEL_SHIFT, ey1);
396            }
397        }
398        self.render_hline(ey1, x_from, POLY_SUBPIXEL_SCALE as i32 - first, x2, fy2);
399    }
400
401    fn sort_cells(&mut self) {
402        if self.sorted {
403            return;
404        }
405
406        self.add_curr_cell();
407        self.curr_cell.initial();
408
409        if self.cells.is_empty() {
410            return;
411        }
412
413        let num_cells = self.cells.len();
414        self.sorted_cells.clear();
415        self.sorted_cells.resize(num_cells, 0);
416
417        let y_range = (self.max_y - self.min_y + 1) as usize;
418        self.sorted_y.clear();
419        self.sorted_y.resize(y_range, SortedY::default());
420
421        for cell in &self.cells {
422            let yi = (cell.y - self.min_y) as usize;
423            self.sorted_y[yi].start += 1;
424        }
425
426        let mut start = 0u32;
427        for sy in &mut self.sorted_y {
428            let count = sy.start;
429            sy.start = start;
430            start += count;
431        }
432
433        for (i, cell) in self.cells.iter().enumerate() {
434            let yi = (cell.y - self.min_y) as usize;
435            let sy = &mut self.sorted_y[yi];
436            self.sorted_cells[(sy.start + sy.num) as usize] = i as u32;
437            sy.num += 1;
438        }
439
440        for sy in &self.sorted_y {
441            if sy.num > 0 {
442                let start = sy.start as usize;
443                let end = (sy.start + sy.num) as usize;
444                let slice = &mut self.sorted_cells[start..end];
445                let cells = &self.cells;
446                slice.sort_unstable_by_key(|&idx| cells[idx as usize].x);
447            }
448        }
449
450        self.sorted = true;
451    }
452}
453
454// ============================================================================
455// Simple inline clipper for compound rasterizer
456// ============================================================================
457
458fn upscale(v: f64) -> i32 {
459    iround(v * POLY_SUBPIXEL_SCALE as f64)
460}
461
462fn downscale(v: i32) -> i32 {
463    v
464}
465
466/// Clipping flags for Liang-Barsky line clipping.
467fn clipping_flags(x: i32, y: i32, clip_box: &[i32; 4]) -> u32 {
468    ((x > clip_box[2]) as u32) << 0
469        | ((y > clip_box[3]) as u32) << 1
470        | ((x < clip_box[0]) as u32) << 2
471        | ((y < clip_box[1]) as u32) << 3
472}
473
474/// Clip and render a line segment.
475fn clip_line_segment(
476    engine: &mut CellsEngine,
477    x1: &mut i32,
478    y1: &mut i32,
479    x2: i32,
480    y2: i32,
481    clip_box: &[i32; 4],
482) {
483    let f1 = clipping_flags(*x1, *y1, clip_box);
484    let f2 = clipping_flags(x2, y2, clip_box);
485
486    if f1 == 0 && f2 == 0 {
487        // Fully visible
488        engine.line(*x1, *y1, x2, y2);
489    } else if (f1 & f2) != 0 {
490        // Fully clipped (both on same side)
491    } else {
492        // Partial — use simple Liang-Barsky approach
493        let mut cx1 = *x1;
494        let mut cy1 = *y1;
495        let mut cx2 = x2;
496        let mut cy2 = y2;
497
498        if liang_barsky_clip(&mut cx1, &mut cy1, &mut cx2, &mut cy2, clip_box) {
499            engine.line(cx1, cy1, cx2, cy2);
500        }
501    }
502    *x1 = x2;
503    *y1 = y2;
504}
505
506/// Liang-Barsky line clipping. Returns true if line is (partially) visible.
507fn liang_barsky_clip(
508    x1: &mut i32,
509    y1: &mut i32,
510    x2: &mut i32,
511    y2: &mut i32,
512    clip: &[i32; 4],
513) -> bool {
514    let dx = *x2 as f64 - *x1 as f64;
515    let dy = *y2 as f64 - *y1 as f64;
516    let mut t0 = 0.0f64;
517    let mut t1 = 1.0f64;
518
519    let clips = [
520        (-dx, *x1 as f64 - clip[0] as f64),
521        (dx, clip[2] as f64 - *x1 as f64),
522        (-dy, *y1 as f64 - clip[1] as f64),
523        (dy, clip[3] as f64 - *y1 as f64),
524    ];
525
526    for &(p, q) in &clips {
527        if p.abs() < 1e-10 {
528            if q < 0.0 {
529                return false;
530            }
531        } else {
532            let t = q / p;
533            if p < 0.0 {
534                if t > t1 {
535                    return false;
536                }
537                if t > t0 {
538                    t0 = t;
539                }
540            } else {
541                if t < t0 {
542                    return false;
543                }
544                if t < t1 {
545                    t1 = t;
546                }
547            }
548        }
549    }
550
551    let ox1 = *x1 as f64;
552    let oy1 = *y1 as f64;
553    if t1 < 1.0 {
554        *x2 = (ox1 + dx * t1) as i32;
555        *y2 = (oy1 + dy * t1) as i32;
556    }
557    if t0 > 0.0 {
558        *x1 = (ox1 + dx * t0) as i32;
559        *y1 = (oy1 + dy * t0) as i32;
560    }
561    true
562}
563
564// ============================================================================
565// StyleInfo / CellInfo — internal data structures
566// ============================================================================
567
568#[derive(Debug, Clone, Copy)]
569struct StyleInfo {
570    start_cell: u32,
571    num_cells: u32,
572    last_x: i32,
573}
574
575impl Default for StyleInfo {
576    fn default() -> Self {
577        Self {
578            start_cell: 0,
579            num_cells: 0,
580            last_x: i32::MIN,
581        }
582    }
583}
584
585#[derive(Debug, Clone, Copy, Default)]
586struct CellInfo {
587    x: i32,
588    area: i32,
589    cover: i32,
590}
591
592// ============================================================================
593// RasterizerCompoundAa
594// ============================================================================
595
596const AA_SHIFT: u32 = 8;
597const AA_SCALE: u32 = 1 << AA_SHIFT;
598const AA_MASK: u32 = AA_SCALE - 1;
599const AA_SCALE2: u32 = AA_SCALE * 2;
600const AA_MASK2: u32 = AA_SCALE2 - 1;
601
602/// Compound anti-aliased rasterizer with per-edge style indices.
603///
604/// Port of C++ `rasterizer_compound_aa`.
605/// Each edge can have independent left and right fill styles, enabling
606/// multi-style rendering in a single pass.
607pub struct RasterizerCompoundAa {
608    outline: CellsEngine,
609    filling_rule: FillingRule,
610    layer_order: LayerOrder,
611    styles: Vec<StyleInfo>,
612    ast: Vec<u32>,   // Active Style Table
613    asm: Vec<u8>,    // Active Style Mask (bitmask)
614    cells: Vec<CellInfo>,
615    cover_buf: Vec<u8>,
616    min_style: i32,
617    max_style: i32,
618    start_x: i32,
619    start_y: i32,
620    scan_y: i32,
621    sl_start: i32,
622    sl_len: u32,
623    // Clipping
624    clipping: bool,
625    clip_box: [i32; 4], // x1, y1, x2, y2 in subpixel coords
626    clip_x1: i32,
627    clip_y1: i32,
628}
629
630impl RasterizerCompoundAa {
631    pub fn new() -> Self {
632        Self {
633            outline: CellsEngine::new(),
634            filling_rule: FillingRule::NonZero,
635            layer_order: LayerOrder::Direct,
636            styles: Vec::new(),
637            ast: Vec::new(),
638            asm: Vec::new(),
639            cells: Vec::new(),
640            cover_buf: Vec::new(),
641            min_style: i32::MAX,
642            max_style: i32::MIN,
643            start_x: 0,
644            start_y: 0,
645            scan_y: i32::MAX,
646            sl_start: 0,
647            sl_len: 0,
648            clipping: false,
649            clip_box: [0; 4],
650            clip_x1: 0,
651            clip_y1: 0,
652        }
653    }
654
655    pub fn reset(&mut self) {
656        self.outline.reset();
657        self.min_style = i32::MAX;
658        self.max_style = i32::MIN;
659        self.scan_y = i32::MAX;
660        self.sl_start = 0;
661        self.sl_len = 0;
662    }
663
664    pub fn reset_clipping(&mut self) {
665        self.reset();
666        self.clipping = false;
667    }
668
669    pub fn clip_box(&mut self, x1: f64, y1: f64, x2: f64, y2: f64) {
670        self.reset();
671        self.clipping = true;
672        self.clip_box = [upscale(x1), upscale(y1), upscale(x2), upscale(y2)];
673    }
674
675    pub fn filling_rule(&mut self, rule: FillingRule) {
676        self.filling_rule = rule;
677    }
678
679    pub fn layer_order(&mut self, order: LayerOrder) {
680        self.layer_order = order;
681    }
682
683    /// Set the left and right fill styles for subsequent edges.
684    pub fn styles(&mut self, left: i32, right: i32) {
685        let mut cell = CellStyleAa::default();
686        cell.initial();
687        cell.left = left as i16;
688        cell.right = right as i16;
689        self.outline.style(&cell);
690        if left >= 0 && left < self.min_style {
691            self.min_style = left;
692        }
693        if left >= 0 && left > self.max_style {
694            self.max_style = left;
695        }
696        if right >= 0 && right < self.min_style {
697            self.min_style = right;
698        }
699        if right >= 0 && right > self.max_style {
700            self.max_style = right;
701        }
702    }
703
704    /// Move to position (subpixel integer coords).
705    pub fn move_to(&mut self, x: i32, y: i32) {
706        if self.outline.sorted() {
707            self.reset();
708        }
709        self.start_x = downscale(x);
710        self.start_y = downscale(y);
711        if self.clipping {
712            self.clip_x1 = self.start_x;
713            self.clip_y1 = self.start_y;
714        }
715    }
716
717    /// Line to position (subpixel integer coords).
718    pub fn line_to(&mut self, x: i32, y: i32) {
719        let x = downscale(x);
720        let y = downscale(y);
721        if self.clipping {
722            clip_line_segment(
723                &mut self.outline,
724                &mut self.clip_x1,
725                &mut self.clip_y1,
726                x,
727                y,
728                &self.clip_box,
729            );
730        } else {
731            self.outline.line(self.clip_x1, self.clip_y1, x, y);
732            self.clip_x1 = x;
733            self.clip_y1 = y;
734        }
735    }
736
737    /// Move to position (f64 coords).
738    pub fn move_to_d(&mut self, x: f64, y: f64) {
739        if self.outline.sorted() {
740            self.reset();
741        }
742        self.start_x = upscale(x);
743        self.start_y = upscale(y);
744        if self.clipping {
745            self.clip_x1 = self.start_x;
746            self.clip_y1 = self.start_y;
747        } else {
748            self.clip_x1 = self.start_x;
749            self.clip_y1 = self.start_y;
750        }
751    }
752
753    /// Line to position (f64 coords).
754    pub fn line_to_d(&mut self, x: f64, y: f64) {
755        let x = upscale(x);
756        let y = upscale(y);
757        if self.clipping {
758            clip_line_segment(
759                &mut self.outline,
760                &mut self.clip_x1,
761                &mut self.clip_y1,
762                x,
763                y,
764                &self.clip_box,
765            );
766        } else {
767            self.outline.line(self.clip_x1, self.clip_y1, x, y);
768            self.clip_x1 = x;
769            self.clip_y1 = y;
770        }
771    }
772
773    /// Process a vertex command.
774    pub fn add_vertex(&mut self, x: f64, y: f64, cmd: u32) {
775        if is_move_to(cmd) {
776            self.move_to_d(x, y);
777        } else if is_vertex(cmd) {
778            self.line_to_d(x, y);
779        } else if is_close(cmd) {
780            let sx = self.start_x;
781            let sy = self.start_y;
782            if self.clipping {
783                clip_line_segment(
784                    &mut self.outline,
785                    &mut self.clip_x1,
786                    &mut self.clip_y1,
787                    sx,
788                    sy,
789                    &self.clip_box,
790                );
791            } else {
792                self.outline.line(self.clip_x1, self.clip_y1, sx, sy);
793                self.clip_x1 = sx;
794                self.clip_y1 = sy;
795            }
796        }
797    }
798
799    /// Add a single edge (subpixel integer coords).
800    pub fn edge(&mut self, x1: i32, y1: i32, x2: i32, y2: i32) {
801        if self.outline.sorted() {
802            self.reset();
803        }
804        let x1 = downscale(x1);
805        let y1 = downscale(y1);
806        let x2 = downscale(x2);
807        let y2 = downscale(y2);
808        self.outline.line(x1, y1, x2, y2);
809    }
810
811    /// Add a single edge (f64 coords).
812    pub fn edge_d(&mut self, x1: f64, y1: f64, x2: f64, y2: f64) {
813        if self.outline.sorted() {
814            self.reset();
815        }
816        self.outline
817            .line(upscale(x1), upscale(y1), upscale(x2), upscale(y2));
818    }
819
820    /// Add an entire path from a vertex source.
821    pub fn add_path<VS: VertexSource>(&mut self, vs: &mut VS, path_id: u32) {
822        let mut x = 0.0;
823        let mut y = 0.0;
824        vs.rewind(path_id);
825        if self.outline.sorted() {
826            self.reset();
827        }
828        loop {
829            let cmd = vs.vertex(&mut x, &mut y);
830            if is_stop(cmd) {
831                break;
832            }
833            self.add_vertex(x, y, cmd);
834        }
835    }
836
837    pub fn min_x(&self) -> i32 {
838        self.outline.min_x()
839    }
840    pub fn min_y(&self) -> i32 {
841        self.outline.min_y()
842    }
843    pub fn max_x(&self) -> i32 {
844        self.outline.max_x()
845    }
846    pub fn max_y(&self) -> i32 {
847        self.outline.max_y()
848    }
849    pub fn min_style(&self) -> i32 {
850        self.min_style
851    }
852    pub fn max_style(&self) -> i32 {
853        self.max_style
854    }
855
856    /// Sort cells.
857    pub fn sort(&mut self) {
858        self.outline.sort_cells();
859    }
860
861    /// Prepare for scanline iteration.
862    pub fn rewind_scanlines(&mut self) -> bool {
863        self.outline.sort_cells();
864        if self.outline.total_cells() == 0 {
865            return false;
866        }
867        if self.max_style < self.min_style {
868            return false;
869        }
870        self.scan_y = self.outline.min_y();
871        let num_styles = (self.max_style - self.min_style + 2) as usize;
872        self.styles.resize(num_styles, StyleInfo::default());
873        true
874    }
875
876    /// Calculate alpha from coverage area.
877    #[inline]
878    pub fn calculate_alpha(&self, area: i32) -> u32 {
879        let mut cover = area >> (POLY_SUBPIXEL_SHIFT * 2 + 1 - AA_SHIFT);
880        if cover < 0 {
881            cover = -cover;
882        }
883        if self.filling_rule == FillingRule::EvenOdd {
884            cover &= AA_MASK2 as i32;
885            if cover > AA_SCALE as i32 {
886                cover = AA_SCALE2 as i32 - cover;
887            }
888        }
889        if cover > AA_MASK as i32 {
890            cover = AA_MASK as i32;
891        }
892        cover as u32
893    }
894
895    /// Internal: add a style to the active style table.
896    fn add_style(&mut self, style_id: i32) {
897        let style_id = if style_id < 0 {
898            0
899        } else {
900            (style_id - self.min_style + 1) as u32
901        } as usize;
902
903        let nbyte = style_id >> 3;
904        let mask = 1u8 << (style_id & 7);
905
906        if (self.asm[nbyte] & mask) == 0 {
907            self.ast.push(style_id as u32);
908            self.asm[nbyte] |= mask;
909            self.styles[style_id].start_cell = 0;
910            self.styles[style_id].num_cells = 0;
911            self.styles[style_id].last_x = i32::MIN;
912        }
913        self.styles[style_id].start_cell += 1;
914    }
915
916    /// Process the current scanline and return the number of active styles.
917    ///
918    /// Call `style(idx)` to get the actual style ID for each index 0..n-1.
919    pub fn sweep_styles(&mut self) -> u32 {
920        loop {
921            if self.scan_y > self.outline.max_y() {
922                return 0;
923            }
924            let num_cells = self.outline.scanline_num_cells(self.scan_y as u32);
925            let cell_indices: Vec<u32> =
926                self.outline.scanline_cells(self.scan_y as u32).to_vec();
927
928            let num_styles = (self.max_style - self.min_style + 2) as usize;
929
930            self.cells
931                .resize(num_cells as usize * 2, CellInfo::default());
932            self.ast.clear();
933            self.ast.reserve(num_styles);
934            self.asm.clear();
935            self.asm.resize((num_styles + 7) >> 3, 0);
936
937            if num_cells > 0 {
938                // Pre-add the "no fill" style (index 0)
939                self.asm[0] |= 1;
940                self.ast.push(0);
941                self.styles[0].start_cell = 0;
942                self.styles[0].num_cells = 0;
943                self.styles[0].last_x = i32::MIN;
944
945                let first_x = self.outline.cell(cell_indices[0]).x;
946                let last_x = self.outline.cell(cell_indices[num_cells as usize - 1]).x;
947                self.sl_start = first_x;
948                self.sl_len = (last_x - first_x + 1) as u32;
949
950                // Pass 1: Count cells per style
951                // Copy left/right to avoid borrow conflict with add_style
952                let style_pairs: Vec<(i16, i16)> = (0..num_cells as usize)
953                    .map(|i| {
954                        let c = self.outline.cell(cell_indices[i]);
955                        (c.left, c.right)
956                    })
957                    .collect();
958                for &(left, right) in &style_pairs {
959                    self.add_style(left as i32);
960                    self.add_style(right as i32);
961                }
962
963                // Convert histogram to starting indices
964                let mut start_cell = 0u32;
965                for i in 0..self.ast.len() {
966                    let si = self.ast[i] as usize;
967                    let v = self.styles[si].start_cell;
968                    self.styles[si].start_cell = start_cell;
969                    start_cell += v;
970                }
971
972                // Pass 2: Distribute cells to styles
973                let cell_indices2: Vec<u32> =
974                    self.outline.scanline_cells(self.scan_y as u32).to_vec();
975                for i in 0..num_cells as usize {
976                    let curr_cell = self.outline.cell(cell_indices2[i]);
977
978                    // Left style: add
979                    let style_id = if curr_cell.left < 0 {
980                        0usize
981                    } else {
982                        (curr_cell.left as i32 - self.min_style + 1) as usize
983                    };
984
985                    let style = &mut self.styles[style_id];
986                    if curr_cell.x == style.last_x {
987                        let ci = (style.start_cell + style.num_cells - 1) as usize;
988                        self.cells[ci].area += curr_cell.area;
989                        self.cells[ci].cover += curr_cell.cover;
990                    } else {
991                        let ci = (style.start_cell + style.num_cells) as usize;
992                        self.cells[ci].x = curr_cell.x;
993                        self.cells[ci].area = curr_cell.area;
994                        self.cells[ci].cover = curr_cell.cover;
995                        style.last_x = curr_cell.x;
996                        style.num_cells += 1;
997                    }
998
999                    // Right style: subtract
1000                    let style_id = if curr_cell.right < 0 {
1001                        0usize
1002                    } else {
1003                        (curr_cell.right as i32 - self.min_style + 1) as usize
1004                    };
1005
1006                    let style = &mut self.styles[style_id];
1007                    if curr_cell.x == style.last_x {
1008                        let ci = (style.start_cell + style.num_cells - 1) as usize;
1009                        self.cells[ci].area -= curr_cell.area;
1010                        self.cells[ci].cover -= curr_cell.cover;
1011                    } else {
1012                        let ci = (style.start_cell + style.num_cells) as usize;
1013                        self.cells[ci].x = curr_cell.x;
1014                        self.cells[ci].area = -curr_cell.area;
1015                        self.cells[ci].cover = -curr_cell.cover;
1016                        style.last_x = curr_cell.x;
1017                        style.num_cells += 1;
1018                    }
1019                }
1020            }
1021
1022            if self.ast.len() > 1 {
1023                break;
1024            }
1025            self.scan_y += 1;
1026        }
1027        self.scan_y += 1;
1028
1029        // Sort styles by ID if requested
1030        if self.layer_order != LayerOrder::Unsorted && self.ast.len() > 1 {
1031            let ast_slice = &mut self.ast[1..];
1032            match self.layer_order {
1033                LayerOrder::Direct => ast_slice.sort_unstable_by(|a, b| b.cmp(a)),
1034                LayerOrder::Inverse => ast_slice.sort_unstable(),
1035                LayerOrder::Unsorted => {}
1036            }
1037        }
1038
1039        (self.ast.len() - 1) as u32
1040    }
1041
1042    /// Get the actual style ID for the given style index (0-based).
1043    #[inline]
1044    pub fn style(&self, style_idx: u32) -> u32 {
1045        (self.ast[style_idx as usize + 1] as i32 + self.min_style - 1) as u32
1046    }
1047
1048    /// Get the X start of the current scanline.
1049    pub fn scanline_start(&self) -> i32 {
1050        self.sl_start
1051    }
1052
1053    /// Get the length of the current scanline.
1054    pub fn scanline_length(&self) -> u32 {
1055        self.sl_len
1056    }
1057
1058    /// Sweep one scanline for one style.
1059    ///
1060    /// `style_idx` is -1 for the "no fill" style, or 0..n-1 for actual styles.
1061    pub fn sweep_scanline<SL: Scanline>(&self, sl: &mut SL, style_idx: i32) -> bool {
1062        let scan_y = self.scan_y - 1;
1063        if scan_y > self.outline.max_y() {
1064            return false;
1065        }
1066
1067        sl.reset_spans();
1068
1069        let si = if style_idx < 0 {
1070            0usize
1071        } else {
1072            (style_idx + 1) as usize
1073        };
1074
1075        let st = &self.styles[self.ast[si] as usize];
1076        let mut num_cells = st.num_cells;
1077        let mut cell_idx = st.start_cell;
1078
1079        let mut cover = 0i32;
1080        while num_cells > 0 {
1081            num_cells -= 1;
1082            let cell = &self.cells[cell_idx as usize];
1083            let x = cell.x;
1084            let area = cell.area;
1085            cover += cell.cover;
1086            cell_idx += 1;
1087
1088            if area != 0 {
1089                let alpha =
1090                    self.calculate_alpha((cover << (POLY_SUBPIXEL_SHIFT + 1)) - area as u32 as i32);
1091                sl.add_cell(x, alpha);
1092                if num_cells > 0 && self.cells[cell_idx as usize].x > x + 1 {
1093                    let alpha = self.calculate_alpha(cover << (POLY_SUBPIXEL_SHIFT + 1));
1094                    if alpha > 0 {
1095                        sl.add_span(
1096                            x + 1,
1097                            (self.cells[cell_idx as usize].x - x - 1) as u32,
1098                            alpha,
1099                        );
1100                    }
1101                }
1102            } else if num_cells > 0 && self.cells[cell_idx as usize].x > x {
1103                let alpha = self.calculate_alpha(cover << (POLY_SUBPIXEL_SHIFT + 1));
1104                if alpha > 0 {
1105                    sl.add_span(x, (self.cells[cell_idx as usize].x - x) as u32, alpha);
1106                }
1107            }
1108        }
1109
1110        if sl.num_spans() == 0 {
1111            return false;
1112        }
1113        sl.finalize(scan_y);
1114        true
1115    }
1116
1117    /// Navigate to a specific scanline.
1118    pub fn navigate_scanline(&mut self, y: i32) -> bool {
1119        self.outline.sort_cells();
1120        if self.outline.total_cells() == 0 {
1121            return false;
1122        }
1123        if self.max_style < self.min_style {
1124            return false;
1125        }
1126        if y < self.outline.min_y() || y > self.outline.max_y() {
1127            return false;
1128        }
1129        self.scan_y = y;
1130        let num_styles = (self.max_style - self.min_style + 2) as usize;
1131        self.styles.resize(num_styles, StyleInfo::default());
1132        true
1133    }
1134
1135    /// Allocate a cover buffer of the given length. Returns a mutable slice.
1136    pub fn allocate_cover_buffer(&mut self, len: u32) -> &mut [u8] {
1137        self.cover_buf.resize(len as usize, 0);
1138        &mut self.cover_buf
1139    }
1140}
1141
1142impl Default for RasterizerCompoundAa {
1143    fn default() -> Self {
1144        Self::new()
1145    }
1146}
1147
1148// ============================================================================
1149// Tests
1150// ============================================================================
1151
1152#[cfg(test)]
1153mod tests {
1154    use super::*;
1155    use crate::scanline_u::ScanlineU8;
1156
1157    #[test]
1158    fn test_cell_style_aa_default() {
1159        let cell = CellStyleAa::default();
1160        assert_eq!(cell.x, i32::MAX);
1161        assert_eq!(cell.y, i32::MAX);
1162        assert_eq!(cell.left, -1);
1163        assert_eq!(cell.right, -1);
1164    }
1165
1166    #[test]
1167    fn test_cell_style_aa_not_equal() {
1168        let mut c1 = CellStyleAa::default();
1169        c1.x = 10;
1170        c1.y = 20;
1171        c1.left = 1;
1172        c1.right = 2;
1173
1174        let mut style = CellStyleAa::default();
1175        style.left = 1;
1176        style.right = 2;
1177        // Same position and style
1178        assert!(!c1.not_equal(10, 20, &style));
1179        // Different position
1180        assert!(c1.not_equal(11, 20, &style));
1181        // Different style
1182        style.left = 3;
1183        assert!(c1.not_equal(10, 20, &style));
1184    }
1185
1186    #[test]
1187    fn test_empty_rasterizer() {
1188        let mut ras = RasterizerCompoundAa::new();
1189        assert!(!ras.rewind_scanlines());
1190    }
1191
1192    #[test]
1193    fn test_simple_rectangle_two_styles() {
1194        let mut ras = RasterizerCompoundAa::new();
1195
1196        // Draw a simple rectangle with style 0 on the left
1197        // Using subpixel coords (multiply by 256)
1198        let scale = POLY_SUBPIXEL_SCALE as i32;
1199
1200        // Left edge going down (style 0 on left, -1 on right)
1201        ras.styles(0, -1);
1202        ras.move_to(10 * scale, 10 * scale);
1203        ras.line_to(10 * scale, 20 * scale);
1204
1205        // Bottom edge going right (style -1 on left, 0 on right)
1206        ras.styles(-1, 0);
1207        ras.line_to(20 * scale, 20 * scale);
1208
1209        // Right edge going up (-1 on left, 0 on right)
1210        ras.styles(-1, 0);
1211        ras.line_to(20 * scale, 10 * scale);
1212
1213        // Top edge going left (style 0 on left, -1 on right)
1214        ras.styles(0, -1);
1215        ras.line_to(10 * scale, 10 * scale);
1216
1217        assert!(ras.rewind_scanlines());
1218        assert_eq!(ras.min_style(), 0);
1219        assert_eq!(ras.max_style(), 0);
1220
1221        // Sweep at least one scanline
1222        let num_styles = ras.sweep_styles();
1223        assert!(num_styles >= 1, "Expected at least 1 style, got {num_styles}");
1224    }
1225
1226    #[test]
1227    fn test_two_adjacent_styles() {
1228        let mut ras = RasterizerCompoundAa::new();
1229        let s = POLY_SUBPIXEL_SCALE as i32;
1230
1231        // Two rectangles side by side: style 0 (left) and style 1 (right)
1232        // Shared edge at x=15
1233
1234        // Left rectangle: style 0
1235        ras.styles(0, -1);
1236        ras.move_to(10 * s, 10 * s);
1237        ras.line_to(10 * s, 20 * s);
1238
1239        // Shared boundary edge: style 0 on left, style 1 on right
1240        ras.styles(0, 1);
1241        ras.line_to(15 * s, 20 * s);
1242        ras.line_to(15 * s, 10 * s);
1243
1244        ras.styles(0, -1);
1245        ras.line_to(10 * s, 10 * s);
1246
1247        // Right rectangle: style 1
1248        ras.styles(1, -1);
1249        ras.move_to(15 * s, 10 * s);
1250        ras.line_to(15 * s, 20 * s);
1251
1252        ras.styles(-1, 1);
1253        ras.line_to(20 * s, 20 * s);
1254        ras.line_to(20 * s, 10 * s);
1255
1256        ras.styles(1, -1);
1257        ras.line_to(15 * s, 10 * s);
1258
1259        assert!(ras.rewind_scanlines());
1260        assert_eq!(ras.min_style(), 0);
1261        assert_eq!(ras.max_style(), 1);
1262    }
1263
1264    #[test]
1265    fn test_sweep_scanline_produces_spans() {
1266        let mut ras = RasterizerCompoundAa::new();
1267        let s = POLY_SUBPIXEL_SCALE as i32;
1268
1269        // Simple filled rectangle with style 0
1270        ras.styles(0, -1);
1271        ras.move_to(10 * s, 10 * s);
1272        ras.line_to(10 * s, 20 * s);
1273
1274        ras.styles(-1, 0);
1275        ras.line_to(20 * s, 20 * s);
1276        ras.line_to(20 * s, 10 * s);
1277
1278        ras.styles(0, -1);
1279        ras.line_to(10 * s, 10 * s);
1280
1281        assert!(ras.rewind_scanlines());
1282
1283        let num_styles = ras.sweep_styles();
1284        assert!(num_styles >= 1);
1285
1286        let mut sl = ScanlineU8::new();
1287        sl.reset(0, 100);
1288        // Sweep scanline for style index 0
1289        let ok = ras.sweep_scanline(&mut sl, 0);
1290        if ok {
1291            assert!(sl.num_spans() > 0);
1292        }
1293    }
1294
1295    #[test]
1296    fn test_layer_order() {
1297        let mut ras = RasterizerCompoundAa::new();
1298        ras.layer_order(LayerOrder::Direct);
1299        ras.layer_order(LayerOrder::Inverse);
1300        ras.layer_order(LayerOrder::Unsorted);
1301    }
1302
1303    #[test]
1304    fn test_calculate_alpha() {
1305        let ras = RasterizerCompoundAa::new();
1306        // Full coverage
1307        let full_area = (POLY_SUBPIXEL_SCALE * POLY_SUBPIXEL_SCALE * 2) as i32;
1308        let alpha = ras.calculate_alpha(full_area);
1309        assert_eq!(alpha, AA_MASK);
1310
1311        // Zero coverage
1312        assert_eq!(ras.calculate_alpha(0), 0);
1313    }
1314
1315    #[test]
1316    fn test_add_path() {
1317        use crate::path_storage::PathStorage;
1318
1319        let mut ras = RasterizerCompoundAa::new();
1320        let mut path = PathStorage::new();
1321        path.move_to(10.0, 10.0);
1322        path.line_to(20.0, 10.0);
1323        path.line_to(20.0, 20.0);
1324        path.close_polygon(0);
1325
1326        ras.styles(0, -1);
1327        ras.add_path(&mut path, 0);
1328
1329        assert!(ras.rewind_scanlines());
1330    }
1331
1332    #[test]
1333    fn test_edge_methods() {
1334        let mut ras = RasterizerCompoundAa::new();
1335        let s = POLY_SUBPIXEL_SCALE as i32;
1336
1337        ras.styles(0, -1);
1338        ras.edge(10 * s, 10 * s, 20 * s, 10 * s);
1339        ras.edge(20 * s, 10 * s, 20 * s, 20 * s);
1340        ras.edge(20 * s, 20 * s, 10 * s, 20 * s);
1341        ras.edge(10 * s, 20 * s, 10 * s, 10 * s);
1342
1343        assert!(ras.rewind_scanlines());
1344    }
1345
1346    #[test]
1347    fn test_clip_box() {
1348        let mut ras = RasterizerCompoundAa::new();
1349        ras.clip_box(0.0, 0.0, 100.0, 100.0);
1350
1351        let s = POLY_SUBPIXEL_SCALE as i32;
1352        ras.styles(0, -1);
1353        ras.move_to(10 * s, 10 * s);
1354        ras.line_to(10 * s, 20 * s);
1355        ras.line_to(20 * s, 20 * s);
1356        ras.line_to(20 * s, 10 * s);
1357        ras.line_to(10 * s, 10 * s);
1358
1359        assert!(ras.rewind_scanlines());
1360    }
1361
1362    #[test]
1363    fn test_allocate_cover_buffer() {
1364        let mut ras = RasterizerCompoundAa::new();
1365        let buf = ras.allocate_cover_buffer(100);
1366        assert_eq!(buf.len(), 100);
1367    }
1368}