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