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