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