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