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