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