1use crate::rasterizer_scanline_aa::{RasterizerScanlineAa, Scanline};
8use crate::scanline_storage_aa::ScanlineStorageAa;
9use crate::scanline_storage_bin::ScanlineStorageBin;
10use crate::scanline_u::ScanlineU8;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum SBoolOp {
15 Or,
16 And,
17 Xor,
18 XorSaddle,
19 XorAbsDiff,
20 AMinusB,
21 BMinusA,
22}
23
24const COVER_FULL: u32 = 255;
25const COVER_SHIFT: u32 = 8;
26
27#[inline]
34fn intersect_covers(c1: u32, c2: u32) -> u8 {
35 let cover = c1 * c2;
36 if cover == COVER_FULL * COVER_FULL {
37 COVER_FULL as u8
38 } else {
39 (cover >> COVER_SHIFT) as u8
40 }
41}
42
43#[inline]
46fn unite_covers(c1: u32, c2: u32) -> u8 {
47 let cover = COVER_FULL * COVER_FULL - (COVER_FULL - c1) * (COVER_FULL - c2);
48 if cover == COVER_FULL * COVER_FULL {
49 COVER_FULL as u8
50 } else {
51 (cover >> COVER_SHIFT) as u8
52 }
53}
54
55#[inline]
58fn subtract_covers(c1: u32, c2: u32) -> u8 {
59 let cover = c1 * (COVER_FULL - c2);
60 if cover == COVER_FULL * COVER_FULL {
61 COVER_FULL as u8
62 } else {
63 (cover >> COVER_SHIFT) as u8
64 }
65}
66
67#[inline]
70fn xor_covers_linear(c1: u32, c2: u32) -> u8 {
71 let cover = c1 + c2;
72 if cover > COVER_FULL {
73 (COVER_FULL + COVER_FULL - cover) as u8
74 } else {
75 cover as u8
76 }
77}
78
79#[inline]
86fn xor_covers_saddle(c1: u32, c2: u32) -> u8 {
87 let k = c1 * c2;
88 if k == COVER_FULL * COVER_FULL {
89 return 0;
90 }
91 let a = (COVER_FULL * COVER_FULL - (c1 << COVER_SHIFT) + k) >> COVER_SHIFT;
92 let b = (COVER_FULL * COVER_FULL - (c2 << COVER_SHIFT) + k) >> COVER_SHIFT;
93 (COVER_FULL - ((a * b) >> COVER_SHIFT)) as u8
94}
95
96#[inline]
99fn xor_covers_abs_diff(c1: u32, c2: u32) -> u8 {
100 (c1 as i32 - c2 as i32).unsigned_abs() as u8
101}
102
103pub fn sbool_combine_shapes_aa(
111 op: SBoolOp,
112 ras1: &mut RasterizerScanlineAa,
113 ras2: &mut RasterizerScanlineAa,
114 sl1: &mut ScanlineU8,
115 sl2: &mut ScanlineU8,
116 sl_result: &mut ScanlineU8,
117 storage1: &mut ScanlineStorageAa,
118 storage2: &mut ScanlineStorageAa,
119 storage_result: &mut ScanlineStorageAa,
120) {
121 storage1.prepare();
123 render_to_storage(ras1, sl1, storage1);
124
125 storage2.prepare();
127 render_to_storage(ras2, sl2, storage2);
128
129 storage_result.prepare();
131 sbool_combine_storages_aa(op, storage1, storage2, sl_result, storage_result);
132}
133
134fn render_to_storage(
136 ras: &mut RasterizerScanlineAa,
137 sl: &mut ScanlineU8,
138 storage: &mut ScanlineStorageAa,
139) {
140 if ras.rewind_scanlines() {
141 sl.reset(ras.min_x(), ras.max_x());
142 while ras.sweep_scanline(sl) {
143 storage.render_scanline_u8(sl);
144 }
145 }
146}
147
148pub fn sbool_combine_storages_aa(
150 op: SBoolOp,
151 storage1: &ScanlineStorageAa,
152 storage2: &ScanlineStorageAa,
153 sl: &mut ScanlineU8,
154 result: &mut ScanlineStorageAa,
155) {
156 let n1 = storage1.num_scanlines();
157 let n2 = storage2.num_scanlines();
158
159 if n1 == 0 && n2 == 0 {
160 return;
161 }
162
163 let min_x = if n1 > 0 && n2 > 0 {
165 storage1.min_x().min(storage2.min_x())
166 } else if n1 > 0 {
167 storage1.min_x()
168 } else {
169 storage2.min_x()
170 };
171 let max_x = if n1 > 0 && n2 > 0 {
172 storage1.max_x().max(storage2.max_x())
173 } else if n1 > 0 {
174 storage1.max_x()
175 } else {
176 storage2.max_x()
177 };
178
179 match op {
181 SBoolOp::Or | SBoolOp::Xor | SBoolOp::XorSaddle | SBoolOp::XorAbsDiff => {
182 if n1 == 0 {
184 copy_storage_aa(storage2, sl, result, min_x, max_x);
185 return;
186 }
187 if n2 == 0 {
188 copy_storage_aa(storage1, sl, result, min_x, max_x);
189 return;
190 }
191 }
192 SBoolOp::And => {
193 if n1 == 0 || n2 == 0 {
195 return;
196 }
197 }
198 SBoolOp::AMinusB => {
199 if n1 == 0 {
200 return; }
202 if n2 == 0 {
203 copy_storage_aa(storage1, sl, result, min_x, max_x);
204 return;
205 }
206 }
207 SBoolOp::BMinusA => {
208 if n2 == 0 {
209 return;
210 }
211 if n1 == 0 {
212 copy_storage_aa(storage2, sl, result, min_x, max_x);
213 return;
214 }
215 }
216 }
217
218 let mut i1 = 0usize;
220 let mut i2 = 0usize;
221
222 while i1 < n1 || i2 < n2 {
223 if i1 >= n1 {
224 match op {
226 SBoolOp::Or | SBoolOp::Xor | SBoolOp::XorSaddle | SBoolOp::XorAbsDiff | SBoolOp::BMinusA => {
227 emit_scanline_from_storage(storage2, i2, sl, result, min_x, max_x);
228 }
229 _ => {} }
231 i2 += 1;
232 continue;
233 }
234 if i2 >= n2 {
235 match op {
237 SBoolOp::Or | SBoolOp::Xor | SBoolOp::XorSaddle | SBoolOp::XorAbsDiff | SBoolOp::AMinusB => {
238 emit_scanline_from_storage(storage1, i1, sl, result, min_x, max_x);
239 }
240 _ => {} }
242 i1 += 1;
243 continue;
244 }
245
246 let y1 = storage1.scanline_y(i1);
247 let y2 = storage2.scanline_y(i2);
248
249 if y1 < y2 {
250 match op {
252 SBoolOp::Or | SBoolOp::Xor | SBoolOp::XorSaddle | SBoolOp::XorAbsDiff | SBoolOp::AMinusB => {
253 emit_scanline_from_storage(storage1, i1, sl, result, min_x, max_x);
254 }
255 _ => {}
256 }
257 i1 += 1;
258 } else if y2 < y1 {
259 match op {
261 SBoolOp::Or | SBoolOp::Xor | SBoolOp::XorSaddle | SBoolOp::XorAbsDiff | SBoolOp::BMinusA => {
262 emit_scanline_from_storage(storage2, i2, sl, result, min_x, max_x);
263 }
264 _ => {}
265 }
266 i2 += 1;
267 } else {
268 combine_scanlines_aa(op, storage1, i1, storage2, i2, sl, result, min_x, max_x);
270 i1 += 1;
271 i2 += 1;
272 }
273 }
274}
275
276fn copy_storage_aa(
278 src: &ScanlineStorageAa,
279 sl: &mut ScanlineU8,
280 result: &mut ScanlineStorageAa,
281 min_x: i32,
282 max_x: i32,
283) {
284 for i in 0..src.num_scanlines() {
285 emit_scanline_from_storage(src, i, sl, result, min_x, max_x);
286 }
287}
288
289fn emit_scanline_from_storage(
291 src: &ScanlineStorageAa,
292 sl_idx: usize,
293 sl: &mut ScanlineU8,
294 result: &mut ScanlineStorageAa,
295 min_x: i32,
296 max_x: i32,
297) {
298 let y = src.scanline_y(sl_idx);
299 sl.reset(min_x, max_x);
300 sl.reset_spans();
301
302 for sp in src.embedded_spans(sl_idx) {
303 if sp.len < 0 {
304 sl.add_span(sp.x, (-sp.len) as u32, sp.covers[0] as u32);
306 } else {
307 for j in 0..sp.len as usize {
308 sl.add_cell(sp.x + j as i32, sp.covers[j] as u32);
309 }
310 }
311 }
312 sl.finalize(y);
313 if sl.num_spans() > 0 {
314 result.render_scanline_u8(sl);
315 }
316}
317
318fn combine_scanlines_aa(
320 op: SBoolOp,
321 storage1: &ScanlineStorageAa,
322 sl_idx1: usize,
323 storage2: &ScanlineStorageAa,
324 sl_idx2: usize,
325 sl: &mut ScanlineU8,
326 result: &mut ScanlineStorageAa,
327 min_x: i32,
328 max_x: i32,
329) {
330 let y = storage1.scanline_y(sl_idx1);
331 sl.reset(min_x, max_x);
332 sl.reset_spans();
333
334 let width = (max_x - min_x + 1) as usize;
337 let mut cov1 = vec![0u8; width];
338 let mut cov2 = vec![0u8; width];
339
340 for sp in storage1.embedded_spans(sl_idx1) {
342 let abs_len = sp.abs_len();
343 for j in 0..abs_len {
344 let x = sp.x + j;
345 if x >= min_x && x <= max_x {
346 cov1[(x - min_x) as usize] = sp.cover_at(j as usize);
347 }
348 }
349 }
350
351 for sp in storage2.embedded_spans(sl_idx2) {
353 let abs_len = sp.abs_len();
354 for j in 0..abs_len {
355 let x = sp.x + j;
356 if x >= min_x && x <= max_x {
357 cov2[(x - min_x) as usize] = sp.cover_at(j as usize);
358 }
359 }
360 }
361
362 for i in 0..width {
364 let c1 = cov1[i] as u32;
365 let c2 = cov2[i] as u32;
366 let result_cover = match op {
367 SBoolOp::Or => {
368 if c1 > 0 && c2 > 0 {
369 unite_covers(c1, c2)
370 } else if c1 > 0 {
371 c1 as u8
372 } else {
373 c2 as u8
374 }
375 }
376 SBoolOp::And => intersect_covers(c1, c2),
377 SBoolOp::Xor => {
378 if c1 > 0 && c2 > 0 {
379 xor_covers_linear(c1, c2)
380 } else if c1 > 0 {
381 c1 as u8
382 } else {
383 c2 as u8
384 }
385 }
386 SBoolOp::XorSaddle => {
387 if c1 > 0 && c2 > 0 {
388 xor_covers_saddle(c1, c2)
389 } else if c1 > 0 {
390 c1 as u8
391 } else {
392 c2 as u8
393 }
394 }
395 SBoolOp::XorAbsDiff => {
396 if c1 > 0 && c2 > 0 {
397 xor_covers_abs_diff(c1, c2)
398 } else if c1 > 0 {
399 c1 as u8
400 } else {
401 c2 as u8
402 }
403 }
404 SBoolOp::AMinusB => subtract_covers(c1, c2),
405 SBoolOp::BMinusA => subtract_covers(c2, c1),
406 };
407 if result_cover > 0 {
408 sl.add_cell(min_x + i as i32, result_cover as u32);
409 }
410 }
411
412 sl.finalize(y);
413 if sl.num_spans() > 0 {
414 result.render_scanline_u8(sl);
415 }
416}
417
418pub fn sbool_combine_shapes_bin(
424 op: SBoolOp,
425 ras1: &mut RasterizerScanlineAa,
426 ras2: &mut RasterizerScanlineAa,
427 sl1: &mut ScanlineU8,
428 sl2: &mut ScanlineU8,
429 sl_result: &mut ScanlineU8,
430 storage1: &mut ScanlineStorageBin,
431 storage2: &mut ScanlineStorageBin,
432 storage_result: &mut ScanlineStorageBin,
433) {
434 storage1.prepare();
436 render_to_bin_storage(ras1, sl1, storage1);
437
438 storage2.prepare();
440 render_to_bin_storage(ras2, sl2, storage2);
441
442 storage_result.prepare();
444 sbool_combine_storages_bin(op, storage1, storage2, sl_result, storage_result);
445}
446
447fn render_to_bin_storage(
449 ras: &mut RasterizerScanlineAa,
450 sl: &mut ScanlineU8,
451 storage: &mut ScanlineStorageBin,
452) {
453 if ras.rewind_scanlines() {
454 sl.reset(ras.min_x(), ras.max_x());
455 while ras.sweep_scanline(sl) {
456 storage.render_scanline_u8(sl);
457 }
458 }
459}
460
461pub fn sbool_combine_storages_bin(
463 op: SBoolOp,
464 storage1: &ScanlineStorageBin,
465 storage2: &ScanlineStorageBin,
466 sl: &mut ScanlineU8,
467 result: &mut ScanlineStorageBin,
468) {
469 let n1 = storage1.num_scanlines();
470 let n2 = storage2.num_scanlines();
471
472 if n1 == 0 && n2 == 0 {
473 return;
474 }
475
476 let min_x = if n1 > 0 && n2 > 0 {
477 storage1.min_x().min(storage2.min_x())
478 } else if n1 > 0 {
479 storage1.min_x()
480 } else {
481 storage2.min_x()
482 };
483 let max_x = if n1 > 0 && n2 > 0 {
484 storage1.max_x().max(storage2.max_x())
485 } else if n1 > 0 {
486 storage1.max_x()
487 } else {
488 storage2.max_x()
489 };
490
491 match op {
492 SBoolOp::Or | SBoolOp::Xor | SBoolOp::XorSaddle | SBoolOp::XorAbsDiff => {
493 if n1 == 0 {
494 copy_storage_bin(storage2, sl, result, min_x, max_x);
495 return;
496 }
497 if n2 == 0 {
498 copy_storage_bin(storage1, sl, result, min_x, max_x);
499 return;
500 }
501 }
502 SBoolOp::And => {
503 if n1 == 0 || n2 == 0 {
504 return;
505 }
506 }
507 SBoolOp::AMinusB => {
508 if n1 == 0 {
509 return;
510 }
511 if n2 == 0 {
512 copy_storage_bin(storage1, sl, result, min_x, max_x);
513 return;
514 }
515 }
516 SBoolOp::BMinusA => {
517 if n2 == 0 {
518 return;
519 }
520 if n1 == 0 {
521 copy_storage_bin(storage2, sl, result, min_x, max_x);
522 return;
523 }
524 }
525 }
526
527 let mut i1 = 0usize;
528 let mut i2 = 0usize;
529
530 while i1 < n1 || i2 < n2 {
531 if i1 >= n1 {
532 match op {
533 SBoolOp::Or | SBoolOp::Xor | SBoolOp::XorSaddle | SBoolOp::XorAbsDiff | SBoolOp::BMinusA => {
534 emit_scanline_from_bin_storage(storage2, i2, sl, result, min_x, max_x);
535 }
536 _ => {}
537 }
538 i2 += 1;
539 continue;
540 }
541 if i2 >= n2 {
542 match op {
543 SBoolOp::Or | SBoolOp::Xor | SBoolOp::XorSaddle | SBoolOp::XorAbsDiff | SBoolOp::AMinusB => {
544 emit_scanline_from_bin_storage(storage1, i1, sl, result, min_x, max_x);
545 }
546 _ => {}
547 }
548 i1 += 1;
549 continue;
550 }
551
552 let y1 = storage1.scanline_y(i1);
553 let y2 = storage2.scanline_y(i2);
554
555 if y1 < y2 {
556 match op {
557 SBoolOp::Or | SBoolOp::Xor | SBoolOp::XorSaddle | SBoolOp::XorAbsDiff | SBoolOp::AMinusB => {
558 emit_scanline_from_bin_storage(storage1, i1, sl, result, min_x, max_x);
559 }
560 _ => {}
561 }
562 i1 += 1;
563 } else if y2 < y1 {
564 match op {
565 SBoolOp::Or | SBoolOp::Xor | SBoolOp::XorSaddle | SBoolOp::XorAbsDiff | SBoolOp::BMinusA => {
566 emit_scanline_from_bin_storage(storage2, i2, sl, result, min_x, max_x);
567 }
568 _ => {}
569 }
570 i2 += 1;
571 } else {
572 combine_scanlines_bin(op, storage1, i1, storage2, i2, sl, result, min_x, max_x);
573 i1 += 1;
574 i2 += 1;
575 }
576 }
577}
578
579fn copy_storage_bin(
580 src: &ScanlineStorageBin,
581 sl: &mut ScanlineU8,
582 result: &mut ScanlineStorageBin,
583 min_x: i32,
584 max_x: i32,
585) {
586 let mut sl_bin = crate::scanline_bin::ScanlineBin::new();
588 for i in 0..src.num_scanlines() {
589 let y = src.scanline_y(i);
590 sl_bin.reset(min_x, max_x);
591 sl_bin.reset_spans();
592 for sp in src.embedded_spans(i) {
593 sl_bin.add_span(sp.x, sp.len as u32, COVER_FULL);
594 }
595 sl_bin.finalize(y);
596 if sl_bin.num_spans() > 0 {
597 result.render_scanline_bin(&sl_bin);
598 }
599 }
600 let _ = sl; }
602
603fn emit_scanline_from_bin_storage(
604 src: &ScanlineStorageBin,
605 sl_idx: usize,
606 sl: &mut ScanlineU8,
607 result: &mut ScanlineStorageBin,
608 min_x: i32,
609 max_x: i32,
610) {
611 let y = src.scanline_y(sl_idx);
612 let mut sl_bin = crate::scanline_bin::ScanlineBin::new();
613 sl_bin.reset(min_x, max_x);
614 sl_bin.reset_spans();
615 for sp in src.embedded_spans(sl_idx) {
616 sl_bin.add_span(sp.x, sp.len as u32, COVER_FULL);
617 }
618 sl_bin.finalize(y);
619 if sl_bin.num_spans() > 0 {
620 result.render_scanline_bin(&sl_bin);
621 }
622 let _ = sl; }
624
625fn combine_scanlines_bin(
626 op: SBoolOp,
627 storage1: &ScanlineStorageBin,
628 sl_idx1: usize,
629 storage2: &ScanlineStorageBin,
630 sl_idx2: usize,
631 sl: &mut ScanlineU8,
632 result: &mut ScanlineStorageBin,
633 min_x: i32,
634 max_x: i32,
635) {
636 let y = storage1.scanline_y(sl_idx1);
637 let width = (max_x - min_x + 1) as usize;
638 let mut bits1 = vec![false; width];
639 let mut bits2 = vec![false; width];
640
641 for sp in storage1.embedded_spans(sl_idx1) {
643 for j in 0..sp.len {
644 let x = sp.x + j;
645 if x >= min_x && x <= max_x {
646 bits1[(x - min_x) as usize] = true;
647 }
648 }
649 }
650
651 for sp in storage2.embedded_spans(sl_idx2) {
653 for j in 0..sp.len {
654 let x = sp.x + j;
655 if x >= min_x && x <= max_x {
656 bits2[(x - min_x) as usize] = true;
657 }
658 }
659 }
660
661 let mut sl_bin = crate::scanline_bin::ScanlineBin::new();
663 sl_bin.reset(min_x, max_x);
664 sl_bin.reset_spans();
665
666 for i in 0..width {
667 let in1 = bits1[i];
668 let in2 = bits2[i];
669 let result_on = match op {
670 SBoolOp::Or => in1 || in2,
671 SBoolOp::And => in1 && in2,
672 SBoolOp::Xor | SBoolOp::XorSaddle | SBoolOp::XorAbsDiff => in1 ^ in2,
673 SBoolOp::AMinusB => in1 && !in2,
674 SBoolOp::BMinusA => in2 && !in1,
675 };
676 if result_on {
677 sl_bin.add_cell(min_x + i as i32, COVER_FULL);
678 }
679 }
680
681 sl_bin.finalize(y);
682 if sl_bin.num_spans() > 0 {
683 result.render_scanline_bin(&sl_bin);
684 }
685 let _ = sl; }
687
688#[cfg(test)]
689mod tests {
690 use super::*;
691
692 fn make_aa_rect(
694 x1: i32,
695 x2: i32,
696 y1: i32,
697 y2: i32,
698 cover: u8,
699 ) -> ScanlineStorageAa {
700 let mut storage = ScanlineStorageAa::new();
701 storage.prepare();
702 let mut sl = ScanlineU8::new();
703 for y in y1..=y2 {
704 sl.reset(x1, x2);
705 sl.reset_spans();
706 for x in x1..=x2 {
707 sl.add_cell(x, cover as u32);
708 }
709 sl.finalize(y);
710 storage.render_scanline_u8(&sl);
711 }
712 storage
713 }
714
715 fn make_bin_rect(x1: i32, x2: i32, y1: i32, y2: i32) -> ScanlineStorageBin {
717 let mut storage = ScanlineStorageBin::new();
718 storage.prepare();
719 let mut sl = crate::scanline_bin::ScanlineBin::new();
720 for y in y1..=y2 {
721 sl.reset(x1, x2);
722 sl.reset_spans();
723 sl.add_span(x1, (x2 - x1 + 1) as u32, 255);
724 sl.finalize(y);
725 storage.render_scanline_bin(&sl);
726 }
727 storage
728 }
729
730 #[test]
731 fn test_aa_intersection() {
732 let s1 = make_aa_rect(0, 19, 0, 19, 255);
734 let s2 = make_aa_rect(10, 29, 10, 29, 255);
735
736 let mut result = ScanlineStorageAa::new();
737 let mut sl = ScanlineU8::new();
738 sbool_combine_storages_aa(SBoolOp::And, &s1, &s2, &mut sl, &mut result);
739
740 assert_eq!(result.num_scanlines(), 10);
742 assert_eq!(result.min_x(), 10);
743 assert_eq!(result.max_x(), 19);
744 assert_eq!(result.min_y(), 10);
745 assert_eq!(result.max_y(), 19);
746 }
747
748 #[test]
749 fn test_aa_union() {
750 let s1 = make_aa_rect(0, 9, 0, 4, 255);
751 let s2 = make_aa_rect(5, 14, 0, 4, 255);
752
753 let mut result = ScanlineStorageAa::new();
754 let mut sl = ScanlineU8::new();
755 sbool_combine_storages_aa(SBoolOp::Or, &s1, &s2, &mut sl, &mut result);
756
757 assert_eq!(result.num_scanlines(), 5);
759 assert_eq!(result.min_x(), 0);
760 assert_eq!(result.max_x(), 14);
761 }
762
763 #[test]
764 fn test_aa_subtract() {
765 let s1 = make_aa_rect(0, 19, 0, 19, 255);
766 let s2 = make_aa_rect(10, 29, 10, 29, 255);
767
768 let mut result = ScanlineStorageAa::new();
769 let mut sl = ScanlineU8::new();
770 sbool_combine_storages_aa(SBoolOp::AMinusB, &s1, &s2, &mut sl, &mut result);
771
772 assert_eq!(result.num_scanlines(), 20);
774 }
775
776 #[test]
777 fn test_aa_xor() {
778 let s1 = make_aa_rect(0, 9, 0, 4, 200);
779 let s2 = make_aa_rect(5, 14, 0, 4, 200);
780
781 let mut result = ScanlineStorageAa::new();
782 let mut sl = ScanlineU8::new();
783 sbool_combine_storages_aa(SBoolOp::Xor, &s1, &s2, &mut sl, &mut result);
784
785 assert_eq!(result.num_scanlines(), 5);
787 }
788
789 #[test]
790 fn test_aa_intersect_no_overlap() {
791 let s1 = make_aa_rect(0, 9, 0, 4, 255);
792 let s2 = make_aa_rect(20, 29, 10, 14, 255);
793
794 let mut result = ScanlineStorageAa::new();
795 let mut sl = ScanlineU8::new();
796 sbool_combine_storages_aa(SBoolOp::And, &s1, &s2, &mut sl, &mut result);
797
798 assert_eq!(result.num_scanlines(), 0);
800 }
801
802 #[test]
803 fn test_aa_union_disjoint() {
804 let s1 = make_aa_rect(0, 4, 0, 2, 255);
805 let s2 = make_aa_rect(10, 14, 5, 7, 255);
806
807 let mut result = ScanlineStorageAa::new();
808 let mut sl = ScanlineU8::new();
809 sbool_combine_storages_aa(SBoolOp::Or, &s1, &s2, &mut sl, &mut result);
810
811 assert_eq!(result.num_scanlines(), 6);
813 }
814
815 #[test]
816 fn test_aa_empty_operand() {
817 let s1 = make_aa_rect(0, 9, 0, 4, 255);
818 let s2 = ScanlineStorageAa::new();
819
820 let mut result = ScanlineStorageAa::new();
821 let mut sl = ScanlineU8::new();
822
823 sbool_combine_storages_aa(SBoolOp::Or, &s1, &s2, &mut sl, &mut result);
825 assert_eq!(result.num_scanlines(), 5);
826
827 result.prepare();
829 sbool_combine_storages_aa(SBoolOp::And, &s1, &s2, &mut sl, &mut result);
830 assert_eq!(result.num_scanlines(), 0);
831 }
832
833 #[test]
834 fn test_aa_semi_transparent_intersection() {
835 let s1 = make_aa_rect(0, 9, 0, 0, 128);
837 let s2 = make_aa_rect(0, 9, 0, 0, 128);
838
839 let mut result = ScanlineStorageAa::new();
840 let mut sl = ScanlineU8::new();
841 sbool_combine_storages_aa(SBoolOp::And, &s1, &s2, &mut sl, &mut result);
842
843 assert_eq!(result.num_scanlines(), 1);
845
846 assert!(result.rewind_scanlines());
848 let mut sl_out = ScanlineU8::new();
849 sl_out.reset(0, 9);
850 assert!(result.sweep_scanline(&mut sl_out));
851 let spans = sl_out.begin();
852 let covers = sl_out.covers();
853 let cover = covers[spans[0].cover_offset];
855 assert_eq!(cover, 64);
856 }
857
858 #[test]
859 fn test_bin_intersection() {
860 let s1 = make_bin_rect(0, 19, 0, 19);
861 let s2 = make_bin_rect(10, 29, 10, 29);
862
863 let mut result = ScanlineStorageBin::new();
864 let mut sl = ScanlineU8::new();
865 sbool_combine_storages_bin(SBoolOp::And, &s1, &s2, &mut sl, &mut result);
866
867 assert_eq!(result.num_scanlines(), 10);
868 }
869
870 #[test]
871 fn test_bin_union() {
872 let s1 = make_bin_rect(0, 4, 0, 2);
873 let s2 = make_bin_rect(10, 14, 5, 7);
874
875 let mut result = ScanlineStorageBin::new();
876 let mut sl = ScanlineU8::new();
877 sbool_combine_storages_bin(SBoolOp::Or, &s1, &s2, &mut sl, &mut result);
878
879 assert_eq!(result.num_scanlines(), 6);
880 }
881
882 #[test]
883 fn test_bin_xor() {
884 let s1 = make_bin_rect(0, 9, 0, 4);
885 let s2 = make_bin_rect(5, 14, 0, 4);
886
887 let mut result = ScanlineStorageBin::new();
888 let mut sl = ScanlineU8::new();
889 sbool_combine_storages_bin(SBoolOp::Xor, &s1, &s2, &mut sl, &mut result);
890
891 assert_eq!(result.num_scanlines(), 5);
892 }
894
895 #[test]
896 fn test_bin_subtract() {
897 let s1 = make_bin_rect(0, 19, 0, 19);
898 let s2 = make_bin_rect(10, 29, 10, 29);
899
900 let mut result = ScanlineStorageBin::new();
901 let mut sl = ScanlineU8::new();
902 sbool_combine_storages_bin(SBoolOp::AMinusB, &s1, &s2, &mut sl, &mut result);
903
904 assert_eq!(result.num_scanlines(), 20);
907 }
908
909 #[test]
910 fn test_cover_math_intersect() {
911 assert_eq!(intersect_covers(255, 255), 255); assert_eq!(intersect_covers(0, 255), 0);
913 assert_eq!(intersect_covers(128, 128), 64); }
915
916 #[test]
917 fn test_cover_math_unite() {
918 assert_eq!(unite_covers(255, 255), 255);
919 assert_eq!(unite_covers(0, 0), 0);
920 assert_eq!(unite_covers(0, 255), 255); assert_eq!(unite_covers(128, 128), 191);
923 }
924
925 #[test]
926 fn test_cover_math_subtract() {
927 assert_eq!(subtract_covers(255, 0), 255); assert_eq!(subtract_covers(255, 255), 0);
929 assert_eq!(subtract_covers(0, 255), 0);
930 }
931
932 #[test]
933 fn test_cover_math_xor_linear() {
934 assert_eq!(xor_covers_linear(0, 0), 0);
935 assert_eq!(xor_covers_linear(255, 0), 255);
936 assert_eq!(xor_covers_linear(0, 255), 255);
937 assert_eq!(xor_covers_linear(255, 255), 0); }
939
940 #[test]
941 fn test_cover_math_xor_saddle() {
942 assert!(xor_covers_saddle(0, 0) <= 5); assert_eq!(xor_covers_saddle(255, 255), 0);
947 let saddle = xor_covers_saddle(128, 128);
950 assert!(saddle > 0 && saddle < 255);
951 let linear = xor_covers_linear(128, 128);
953 assert_ne!(saddle, linear);
954 }
955
956 #[test]
957 fn test_cover_math_xor_abs_diff() {
958 assert_eq!(xor_covers_abs_diff(0, 0), 0);
959 assert_eq!(xor_covers_abs_diff(255, 255), 0);
960 assert_eq!(xor_covers_abs_diff(200, 100), 100);
961 assert_eq!(xor_covers_abs_diff(100, 200), 100);
962 }
963}