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 "Test.h"
9 #if SK_SUPPORT_GPU
10     #include "GrReducedClip.h"
11 #endif
12 #include "SkClipStack.h"
13 #include "SkPath.h"
14 #include "SkRandom.h"
15 #include "SkRect.h"
16 #include "SkRegion.h"
17 
test_assign_and_comparison(skiatest::Reporter * reporter)18 static void test_assign_and_comparison(skiatest::Reporter* reporter) {
19     SkClipStack s;
20     bool doAA = false;
21 
22     REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
23 
24     // Build up a clip stack with a path, an empty clip, and a rect.
25     s.save();
26     REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
27 
28     SkPath p;
29     p.moveTo(5, 6);
30     p.lineTo(7, 8);
31     p.lineTo(5, 9);
32     p.close();
33     s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
34 
35     s.save();
36     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
37 
38     SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
39     s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
40     r = SkRect::MakeLTRB(10, 11, 12, 13);
41     s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
42 
43     s.save();
44     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
45 
46     r = SkRect::MakeLTRB(14, 15, 16, 17);
47     s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
48 
49     // Test that assignment works.
50     SkClipStack copy = s;
51     REPORTER_ASSERT(reporter, s == copy);
52 
53     // Test that different save levels triggers not equal.
54     s.restore();
55     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
56     REPORTER_ASSERT(reporter, s != copy);
57 
58     // Test that an equal, but not copied version is equal.
59     s.save();
60     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
61     r = SkRect::MakeLTRB(14, 15, 16, 17);
62     s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
63     REPORTER_ASSERT(reporter, s == copy);
64 
65     // Test that a different op on one level triggers not equal.
66     s.restore();
67     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
68     s.save();
69     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
70     r = SkRect::MakeLTRB(14, 15, 16, 17);
71     s.clipDevRect(r, SkRegion::kIntersect_Op, doAA);
72     REPORTER_ASSERT(reporter, s != copy);
73 
74     // Test that version constructed with rect-path rather than a rect is still considered equal.
75     s.restore();
76     s.save();
77     SkPath rp;
78     rp.addRect(r);
79     s.clipDevPath(rp, SkRegion::kUnion_Op, doAA);
80     REPORTER_ASSERT(reporter, s == copy);
81 
82     // Test that different rects triggers not equal.
83     s.restore();
84     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
85     s.save();
86     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
87 
88     r = SkRect::MakeLTRB(24, 25, 26, 27);
89     s.clipDevRect(r, SkRegion::kUnion_Op, doAA);
90     REPORTER_ASSERT(reporter, s != copy);
91 
92     // Sanity check
93     s.restore();
94     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
95 
96     copy.restore();
97     REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
98     REPORTER_ASSERT(reporter, s == copy);
99     s.restore();
100     REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
101     copy.restore();
102     REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
103     REPORTER_ASSERT(reporter, s == copy);
104 
105     // Test that different paths triggers not equal.
106     s.restore();
107     REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
108     s.save();
109     REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
110 
111     p.addRect(r);
112     s.clipDevPath(p, SkRegion::kIntersect_Op, doAA);
113     REPORTER_ASSERT(reporter, s != copy);
114 }
115 
assert_count(skiatest::Reporter * reporter,const SkClipStack & stack,int count)116 static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
117                          int count) {
118     SkClipStack::B2TIter iter(stack);
119     int counter = 0;
120     while (iter.next()) {
121         counter += 1;
122     }
123     REPORTER_ASSERT(reporter, count == counter);
124 }
125 
126 // Exercise the SkClipStack's bottom to top and bidirectional iterators
127 // (including the skipToTopmost functionality)
test_iterators(skiatest::Reporter * reporter)128 static void test_iterators(skiatest::Reporter* reporter) {
129     SkClipStack stack;
130 
131     static const SkRect gRects[] = {
132         { 0,   0,  40,  40 },
133         { 60,  0, 100,  40 },
134         { 0,  60,  40, 100 },
135         { 60, 60, 100, 100 }
136     };
137 
138     for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
139         // the union op will prevent these from being fused together
140         stack.clipDevRect(gRects[i], SkRegion::kUnion_Op, false);
141     }
142 
143     assert_count(reporter, stack, 4);
144 
145     // bottom to top iteration
146     {
147         const SkClipStack::Element* element = nullptr;
148 
149         SkClipStack::B2TIter iter(stack);
150         int i;
151 
152         for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
153             REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
154             REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
155         }
156 
157         SkASSERT(i == 4);
158     }
159 
160     // top to bottom iteration
161     {
162         const SkClipStack::Element* element = nullptr;
163 
164         SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
165         int i;
166 
167         for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
168             REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
169             REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
170         }
171 
172         SkASSERT(i == -1);
173     }
174 
175     // skipToTopmost
176     {
177         const SkClipStack::Element* element = nullptr;
178 
179         SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
180 
181         element = iter.skipToTopmost(SkRegion::kUnion_Op);
182         REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
183         REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
184     }
185 }
186 
187 // Exercise the SkClipStack's getConservativeBounds computation
test_bounds(skiatest::Reporter * reporter,SkClipStack::Element::Type primType)188 static void test_bounds(skiatest::Reporter* reporter, SkClipStack::Element::Type primType) {
189     static const int gNumCases = 20;
190     static const SkRect gAnswerRectsBW[gNumCases] = {
191         // A op B
192         { 40, 40, 50, 50 },
193         { 10, 10, 50, 50 },
194         { 10, 10, 80, 80 },
195         { 10, 10, 80, 80 },
196         { 40, 40, 80, 80 },
197 
198         // invA op B
199         { 40, 40, 80, 80 },
200         { 0, 0, 100, 100 },
201         { 0, 0, 100, 100 },
202         { 0, 0, 100, 100 },
203         { 40, 40, 50, 50 },
204 
205         // A op invB
206         { 10, 10, 50, 50 },
207         { 40, 40, 50, 50 },
208         { 0, 0, 100, 100 },
209         { 0, 0, 100, 100 },
210         { 0, 0, 100, 100 },
211 
212         // invA op invB
213         { 0, 0, 100, 100 },
214         { 40, 40, 80, 80 },
215         { 0, 0, 100, 100 },
216         { 10, 10, 80, 80 },
217         { 10, 10, 50, 50 },
218     };
219 
220     static const SkRegion::Op gOps[] = {
221         SkRegion::kIntersect_Op,
222         SkRegion::kDifference_Op,
223         SkRegion::kUnion_Op,
224         SkRegion::kXOR_Op,
225         SkRegion::kReverseDifference_Op
226     };
227 
228     SkRect rectA, rectB;
229 
230     rectA.iset(10, 10, 50, 50);
231     rectB.iset(40, 40, 80, 80);
232 
233     SkRRect rrectA, rrectB;
234     rrectA.setOval(rectA);
235     rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
236 
237     SkPath pathA, pathB;
238 
239     pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
240     pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
241 
242     SkClipStack stack;
243     SkRect devClipBound;
244     bool isIntersectionOfRects = false;
245 
246     int testCase = 0;
247     int numBitTests = SkClipStack::Element::kPath_Type == primType ? 4 : 1;
248     for (int invBits = 0; invBits < numBitTests; ++invBits) {
249         for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
250 
251             stack.save();
252             bool doInvA = SkToBool(invBits & 1);
253             bool doInvB = SkToBool(invBits & 2);
254 
255             pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
256                                        SkPath::kEvenOdd_FillType);
257             pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
258                                        SkPath::kEvenOdd_FillType);
259 
260             switch (primType) {
261                 case SkClipStack::Element::kEmpty_Type:
262                     SkDEBUGFAIL("Don't call this with kEmpty.");
263                     break;
264                 case SkClipStack::Element::kRect_Type:
265                     stack.clipDevRect(rectA, SkRegion::kIntersect_Op, false);
266                     stack.clipDevRect(rectB, gOps[op], false);
267                     break;
268                 case SkClipStack::Element::kRRect_Type:
269                     stack.clipDevRRect(rrectA, SkRegion::kIntersect_Op, false);
270                     stack.clipDevRRect(rrectB, gOps[op], false);
271                     break;
272                 case SkClipStack::Element::kPath_Type:
273                     stack.clipDevPath(pathA, SkRegion::kIntersect_Op, false);
274                     stack.clipDevPath(pathB, gOps[op], false);
275                     break;
276             }
277 
278             REPORTER_ASSERT(reporter, !stack.isWideOpen());
279             REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
280 
281             stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
282                                         &isIntersectionOfRects);
283 
284             if (SkClipStack::Element::kRect_Type == primType) {
285                 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
286                         (gOps[op] == SkRegion::kIntersect_Op));
287             } else {
288                 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
289             }
290 
291             SkASSERT(testCase < gNumCases);
292             REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
293             ++testCase;
294 
295             stack.restore();
296         }
297     }
298 }
299 
300 // Test out 'isWideOpen' entry point
test_isWideOpen(skiatest::Reporter * reporter)301 static void test_isWideOpen(skiatest::Reporter* reporter) {
302     {
303         // Empty stack is wide open. Wide open stack means that gen id is wide open.
304         SkClipStack stack;
305         REPORTER_ASSERT(reporter, stack.isWideOpen());
306         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
307     }
308 
309     SkRect rectA, rectB;
310 
311     rectA.iset(10, 10, 40, 40);
312     rectB.iset(50, 50, 80, 80);
313 
314     // Stack should initially be wide open
315     {
316         SkClipStack stack;
317 
318         REPORTER_ASSERT(reporter, stack.isWideOpen());
319         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
320     }
321 
322     // Test out case where the user specifies a union that includes everything
323     {
324         SkClipStack stack;
325 
326         SkPath clipA, clipB;
327 
328         clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
329         clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
330 
331         clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
332         clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
333 
334         stack.clipDevPath(clipA, SkRegion::kReplace_Op, false);
335         stack.clipDevPath(clipB, SkRegion::kUnion_Op, false);
336 
337         REPORTER_ASSERT(reporter, stack.isWideOpen());
338         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
339     }
340 
341     // Test out union w/ a wide open clip
342     {
343         SkClipStack stack;
344 
345         stack.clipDevRect(rectA, SkRegion::kUnion_Op, false);
346 
347         REPORTER_ASSERT(reporter, stack.isWideOpen());
348         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
349     }
350 
351     // Test out empty difference from a wide open clip
352     {
353         SkClipStack stack;
354 
355         SkRect emptyRect;
356         emptyRect.setEmpty();
357 
358         stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false);
359 
360         REPORTER_ASSERT(reporter, stack.isWideOpen());
361         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
362     }
363 
364     // Test out return to wide open
365     {
366         SkClipStack stack;
367 
368         stack.save();
369 
370         stack.clipDevRect(rectA, SkRegion::kReplace_Op, false);
371 
372         REPORTER_ASSERT(reporter, !stack.isWideOpen());
373         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
374 
375         stack.restore();
376 
377         REPORTER_ASSERT(reporter, stack.isWideOpen());
378         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
379     }
380 }
381 
count(const SkClipStack & stack)382 static int count(const SkClipStack& stack) {
383 
384     SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
385 
386     const SkClipStack::Element* element = nullptr;
387     int count = 0;
388 
389     for (element = iter.prev(); element; element = iter.prev(), ++count) {
390         ;
391     }
392 
393     return count;
394 }
395 
test_rect_inverse_fill(skiatest::Reporter * reporter)396 static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
397     // non-intersecting rectangles
398     SkRect rect  = SkRect::MakeLTRB(0, 0, 10, 10);
399 
400     SkPath path;
401     path.addRect(rect);
402     path.toggleInverseFillType();
403     SkClipStack stack;
404     stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
405 
406     SkRect bounds;
407     SkClipStack::BoundsType boundsType;
408     stack.getBounds(&bounds, &boundsType);
409     REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
410     REPORTER_ASSERT(reporter, bounds == rect);
411 }
412 
test_rect_replace(skiatest::Reporter * reporter)413 static void test_rect_replace(skiatest::Reporter* reporter) {
414     SkRect rect = SkRect::MakeWH(100, 100);
415     SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
416 
417     SkRect bound;
418     SkClipStack::BoundsType type;
419     bool isIntersectionOfRects;
420 
421     // Adding a new rect with the replace operator should not increase
422     // the stack depth. BW replacing BW.
423     {
424         SkClipStack stack;
425         REPORTER_ASSERT(reporter, 0 == count(stack));
426         stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
427         REPORTER_ASSERT(reporter, 1 == count(stack));
428         stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
429         REPORTER_ASSERT(reporter, 1 == count(stack));
430     }
431 
432     // Adding a new rect with the replace operator should not increase
433     // the stack depth. AA replacing AA.
434     {
435         SkClipStack stack;
436         REPORTER_ASSERT(reporter, 0 == count(stack));
437         stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
438         REPORTER_ASSERT(reporter, 1 == count(stack));
439         stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
440         REPORTER_ASSERT(reporter, 1 == count(stack));
441     }
442 
443     // Adding a new rect with the replace operator should not increase
444     // the stack depth. BW replacing AA replacing BW.
445     {
446         SkClipStack stack;
447         REPORTER_ASSERT(reporter, 0 == count(stack));
448         stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
449         REPORTER_ASSERT(reporter, 1 == count(stack));
450         stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
451         REPORTER_ASSERT(reporter, 1 == count(stack));
452         stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
453         REPORTER_ASSERT(reporter, 1 == count(stack));
454     }
455 
456     // Make sure replace clip rects don't collapse too much.
457     {
458         SkClipStack stack;
459         stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
460         stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
461         REPORTER_ASSERT(reporter, 1 == count(stack));
462 
463         stack.save();
464         stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
465         REPORTER_ASSERT(reporter, 2 == count(stack));
466         stack.getBounds(&bound, &type, &isIntersectionOfRects);
467         REPORTER_ASSERT(reporter, bound == rect);
468         stack.restore();
469         REPORTER_ASSERT(reporter, 1 == count(stack));
470 
471         stack.save();
472         stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
473         stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
474         REPORTER_ASSERT(reporter, 2 == count(stack));
475         stack.restore();
476         REPORTER_ASSERT(reporter, 1 == count(stack));
477 
478         stack.save();
479         stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
480         stack.clipDevRect(rect2, SkRegion::kIntersect_Op, false);
481         stack.clipDevRect(rect, SkRegion::kReplace_Op, false);
482         REPORTER_ASSERT(reporter, 2 == count(stack));
483         stack.restore();
484         REPORTER_ASSERT(reporter, 1 == count(stack));
485     }
486 }
487 
488 // Simplified path-based version of test_rect_replace.
test_path_replace(skiatest::Reporter * reporter)489 static void test_path_replace(skiatest::Reporter* reporter) {
490     SkRect rect = SkRect::MakeWH(100, 100);
491     SkPath path;
492     path.addCircle(50, 50, 50);
493 
494     // Replace operation doesn't grow the stack.
495     {
496         SkClipStack stack;
497         REPORTER_ASSERT(reporter, 0 == count(stack));
498         stack.clipDevPath(path, SkRegion::kReplace_Op, false);
499         REPORTER_ASSERT(reporter, 1 == count(stack));
500         stack.clipDevPath(path, SkRegion::kReplace_Op, false);
501         REPORTER_ASSERT(reporter, 1 == count(stack));
502     }
503 
504     // Replacing rect with path.
505     {
506         SkClipStack stack;
507         stack.clipDevRect(rect, SkRegion::kReplace_Op, true);
508         REPORTER_ASSERT(reporter, 1 == count(stack));
509         stack.clipDevPath(path, SkRegion::kReplace_Op, true);
510         REPORTER_ASSERT(reporter, 1 == count(stack));
511     }
512 }
513 
514 // Test out SkClipStack's merging of rect clips. In particular exercise
515 // merging of aa vs. bw rects.
test_rect_merging(skiatest::Reporter * reporter)516 static void test_rect_merging(skiatest::Reporter* reporter) {
517 
518     SkRect overlapLeft  = SkRect::MakeLTRB(10, 10, 50, 50);
519     SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
520 
521     SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
522     SkRect nestedChild  = SkRect::MakeLTRB(40, 40, 60, 60);
523 
524     SkRect bound;
525     SkClipStack::BoundsType type;
526     bool isIntersectionOfRects;
527 
528     // all bw overlapping - should merge
529     {
530         SkClipStack stack;
531 
532         stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false);
533 
534         stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
535 
536         REPORTER_ASSERT(reporter, 1 == count(stack));
537 
538         stack.getBounds(&bound, &type, &isIntersectionOfRects);
539 
540         REPORTER_ASSERT(reporter, isIntersectionOfRects);
541     }
542 
543     // all aa overlapping - should merge
544     {
545         SkClipStack stack;
546 
547         stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
548 
549         stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true);
550 
551         REPORTER_ASSERT(reporter, 1 == count(stack));
552 
553         stack.getBounds(&bound, &type, &isIntersectionOfRects);
554 
555         REPORTER_ASSERT(reporter, isIntersectionOfRects);
556     }
557 
558     // mixed overlapping - should _not_ merge
559     {
560         SkClipStack stack;
561 
562         stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
563 
564         stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
565 
566         REPORTER_ASSERT(reporter, 2 == count(stack));
567 
568         stack.getBounds(&bound, &type, &isIntersectionOfRects);
569 
570         REPORTER_ASSERT(reporter, !isIntersectionOfRects);
571     }
572 
573     // mixed nested (bw inside aa) - should merge
574     {
575         SkClipStack stack;
576 
577         stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true);
578 
579         stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false);
580 
581         REPORTER_ASSERT(reporter, 1 == count(stack));
582 
583         stack.getBounds(&bound, &type, &isIntersectionOfRects);
584 
585         REPORTER_ASSERT(reporter, isIntersectionOfRects);
586     }
587 
588     // mixed nested (aa inside bw) - should merge
589     {
590         SkClipStack stack;
591 
592         stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false);
593 
594         stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true);
595 
596         REPORTER_ASSERT(reporter, 1 == count(stack));
597 
598         stack.getBounds(&bound, &type, &isIntersectionOfRects);
599 
600         REPORTER_ASSERT(reporter, isIntersectionOfRects);
601     }
602 
603     // reverse nested (aa inside bw) - should _not_ merge
604     {
605         SkClipStack stack;
606 
607         stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false);
608 
609         stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true);
610 
611         REPORTER_ASSERT(reporter, 2 == count(stack));
612 
613         stack.getBounds(&bound, &type, &isIntersectionOfRects);
614 
615         REPORTER_ASSERT(reporter, !isIntersectionOfRects);
616     }
617 }
618 
test_quickContains(skiatest::Reporter * reporter)619 static void test_quickContains(skiatest::Reporter* reporter) {
620     SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
621     SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
622     SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
623     SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
624     SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
625 
626     SkPath insideCircle;
627     insideCircle.addCircle(25, 25, 5);
628     SkPath intersectingCircle;
629     intersectingCircle.addCircle(25, 40, 10);
630     SkPath outsideCircle;
631     outsideCircle.addCircle(25, 25, 50);
632     SkPath nonIntersectingCircle;
633     nonIntersectingCircle.addCircle(100, 100, 5);
634 
635     {
636         SkClipStack stack;
637         stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false);
638         // return false because quickContains currently does not care for kDifference_Op
639         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
640     }
641 
642     // Replace Op tests
643     {
644         SkClipStack stack;
645         stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
646         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
647     }
648 
649     {
650         SkClipStack stack;
651         stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
652         stack.save(); // To prevent in-place substitution by replace OP
653         stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
654         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
655         stack.restore();
656     }
657 
658     {
659         SkClipStack stack;
660         stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
661         stack.save(); // To prevent in-place substitution by replace OP
662         stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false);
663         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
664         stack.restore();
665     }
666 
667     // Verify proper traversal of multi-element clip
668     {
669         SkClipStack stack;
670         stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
671         // Use a path for second clip to prevent in-place intersection
672         stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
673         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
674     }
675 
676     // Intersect Op tests with rectangles
677     {
678         SkClipStack stack;
679         stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
680         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
681     }
682 
683     {
684         SkClipStack stack;
685         stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
686         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
687     }
688 
689     {
690         SkClipStack stack;
691         stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false);
692         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
693     }
694 
695     {
696         SkClipStack stack;
697         stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false);
698         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
699     }
700 
701     // Intersect Op tests with circle paths
702     {
703         SkClipStack stack;
704         stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
705         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
706     }
707 
708     {
709         SkClipStack stack;
710         stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false);
711         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
712     }
713 
714     {
715         SkClipStack stack;
716         stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false);
717         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
718     }
719 
720     {
721         SkClipStack stack;
722         stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false);
723         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
724     }
725 
726     // Intersect Op tests with inverse filled rectangles
727     {
728         SkClipStack stack;
729         SkPath path;
730         path.addRect(outsideRect);
731         path.toggleInverseFillType();
732         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
733         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
734     }
735 
736     {
737         SkClipStack stack;
738         SkPath path;
739         path.addRect(insideRect);
740         path.toggleInverseFillType();
741         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
742         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
743     }
744 
745     {
746         SkClipStack stack;
747         SkPath path;
748         path.addRect(intersectingRect);
749         path.toggleInverseFillType();
750         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
751         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
752     }
753 
754     {
755         SkClipStack stack;
756         SkPath path;
757         path.addRect(nonIntersectingRect);
758         path.toggleInverseFillType();
759         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
760         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
761     }
762 
763     // Intersect Op tests with inverse filled circles
764     {
765         SkClipStack stack;
766         SkPath path = outsideCircle;
767         path.toggleInverseFillType();
768         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
769         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
770     }
771 
772     {
773         SkClipStack stack;
774         SkPath path = insideCircle;
775         path.toggleInverseFillType();
776         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
777         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
778     }
779 
780     {
781         SkClipStack stack;
782         SkPath path = intersectingCircle;
783         path.toggleInverseFillType();
784         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
785         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
786     }
787 
788     {
789         SkClipStack stack;
790         SkPath path = nonIntersectingCircle;
791         path.toggleInverseFillType();
792         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
793         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
794     }
795 }
796 
797 ///////////////////////////////////////////////////////////////////////////////////////////////////
798 
799 #if SK_SUPPORT_GPU
800 // Functions that add a shape to the clip stack. The shape is computed from a rectangle.
801 // AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
802 // stack. A fractional edge repeated in different elements may be rasterized fewer times using the
803 // reduced stack.
804 typedef void (*AddElementFunc) (const SkRect& rect,
805                                 bool invert,
806                                 SkRegion::Op op,
807                                 SkClipStack* stack);
808 
add_round_rect(const SkRect & rect,bool invert,SkRegion::Op op,SkClipStack * stack)809 static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
810     SkScalar rx = rect.width() / 10;
811     SkScalar ry = rect.height() / 20;
812     if (invert) {
813         SkPath path;
814         path.addRoundRect(rect, rx, ry);
815         path.setFillType(SkPath::kInverseWinding_FillType);
816         stack->clipDevPath(path, op, false);
817     } else {
818         SkRRect rrect;
819         rrect.setRectXY(rect, rx, ry);
820         stack->clipDevRRect(rrect, op, false);
821     }
822 };
823 
add_rect(const SkRect & rect,bool invert,SkRegion::Op op,SkClipStack * stack)824 static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
825     if (invert) {
826         SkPath path;
827         path.addRect(rect);
828         path.setFillType(SkPath::kInverseWinding_FillType);
829         stack->clipDevPath(path, op, false);
830     } else {
831         stack->clipDevRect(rect, op, false);
832     }
833 };
834 
add_oval(const SkRect & rect,bool invert,SkRegion::Op op,SkClipStack * stack)835 static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
836     SkPath path;
837     path.addOval(rect);
838     if (invert) {
839         path.setFillType(SkPath::kInverseWinding_FillType);
840     }
841     stack->clipDevPath(path, op, false);
842 };
843 
add_elem_to_stack(const SkClipStack::Element & element,SkClipStack * stack)844 static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
845     switch (element.getType()) {
846         case SkClipStack::Element::kRect_Type:
847             stack->clipDevRect(element.getRect(), element.getOp(), element.isAA());
848             break;
849         case SkClipStack::Element::kRRect_Type:
850             stack->clipDevRRect(element.getRRect(), element.getOp(), element.isAA());
851             break;
852         case SkClipStack::Element::kPath_Type:
853             stack->clipDevPath(element.getPath(), element.getOp(), element.isAA());
854             break;
855         case SkClipStack::Element::kEmpty_Type:
856             SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
857             stack->clipEmpty();
858             break;
859     }
860 }
861 
add_elem_to_region(const SkClipStack::Element & element,const SkIRect & bounds,SkRegion * region)862 static void add_elem_to_region(const SkClipStack::Element& element,
863                                const SkIRect& bounds,
864                                SkRegion* region) {
865     SkRegion elemRegion;
866     SkRegion boundsRgn(bounds);
867     SkPath path;
868 
869     switch (element.getType()) {
870         case SkClipStack::Element::kEmpty_Type:
871             elemRegion.setEmpty();
872             break;
873         default:
874             element.asPath(&path);
875             elemRegion.setPath(path, boundsRgn);
876             break;
877     }
878     region->op(elemRegion, element.getOp());
879 }
880 
test_reduced_clip_stack(skiatest::Reporter * reporter)881 static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
882     // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
883     // they are equal.
884 
885     // All the clip elements will be contained within these bounds.
886     static const SkRect kBounds = SkRect::MakeWH(100, 100);
887 
888     enum {
889         kNumTests = 200,
890         kMinElemsPerTest = 1,
891         kMaxElemsPerTest = 50,
892     };
893 
894     // min/max size of a clip element as a fraction of kBounds.
895     static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
896     static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
897 
898     static const SkRegion::Op kOps[] = {
899         SkRegion::kDifference_Op,
900         SkRegion::kIntersect_Op,
901         SkRegion::kUnion_Op,
902         SkRegion::kXOR_Op,
903         SkRegion::kReverseDifference_Op,
904         SkRegion::kReplace_Op,
905     };
906 
907     // Replace operations short-circuit the optimizer. We want to make sure that we test this code
908     // path a little bit but we don't want it to prevent us from testing many longer traversals in
909     // the optimizer.
910     static const int kReplaceDiv = 4 * kMaxElemsPerTest;
911 
912     // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
913     static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
914 
915     static const AddElementFunc kElementFuncs[] = {
916         add_rect,
917         add_round_rect,
918         add_oval,
919     };
920 
921     SkRandom r;
922 
923     for (int i = 0; i < kNumTests; ++i) {
924         // Randomly generate a clip stack.
925         SkClipStack stack;
926         int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
927         for (int e = 0; e < numElems; ++e) {
928             SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
929             if (op == SkRegion::kReplace_Op) {
930                 if (r.nextU() % kReplaceDiv) {
931                     --e;
932                     continue;
933                 }
934             }
935 
936             // saves can change the clip stack behavior when an element is added.
937             bool doSave = r.nextBool();
938 
939             SkSize size = SkSize::Make(
940                 SkScalarFloorToScalar(SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))),
941                 SkScalarFloorToScalar(SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))));
942 
943             SkPoint xy = {SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth)),
944                           SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight))};
945 
946             SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
947 
948             bool invert = r.nextBiasedBool(kFractionInverted);
949 
950             kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack);
951             if (doSave) {
952                 stack.save();
953             }
954         }
955 
956         SkRect inflatedBounds = kBounds;
957         inflatedBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
958         SkIRect inflatedIBounds;
959         inflatedBounds.roundOut(&inflatedIBounds);
960 
961         typedef GrReducedClip::ElementList ElementList;
962         // Get the reduced version of the stack.
963         ElementList reducedClips;
964         int32_t reducedGenID;
965         GrReducedClip::InitialState initial;
966         SkIRect tBounds(inflatedIBounds);
967         SkIRect* tightBounds = r.nextBool() ? &tBounds : nullptr;
968         GrReducedClip::ReduceClipStack(stack,
969                                        inflatedIBounds,
970                                        &reducedClips,
971                                        &reducedGenID,
972                                        &initial,
973                                        tightBounds);
974 
975         REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID);
976 
977         // Build a new clip stack based on the reduced clip elements
978         SkClipStack reducedStack;
979         if (GrReducedClip::kAllOut_InitialState == initial) {
980             // whether the result is bounded or not, the whole plane should start outside the clip.
981             reducedStack.clipEmpty();
982         }
983         for (ElementList::Iter iter = reducedClips.headIter(); iter.get(); iter.next()) {
984             add_elem_to_stack(*iter.get(), &reducedStack);
985         }
986 
987         // GrReducedClipStack assumes that the final result is clipped to the returned bounds
988         if (tightBounds) {
989             reducedStack.clipDevRect(*tightBounds, SkRegion::kIntersect_Op);
990         }
991 
992         // convert both the original stack and reduced stack to SkRegions and see if they're equal
993         SkRegion region;
994         SkRegion reducedRegion;
995 
996         region.setRect(inflatedIBounds);
997         const SkClipStack::Element* element;
998         SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
999         while ((element = iter.next())) {
1000             add_elem_to_region(*element, inflatedIBounds, &region);
1001         }
1002 
1003         reducedRegion.setRect(inflatedIBounds);
1004         iter.reset(reducedStack, SkClipStack::Iter::kBottom_IterStart);
1005         while ((element = iter.next())) {
1006             add_elem_to_region(*element, inflatedIBounds, &reducedRegion);
1007         }
1008         SkString testCase;
1009         testCase.printf("Iteration %d", i);
1010         REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str());
1011     }
1012 }
1013 
1014 #if defined(WIN32)
1015     #define SUPPRESS_VISIBILITY_WARNING
1016 #else
1017     #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1018 #endif
1019 
test_reduced_clip_stack_genid(skiatest::Reporter * reporter)1020 static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1021     {
1022         SkClipStack stack;
1023         stack.clipDevRect(SkRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op, true);
1024         stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkRegion::kReplace_Op, true);
1025         SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
1026 
1027         GrReducedClip::ElementList reducedClips;
1028         int32_t reducedGenID;
1029         GrReducedClip::InitialState initial;
1030         SkIRect tightBounds;
1031 
1032         GrReducedClip::ReduceClipStack(stack,
1033                                        inflatedIBounds,
1034                                        &reducedClips,
1035                                        &reducedGenID,
1036                                        &initial,
1037                                        &tightBounds);
1038 
1039         REPORTER_ASSERT(reporter, reducedClips.count() == 1);
1040         // Clips will be cached based on the generation id. Make sure the gen id is valid.
1041         REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reducedGenID);
1042     }
1043     {
1044         SkClipStack stack;
1045 
1046         // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1047         //  A  B
1048         //  C  D
1049 
1050         stack.clipDevRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kReplace_Op, true);
1051         int32_t genIDA = stack.getTopmostGenID();
1052         stack.clipDevRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1053         int32_t genIDB = stack.getTopmostGenID();
1054         stack.clipDevRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1055         int32_t genIDC = stack.getTopmostGenID();
1056         stack.clipDevRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkRegion::kUnion_Op, true);
1057         int32_t genIDD = stack.getTopmostGenID();
1058 
1059 
1060 #define XYWH SkIRect::MakeXYWH
1061 
1062         SkIRect unused;
1063         unused.setEmpty();
1064         SkIRect stackBounds = XYWH(0, 0, 76, 76);
1065 
1066         // The base test is to test each rect in two ways:
1067         // 1) The box dimensions. (Should reduce to "all in", no elements).
1068         // 2) A bit over the box dimensions.
1069         // In the case 2, test that the generation id is what is expected.
1070         // The rects are of fractional size so that case 2 never gets optimized to an empty element
1071         // list.
1072 
1073         // Not passing in tighter bounds is tested for consistency.
1074         static const struct SUPPRESS_VISIBILITY_WARNING {
1075             SkIRect testBounds;
1076             int reducedClipCount;
1077             int32_t reducedGenID;
1078             GrReducedClip::InitialState initialState;
1079             SkIRect tighterBounds; // If this is empty, the query will not pass tighter bounds
1080             // parameter.
1081         } testCases[] = {
1082             // Rect A.
1083             { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 0, 25, 25) },
1084             { XYWH(0, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1085             { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, XYWH(0, 0, 27, 27)},
1086             { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::kAllOut_InitialState, unused },
1087 
1088             // Rect B.
1089             { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 0, 25, 25) },
1090             { XYWH(50, 0, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1091             { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, XYWH(50, 0, 26, 27) },
1092             { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::kAllOut_InitialState, unused },
1093 
1094             // Rect C.
1095             { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(0, 50, 25, 25) },
1096             { XYWH(0, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1097             { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, XYWH(0, 50, 27, 26) },
1098             { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::kAllOut_InitialState, unused },
1099 
1100             // Rect D.
1101             { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, unused },
1102             { XYWH(50, 50, 25, 25), 0, SkClipStack::kWideOpenGenID, GrReducedClip::kAllIn_InitialState, XYWH(50, 50, 25, 25)},
1103             { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1104             { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::kAllOut_InitialState,  XYWH(50, 50, 26, 26)},
1105 
1106             // Other tests:
1107             { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1108             { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::kAllOut_InitialState, stackBounds },
1109 
1110             // Rect in the middle, touches none.
1111             { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, unused },
1112             { XYWH(26, 26, 24, 24), 0, SkClipStack::kEmptyGenID, GrReducedClip::kAllOut_InitialState, XYWH(26, 26, 24, 24) },
1113 
1114             // Rect in the middle, touches all the rects. GenID is the last rect.
1115             { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, unused },
1116             { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::kAllOut_InitialState, XYWH(24, 24, 27, 27) },
1117         };
1118 
1119 #undef XYWH
1120 
1121         for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
1122             GrReducedClip::ElementList reducedClips;
1123             int32_t reducedGenID;
1124             GrReducedClip::InitialState initial;
1125             SkIRect tightBounds;
1126 
1127             GrReducedClip::ReduceClipStack(stack,
1128                                            testCases[i].testBounds,
1129                                            &reducedClips,
1130                                            &reducedGenID,
1131                                            &initial,
1132                                            testCases[i].tighterBounds.isEmpty() ? nullptr : &tightBounds);
1133 
1134             REPORTER_ASSERT(reporter, reducedClips.count() == testCases[i].reducedClipCount);
1135             SkASSERT(reducedClips.count() == testCases[i].reducedClipCount);
1136             REPORTER_ASSERT(reporter, reducedGenID == testCases[i].reducedGenID);
1137             SkASSERT(reducedGenID == testCases[i].reducedGenID);
1138             REPORTER_ASSERT(reporter, initial == testCases[i].initialState);
1139             SkASSERT(initial == testCases[i].initialState);
1140             if (!testCases[i].tighterBounds.isEmpty()) {
1141                 REPORTER_ASSERT(reporter, tightBounds == testCases[i].tighterBounds);
1142                 SkASSERT(tightBounds == testCases[i].tighterBounds);
1143             }
1144         }
1145     }
1146 }
1147 
test_reduced_clip_stack_no_aa_crash(skiatest::Reporter * reporter)1148 static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1149     SkClipStack stack;
1150     stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), SkRegion::kReplace_Op);
1151     stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), SkRegion::kReplace_Op);
1152     SkIRect inflatedIBounds = SkIRect::MakeXYWH(0, 0, 100, 100);
1153 
1154     GrReducedClip::ElementList reducedClips;
1155     int32_t reducedGenID;
1156     GrReducedClip::InitialState initial;
1157     SkIRect tightBounds;
1158 
1159     // At the time, this would crash.
1160     GrReducedClip::ReduceClipStack(stack,
1161                                    inflatedIBounds,
1162                                    &reducedClips,
1163                                    &reducedGenID,
1164                                    &initial,
1165                                    &tightBounds);
1166 
1167     REPORTER_ASSERT(reporter, 0 == reducedClips.count());
1168 }
1169 
1170 #endif
1171 
DEF_TEST(ClipStack,reporter)1172 DEF_TEST(ClipStack, reporter) {
1173     SkClipStack stack;
1174 
1175     REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
1176     assert_count(reporter, stack, 0);
1177 
1178     static const SkIRect gRects[] = {
1179         { 0, 0, 100, 100 },
1180         { 25, 25, 125, 125 },
1181         { 0, 0, 1000, 1000 },
1182         { 0, 0, 75, 75 }
1183     };
1184     for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
1185         stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op);
1186     }
1187 
1188     // all of the above rects should have been intersected, leaving only 1 rect
1189     SkClipStack::B2TIter iter(stack);
1190     const SkClipStack::Element* element = iter.next();
1191     SkRect answer;
1192     answer.iset(25, 25, 75, 75);
1193 
1194     REPORTER_ASSERT(reporter, element);
1195     REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
1196     REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp());
1197     REPORTER_ASSERT(reporter, element->getRect() == answer);
1198     // now check that we only had one in our iterator
1199     REPORTER_ASSERT(reporter, !iter.next());
1200 
1201     stack.reset();
1202     REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
1203     assert_count(reporter, stack, 0);
1204 
1205     test_assign_and_comparison(reporter);
1206     test_iterators(reporter);
1207     test_bounds(reporter, SkClipStack::Element::kRect_Type);
1208     test_bounds(reporter, SkClipStack::Element::kRRect_Type);
1209     test_bounds(reporter, SkClipStack::Element::kPath_Type);
1210     test_isWideOpen(reporter);
1211     test_rect_merging(reporter);
1212     test_rect_replace(reporter);
1213     test_rect_inverse_fill(reporter);
1214     test_path_replace(reporter);
1215     test_quickContains(reporter);
1216 #if SK_SUPPORT_GPU
1217     test_reduced_clip_stack(reporter);
1218     test_reduced_clip_stack_genid(reporter);
1219     test_reduced_clip_stack_no_aa_crash(reporter);
1220 #endif
1221 }
1222