xref: /aosp_15_r20/external/skia/tests/ClipStackTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 
8 #include "include/core/SkClipOp.h"
9 #include "include/core/SkMatrix.h"
10 #include "include/core/SkPath.h"
11 #include "include/core/SkPathTypes.h"
12 #include "include/core/SkRRect.h"
13 #include "include/core/SkRect.h"
14 #include "include/core/SkRegion.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkTypes.h"
17 #include "include/private/base/SkTo.h"
18 #include "src/core/SkClipStack.h"
19 #include "tests/Test.h"
20 
21 #include <array>
22 #include <cstring>
23 #include <initializer_list>
24 
test_assign_and_comparison(skiatest::Reporter * reporter)25 static void test_assign_and_comparison(skiatest::Reporter* reporter) {
26     SkClipStack s;
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.
57     SkClipStack copy = s;
58     REPORTER_ASSERT(reporter, s == copy);
59 
60     // Test that different save levels triggers not equal.
61     s.restore();
62     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
63     REPORTER_ASSERT(reporter, s != copy);
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);
70     REPORTER_ASSERT(reporter, s == copy);
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);
79     REPORTER_ASSERT(reporter, s != copy);
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);
87     REPORTER_ASSERT(reporter, s == copy);
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);
97     REPORTER_ASSERT(reporter, s != copy);
98 
99     s.restore();
100     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
101 
102     copy.restore();
103     REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
104     REPORTER_ASSERT(reporter, s == copy);
105     s.restore();
106     REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
107     copy.restore();
108     REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
109     REPORTER_ASSERT(reporter, s == copy);
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);
119     REPORTER_ASSERT(reporter, s != copy);
120 }
121 
assert_count(skiatest::Reporter * reporter,const SkClipStack & stack,int count)122 static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
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)
test_iterators(skiatest::Reporter * reporter)134 static void test_iterators(skiatest::Reporter* reporter) {
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()) {
159             REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
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 
171         SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
172         int i;
173 
174         for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
175             REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
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 
187         SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
188 
189         element = iter.skipToTopmost(SkClipOp::kDifference);
190         REPORTER_ASSERT(reporter, SkClipStack::Element::DeviceSpaceType::kRect ==
191                                           element->getDeviceSpaceType());
192         REPORTER_ASSERT(reporter, element->getDeviceSpaceRect() == gRects[3]);
193     }
194 }
195 
196 // Exercise the SkClipStack's getConservativeBounds computation
test_bounds(skiatest::Reporter * reporter,SkClipStack::Element::DeviceSpaceType primType)197 static void test_bounds(skiatest::Reporter* reporter,
198                         SkClipStack::Element::DeviceSpaceType primType) {
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[] = {
219         SkClipOp::kIntersect,
220         SkClipOp::kDifference
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 
250             pathA.setFillType(doInvA ? SkPathFillType::kInverseEvenOdd :
251                                        SkPathFillType::kEvenOdd);
252             pathB.setFillType(doInvB ? SkPathFillType::kInverseEvenOdd :
253                                        SkPathFillType::kEvenOdd);
254 
255             switch (primType) {
256                 case SkClipStack::Element::DeviceSpaceType::kShader:
257                 case SkClipStack::Element::DeviceSpaceType::kEmpty:
258                     SkDEBUGFAIL("Don't call this with kEmpty or kShader.");
259                     break;
260                 case SkClipStack::Element::DeviceSpaceType::kRect:
261                     stack.clipRect(rectA, SkMatrix::I(), SkClipOp::kIntersect, false);
262                     stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
263                     break;
264                 case SkClipStack::Element::DeviceSpaceType::kRRect:
265                     stack.clipRRect(rrectA, SkMatrix::I(), SkClipOp::kIntersect, false);
266                     stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
267                     break;
268                 case SkClipStack::Element::DeviceSpaceType::kPath:
269                     stack.clipPath(pathA, SkMatrix::I(), SkClipOp::kIntersect, false);
270                     stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
271                     break;
272             }
273 
274             REPORTER_ASSERT(reporter, !stack.isWideOpen());
275             REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
276 
277             stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
278                                         &isIntersectionOfRects);
279 
280             if (SkClipStack::Element::DeviceSpaceType::kRect == primType) {
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
test_isWideOpen(skiatest::Reporter * reporter)297 static void test_isWideOpen(skiatest::Reporter* reporter) {
298     {
299         // Empty stack is wide open. Wide open stack means that gen id is wide open.
300         SkClipStack stack;
301         REPORTER_ASSERT(reporter, stack.isWideOpen());
302         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
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 
314         REPORTER_ASSERT(reporter, stack.isWideOpen());
315         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
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 
327         REPORTER_ASSERT(reporter, stack.isWideOpen());
328         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
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 
339         REPORTER_ASSERT(reporter, !stack.isWideOpen());
340         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
341 
342         stack.restore();
343 
344         REPORTER_ASSERT(reporter, stack.isWideOpen());
345         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
346     }
347 }
348 
count(const SkClipStack & stack)349 static int count(const SkClipStack& stack) {
350 
351     SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
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 
test_rect_inverse_fill(skiatest::Reporter * reporter)362 static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
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);
375     REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
376     REPORTER_ASSERT(reporter, bounds == rect);
377 }
378 
test_rect_replace(skiatest::Reporter * reporter)379 static void test_rect_replace(skiatest::Reporter* reporter) {
380     SkRect rect = SkRect::MakeWH(100, 100);
381     SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
382 
383     SkRect bound;
384     SkClipStack::BoundsType type;
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.
test_path_replace(skiatest::Reporter * reporter)455 static void test_path_replace(skiatest::Reporter* reporter) {
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.
test_rect_merging(skiatest::Reporter * reporter)490 static void test_rect_merging(skiatest::Reporter* reporter) {
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;
499     SkClipStack::BoundsType type;
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 
test_quickContains(skiatest::Reporter * reporter)581 static void test_quickContains(skiatest::Reporter* reporter) {
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 
set_region_to_stack(const SkClipStack & stack,const SkIRect & bounds,SkRegion * region)759 static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
760     region->setRect(bounds);
761     SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
762     while (const SkClipStack::Element *element = iter.next()) {
763         SkRegion elemRegion;
764         SkRegion boundsRgn(bounds);
765         SkPath path;
766 
767         switch (element->getDeviceSpaceType()) {
768             case SkClipStack::Element::DeviceSpaceType::kEmpty:
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 
test_invfill_diff_bug(skiatest::Reporter * reporter)782 static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
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 
791     REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
792 
793     SkRect stackBounds;
794     SkClipStack::BoundsType stackBoundsType;
795     stack.getBounds(&stackBounds, &stackBoundsType);
796 
797     REPORTER_ASSERT(reporter, stackBounds.isEmpty());
798     REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
799 
800     SkRegion region;
801     set_region_to_stack(stack, {0, 0, 50, 30}, &region);
802 
803     REPORTER_ASSERT(reporter, region.isEmpty());
804 }
805 
806 ///////////////////////////////////////////////////////////////////////////////////////////////////
807 
test_is_rrect_deep_rect_stack(skiatest::Reporter * reporter)808 static void test_is_rrect_deep_rect_stack(skiatest::Reporter* reporter) {
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()),
816                            SkMatrix::I(), SkClipOp::kIntersect, aa);
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()),
836                        SkMatrix::I(), SkClipOp::kIntersect, aa);
837     }
838     SkRRect rrect;
839     bool isAA;
840     REPORTER_ASSERT(reporter, !stack.isRRect(kTargetBounds, &rrect, &isAA));
841 }
842 
DEF_TEST(ClipStack,reporter)843 DEF_TEST(ClipStack, reporter) {
844     SkClipStack stack;
845 
846     REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
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);
866     REPORTER_ASSERT(reporter,
867                     SkClipStack::Element::DeviceSpaceType::kRect == element->getDeviceSpaceType());
868     REPORTER_ASSERT(reporter, SkClipOp::kIntersect == element->getOp());
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();
874     REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
875     assert_count(reporter, stack, 0);
876 
877     test_assign_and_comparison(reporter);
878     test_iterators(reporter);
879     test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRect);
880     test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kRRect);
881     test_bounds(reporter, SkClipStack::Element::DeviceSpaceType::kPath);
882     test_isWideOpen(reporter);
883     test_rect_merging(reporter);
884     test_rect_replace(reporter);
885     test_rect_inverse_fill(reporter);
886     test_path_replace(reporter);
887     test_quickContains(reporter);
888     test_invfill_diff_bug(reporter);
889     test_is_rrect_deep_rect_stack(reporter);
890 }
891