Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
ClipStackTest.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
10#include "include/core/SkPath.h"
13#include "include/core/SkRect.h"
19#include "tests/Test.h"
20
21#include <array>
22#include <cstring>
23#include <initializer_list>
24
27 bool doAA = false;
28
29 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
30
31 // Build up a clip stack with a path, an empty clip, and a rect.
32 s.save();
33 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
34
35 SkPath p;
36 p.moveTo(5, 6);
37 p.lineTo(7, 8);
38 p.lineTo(5, 9);
39 p.close();
40 s.clipPath(p, SkMatrix::I(), SkClipOp::kIntersect, doAA);
41
42 s.save();
43 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
44
45 SkRect r = SkRect::MakeLTRB(1, 2, 103, 104);
46 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
47 r = SkRect::MakeLTRB(4, 5, 56, 57);
48 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
49
50 s.save();
51 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
52
53 r = SkRect::MakeLTRB(14, 15, 16, 17);
54 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, doAA);
55
56 // Test that assignment works.
59
60 // Test that different save levels triggers not equal.
61 s.restore();
62 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
64
65 // Test that an equal, but not copied version is equal.
66 s.save();
67 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
68 r = SkRect::MakeLTRB(14, 15, 16, 17);
69 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, doAA);
71
72 // Test that a different op on one level triggers not equal.
73 s.restore();
74 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
75 s.save();
76 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
77 r = SkRect::MakeLTRB(14, 15, 16, 17);
78 s.clipRect(r, SkMatrix::I(), SkClipOp::kIntersect, doAA);
80
81 // Test that version constructed with rect-path rather than a rect is still considered equal.
82 s.restore();
83 s.save();
84 SkPath rp;
85 rp.addRect(r);
86 s.clipPath(rp, SkMatrix::I(), SkClipOp::kDifference, doAA);
88
89 // Test that different rects triggers not equal.
90 s.restore();
91 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
92 s.save();
93 REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
94
95 r = SkRect::MakeLTRB(24, 25, 26, 27);
96 s.clipRect(r, SkMatrix::I(), SkClipOp::kDifference, doAA);
98
99 s.restore();
100 REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
101
102 copy.restore();
103 REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
105 s.restore();
106 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
107 copy.restore();
108 REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
110
111 // Test that different paths triggers not equal.
112 s.restore();
113 REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
114 s.save();
115 REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
116
117 p.addRect(r);
118 s.clipPath(p, SkMatrix::I(), SkClipOp::kIntersect, doAA);
120}
121
123 int count) {
124 SkClipStack::B2TIter iter(stack);
125 int counter = 0;
126 while (iter.next()) {
127 counter += 1;
128 }
129 REPORTER_ASSERT(reporter, count == counter);
130}
131
132// Exercise the SkClipStack's bottom to top and bidirectional iterators
133// (including the skipToTopmost functionality)
135 SkClipStack stack;
136
137 static const SkRect gRects[] = {
138 { 0, 0, 40, 40 },
139 { 60, 0, 100, 40 },
140 { 0, 60, 40, 100 },
141 { 60, 60, 100, 100 }
142 };
143
144 for (size_t i = 0; i < std::size(gRects); i++) {
145 // the difference op will prevent these from being fused together
146 stack.clipRect(gRects[i], SkMatrix::I(), SkClipOp::kDifference, false);
147 }
148
149 assert_count(reporter, stack, 4);
150
151 // bottom to top iteration
152 {
153 const SkClipStack::Element* element = nullptr;
154
155 SkClipStack::B2TIter iter(stack);
156 int i;
157
158 for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
160 element->getDeviceSpaceType());
161 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
162 }
163
164 SkASSERT(i == 4);
165 }
166
167 // top to bottom iteration
168 {
169 const SkClipStack::Element* element = nullptr;
170
172 int i;
173
174 for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
176 element->getDeviceSpaceType());
177 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[i]);
178 }
179
180 SkASSERT(i == -1);
181 }
182
183 // skipToTopmost
184 {
185 const SkClipStack::Element* element = nullptr;
186
188
189 element = iter.skipToTopmost(SkClipOp::kDifference);
191 element->getDeviceSpaceType());
192 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[3]);
193 }
194}
195
196// Exercise the SkClipStack's getConservativeBounds computation
199 static const int gNumCases = 8;
200 static const SkRect gAnswerRectsBW[gNumCases] = {
201 // A op B
202 { 40, 40, 50, 50 },
203 { 10, 10, 50, 50 },
204
205 // invA op B
206 { 40, 40, 80, 80 },
207 { 0, 0, 100, 100 },
208
209 // A op invB
210 { 10, 10, 50, 50 },
211 { 40, 40, 50, 50 },
212
213 // invA op invB
214 { 0, 0, 100, 100 },
215 { 40, 40, 80, 80 },
216 };
217
218 static const SkClipOp gOps[] = {
221 };
222
223 SkRect rectA, rectB;
224
225 rectA.setLTRB(10, 10, 50, 50);
226 rectB.setLTRB(40, 40, 80, 80);
227
228 SkRRect rrectA, rrectB;
229 rrectA.setOval(rectA);
230 rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
231
232 SkPath pathA, pathB;
233
234 pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
235 pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
236
237 SkClipStack stack;
238 SkRect devClipBound;
239 bool isIntersectionOfRects = false;
240
241 int testCase = 0;
242 int numBitTests = SkClipStack::Element::DeviceSpaceType::kPath == primType ? 4 : 1;
243 for (int invBits = 0; invBits < numBitTests; ++invBits) {
244 for (size_t op = 0; op < std::size(gOps); ++op) {
245
246 stack.save();
247 bool doInvA = SkToBool(invBits & 1);
248 bool doInvB = SkToBool(invBits & 2);
249
254
255 switch (primType) {
258 SkDEBUGFAIL("Don't call this with kEmpty or kShader.");
259 break;
261 stack.clipRect(rectA, SkMatrix::I(), SkClipOp::kIntersect, false);
262 stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
263 break;
265 stack.clipRRect(rrectA, SkMatrix::I(), SkClipOp::kIntersect, false);
266 stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
267 break;
269 stack.clipPath(pathA, SkMatrix::I(), SkClipOp::kIntersect, false);
270 stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
271 break;
272 }
273
276
277 stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
278 &isIntersectionOfRects);
279
281 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
282 (gOps[op] == SkClipOp::kIntersect));
283 } else {
284 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
285 }
286
287 SkASSERT(testCase < gNumCases);
288 REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
289 ++testCase;
290
291 stack.restore();
292 }
293 }
294}
295
296// Test out 'isWideOpen' entry point
298 {
299 // Empty stack is wide open. Wide open stack means that gen id is wide open.
300 SkClipStack stack;
303 }
304
305 SkRect rectA, rectB;
306
307 rectA.setLTRB(10, 10, 40, 40);
308 rectB.setLTRB(50, 50, 80, 80);
309
310 // Stack should initially be wide open
311 {
312 SkClipStack stack;
313
316 }
317
318 // Test out empty difference from a wide open clip
319 {
320 SkClipStack stack;
321
322 SkRect emptyRect;
323 emptyRect.setEmpty();
324
325 stack.clipRect(emptyRect, SkMatrix::I(), SkClipOp::kDifference, false);
326
329 }
330
331 // Test out return to wide open
332 {
333 SkClipStack stack;
334
335 stack.save();
336
337 stack.clipRect(rectA, SkMatrix::I(), SkClipOp::kIntersect, false);
338
341
342 stack.restore();
343
346 }
347}
348
349static int count(const SkClipStack& stack) {
350
352
353 const SkClipStack::Element* element = nullptr;
354 int count = 0;
355
356 for (element = iter.prev(); element; element = iter.prev(), ++count) {
357 }
358
359 return count;
360}
361
363 // non-intersecting rectangles
364 SkRect rect = SkRect::MakeLTRB(0, 0, 10, 10);
365
366 SkPath path;
367 path.addRect(rect);
368 path.toggleInverseFillType();
369 SkClipStack stack;
370 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
371
372 SkRect bounds;
373 SkClipStack::BoundsType boundsType;
374 stack.getBounds(&bounds, &boundsType);
376 REPORTER_ASSERT(reporter, bounds == rect);
377}
378
380 SkRect rect = SkRect::MakeWH(100, 100);
381 SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
382
383 SkRect bound;
385 bool isIntersectionOfRects;
386
387 // Adding a new rect with the replace operator should not increase
388 // the stack depth. BW replacing BW.
389 {
390 SkClipStack stack;
391 REPORTER_ASSERT(reporter, 0 == count(stack));
392 stack.replaceClip(rect, false);
393 REPORTER_ASSERT(reporter, 1 == count(stack));
394 stack.replaceClip(rect, false);
395 REPORTER_ASSERT(reporter, 1 == count(stack));
396 }
397
398 // Adding a new rect with the replace operator should not increase
399 // the stack depth. AA replacing AA.
400 {
401 SkClipStack stack;
402 REPORTER_ASSERT(reporter, 0 == count(stack));
403 stack.replaceClip(rect, true);
404 REPORTER_ASSERT(reporter, 1 == count(stack));
405 stack.replaceClip(rect, true);
406 REPORTER_ASSERT(reporter, 1 == count(stack));
407 }
408
409 // Adding a new rect with the replace operator should not increase
410 // the stack depth. BW replacing AA replacing BW.
411 {
412 SkClipStack stack;
413 REPORTER_ASSERT(reporter, 0 == count(stack));
414 stack.replaceClip(rect, false);
415 REPORTER_ASSERT(reporter, 1 == count(stack));
416 stack.replaceClip(rect, true);
417 REPORTER_ASSERT(reporter, 1 == count(stack));
418 stack.replaceClip(rect, false);
419 REPORTER_ASSERT(reporter, 1 == count(stack));
420 }
421
422 // Make sure replace clip rects don't collapse too much.
423 {
424 SkClipStack stack;
425 stack.replaceClip(rect, false);
426 stack.clipRect(rect2, SkMatrix::I(), SkClipOp::kIntersect, false);
427 REPORTER_ASSERT(reporter, 1 == count(stack));
428
429 stack.save();
430 stack.replaceClip(rect, false);
431 REPORTER_ASSERT(reporter, 2 == count(stack));
432 stack.getBounds(&bound, &type, &isIntersectionOfRects);
433 REPORTER_ASSERT(reporter, bound == rect);
434 stack.restore();
435 REPORTER_ASSERT(reporter, 1 == count(stack));
436
437 stack.save();
438 stack.replaceClip(rect, false);
439 stack.replaceClip(rect, false);
440 REPORTER_ASSERT(reporter, 2 == count(stack));
441 stack.restore();
442 REPORTER_ASSERT(reporter, 1 == count(stack));
443
444 stack.save();
445 stack.replaceClip(rect, false);
446 stack.clipRect(rect2, SkMatrix::I(), SkClipOp::kIntersect, false);
447 stack.replaceClip(rect, false);
448 REPORTER_ASSERT(reporter, 2 == count(stack));
449 stack.restore();
450 REPORTER_ASSERT(reporter, 1 == count(stack));
451 }
452}
453
454// Simplified path-based version of test_rect_replace.
456 auto replacePath = [](SkClipStack* stack, const SkPath& path, bool doAA) {
457 const SkRect wideOpen = SkRect::MakeLTRB(-1000, -1000, 1000, 1000);
458 stack->replaceClip(wideOpen, false);
459 stack->clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, doAA);
460 };
461 SkRect rect = SkRect::MakeWH(100, 100);
462 SkPath path;
463 path.addCircle(50, 50, 50);
464
465 // Emulating replace operations with more complex geometry is not atomic, it's a replace
466 // with a wide-open rect and then an intersection with the complex geometry. The replace can
467 // combine with prior elements, but the subsequent intersect cannot be combined so the stack
468 // continues to grow.
469 {
470 SkClipStack stack;
471 REPORTER_ASSERT(reporter, 0 == count(stack));
472 replacePath(&stack, path, false);
473 REPORTER_ASSERT(reporter, 2 == count(stack));
474 replacePath(&stack, path, false);
475 REPORTER_ASSERT(reporter, 2 == count(stack));
476 }
477
478 // Replacing rect with path.
479 {
480 SkClipStack stack;
481 stack.replaceClip(rect, true);
482 REPORTER_ASSERT(reporter, 1 == count(stack));
483 replacePath(&stack, path, true);
484 REPORTER_ASSERT(reporter, 2 == count(stack));
485 }
486}
487
488// Test out SkClipStack's merging of rect clips. In particular exercise
489// merging of aa vs. bw rects.
491
492 SkRect overlapLeft = SkRect::MakeLTRB(10, 10, 50, 50);
493 SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
494
495 SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
496 SkRect nestedChild = SkRect::MakeLTRB(40, 40, 60, 60);
497
498 SkRect bound;
500 bool isIntersectionOfRects;
501
502 // all bw overlapping - should merge
503 {
504 SkClipStack stack;
505 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, false);
506 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, false);
507
508 REPORTER_ASSERT(reporter, 1 == count(stack));
509
510 stack.getBounds(&bound, &type, &isIntersectionOfRects);
511
512 REPORTER_ASSERT(reporter, isIntersectionOfRects);
513 }
514
515 // all aa overlapping - should merge
516 {
517 SkClipStack stack;
518 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, true);
519 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, true);
520
521 REPORTER_ASSERT(reporter, 1 == count(stack));
522
523 stack.getBounds(&bound, &type, &isIntersectionOfRects);
524
525 REPORTER_ASSERT(reporter, isIntersectionOfRects);
526 }
527
528 // mixed overlapping - should _not_ merge
529 {
530 SkClipStack stack;
531 stack.clipRect(overlapLeft, SkMatrix::I(), SkClipOp::kIntersect, true);
532 stack.clipRect(overlapRight, SkMatrix::I(), SkClipOp::kIntersect, false);
533
534 REPORTER_ASSERT(reporter, 2 == count(stack));
535
536 stack.getBounds(&bound, &type, &isIntersectionOfRects);
537
538 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
539 }
540
541 // mixed nested (bw inside aa) - should merge
542 {
543 SkClipStack stack;
544 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, true);
545 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, false);
546
547 REPORTER_ASSERT(reporter, 1 == count(stack));
548
549 stack.getBounds(&bound, &type, &isIntersectionOfRects);
550
551 REPORTER_ASSERT(reporter, isIntersectionOfRects);
552 }
553
554 // mixed nested (aa inside bw) - should merge
555 {
556 SkClipStack stack;
557 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, false);
558 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, true);
559
560 REPORTER_ASSERT(reporter, 1 == count(stack));
561
562 stack.getBounds(&bound, &type, &isIntersectionOfRects);
563
564 REPORTER_ASSERT(reporter, isIntersectionOfRects);
565 }
566
567 // reverse nested (aa inside bw) - should _not_ merge
568 {
569 SkClipStack stack;
570 stack.clipRect(nestedChild, SkMatrix::I(), SkClipOp::kIntersect, false);
571 stack.clipRect(nestedParent, SkMatrix::I(), SkClipOp::kIntersect, true);
572
573 REPORTER_ASSERT(reporter, 2 == count(stack));
574
575 stack.getBounds(&bound, &type, &isIntersectionOfRects);
576
577 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
578 }
579}
580
582 SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
583 SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
584 SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
585 SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
586 SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
587
588 SkPath insideCircle;
589 insideCircle.addCircle(25, 25, 5);
590 SkPath intersectingCircle;
591 intersectingCircle.addCircle(25, 40, 10);
592 SkPath outsideCircle;
593 outsideCircle.addCircle(25, 25, 50);
594 SkPath nonIntersectingCircle;
595 nonIntersectingCircle.addCircle(100, 100, 5);
596
597 {
598 SkClipStack stack;
599 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kDifference, false);
600 // return false because quickContains currently does not care for kDifference
601 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
602 }
603
604 // Replace Op tests
605 {
606 SkClipStack stack;
607 stack.replaceClip(outsideRect, false);
608 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
609 }
610
611 {
612 SkClipStack stack;
613 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
614 stack.save(); // To prevent in-place substitution by replace OP
615 stack.replaceClip(outsideRect, false);
616 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
617 stack.restore();
618 }
619
620 {
621 SkClipStack stack;
622 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
623 stack.save(); // To prevent in-place substitution by replace OP
624 stack.replaceClip(insideRect, false);
625 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
626 stack.restore();
627 }
628
629 // Verify proper traversal of multi-element clip
630 {
631 SkClipStack stack;
632 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
633 // Use a path for second clip to prevent in-place intersection
634 stack.clipPath(outsideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
635 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
636 }
637
638 // Intersect Op tests with rectangles
639 {
640 SkClipStack stack;
641 stack.clipRect(outsideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
642 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
643 }
644
645 {
646 SkClipStack stack;
647 stack.clipRect(insideRect, SkMatrix::I(), SkClipOp::kIntersect, false);
648 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
649 }
650
651 {
652 SkClipStack stack;
653 stack.clipRect(intersectingRect, SkMatrix::I(), SkClipOp::kIntersect, false);
654 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
655 }
656
657 {
658 SkClipStack stack;
659 stack.clipRect(nonIntersectingRect, SkMatrix::I(), SkClipOp::kIntersect, false);
660 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
661 }
662
663 // Intersect Op tests with circle paths
664 {
665 SkClipStack stack;
666 stack.clipPath(outsideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
667 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
668 }
669
670 {
671 SkClipStack stack;
672 stack.clipPath(insideCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
673 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
674 }
675
676 {
677 SkClipStack stack;
678 stack.clipPath(intersectingCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
679 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
680 }
681
682 {
683 SkClipStack stack;
684 stack.clipPath(nonIntersectingCircle, SkMatrix::I(), SkClipOp::kIntersect, false);
685 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
686 }
687
688 // Intersect Op tests with inverse filled rectangles
689 {
690 SkClipStack stack;
691 SkPath path;
692 path.addRect(outsideRect);
693 path.toggleInverseFillType();
694 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
695 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
696 }
697
698 {
699 SkClipStack stack;
700 SkPath path;
701 path.addRect(insideRect);
702 path.toggleInverseFillType();
703 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
704 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
705 }
706
707 {
708 SkClipStack stack;
709 SkPath path;
710 path.addRect(intersectingRect);
711 path.toggleInverseFillType();
712 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
713 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
714 }
715
716 {
717 SkClipStack stack;
718 SkPath path;
719 path.addRect(nonIntersectingRect);
720 path.toggleInverseFillType();
721 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
722 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
723 }
724
725 // Intersect Op tests with inverse filled circles
726 {
727 SkClipStack stack;
728 SkPath path = outsideCircle;
729 path.toggleInverseFillType();
730 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
731 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
732 }
733
734 {
735 SkClipStack stack;
736 SkPath path = insideCircle;
737 path.toggleInverseFillType();
738 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
739 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
740 }
741
742 {
743 SkClipStack stack;
744 SkPath path = intersectingCircle;
745 path.toggleInverseFillType();
746 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
747 REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
748 }
749
750 {
751 SkClipStack stack;
752 SkPath path = nonIntersectingCircle;
753 path.toggleInverseFillType();
754 stack.clipPath(path, SkMatrix::I(), SkClipOp::kIntersect, false);
755 REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
756 }
757}
758
759static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
760 region->setRect(bounds);
762 while (const SkClipStack::Element *element = iter.next()) {
763 SkRegion elemRegion;
764 SkRegion boundsRgn(bounds);
765 SkPath path;
766
767 switch (element->getDeviceSpaceType()) {
769 elemRegion.setEmpty();
770 break;
771 default:
772 element->asDeviceSpacePath(&path);
773 elemRegion.setPath(path, boundsRgn);
774 break;
775 }
776
777 region->op(elemRegion, element->isReplaceOp() ? SkRegion::kReplace_Op
778 : (SkRegion::Op) element->getOp());
779 }
780}
781
783 SkClipStack stack;
784 stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), SkClipOp::kIntersect, false);
785
786 SkPath path;
787 path.addRect({30, 10, 40, 20});
788 path.setFillType(SkPathFillType::kInverseWinding);
789 stack.clipPath(path, SkMatrix::I(), SkClipOp::kDifference, false);
790
792
793 SkRect stackBounds;
794 SkClipStack::BoundsType stackBoundsType;
795 stack.getBounds(&stackBounds, &stackBoundsType);
796
797 REPORTER_ASSERT(reporter, stackBounds.isEmpty());
799
800 SkRegion region;
801 set_region_to_stack(stack, {0, 0, 50, 30}, &region);
802
804}
805
806///////////////////////////////////////////////////////////////////////////////////////////////////
807
809 static constexpr SkRect kTargetBounds = SkRect::MakeWH(1000, 500);
810 // All antialiased or all not antialiased.
811 for (bool aa : {false, true}) {
812 SkClipStack stack;
813 for (int i = 0; i <= 100; ++i) {
814 stack.save();
815 stack.clipRect(SkRect::MakeLTRB(i, 0.5, kTargetBounds.width(), kTargetBounds.height()),
817 }
818 SkRRect rrect;
819 bool isAA;
820 SkRRect expected = SkRRect::MakeRect(
821 SkRect::MakeLTRB(100, 0.5, kTargetBounds.width(), kTargetBounds.height()));
822 if (stack.isRRect(kTargetBounds, &rrect, &isAA)) {
823 REPORTER_ASSERT(reporter, rrect == expected);
824 REPORTER_ASSERT(reporter, aa == isAA);
825 } else {
826 ERRORF(reporter, "Expected to be an rrect.");
827 }
828 }
829 // Mixed AA and non-AA without simple containment.
830 SkClipStack stack;
831 for (int i = 0; i <= 100; ++i) {
832 bool aa = i & 0b1;
833 int j = 100 - i;
834 stack.save();
835 stack.clipRect(SkRect::MakeLTRB(i, j + 0.5, kTargetBounds.width(), kTargetBounds.height()),
837 }
838 SkRRect rrect;
839 bool isAA;
840 REPORTER_ASSERT(reporter, !stack.isRRect(kTargetBounds, &rrect, &isAA));
841}
842
843DEF_TEST(ClipStack, reporter) {
844 SkClipStack stack;
845
847 assert_count(reporter, stack, 0);
848
849 static const SkIRect gRects[] = {
850 { 0, 0, 100, 100 },
851 { 25, 25, 125, 125 },
852 { 0, 0, 1000, 1000 },
853 { 0, 0, 75, 75 }
854 };
855 for (size_t i = 0; i < std::size(gRects); i++) {
856 stack.clipDevRect(gRects[i], SkClipOp::kIntersect);
857 }
858
859 // all of the above rects should have been intersected, leaving only 1 rect
860 SkClipStack::B2TIter iter(stack);
861 const SkClipStack::Element* element = iter.next();
862 SkRect answer;
863 answer.setLTRB(25, 25, 75, 75);
864
865 REPORTER_ASSERT(reporter, element);
869 REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == answer);
870 // now check that we only had one in our iterator
871 REPORTER_ASSERT(reporter, !iter.next());
872
873 stack.reset();
875 assert_count(reporter, stack, 0);
876
890}
static void assert_count(skiatest::Reporter *reporter, const SkClipStack &stack, int count)
static void test_rect_merging(skiatest::Reporter *reporter)
static void set_region_to_stack(const SkClipStack &stack, const SkIRect &bounds, SkRegion *region)
static void test_rect_inverse_fill(skiatest::Reporter *reporter)
static void test_bounds(skiatest::Reporter *reporter, SkClipStack::Element::DeviceSpaceType primType)
static void test_is_rrect_deep_rect_stack(skiatest::Reporter *reporter)
static void test_assign_and_comparison(skiatest::Reporter *reporter)
static void test_rect_replace(skiatest::Reporter *reporter)
static void test_isWideOpen(skiatest::Reporter *reporter)
static void test_iterators(skiatest::Reporter *reporter)
static void test_path_replace(skiatest::Reporter *reporter)
static void test_invfill_diff_bug(skiatest::Reporter *reporter)
static void test_quickContains(skiatest::Reporter *reporter)
reporter
int count
#define SkDEBUGFAIL(message)
Definition SkAssert.h:118
#define SkASSERT(cond)
Definition SkAssert.h:116
SkClipOp
Definition SkClipOp.h:13
#define SkIntToScalar(x)
Definition SkScalar.h:57
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
#define DEF_TEST(name, reporter)
Definition Test.h:312
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
#define ERRORF(r,...)
Definition Test.h:293
const Element * next()
@ kPath
This element does not have geometry, but applies a shader to the clip.
@ kEmpty
This element makes the clip empty (regardless of previous elements).
@ kRect
This element combines a device space round-rect with the current clip.
@ kRRect
This element combines a device space path with the current clip.
SkClipOp getOp() const
const SkRect & getDeviceSpaceRect() const
Call if getDeviceSpaceType() is kShader to get a reference to the clip shader.
DeviceSpaceType getDeviceSpaceType() const
Call to get the save count associated with this clip element.
const Element * skipToTopmost(SkClipOp op)
const Element * next()
const Element * prev()
@ kInsideOut_BoundsType
Definition SkClipStack.h:42
void clipRect(const SkRect &, const SkMatrix &matrix, SkClipOp, bool doAA)
bool isRRect(const SkRect &bounds, SkRRect *rrect, bool *aa) const
static const uint32_t kEmptyGenID
void clipRRect(const SkRRect &, const SkMatrix &matrix, SkClipOp, bool doAA)
bool quickContains(const SkRect &devRect) const
void getBounds(SkRect *canvFiniteBound, BoundsType *boundType, bool *isIntersectionOfRects=nullptr) const
void clipDevRect(const SkIRect &ir, SkClipOp op)
void getConservativeBounds(int offsetX, int offsetY, int maxWidth, int maxHeight, SkRect *devBounds, bool *isIntersectionOfRects=nullptr) const
uint32_t getTopmostGenID() const
int getSaveCount() const
static const uint32_t kWideOpenGenID
bool isWideOpen() const
void clipPath(const SkPath &, const SkMatrix &matrix, SkClipOp, bool doAA)
void replaceClip(const SkRect &devRect, bool doAA)
static const SkMatrix & I()
SkPath & addCircle(SkScalar x, SkScalar y, SkScalar radius, SkPathDirection dir=SkPathDirection::kCW)
Definition SkPath.cpp:1149
void setFillType(SkPathFillType ft)
Definition SkPath.h:235
SkPath & addRoundRect(const SkRect &rect, SkScalar rx, SkScalar ry, SkPathDirection dir=SkPathDirection::kCW)
Definition SkPath.cpp:1088
SkPath & addRect(const SkRect &rect, SkPathDirection dir, unsigned start)
Definition SkPath.cpp:854
static SkRRect MakeRect(const SkRect &r)
Definition SkRRect.h:149
void setOval(const SkRect &oval)
Definition SkRRect.cpp:30
void setRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
Definition SkRRect.cpp:52
bool setEmpty()
Definition SkRegion.cpp:185
@ kReplace_Op
replace target with operand
Definition SkRegion.h:372
bool op(const SkIRect &rect, Op op)
Definition SkRegion.h:384
bool setRect(const SkIRect &rect)
Definition SkRegion.cpp:192
bool isEmpty() const
Definition SkRegion.h:146
bool setPath(const SkPath &path, const SkRegion &clip)
struct MyStruct s
Definition copy.py:1
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition SkRect.h:659
constexpr float height() const
Definition SkRect.h:769
void setLTRB(float left, float top, float right, float bottom)
Definition SkRect.h:865
constexpr float width() const
Definition SkRect.h:762
bool isEmpty() const
Definition SkRect.h:693
static constexpr SkRect MakeWH(float w, float h)
Definition SkRect.h:609
static constexpr SkRect MakeLTRB(float l, float t, float r, float b)
Definition SkRect.h:646
void setEmpty()
Definition SkRect.h:842