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