• Home
  • History
  • Annotate
  • 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 "Test.h"
9 #include "SkClipStack.h"
10 #include "SkPath.h"
11 #include "SkRandom.h"
12 #include "SkRect.h"
13 #include "SkRegion.h"
14 
15 #if SK_SUPPORT_GPU
16 #include "GrClipStackClip.h"
17 #include "GrReducedClip.h"
18 #include "GrResourceCache.h"
19 #include "GrTextureProxy.h"
20 typedef GrReducedClip::ElementList ElementList;
21 typedef GrReducedClip::InitialState InitialState;
22 #endif
23 
test_assign_and_comparison(skiatest::Reporter * reporter)24 static void test_assign_and_comparison(skiatest::Reporter* reporter) {
25     SkClipStack s;
26     bool doAA = false;
27 
28     REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
29 
30     // Build up a clip stack with a path, an empty clip, and a rect.
31     s.save();
32     REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
33 
34     SkPath p;
35     p.moveTo(5, 6);
36     p.lineTo(7, 8);
37     p.lineTo(5, 9);
38     p.close();
39     s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
40 
41     s.save();
42     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
43 
44     SkRect r = SkRect::MakeLTRB(1, 2, 3, 4);
45     s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
46     r = SkRect::MakeLTRB(10, 11, 12, 13);
47     s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
48 
49     s.save();
50     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
51 
52     r = SkRect::MakeLTRB(14, 15, 16, 17);
53     s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
54 
55     // Test that assignment works.
56     SkClipStack copy = s;
57     REPORTER_ASSERT(reporter, s == copy);
58 
59     // Test that different save levels triggers not equal.
60     s.restore();
61     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
62     REPORTER_ASSERT(reporter, s != copy);
63 
64     // Test that an equal, but not copied version is equal.
65     s.save();
66     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
67     r = SkRect::MakeLTRB(14, 15, 16, 17);
68     s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
69     REPORTER_ASSERT(reporter, s == copy);
70 
71     // Test that a different op on one level triggers not equal.
72     s.restore();
73     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
74     s.save();
75     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
76     r = SkRect::MakeLTRB(14, 15, 16, 17);
77     s.clipRect(r, SkMatrix::I(), kIntersect_SkClipOp, doAA);
78     REPORTER_ASSERT(reporter, s != copy);
79 
80     // Test that version constructed with rect-path rather than a rect is still considered equal.
81     s.restore();
82     s.save();
83     SkPath rp;
84     rp.addRect(r);
85     s.clipPath(rp, SkMatrix::I(), kUnion_SkClipOp, doAA);
86     REPORTER_ASSERT(reporter, s == copy);
87 
88     // Test that different rects triggers not equal.
89     s.restore();
90     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
91     s.save();
92     REPORTER_ASSERT(reporter, 3 == s.getSaveCount());
93 
94     r = SkRect::MakeLTRB(24, 25, 26, 27);
95     s.clipRect(r, SkMatrix::I(), kUnion_SkClipOp, doAA);
96     REPORTER_ASSERT(reporter, s != copy);
97 
98     // Sanity check
99     s.restore();
100     REPORTER_ASSERT(reporter, 2 == s.getSaveCount());
101 
102     copy.restore();
103     REPORTER_ASSERT(reporter, 2 == copy.getSaveCount());
104     REPORTER_ASSERT(reporter, s == copy);
105     s.restore();
106     REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
107     copy.restore();
108     REPORTER_ASSERT(reporter, 1 == copy.getSaveCount());
109     REPORTER_ASSERT(reporter, s == copy);
110 
111     // Test that different paths triggers not equal.
112     s.restore();
113     REPORTER_ASSERT(reporter, 0 == s.getSaveCount());
114     s.save();
115     REPORTER_ASSERT(reporter, 1 == s.getSaveCount());
116 
117     p.addRect(r);
118     s.clipPath(p, SkMatrix::I(), kIntersect_SkClipOp, doAA);
119     REPORTER_ASSERT(reporter, s != copy);
120 }
121 
assert_count(skiatest::Reporter * reporter,const SkClipStack & stack,int count)122 static void assert_count(skiatest::Reporter* reporter, const SkClipStack& stack,
123                          int count) {
124     SkClipStack::B2TIter iter(stack);
125     int counter = 0;
126     while (iter.next()) {
127         counter += 1;
128     }
129     REPORTER_ASSERT(reporter, count == counter);
130 }
131 
132 // Exercise the SkClipStack's bottom to top and bidirectional iterators
133 // (including the skipToTopmost functionality)
test_iterators(skiatest::Reporter * reporter)134 static void test_iterators(skiatest::Reporter* reporter) {
135     SkClipStack stack;
136 
137     static const SkRect gRects[] = {
138         { 0,   0,  40,  40 },
139         { 60,  0, 100,  40 },
140         { 0,  60,  40, 100 },
141         { 60, 60, 100, 100 }
142     };
143 
144     for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
145         // the union op will prevent these from being fused together
146         stack.clipRect(gRects[i], SkMatrix::I(), kUnion_SkClipOp, false);
147     }
148 
149     assert_count(reporter, stack, 4);
150 
151     // bottom to top iteration
152     {
153         const SkClipStack::Element* element = nullptr;
154 
155         SkClipStack::B2TIter iter(stack);
156         int i;
157 
158         for (i = 0, element = iter.next(); element; ++i, element = iter.next()) {
159             REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
160             REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
161         }
162 
163         SkASSERT(i == 4);
164     }
165 
166     // top to bottom iteration
167     {
168         const SkClipStack::Element* element = nullptr;
169 
170         SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
171         int i;
172 
173         for (i = 3, element = iter.prev(); element; --i, element = iter.prev()) {
174             REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
175             REPORTER_ASSERT(reporter, element->getRect() == gRects[i]);
176         }
177 
178         SkASSERT(i == -1);
179     }
180 
181     // skipToTopmost
182     {
183         const SkClipStack::Element* element = nullptr;
184 
185         SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
186 
187         element = iter.skipToTopmost(kUnion_SkClipOp);
188         REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
189         REPORTER_ASSERT(reporter, element->getRect() == gRects[3]);
190     }
191 }
192 
193 // Exercise the SkClipStack's getConservativeBounds computation
test_bounds(skiatest::Reporter * reporter,SkClipStack::Element::Type primType)194 static void test_bounds(skiatest::Reporter* reporter, SkClipStack::Element::Type primType) {
195     static const int gNumCases = 20;
196     static const SkRect gAnswerRectsBW[gNumCases] = {
197         // A op B
198         { 40, 40, 50, 50 },
199         { 10, 10, 50, 50 },
200         { 10, 10, 80, 80 },
201         { 10, 10, 80, 80 },
202         { 40, 40, 80, 80 },
203 
204         // invA op B
205         { 40, 40, 80, 80 },
206         { 0, 0, 100, 100 },
207         { 0, 0, 100, 100 },
208         { 0, 0, 100, 100 },
209         { 40, 40, 50, 50 },
210 
211         // A op invB
212         { 10, 10, 50, 50 },
213         { 40, 40, 50, 50 },
214         { 0, 0, 100, 100 },
215         { 0, 0, 100, 100 },
216         { 0, 0, 100, 100 },
217 
218         // invA op invB
219         { 0, 0, 100, 100 },
220         { 40, 40, 80, 80 },
221         { 0, 0, 100, 100 },
222         { 10, 10, 80, 80 },
223         { 10, 10, 50, 50 },
224     };
225 
226     static const SkClipOp gOps[] = {
227         kIntersect_SkClipOp,
228         kDifference_SkClipOp,
229         kUnion_SkClipOp,
230         kXOR_SkClipOp,
231         kReverseDifference_SkClipOp
232     };
233 
234     SkRect rectA, rectB;
235 
236     rectA.iset(10, 10, 50, 50);
237     rectB.iset(40, 40, 80, 80);
238 
239     SkRRect rrectA, rrectB;
240     rrectA.setOval(rectA);
241     rrectB.setRectXY(rectB, SkIntToScalar(1), SkIntToScalar(2));
242 
243     SkPath pathA, pathB;
244 
245     pathA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
246     pathB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
247 
248     SkClipStack stack;
249     SkRect devClipBound;
250     bool isIntersectionOfRects = false;
251 
252     int testCase = 0;
253     int numBitTests = SkClipStack::Element::kPath_Type == primType ? 4 : 1;
254     for (int invBits = 0; invBits < numBitTests; ++invBits) {
255         for (size_t op = 0; op < SK_ARRAY_COUNT(gOps); ++op) {
256 
257             stack.save();
258             bool doInvA = SkToBool(invBits & 1);
259             bool doInvB = SkToBool(invBits & 2);
260 
261             pathA.setFillType(doInvA ? SkPath::kInverseEvenOdd_FillType :
262                                        SkPath::kEvenOdd_FillType);
263             pathB.setFillType(doInvB ? SkPath::kInverseEvenOdd_FillType :
264                                        SkPath::kEvenOdd_FillType);
265 
266             switch (primType) {
267                 case SkClipStack::Element::kEmpty_Type:
268                     SkDEBUGFAIL("Don't call this with kEmpty.");
269                     break;
270                 case SkClipStack::Element::kRect_Type:
271                     stack.clipRect(rectA, SkMatrix::I(), kIntersect_SkClipOp, false);
272                     stack.clipRect(rectB, SkMatrix::I(), gOps[op], false);
273                     break;
274                 case SkClipStack::Element::kRRect_Type:
275                     stack.clipRRect(rrectA, SkMatrix::I(), kIntersect_SkClipOp, false);
276                     stack.clipRRect(rrectB, SkMatrix::I(), gOps[op], false);
277                     break;
278                 case SkClipStack::Element::kPath_Type:
279                     stack.clipPath(pathA, SkMatrix::I(), kIntersect_SkClipOp, false);
280                     stack.clipPath(pathB, SkMatrix::I(), gOps[op], false);
281                     break;
282             }
283 
284             REPORTER_ASSERT(reporter, !stack.isWideOpen());
285             REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
286 
287             stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
288                                         &isIntersectionOfRects);
289 
290             if (SkClipStack::Element::kRect_Type == primType) {
291                 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
292                         (gOps[op] == kIntersect_SkClipOp));
293             } else {
294                 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
295             }
296 
297             SkASSERT(testCase < gNumCases);
298             REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
299             ++testCase;
300 
301             stack.restore();
302         }
303     }
304 }
305 
306 // Test out 'isWideOpen' entry point
test_isWideOpen(skiatest::Reporter * reporter)307 static void test_isWideOpen(skiatest::Reporter* reporter) {
308     {
309         // Empty stack is wide open. Wide open stack means that gen id is wide open.
310         SkClipStack stack;
311         REPORTER_ASSERT(reporter, stack.isWideOpen());
312         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
313     }
314 
315     SkRect rectA, rectB;
316 
317     rectA.iset(10, 10, 40, 40);
318     rectB.iset(50, 50, 80, 80);
319 
320     // Stack should initially be wide open
321     {
322         SkClipStack stack;
323 
324         REPORTER_ASSERT(reporter, stack.isWideOpen());
325         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
326     }
327 
328     // Test out case where the user specifies a union that includes everything
329     {
330         SkClipStack stack;
331 
332         SkPath clipA, clipB;
333 
334         clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
335         clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
336 
337         clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
338         clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
339 
340         stack.clipPath(clipA, SkMatrix::I(), kReplace_SkClipOp, false);
341         stack.clipPath(clipB, SkMatrix::I(), kUnion_SkClipOp, false);
342 
343         REPORTER_ASSERT(reporter, stack.isWideOpen());
344         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
345     }
346 
347     // Test out union w/ a wide open clip
348     {
349         SkClipStack stack;
350 
351         stack.clipRect(rectA, SkMatrix::I(), kUnion_SkClipOp, false);
352 
353         REPORTER_ASSERT(reporter, stack.isWideOpen());
354         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
355     }
356 
357     // Test out empty difference from a wide open clip
358     {
359         SkClipStack stack;
360 
361         SkRect emptyRect;
362         emptyRect.setEmpty();
363 
364         stack.clipRect(emptyRect, SkMatrix::I(), kDifference_SkClipOp, false);
365 
366         REPORTER_ASSERT(reporter, stack.isWideOpen());
367         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
368     }
369 
370     // Test out return to wide open
371     {
372         SkClipStack stack;
373 
374         stack.save();
375 
376         stack.clipRect(rectA, SkMatrix::I(), kReplace_SkClipOp, false);
377 
378         REPORTER_ASSERT(reporter, !stack.isWideOpen());
379         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID != stack.getTopmostGenID());
380 
381         stack.restore();
382 
383         REPORTER_ASSERT(reporter, stack.isWideOpen());
384         REPORTER_ASSERT(reporter, SkClipStack::kWideOpenGenID == stack.getTopmostGenID());
385     }
386 }
387 
count(const SkClipStack & stack)388 static int count(const SkClipStack& stack) {
389 
390     SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
391 
392     const SkClipStack::Element* element = nullptr;
393     int count = 0;
394 
395     for (element = iter.prev(); element; element = iter.prev(), ++count) {
396         ;
397     }
398 
399     return count;
400 }
401 
test_rect_inverse_fill(skiatest::Reporter * reporter)402 static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
403     // non-intersecting rectangles
404     SkRect rect  = SkRect::MakeLTRB(0, 0, 10, 10);
405 
406     SkPath path;
407     path.addRect(rect);
408     path.toggleInverseFillType();
409     SkClipStack stack;
410     stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
411 
412     SkRect bounds;
413     SkClipStack::BoundsType boundsType;
414     stack.getBounds(&bounds, &boundsType);
415     REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
416     REPORTER_ASSERT(reporter, bounds == rect);
417 }
418 
test_rect_replace(skiatest::Reporter * reporter)419 static void test_rect_replace(skiatest::Reporter* reporter) {
420     SkRect rect = SkRect::MakeWH(100, 100);
421     SkRect rect2 = SkRect::MakeXYWH(50, 50, 100, 100);
422 
423     SkRect bound;
424     SkClipStack::BoundsType type;
425     bool isIntersectionOfRects;
426 
427     // Adding a new rect with the replace operator should not increase
428     // the stack depth. BW replacing BW.
429     {
430         SkClipStack stack;
431         REPORTER_ASSERT(reporter, 0 == count(stack));
432         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
433         REPORTER_ASSERT(reporter, 1 == count(stack));
434         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
435         REPORTER_ASSERT(reporter, 1 == count(stack));
436     }
437 
438     // Adding a new rect with the replace operator should not increase
439     // the stack depth. AA replacing AA.
440     {
441         SkClipStack stack;
442         REPORTER_ASSERT(reporter, 0 == count(stack));
443         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
444         REPORTER_ASSERT(reporter, 1 == count(stack));
445         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
446         REPORTER_ASSERT(reporter, 1 == count(stack));
447     }
448 
449     // Adding a new rect with the replace operator should not increase
450     // the stack depth. BW replacing AA replacing BW.
451     {
452         SkClipStack stack;
453         REPORTER_ASSERT(reporter, 0 == count(stack));
454         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
455         REPORTER_ASSERT(reporter, 1 == count(stack));
456         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
457         REPORTER_ASSERT(reporter, 1 == count(stack));
458         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
459         REPORTER_ASSERT(reporter, 1 == count(stack));
460     }
461 
462     // Make sure replace clip rects don't collapse too much.
463     {
464         SkClipStack stack;
465         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
466         stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
467         REPORTER_ASSERT(reporter, 1 == count(stack));
468 
469         stack.save();
470         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
471         REPORTER_ASSERT(reporter, 2 == count(stack));
472         stack.getBounds(&bound, &type, &isIntersectionOfRects);
473         REPORTER_ASSERT(reporter, bound == rect);
474         stack.restore();
475         REPORTER_ASSERT(reporter, 1 == count(stack));
476 
477         stack.save();
478         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
479         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
480         REPORTER_ASSERT(reporter, 2 == count(stack));
481         stack.restore();
482         REPORTER_ASSERT(reporter, 1 == count(stack));
483 
484         stack.save();
485         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
486         stack.clipRect(rect2, SkMatrix::I(), kIntersect_SkClipOp, false);
487         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, false);
488         REPORTER_ASSERT(reporter, 2 == count(stack));
489         stack.restore();
490         REPORTER_ASSERT(reporter, 1 == count(stack));
491     }
492 }
493 
494 // Simplified path-based version of test_rect_replace.
test_path_replace(skiatest::Reporter * reporter)495 static void test_path_replace(skiatest::Reporter* reporter) {
496     SkRect rect = SkRect::MakeWH(100, 100);
497     SkPath path;
498     path.addCircle(50, 50, 50);
499 
500     // Replace operation doesn't grow the stack.
501     {
502         SkClipStack stack;
503         REPORTER_ASSERT(reporter, 0 == count(stack));
504         stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
505         REPORTER_ASSERT(reporter, 1 == count(stack));
506         stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, false);
507         REPORTER_ASSERT(reporter, 1 == count(stack));
508     }
509 
510     // Replacing rect with path.
511     {
512         SkClipStack stack;
513         stack.clipRect(rect, SkMatrix::I(), kReplace_SkClipOp, true);
514         REPORTER_ASSERT(reporter, 1 == count(stack));
515         stack.clipPath(path, SkMatrix::I(), kReplace_SkClipOp, true);
516         REPORTER_ASSERT(reporter, 1 == count(stack));
517     }
518 }
519 
520 // Test out SkClipStack's merging of rect clips. In particular exercise
521 // merging of aa vs. bw rects.
test_rect_merging(skiatest::Reporter * reporter)522 static void test_rect_merging(skiatest::Reporter* reporter) {
523 
524     SkRect overlapLeft  = SkRect::MakeLTRB(10, 10, 50, 50);
525     SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
526 
527     SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
528     SkRect nestedChild  = SkRect::MakeLTRB(40, 40, 60, 60);
529 
530     SkRect bound;
531     SkClipStack::BoundsType type;
532     bool isIntersectionOfRects;
533 
534     // all bw overlapping - should merge
535     {
536         SkClipStack stack;
537 
538         stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, false);
539 
540         stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
541 
542         REPORTER_ASSERT(reporter, 1 == count(stack));
543 
544         stack.getBounds(&bound, &type, &isIntersectionOfRects);
545 
546         REPORTER_ASSERT(reporter, isIntersectionOfRects);
547     }
548 
549     // all aa overlapping - should merge
550     {
551         SkClipStack stack;
552 
553         stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
554 
555         stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, true);
556 
557         REPORTER_ASSERT(reporter, 1 == count(stack));
558 
559         stack.getBounds(&bound, &type, &isIntersectionOfRects);
560 
561         REPORTER_ASSERT(reporter, isIntersectionOfRects);
562     }
563 
564     // mixed overlapping - should _not_ merge
565     {
566         SkClipStack stack;
567 
568         stack.clipRect(overlapLeft, SkMatrix::I(), kReplace_SkClipOp, true);
569 
570         stack.clipRect(overlapRight, SkMatrix::I(), kIntersect_SkClipOp, false);
571 
572         REPORTER_ASSERT(reporter, 2 == count(stack));
573 
574         stack.getBounds(&bound, &type, &isIntersectionOfRects);
575 
576         REPORTER_ASSERT(reporter, !isIntersectionOfRects);
577     }
578 
579     // mixed nested (bw inside aa) - should merge
580     {
581         SkClipStack stack;
582 
583         stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, true);
584 
585         stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, false);
586 
587         REPORTER_ASSERT(reporter, 1 == count(stack));
588 
589         stack.getBounds(&bound, &type, &isIntersectionOfRects);
590 
591         REPORTER_ASSERT(reporter, isIntersectionOfRects);
592     }
593 
594     // mixed nested (aa inside bw) - should merge
595     {
596         SkClipStack stack;
597 
598         stack.clipRect(nestedParent, SkMatrix::I(), kReplace_SkClipOp, false);
599 
600         stack.clipRect(nestedChild, SkMatrix::I(), kIntersect_SkClipOp, true);
601 
602         REPORTER_ASSERT(reporter, 1 == count(stack));
603 
604         stack.getBounds(&bound, &type, &isIntersectionOfRects);
605 
606         REPORTER_ASSERT(reporter, isIntersectionOfRects);
607     }
608 
609     // reverse nested (aa inside bw) - should _not_ merge
610     {
611         SkClipStack stack;
612 
613         stack.clipRect(nestedChild, SkMatrix::I(), kReplace_SkClipOp, false);
614 
615         stack.clipRect(nestedParent, SkMatrix::I(), kIntersect_SkClipOp, true);
616 
617         REPORTER_ASSERT(reporter, 2 == count(stack));
618 
619         stack.getBounds(&bound, &type, &isIntersectionOfRects);
620 
621         REPORTER_ASSERT(reporter, !isIntersectionOfRects);
622     }
623 }
624 
test_quickContains(skiatest::Reporter * reporter)625 static void test_quickContains(skiatest::Reporter* reporter) {
626     SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
627     SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
628     SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
629     SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
630     SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
631 
632     SkPath insideCircle;
633     insideCircle.addCircle(25, 25, 5);
634     SkPath intersectingCircle;
635     intersectingCircle.addCircle(25, 40, 10);
636     SkPath outsideCircle;
637     outsideCircle.addCircle(25, 25, 50);
638     SkPath nonIntersectingCircle;
639     nonIntersectingCircle.addCircle(100, 100, 5);
640 
641     {
642         SkClipStack stack;
643         stack.clipRect(outsideRect, SkMatrix::I(), kDifference_SkClipOp, false);
644         // return false because quickContains currently does not care for kDifference_SkClipOp
645         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
646     }
647 
648     // Replace Op tests
649     {
650         SkClipStack stack;
651         stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
652         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
653     }
654 
655     {
656         SkClipStack stack;
657         stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
658         stack.save(); // To prevent in-place substitution by replace OP
659         stack.clipRect(outsideRect, SkMatrix::I(), kReplace_SkClipOp, false);
660         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
661         stack.restore();
662     }
663 
664     {
665         SkClipStack stack;
666         stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
667         stack.save(); // To prevent in-place substitution by replace OP
668         stack.clipRect(insideRect, SkMatrix::I(), kReplace_SkClipOp, false);
669         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
670         stack.restore();
671     }
672 
673     // Verify proper traversal of multi-element clip
674     {
675         SkClipStack stack;
676         stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
677         // Use a path for second clip to prevent in-place intersection
678         stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
679         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
680     }
681 
682     // Intersect Op tests with rectangles
683     {
684         SkClipStack stack;
685         stack.clipRect(outsideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
686         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
687     }
688 
689     {
690         SkClipStack stack;
691         stack.clipRect(insideRect, SkMatrix::I(), kIntersect_SkClipOp, false);
692         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
693     }
694 
695     {
696         SkClipStack stack;
697         stack.clipRect(intersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
698         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
699     }
700 
701     {
702         SkClipStack stack;
703         stack.clipRect(nonIntersectingRect, SkMatrix::I(), kIntersect_SkClipOp, false);
704         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
705     }
706 
707     // Intersect Op tests with circle paths
708     {
709         SkClipStack stack;
710         stack.clipPath(outsideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
711         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
712     }
713 
714     {
715         SkClipStack stack;
716         stack.clipPath(insideCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
717         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
718     }
719 
720     {
721         SkClipStack stack;
722         stack.clipPath(intersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
723         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
724     }
725 
726     {
727         SkClipStack stack;
728         stack.clipPath(nonIntersectingCircle, SkMatrix::I(), kIntersect_SkClipOp, false);
729         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
730     }
731 
732     // Intersect Op tests with inverse filled rectangles
733     {
734         SkClipStack stack;
735         SkPath path;
736         path.addRect(outsideRect);
737         path.toggleInverseFillType();
738         stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
739         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
740     }
741 
742     {
743         SkClipStack stack;
744         SkPath path;
745         path.addRect(insideRect);
746         path.toggleInverseFillType();
747         stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
748         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
749     }
750 
751     {
752         SkClipStack stack;
753         SkPath path;
754         path.addRect(intersectingRect);
755         path.toggleInverseFillType();
756         stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
757         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
758     }
759 
760     {
761         SkClipStack stack;
762         SkPath path;
763         path.addRect(nonIntersectingRect);
764         path.toggleInverseFillType();
765         stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
766         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
767     }
768 
769     // Intersect Op tests with inverse filled circles
770     {
771         SkClipStack stack;
772         SkPath path = outsideCircle;
773         path.toggleInverseFillType();
774         stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
775         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
776     }
777 
778     {
779         SkClipStack stack;
780         SkPath path = insideCircle;
781         path.toggleInverseFillType();
782         stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
783         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
784     }
785 
786     {
787         SkClipStack stack;
788         SkPath path = intersectingCircle;
789         path.toggleInverseFillType();
790         stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
791         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
792     }
793 
794     {
795         SkClipStack stack;
796         SkPath path = nonIntersectingCircle;
797         path.toggleInverseFillType();
798         stack.clipPath(path, SkMatrix::I(), kIntersect_SkClipOp, false);
799         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
800     }
801 }
802 
set_region_to_stack(const SkClipStack & stack,const SkIRect & bounds,SkRegion * region)803 static void set_region_to_stack(const SkClipStack& stack, const SkIRect& bounds, SkRegion* region) {
804     region->setRect(bounds);
805     SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
806     while (const SkClipStack::Element *element = iter.next()) {
807         SkRegion elemRegion;
808         SkRegion boundsRgn(bounds);
809         SkPath path;
810 
811         switch (element->getType()) {
812             case SkClipStack::Element::kEmpty_Type:
813                 elemRegion.setEmpty();
814                 break;
815             default:
816                 element->asPath(&path);
817                 elemRegion.setPath(path, boundsRgn);
818                 break;
819         }
820         region->op(elemRegion, (SkRegion::Op)element->getOp());
821     }
822 }
823 
test_invfill_diff_bug(skiatest::Reporter * reporter)824 static void test_invfill_diff_bug(skiatest::Reporter* reporter) {
825     SkClipStack stack;
826     stack.clipRect({10, 10, 20, 20}, SkMatrix::I(), kIntersect_SkClipOp, false);
827 
828     SkPath path;
829     path.addRect({30, 10, 40, 20});
830     path.setFillType(SkPath::kInverseWinding_FillType);
831     stack.clipPath(path, SkMatrix::I(), kDifference_SkClipOp, false);
832 
833     REPORTER_ASSERT(reporter, SkClipStack::kEmptyGenID == stack.getTopmostGenID());
834 
835     SkRect stackBounds;
836     SkClipStack::BoundsType stackBoundsType;
837     stack.getBounds(&stackBounds, &stackBoundsType);
838 
839     REPORTER_ASSERT(reporter, stackBounds.isEmpty());
840     REPORTER_ASSERT(reporter, SkClipStack::kNormal_BoundsType == stackBoundsType);
841 
842     SkRegion region;
843     set_region_to_stack(stack, {0, 0, 50, 30}, &region);
844 
845     REPORTER_ASSERT(reporter, region.isEmpty());
846 }
847 
848 ///////////////////////////////////////////////////////////////////////////////////////////////////
849 
850 #if SK_SUPPORT_GPU
851 // Functions that add a shape to the clip stack. The shape is computed from a rectangle.
852 // AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
853 // stack. A fractional edge repeated in different elements may be rasterized fewer times using the
854 // reduced stack.
855 typedef void (*AddElementFunc) (const SkRect& rect,
856                                 bool invert,
857                                 SkClipOp op,
858                                 SkClipStack* stack,
859                                 bool doAA);
860 
add_round_rect(const SkRect & rect,bool invert,SkClipOp op,SkClipStack * stack,bool doAA)861 static void add_round_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
862                            bool doAA) {
863     SkScalar rx = rect.width() / 10;
864     SkScalar ry = rect.height() / 20;
865     if (invert) {
866         SkPath path;
867         path.addRoundRect(rect, rx, ry);
868         path.setFillType(SkPath::kInverseWinding_FillType);
869         stack->clipPath(path, SkMatrix::I(), op, doAA);
870     } else {
871         SkRRect rrect;
872         rrect.setRectXY(rect, rx, ry);
873         stack->clipRRect(rrect, SkMatrix::I(), op, doAA);
874     }
875 };
876 
add_rect(const SkRect & rect,bool invert,SkClipOp op,SkClipStack * stack,bool doAA)877 static void add_rect(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
878                      bool doAA) {
879     if (invert) {
880         SkPath path;
881         path.addRect(rect);
882         path.setFillType(SkPath::kInverseWinding_FillType);
883         stack->clipPath(path, SkMatrix::I(), op, doAA);
884     } else {
885         stack->clipRect(rect, SkMatrix::I(), op, doAA);
886     }
887 };
888 
add_oval(const SkRect & rect,bool invert,SkClipOp op,SkClipStack * stack,bool doAA)889 static void add_oval(const SkRect& rect, bool invert, SkClipOp op, SkClipStack* stack,
890                      bool doAA) {
891     SkPath path;
892     path.addOval(rect);
893     if (invert) {
894         path.setFillType(SkPath::kInverseWinding_FillType);
895     }
896     stack->clipPath(path, SkMatrix::I(), op, doAA);
897 };
898 
add_elem_to_stack(const SkClipStack::Element & element,SkClipStack * stack)899 static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
900     switch (element.getType()) {
901         case SkClipStack::Element::kRect_Type:
902             stack->clipRect(element.getRect(), SkMatrix::I(), element.getOp(), element.isAA());
903             break;
904         case SkClipStack::Element::kRRect_Type:
905             stack->clipRRect(element.getRRect(), SkMatrix::I(), element.getOp(), element.isAA());
906             break;
907         case SkClipStack::Element::kPath_Type:
908             stack->clipPath(element.getPath(), SkMatrix::I(), element.getOp(), element.isAA());
909             break;
910         case SkClipStack::Element::kEmpty_Type:
911             SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
912             stack->clipEmpty();
913             break;
914     }
915 }
916 
test_reduced_clip_stack(skiatest::Reporter * reporter)917 static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
918     // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
919     // they are equal.
920 
921     // All the clip elements will be contained within these bounds.
922     static const SkIRect kIBounds = SkIRect::MakeWH(100, 100);
923     static const SkRect kBounds = SkRect::Make(kIBounds);
924 
925     enum {
926         kNumTests = 250,
927         kMinElemsPerTest = 1,
928         kMaxElemsPerTest = 50,
929     };
930 
931     // min/max size of a clip element as a fraction of kBounds.
932     static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
933     static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
934 
935     static const SkClipOp kOps[] = {
936         kDifference_SkClipOp,
937         kIntersect_SkClipOp,
938         kUnion_SkClipOp,
939         kXOR_SkClipOp,
940         kReverseDifference_SkClipOp,
941         kReplace_SkClipOp,
942     };
943 
944     // Replace operations short-circuit the optimizer. We want to make sure that we test this code
945     // path a little bit but we don't want it to prevent us from testing many longer traversals in
946     // the optimizer.
947     static const int kReplaceDiv = 4 * kMaxElemsPerTest;
948 
949     // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
950     static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
951 
952     static const SkScalar kFractionAntialiased = 0.25;
953 
954     static const AddElementFunc kElementFuncs[] = {
955         add_rect,
956         add_round_rect,
957         add_oval,
958     };
959 
960     SkRandom r;
961 
962     for (int i = 0; i < kNumTests; ++i) {
963         SkString testCase;
964         testCase.printf("Iteration %d", i);
965 
966         // Randomly generate a clip stack.
967         SkClipStack stack;
968         int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
969         bool doAA = r.nextBiasedBool(kFractionAntialiased);
970         for (int e = 0; e < numElems; ++e) {
971             SkClipOp op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
972             if (op == kReplace_SkClipOp) {
973                 if (r.nextU() % kReplaceDiv) {
974                     --e;
975                     continue;
976                 }
977             }
978 
979             // saves can change the clip stack behavior when an element is added.
980             bool doSave = r.nextBool();
981 
982             SkSize size = SkSize::Make(
983                 kBounds.width()  * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac),
984                 kBounds.height() * r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac));
985 
986             SkPoint xy = {r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth),
987                           r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight)};
988 
989             SkRect rect;
990             if (doAA) {
991                 rect.setXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
992                 if (GrClip::IsPixelAligned(rect)) {
993                     // Don't create an element that may accidentally become not antialiased.
994                     rect.outset(0.5f, 0.5f);
995                 }
996                 SkASSERT(!GrClip::IsPixelAligned(rect));
997             } else {
998                 rect.setXYWH(SkScalarFloorToScalar(xy.fX),
999                              SkScalarFloorToScalar(xy.fY),
1000                              SkScalarCeilToScalar(size.fWidth),
1001                              SkScalarCeilToScalar(size.fHeight));
1002             }
1003 
1004             bool invert = r.nextBiasedBool(kFractionInverted);
1005 
1006             kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack,
1007                                                                           doAA);
1008             if (doSave) {
1009                 stack.save();
1010             }
1011         }
1012 
1013         // Zero the memory we will new the GrReducedClip into. This ensures the elements gen ID
1014         // will be kInvalidGenID if left uninitialized.
1015         SkAlignedSTStorage<1, GrReducedClip> storage;
1016         memset(storage.get(), 0, sizeof(GrReducedClip));
1017         GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1018 
1019         // Get the reduced version of the stack.
1020         SkRect queryBounds = kBounds;
1021         queryBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
1022         const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, queryBounds);
1023 
1024         REPORTER_ASSERT_MESSAGE(reporter,
1025                                 reduced->elements().isEmpty() ||
1026                                 SkClipStack::kInvalidGenID != reduced->elementsGenID(),
1027                                 testCase.c_str());
1028 
1029         if (!reduced->elements().isEmpty()) {
1030             REPORTER_ASSERT_MESSAGE(reporter, reduced->hasIBounds(), testCase.c_str());
1031             SkRect stackBounds;
1032             SkClipStack::BoundsType stackBoundsType;
1033             stack.getBounds(&stackBounds, &stackBoundsType);
1034             if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1035                 // Unless GrReducedClip starts doing some heroic tightening of the clip bounds, this
1036                 // will be true since the stack bounds are completely contained inside the query.
1037                 REPORTER_ASSERT_MESSAGE(reporter,
1038                                         GrClip::IsInsideClip(reduced->ibounds(), stackBounds),
1039                                         testCase.c_str());
1040             }
1041             REPORTER_ASSERT_MESSAGE(reporter, reduced->requiresAA() == doAA, testCase.c_str());
1042         }
1043 
1044         // Build a new clip stack based on the reduced clip elements
1045         SkClipStack reducedStack;
1046         if (GrReducedClip::InitialState::kAllOut == reduced->initialState()) {
1047             // whether the result is bounded or not, the whole plane should start outside the clip.
1048             reducedStack.clipEmpty();
1049         }
1050         for (ElementList::Iter iter(reduced->elements()); iter.get(); iter.next()) {
1051             add_elem_to_stack(*iter.get(), &reducedStack);
1052         }
1053 
1054         SkIRect ibounds = reduced->hasIBounds() ? reduced->ibounds() : kIBounds;
1055 
1056         // GrReducedClipStack assumes that the final result is clipped to the returned bounds
1057         reducedStack.clipDevRect(ibounds, kIntersect_SkClipOp);
1058         stack.clipDevRect(ibounds, kIntersect_SkClipOp);
1059 
1060         // convert both the original stack and reduced stack to SkRegions and see if they're equal
1061         SkRegion region;
1062         set_region_to_stack(stack, ibounds, &region);
1063 
1064         SkRegion reducedRegion;
1065         set_region_to_stack(reducedStack, ibounds, &reducedRegion);
1066 
1067         REPORTER_ASSERT_MESSAGE(reporter, region == reducedRegion, testCase.c_str());
1068 
1069         reduced->~GrReducedClip();
1070     }
1071 }
1072 
1073 #ifdef SK_BUILD_FOR_WIN
1074     #define SUPPRESS_VISIBILITY_WARNING
1075 #else
1076     #define SUPPRESS_VISIBILITY_WARNING __attribute__((visibility("hidden")))
1077 #endif
1078 
test_reduced_clip_stack_genid(skiatest::Reporter * reporter)1079 static void test_reduced_clip_stack_genid(skiatest::Reporter* reporter) {
1080     {
1081         SkClipStack stack;
1082         stack.clipRect(SkRect::MakeXYWH(0, 0, 100, 100), SkMatrix::I(), kReplace_SkClipOp,
1083                        true);
1084         stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(50.3), SkScalar(50.3)), SkMatrix::I(),
1085                        kReplace_SkClipOp, true);
1086         SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
1087 
1088         SkAlignedSTStorage<1, GrReducedClip> storage;
1089         memset(storage.get(), 0, sizeof(GrReducedClip));
1090         GR_STATIC_ASSERT(0 == SkClipStack::kInvalidGenID);
1091         const GrReducedClip* reduced = new (storage.get()) GrReducedClip(stack, bounds);
1092 
1093         REPORTER_ASSERT(reporter, reduced->elements().count() == 1);
1094         // Clips will be cached based on the generation id. Make sure the gen id is valid.
1095         REPORTER_ASSERT(reporter, SkClipStack::kInvalidGenID != reduced->elementsGenID());
1096 
1097         reduced->~GrReducedClip();
1098     }
1099     {
1100         SkClipStack stack;
1101 
1102         // Create a clip with following 25.3, 25.3 boxes which are 25 apart:
1103         //  A  B
1104         //  C  D
1105 
1106         stack.clipRect(SkRect::MakeXYWH(0, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
1107                        kReplace_SkClipOp, true);
1108         int32_t genIDA = stack.getTopmostGenID();
1109         stack.clipRect(SkRect::MakeXYWH(50, 0, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
1110                        kUnion_SkClipOp, true);
1111         int32_t genIDB = stack.getTopmostGenID();
1112         stack.clipRect(SkRect::MakeXYWH(0, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
1113                        kUnion_SkClipOp, true);
1114         int32_t genIDC = stack.getTopmostGenID();
1115         stack.clipRect(SkRect::MakeXYWH(50, 50, SkScalar(25.3), SkScalar(25.3)), SkMatrix::I(),
1116                        kUnion_SkClipOp, true);
1117         int32_t genIDD = stack.getTopmostGenID();
1118 
1119 
1120 #define IXYWH SkIRect::MakeXYWH
1121 #define XYWH SkRect::MakeXYWH
1122 
1123         SkIRect stackBounds = IXYWH(0, 0, 76, 76);
1124 
1125         // The base test is to test each rect in two ways:
1126         // 1) The box dimensions. (Should reduce to "all in", no elements).
1127         // 2) A bit over the box dimensions.
1128         // In the case 2, test that the generation id is what is expected.
1129         // The rects are of fractional size so that case 2 never gets optimized to an empty element
1130         // list.
1131 
1132         // Not passing in tighter bounds is tested for consistency.
1133         static const struct SUPPRESS_VISIBILITY_WARNING {
1134             SkRect testBounds;
1135             int reducedClipCount;
1136             int32_t reducedGenID;
1137             InitialState initialState;
1138             SkIRect clipIRect;
1139             // parameter.
1140         } testCases[] = {
1141 
1142             // Rect A.
1143             { XYWH(0, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 25, 25) },
1144             { XYWH(0.1f, 0.1f, 25.1f, 25.1f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 0, 26, 26) },
1145             { XYWH(0, 0, 27, 27), 1, genIDA, GrReducedClip::InitialState::kAllOut, IXYWH(0, 0, 27, 27)},
1146 
1147             // Rect B.
1148             { XYWH(50, 0, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 25, 25) },
1149             { XYWH(50, 0, 25.3f, 25.3f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 0, 26, 26) },
1150             { XYWH(50, 0, 27, 27), 1, genIDB, GrReducedClip::InitialState::kAllOut, IXYWH(50, 0, 26, 27) },
1151 
1152             // Rect C.
1153             { XYWH(0, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 25, 25) },
1154             { XYWH(0.2f, 50.1f, 25.1f, 25.2f), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(0, 50, 26, 26) },
1155             { XYWH(0, 50, 27, 27), 1, genIDC, GrReducedClip::InitialState::kAllOut, IXYWH(0, 50, 27, 26) },
1156 
1157             // Rect D.
1158             { XYWH(50, 50, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 25, 25)},
1159             { XYWH(50.3f, 50.3f, 25, 25), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllIn, IXYWH(50, 50, 26, 26)},
1160             { XYWH(50, 50, 27, 27), 1, genIDD, GrReducedClip::InitialState::kAllOut,  IXYWH(50, 50, 26, 26)},
1161 
1162             // Other tests:
1163             { XYWH(0, 0, 100, 100), 4, genIDD, GrReducedClip::InitialState::kAllOut, stackBounds },
1164 
1165             // Rect in the middle, touches none.
1166             { XYWH(26, 26, 24, 24), 0, SkClipStack::kInvalidGenID, GrReducedClip::InitialState::kAllOut, IXYWH(26, 26, 24, 24) },
1167 
1168             // Rect in the middle, touches all the rects. GenID is the last rect.
1169             { XYWH(24, 24, 27, 27), 4, genIDD, GrReducedClip::InitialState::kAllOut, IXYWH(24, 24, 27, 27) },
1170         };
1171 
1172 #undef XYWH
1173 #undef IXYWH
1174 
1175         for (size_t i = 0; i < SK_ARRAY_COUNT(testCases); ++i) {
1176             const GrReducedClip reduced(stack, testCases[i].testBounds);
1177             REPORTER_ASSERT(reporter, reduced.elements().count() == testCases[i].reducedClipCount);
1178             SkASSERT(reduced.elements().count() == testCases[i].reducedClipCount);
1179             if (reduced.elements().count()) {
1180                 REPORTER_ASSERT(reporter, reduced.elementsGenID() == testCases[i].reducedGenID);
1181                 SkASSERT(reduced.elementsGenID() == testCases[i].reducedGenID);
1182             }
1183             REPORTER_ASSERT(reporter, reduced.initialState() == testCases[i].initialState);
1184             SkASSERT(reduced.initialState() == testCases[i].initialState);
1185             REPORTER_ASSERT(reporter, reduced.hasIBounds());
1186             SkASSERT(reduced.hasIBounds());
1187             REPORTER_ASSERT(reporter, reduced.ibounds() == testCases[i].clipIRect);
1188             SkASSERT(reduced.ibounds() == testCases[i].clipIRect);
1189         }
1190     }
1191 }
1192 
test_reduced_clip_stack_no_aa_crash(skiatest::Reporter * reporter)1193 static void test_reduced_clip_stack_no_aa_crash(skiatest::Reporter* reporter) {
1194     SkClipStack stack;
1195     stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 100, 100), kReplace_SkClipOp);
1196     stack.clipDevRect(SkIRect::MakeXYWH(0, 0, 50, 50), kReplace_SkClipOp);
1197     SkRect bounds = SkRect::MakeXYWH(0, 0, 100, 100);
1198 
1199     // At the time, this would crash.
1200     const GrReducedClip reduced(stack, bounds);
1201     REPORTER_ASSERT(reporter, reduced.elements().isEmpty());
1202 }
1203 
1204 enum class ClipMethod {
1205     kSkipDraw,
1206     kIgnoreClip,
1207     kScissor,
1208     kAAElements
1209 };
1210 
test_aa_query(skiatest::Reporter * reporter,const SkString & testName,const SkClipStack & stack,const SkMatrix & queryXform,const SkRect & preXformQuery,ClipMethod expectedMethod,int numExpectedElems=0)1211 static void test_aa_query(skiatest::Reporter* reporter, const SkString& testName,
1212                           const SkClipStack& stack, const SkMatrix& queryXform,
1213                           const SkRect& preXformQuery, ClipMethod expectedMethod,
1214                           int numExpectedElems = 0) {
1215     SkRect queryBounds;
1216     queryXform.mapRect(&queryBounds, preXformQuery);
1217     const GrReducedClip reduced(stack, queryBounds);
1218 
1219     SkClipStack::BoundsType stackBoundsType;
1220     SkRect stackBounds;
1221     stack.getBounds(&stackBounds, &stackBoundsType);
1222 
1223     switch (expectedMethod) {
1224         case ClipMethod::kSkipDraw:
1225             SkASSERT(0 == numExpectedElems);
1226             REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1227             REPORTER_ASSERT_MESSAGE(reporter,
1228                                     GrReducedClip::InitialState::kAllOut == reduced.initialState(),
1229                                     testName.c_str());
1230             return;
1231         case ClipMethod::kIgnoreClip:
1232             SkASSERT(0 == numExpectedElems);
1233             REPORTER_ASSERT_MESSAGE(reporter,
1234                                     !reduced.hasIBounds() ||
1235                                     GrClip::IsInsideClip(reduced.ibounds(), queryBounds),
1236                                     testName.c_str());
1237             REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1238             REPORTER_ASSERT_MESSAGE(reporter,
1239                                     GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1240                                     testName.c_str());
1241             return;
1242         case ClipMethod::kScissor: {
1243             SkASSERT(SkClipStack::kNormal_BoundsType == stackBoundsType);
1244             SkASSERT(0 == numExpectedElems);
1245             SkIRect expectedScissor;
1246             stackBounds.round(&expectedScissor);
1247             REPORTER_ASSERT_MESSAGE(reporter, reduced.elements().isEmpty(), testName.c_str());
1248             REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1249             REPORTER_ASSERT_MESSAGE(reporter, expectedScissor == reduced.ibounds(),
1250                                     testName.c_str());
1251             REPORTER_ASSERT_MESSAGE(reporter,
1252                                     GrReducedClip::InitialState::kAllIn == reduced.initialState(),
1253                                     testName.c_str());
1254             return;
1255         }
1256         case ClipMethod::kAAElements: {
1257             SkIRect expectedClipIBounds = GrClip::GetPixelIBounds(queryBounds);
1258             if (SkClipStack::kNormal_BoundsType == stackBoundsType) {
1259                 SkAssertResult(expectedClipIBounds.intersect(GrClip::GetPixelIBounds(stackBounds)));
1260             }
1261             REPORTER_ASSERT_MESSAGE(reporter, numExpectedElems == reduced.elements().count(),
1262                                     testName.c_str());
1263             REPORTER_ASSERT_MESSAGE(reporter, reduced.hasIBounds(), testName.c_str());
1264             REPORTER_ASSERT_MESSAGE(reporter, expectedClipIBounds == reduced.ibounds(),
1265                                     testName.c_str());
1266             REPORTER_ASSERT_MESSAGE(reporter, reduced.requiresAA() == !reduced.elements().isEmpty(),
1267                                     testName.c_str());
1268             break;
1269         }
1270     }
1271 }
1272 
test_reduced_clip_stack_aa(skiatest::Reporter * reporter)1273 static void test_reduced_clip_stack_aa(skiatest::Reporter* reporter) {
1274     constexpr SkScalar IL = 2, IT = 1, IR = 6, IB = 7;         // Pixel aligned rect.
1275     constexpr SkScalar L = 2.2f, T = 1.7f, R = 5.8f, B = 7.3f; // Generic rect.
1276     constexpr SkScalar l = 3.3f, t = 2.8f, r = 4.7f, b = 6.2f; // Small rect contained in R.
1277 
1278     SkRect alignedRect = {IL, IT, IR, IB};
1279     SkRect rect = {L, T, R, B};
1280     SkRect innerRect = {l, t, r, b};
1281 
1282     SkMatrix m;
1283     m.setIdentity();
1284 
1285     constexpr SkScalar kMinScale = 2.0001f;
1286     constexpr SkScalar kMaxScale = 3;
1287     constexpr int kNumIters = 8;
1288 
1289     SkString name;
1290     SkRandom rand;
1291 
1292     for (int i = 0; i < kNumIters; ++i) {
1293         // Pixel-aligned rect (iior=true).
1294         name.printf("Pixel-aligned rect test, iter %i", i);
1295         SkClipStack stack;
1296         stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1297         test_aa_query(reporter, name, stack, m, {IL, IT, IR, IB}, ClipMethod::kIgnoreClip);
1298         test_aa_query(reporter, name, stack, m, {IL, IT-1, IR, IT}, ClipMethod::kSkipDraw);
1299         test_aa_query(reporter, name, stack, m, {IL, IT-2, IR, IB}, ClipMethod::kScissor);
1300 
1301         // Rect (iior=true).
1302         name.printf("Rect test, iter %i", i);
1303         stack.reset();
1304         stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1305         test_aa_query(reporter, name, stack, m, {L, T,  R, B}, ClipMethod::kIgnoreClip);
1306         test_aa_query(reporter, name, stack, m, {L-.1f, T, L, B}, ClipMethod::kSkipDraw);
1307         test_aa_query(reporter, name, stack, m, {L-.1f, T, L+.1f, B}, ClipMethod::kAAElements, 1);
1308 
1309         // Difference rect (iior=false, inside-out bounds).
1310         name.printf("Difference rect test, iter %i", i);
1311         stack.reset();
1312         stack.clipRect(rect, SkMatrix::I(), kDifference_SkClipOp, true);
1313         test_aa_query(reporter, name, stack, m, {L, T, R, B}, ClipMethod::kSkipDraw);
1314         test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T}, ClipMethod::kIgnoreClip);
1315         test_aa_query(reporter, name, stack, m, {L, T-.1f, R, T+.1f}, ClipMethod::kAAElements, 1);
1316 
1317         // Complex clip (iior=false, normal bounds).
1318         name.printf("Complex clip test, iter %i", i);
1319         stack.reset();
1320         stack.clipRect(rect, SkMatrix::I(), kIntersect_SkClipOp, true);
1321         stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
1322         test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1323         test_aa_query(reporter, name, stack, m, {r-.1f, t, R, b}, ClipMethod::kAAElements, 1);
1324         test_aa_query(reporter, name, stack, m, {r-.1f, t, R+.1f, b}, ClipMethod::kAAElements, 2);
1325         test_aa_query(reporter, name, stack, m, {r, t, R+.1f, b}, ClipMethod::kAAElements, 1);
1326         test_aa_query(reporter, name, stack, m, {r, t, R, b}, ClipMethod::kIgnoreClip);
1327         test_aa_query(reporter, name, stack, m, {R, T, R+.1f, B}, ClipMethod::kSkipDraw);
1328 
1329         // Complex clip where outer rect is pixel aligned (iior=false, normal bounds).
1330         name.printf("Aligned Complex clip test, iter %i", i);
1331         stack.reset();
1332         stack.clipRect(alignedRect, SkMatrix::I(), kIntersect_SkClipOp, true);
1333         stack.clipRect(innerRect, SkMatrix::I(), kXOR_SkClipOp, true);
1334         test_aa_query(reporter, name, stack, m, {l, t, r, b}, ClipMethod::kSkipDraw);
1335         test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB}, ClipMethod::kAAElements, 1);
1336         test_aa_query(reporter, name, stack, m, {l, b-.1f, r, IB+.1f}, ClipMethod::kAAElements, 1);
1337         test_aa_query(reporter, name, stack, m, {l, b, r, IB+.1f}, ClipMethod::kAAElements, 0);
1338         test_aa_query(reporter, name, stack, m, {l, b, r, IB}, ClipMethod::kIgnoreClip);
1339         test_aa_query(reporter, name, stack, m, {IL, IB, IR, IB+.1f}, ClipMethod::kSkipDraw);
1340 
1341         // Apply random transforms and try again. This ensures the clip stack reduction is hardened
1342         // against FP rounding error.
1343         SkScalar sx = rand.nextRangeScalar(kMinScale, kMaxScale);
1344         sx = SkScalarFloorToScalar(sx * alignedRect.width()) / alignedRect.width();
1345         SkScalar sy = rand.nextRangeScalar(kMinScale, kMaxScale);
1346         sy = SkScalarFloorToScalar(sy * alignedRect.height()) / alignedRect.height();
1347         SkScalar tx = SkScalarRoundToScalar(sx * alignedRect.x()) - sx * alignedRect.x();
1348         SkScalar ty = SkScalarRoundToScalar(sy * alignedRect.y()) - sy * alignedRect.y();
1349 
1350         SkMatrix xform = SkMatrix::MakeScale(sx, sy);
1351         xform.postTranslate(tx, ty);
1352         xform.mapRect(&alignedRect);
1353         xform.mapRect(&rect);
1354         xform.mapRect(&innerRect);
1355         m.postConcat(xform);
1356     }
1357 }
1358 
1359 #endif
1360 
DEF_TEST(ClipStack,reporter)1361 DEF_TEST(ClipStack, reporter) {
1362     SkClipStack stack;
1363 
1364     REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
1365     assert_count(reporter, stack, 0);
1366 
1367     static const SkIRect gRects[] = {
1368         { 0, 0, 100, 100 },
1369         { 25, 25, 125, 125 },
1370         { 0, 0, 1000, 1000 },
1371         { 0, 0, 75, 75 }
1372     };
1373     for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
1374         stack.clipDevRect(gRects[i], kIntersect_SkClipOp);
1375     }
1376 
1377     // all of the above rects should have been intersected, leaving only 1 rect
1378     SkClipStack::B2TIter iter(stack);
1379     const SkClipStack::Element* element = iter.next();
1380     SkRect answer;
1381     answer.iset(25, 25, 75, 75);
1382 
1383     REPORTER_ASSERT(reporter, element);
1384     REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
1385     REPORTER_ASSERT(reporter, kIntersect_SkClipOp == element->getOp());
1386     REPORTER_ASSERT(reporter, element->getRect() == answer);
1387     // now check that we only had one in our iterator
1388     REPORTER_ASSERT(reporter, !iter.next());
1389 
1390     stack.reset();
1391     REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
1392     assert_count(reporter, stack, 0);
1393 
1394     test_assign_and_comparison(reporter);
1395     test_iterators(reporter);
1396     test_bounds(reporter, SkClipStack::Element::kRect_Type);
1397     test_bounds(reporter, SkClipStack::Element::kRRect_Type);
1398     test_bounds(reporter, SkClipStack::Element::kPath_Type);
1399     test_isWideOpen(reporter);
1400     test_rect_merging(reporter);
1401     test_rect_replace(reporter);
1402     test_rect_inverse_fill(reporter);
1403     test_path_replace(reporter);
1404     test_quickContains(reporter);
1405     test_invfill_diff_bug(reporter);
1406 #if SK_SUPPORT_GPU
1407     test_reduced_clip_stack(reporter);
1408     test_reduced_clip_stack_genid(reporter);
1409     test_reduced_clip_stack_no_aa_crash(reporter);
1410     test_reduced_clip_stack_aa(reporter);
1411 #endif
1412 }
1413 
1414 //////////////////////////////////////////////////////////////////////////////
1415 
1416 #if SK_SUPPORT_GPU
testingOnly_createClipMask(GrContext * context) const1417 sk_sp<GrTextureProxy> GrClipStackClip::testingOnly_createClipMask(GrContext* context) const {
1418     const GrReducedClip reducedClip(*fStack, SkRect::MakeWH(512, 512), 0);
1419     return this->createSoftwareClipMask(context, reducedClip);
1420 }
1421 
1422 // Verify that clip masks are freed up when the clip state that generated them goes away.
DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache,reporter,ctxInfo)1423 DEF_GPUTEST_FOR_ALL_CONTEXTS(ClipMaskCache, reporter, ctxInfo) {
1424     // This test uses resource key tags which only function in debug builds.
1425 #ifdef SK_DEBUG
1426     GrContext* context = ctxInfo.grContext();
1427     SkClipStack stack;
1428 
1429     SkPath path;
1430     path.addCircle(10, 10, 8);
1431     path.addCircle(15, 15, 8);
1432     path.setFillType(SkPath::kEvenOdd_FillType);
1433 
1434     static const char* kTag = GrClipStackClip::kMaskTestTag;
1435     GrResourceCache* cache = context->getResourceCache();
1436 
1437     static constexpr int kN = 5;
1438 
1439     for (int i = 0; i < kN; ++i) {
1440         SkMatrix m;
1441         m.setTranslate(0.5, 0.5);
1442         stack.save();
1443         stack.clipPath(path, m, SkClipOp::kIntersect, true);
1444         sk_sp<GrTextureProxy> mask = GrClipStackClip(&stack).testingOnly_createClipMask(context);
1445         GrTexture* tex = mask->instantiate(context->resourceProvider());
1446         REPORTER_ASSERT(reporter, 0 == strcmp(tex->getUniqueKey().tag(), kTag));
1447         // Make sure mask isn't pinned in cache.
1448         mask.reset(nullptr);
1449         context->flush();
1450         REPORTER_ASSERT(reporter, i + 1 == cache->countUniqueKeysWithTag(kTag));
1451     }
1452 
1453     for (int i = 0; i < kN; ++i) {
1454         stack.restore();
1455         cache->purgeAsNeeded();
1456         REPORTER_ASSERT(reporter, kN - (i + 1) == cache->countUniqueKeysWithTag(kTag));
1457     }
1458 #endif
1459 }
1460 
1461 #include "SkSurface.h"
DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn,reporter,ctxInfo)1462 DEF_GPUTEST_FOR_ALL_CONTEXTS(canvas_private_clipRgn, reporter, ctxInfo) {
1463     GrContext* context = ctxInfo.grContext();
1464 
1465     const int w = 10;
1466     const int h = 10;
1467     SkImageInfo info = SkImageInfo::MakeN32Premul(w, h);
1468     sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(context, SkBudgeted::kNo, info);
1469     SkCanvas* canvas = surf->getCanvas();
1470     SkRegion rgn;
1471 
1472     canvas->temporary_internal_getRgnClip(&rgn);
1473     REPORTER_ASSERT(reporter, rgn.isRect());
1474     REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1475 
1476     canvas->save();
1477     canvas->clipRect(SkRect::MakeWH(5, 5), kDifference_SkClipOp);
1478     canvas->temporary_internal_getRgnClip(&rgn);
1479     REPORTER_ASSERT(reporter, rgn.isComplex());
1480     REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeWH(w, h));
1481     canvas->restore();
1482 
1483     canvas->save();
1484     canvas->clipRRect(SkRRect::MakeOval(SkRect::MakeLTRB(3, 3, 7, 7)));
1485     canvas->temporary_internal_getRgnClip(&rgn);
1486     REPORTER_ASSERT(reporter, rgn.isComplex());
1487     REPORTER_ASSERT(reporter, rgn.getBounds() == SkIRect::MakeLTRB(3, 3, 7, 7));
1488     canvas->restore();
1489 }
1490 #endif
1491