Home | History | Annotate | Download | only in tests
      1 
      2 /*
      3  * Copyright 2011 Google Inc.
      4  *
      5  * Use of this source code is governed by a BSD-style license that can be
      6  * found in the LICENSE file.
      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 
     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 
    275             stack.getConservativeBounds(0, 0, 100, 100, &devClipBound,
    276                                         &isIntersectionOfRects);
    277 
    278             if (useRects) {
    279                 REPORTER_ASSERT(reporter, isIntersectionOfRects ==
    280                         (gOps[op] == SkRegion::kIntersect_Op));
    281             } else {
    282                 REPORTER_ASSERT(reporter, !isIntersectionOfRects);
    283             }
    284 
    285             SkASSERT(testCase < gNumCases);
    286             REPORTER_ASSERT(reporter, devClipBound == gAnswerRectsBW[testCase]);
    287             ++testCase;
    288 
    289             stack.restore();
    290         }
    291     }
    292 }
    293 
    294 // Test out 'isWideOpen' entry point
    295 static void test_isWideOpen(skiatest::Reporter* reporter) {
    296 
    297     SkRect rectA, rectB;
    298 
    299     rectA.iset(10, 10, 40, 40);
    300     rectB.iset(50, 50, 80, 80);
    301 
    302     // Stack should initially be wide open
    303     {
    304         SkClipStack stack;
    305 
    306         REPORTER_ASSERT(reporter, stack.isWideOpen());
    307     }
    308 
    309     // Test out case where the user specifies a union that includes everything
    310     {
    311         SkClipStack stack;
    312 
    313         SkPath clipA, clipB;
    314 
    315         clipA.addRoundRect(rectA, SkIntToScalar(5), SkIntToScalar(5));
    316         clipA.setFillType(SkPath::kInverseEvenOdd_FillType);
    317 
    318         clipB.addRoundRect(rectB, SkIntToScalar(5), SkIntToScalar(5));
    319         clipB.setFillType(SkPath::kInverseEvenOdd_FillType);
    320 
    321         stack.clipDevPath(clipA, SkRegion::kReplace_Op, false);
    322         stack.clipDevPath(clipB, SkRegion::kUnion_Op, false);
    323 
    324         REPORTER_ASSERT(reporter, stack.isWideOpen());
    325     }
    326 
    327     // Test out union w/ a wide open clip
    328     {
    329         SkClipStack stack;
    330 
    331         stack.clipDevRect(rectA, SkRegion::kUnion_Op, false);
    332 
    333         REPORTER_ASSERT(reporter, stack.isWideOpen());
    334     }
    335 
    336     // Test out empty difference from a wide open clip
    337     {
    338         SkClipStack stack;
    339 
    340         SkRect emptyRect;
    341         emptyRect.setEmpty();
    342 
    343         stack.clipDevRect(emptyRect, SkRegion::kDifference_Op, false);
    344 
    345         REPORTER_ASSERT(reporter, stack.isWideOpen());
    346     }
    347 
    348     // Test out return to wide open
    349     {
    350         SkClipStack stack;
    351 
    352         stack.save();
    353 
    354         stack.clipDevRect(rectA, SkRegion::kReplace_Op, false);
    355 
    356         REPORTER_ASSERT(reporter, !stack.isWideOpen());
    357 
    358         stack.restore();
    359 
    360         REPORTER_ASSERT(reporter, stack.isWideOpen());
    361     }
    362 }
    363 
    364 static int count(const SkClipStack& stack) {
    365 
    366     SkClipStack::Iter iter(stack, SkClipStack::Iter::kTop_IterStart);
    367 
    368     const SkClipStack::Element* element = NULL;
    369     int count = 0;
    370 
    371     for (element = iter.prev(); element; element = iter.prev(), ++count) {
    372         ;
    373     }
    374 
    375     return count;
    376 }
    377 
    378 static void test_rect_inverse_fill(skiatest::Reporter* reporter) {
    379     // non-intersecting rectangles
    380     SkRect rect  = SkRect::MakeLTRB(0, 0, 10, 10);
    381 
    382     SkPath path;
    383     path.addRect(rect);
    384     path.toggleInverseFillType();
    385     SkClipStack stack;
    386     stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
    387 
    388     SkRect bounds;
    389     SkClipStack::BoundsType boundsType;
    390     stack.getBounds(&bounds, &boundsType);
    391     REPORTER_ASSERT(reporter, SkClipStack::kInsideOut_BoundsType == boundsType);
    392     REPORTER_ASSERT(reporter, bounds == rect);
    393 }
    394 
    395 // Test out SkClipStack's merging of rect clips. In particular exercise
    396 // merging of aa vs. bw rects.
    397 static void test_rect_merging(skiatest::Reporter* reporter) {
    398 
    399     SkRect overlapLeft  = SkRect::MakeLTRB(10, 10, 50, 50);
    400     SkRect overlapRight = SkRect::MakeLTRB(40, 40, 80, 80);
    401 
    402     SkRect nestedParent = SkRect::MakeLTRB(10, 10, 90, 90);
    403     SkRect nestedChild  = SkRect::MakeLTRB(40, 40, 60, 60);
    404 
    405     SkRect bound;
    406     SkClipStack::BoundsType type;
    407     bool isIntersectionOfRects;
    408 
    409     // all bw overlapping - should merge
    410     {
    411         SkClipStack stack;
    412 
    413         stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, false);
    414 
    415         stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
    416 
    417         REPORTER_ASSERT(reporter, 1 == count(stack));
    418 
    419         stack.getBounds(&bound, &type, &isIntersectionOfRects);
    420 
    421         REPORTER_ASSERT(reporter, isIntersectionOfRects);
    422     }
    423 
    424     // all aa overlapping - should merge
    425     {
    426         SkClipStack stack;
    427 
    428         stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
    429 
    430         stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, true);
    431 
    432         REPORTER_ASSERT(reporter, 1 == count(stack));
    433 
    434         stack.getBounds(&bound, &type, &isIntersectionOfRects);
    435 
    436         REPORTER_ASSERT(reporter, isIntersectionOfRects);
    437     }
    438 
    439     // mixed overlapping - should _not_ merge
    440     {
    441         SkClipStack stack;
    442 
    443         stack.clipDevRect(overlapLeft, SkRegion::kReplace_Op, true);
    444 
    445         stack.clipDevRect(overlapRight, SkRegion::kIntersect_Op, false);
    446 
    447         REPORTER_ASSERT(reporter, 2 == count(stack));
    448 
    449         stack.getBounds(&bound, &type, &isIntersectionOfRects);
    450 
    451         REPORTER_ASSERT(reporter, !isIntersectionOfRects);
    452     }
    453 
    454     // mixed nested (bw inside aa) - should merge
    455     {
    456         SkClipStack stack;
    457 
    458         stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, true);
    459 
    460         stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, false);
    461 
    462         REPORTER_ASSERT(reporter, 1 == count(stack));
    463 
    464         stack.getBounds(&bound, &type, &isIntersectionOfRects);
    465 
    466         REPORTER_ASSERT(reporter, isIntersectionOfRects);
    467     }
    468 
    469     // mixed nested (aa inside bw) - should merge
    470     {
    471         SkClipStack stack;
    472 
    473         stack.clipDevRect(nestedParent, SkRegion::kReplace_Op, false);
    474 
    475         stack.clipDevRect(nestedChild, SkRegion::kIntersect_Op, true);
    476 
    477         REPORTER_ASSERT(reporter, 1 == count(stack));
    478 
    479         stack.getBounds(&bound, &type, &isIntersectionOfRects);
    480 
    481         REPORTER_ASSERT(reporter, isIntersectionOfRects);
    482     }
    483 
    484     // reverse nested (aa inside bw) - should _not_ merge
    485     {
    486         SkClipStack stack;
    487 
    488         stack.clipDevRect(nestedChild, SkRegion::kReplace_Op, false);
    489 
    490         stack.clipDevRect(nestedParent, SkRegion::kIntersect_Op, true);
    491 
    492         REPORTER_ASSERT(reporter, 2 == count(stack));
    493 
    494         stack.getBounds(&bound, &type, &isIntersectionOfRects);
    495 
    496         REPORTER_ASSERT(reporter, !isIntersectionOfRects);
    497     }
    498 }
    499 
    500 static void test_quickContains(skiatest::Reporter* reporter) {
    501     SkRect testRect = SkRect::MakeLTRB(10, 10, 40, 40);
    502     SkRect insideRect = SkRect::MakeLTRB(20, 20, 30, 30);
    503     SkRect intersectingRect = SkRect::MakeLTRB(25, 25, 50, 50);
    504     SkRect outsideRect = SkRect::MakeLTRB(0, 0, 50, 50);
    505     SkRect nonIntersectingRect = SkRect::MakeLTRB(100, 100, 110, 110);
    506 
    507     SkPath insideCircle;
    508     insideCircle.addCircle(25, 25, 5);
    509     SkPath intersectingCircle;
    510     intersectingCircle.addCircle(25, 40, 10);
    511     SkPath outsideCircle;
    512     outsideCircle.addCircle(25, 25, 50);
    513     SkPath nonIntersectingCircle;
    514     nonIntersectingCircle.addCircle(100, 100, 5);
    515 
    516     {
    517         SkClipStack stack;
    518         stack.clipDevRect(outsideRect, SkRegion::kDifference_Op, false);
    519         // return false because quickContains currently does not care for kDifference_Op
    520         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    521     }
    522 
    523     // Replace Op tests
    524     {
    525         SkClipStack stack;
    526         stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
    527         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
    528     }
    529 
    530     {
    531         SkClipStack stack;
    532         stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
    533         stack.save(); // To prevent in-place substitution by replace OP
    534         stack.clipDevRect(outsideRect, SkRegion::kReplace_Op, false);
    535         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
    536         stack.restore();
    537     }
    538 
    539     {
    540         SkClipStack stack;
    541         stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
    542         stack.save(); // To prevent in-place substitution by replace OP
    543         stack.clipDevRect(insideRect, SkRegion::kReplace_Op, false);
    544         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    545         stack.restore();
    546     }
    547 
    548     // Verify proper traversal of multi-element clip
    549     {
    550         SkClipStack stack;
    551         stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
    552         // Use a path for second clip to prevent in-place intersection
    553         stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
    554         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    555     }
    556 
    557     // Intersect Op tests with rectangles
    558     {
    559         SkClipStack stack;
    560         stack.clipDevRect(outsideRect, SkRegion::kIntersect_Op, false);
    561         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
    562     }
    563 
    564     {
    565         SkClipStack stack;
    566         stack.clipDevRect(insideRect, SkRegion::kIntersect_Op, false);
    567         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    568     }
    569 
    570     {
    571         SkClipStack stack;
    572         stack.clipDevRect(intersectingRect, SkRegion::kIntersect_Op, false);
    573         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    574     }
    575 
    576     {
    577         SkClipStack stack;
    578         stack.clipDevRect(nonIntersectingRect, SkRegion::kIntersect_Op, false);
    579         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    580     }
    581 
    582     // Intersect Op tests with circle paths
    583     {
    584         SkClipStack stack;
    585         stack.clipDevPath(outsideCircle, SkRegion::kIntersect_Op, false);
    586         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
    587     }
    588 
    589     {
    590         SkClipStack stack;
    591         stack.clipDevPath(insideCircle, SkRegion::kIntersect_Op, false);
    592         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    593     }
    594 
    595     {
    596         SkClipStack stack;
    597         stack.clipDevPath(intersectingCircle, SkRegion::kIntersect_Op, false);
    598         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    599     }
    600 
    601     {
    602         SkClipStack stack;
    603         stack.clipDevPath(nonIntersectingCircle, SkRegion::kIntersect_Op, false);
    604         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    605     }
    606 
    607     // Intersect Op tests with inverse filled rectangles
    608     {
    609         SkClipStack stack;
    610         SkPath path;
    611         path.addRect(outsideRect);
    612         path.toggleInverseFillType();
    613         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
    614         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    615     }
    616 
    617     {
    618         SkClipStack stack;
    619         SkPath path;
    620         path.addRect(insideRect);
    621         path.toggleInverseFillType();
    622         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
    623         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    624     }
    625 
    626     {
    627         SkClipStack stack;
    628         SkPath path;
    629         path.addRect(intersectingRect);
    630         path.toggleInverseFillType();
    631         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
    632         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    633     }
    634 
    635     {
    636         SkClipStack stack;
    637         SkPath path;
    638         path.addRect(nonIntersectingRect);
    639         path.toggleInverseFillType();
    640         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
    641         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
    642     }
    643 
    644     // Intersect Op tests with inverse filled circles
    645     {
    646         SkClipStack stack;
    647         SkPath path = outsideCircle;
    648         path.toggleInverseFillType();
    649         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
    650         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    651     }
    652 
    653     {
    654         SkClipStack stack;
    655         SkPath path = insideCircle;
    656         path.toggleInverseFillType();
    657         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
    658         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    659     }
    660 
    661     {
    662         SkClipStack stack;
    663         SkPath path = intersectingCircle;
    664         path.toggleInverseFillType();
    665         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
    666         REPORTER_ASSERT(reporter, false == stack.quickContains(testRect));
    667     }
    668 
    669     {
    670         SkClipStack stack;
    671         SkPath path = nonIntersectingCircle;
    672         path.toggleInverseFillType();
    673         stack.clipDevPath(path, SkRegion::kIntersect_Op, false);
    674         REPORTER_ASSERT(reporter, true == stack.quickContains(testRect));
    675     }
    676 }
    677 
    678 ///////////////////////////////////////////////////////////////////////////////////////////////////
    679 
    680 #if SK_SUPPORT_GPU
    681 // Functions that add a shape to the clip stack. The shape is computed from a rectangle.
    682 // AA is always disabled since the clip stack reducer can cause changes in aa rasterization of the
    683 // stack. A fractional edge repeated in different elements may be rasterized fewer times using the
    684 // reduced stack.
    685 typedef void (*AddElementFunc) (const SkRect& rect,
    686                                 bool invert,
    687                                 SkRegion::Op op,
    688                                 SkClipStack* stack);
    689 
    690 static void add_round_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
    691     SkPath path;
    692     SkScalar rx = rect.width() / 10;
    693     SkScalar ry = rect.height() / 20;
    694     path.addRoundRect(rect, rx, ry);
    695     if (invert) {
    696         path.setFillType(SkPath::kInverseWinding_FillType);
    697     }
    698     stack->clipDevPath(path, op, false);
    699 };
    700 
    701 static void add_rect(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
    702     if (invert) {
    703         SkPath path;
    704         path.addRect(rect);
    705         path.setFillType(SkPath::kInverseWinding_FillType);
    706         stack->clipDevPath(path, op, false);
    707     } else {
    708         stack->clipDevRect(rect, op, false);
    709     }
    710 };
    711 
    712 static void add_oval(const SkRect& rect, bool invert, SkRegion::Op op, SkClipStack* stack) {
    713     SkPath path;
    714     path.addOval(rect);
    715     if (invert) {
    716         path.setFillType(SkPath::kInverseWinding_FillType);
    717     }
    718     stack->clipDevPath(path, op, false);
    719 };
    720 
    721 static void add_elem_to_stack(const SkClipStack::Element& element, SkClipStack* stack) {
    722     switch (element.getType()) {
    723         case SkClipStack::Element::kRect_Type:
    724             stack->clipDevRect(element.getRect(), element.getOp(), element.isAA());
    725             break;
    726         case SkClipStack::Element::kPath_Type:
    727             stack->clipDevPath(element.getPath(), element.getOp(), element.isAA());
    728             break;
    729         case SkClipStack::Element::kEmpty_Type:
    730             SkDEBUGFAIL("Why did the reducer produce an explicit empty.");
    731             stack->clipEmpty();
    732             break;
    733     }
    734 }
    735 
    736 static void add_elem_to_region(const SkClipStack::Element& element,
    737                                const SkIRect& bounds,
    738                                SkRegion* region) {
    739     SkRegion elemRegion;
    740     SkRegion boundsRgn(bounds);
    741 
    742     switch (element.getType()) {
    743         case SkClipStack::Element::kRect_Type: {
    744             SkPath path;
    745             path.addRect(element.getRect());
    746             elemRegion.setPath(path, boundsRgn);
    747             break;
    748         }
    749         case SkClipStack::Element::kPath_Type:
    750             elemRegion.setPath(element.getPath(), boundsRgn);
    751             break;
    752         case SkClipStack::Element::kEmpty_Type:
    753             //
    754             region->setEmpty();
    755             return;
    756     }
    757     region->op(elemRegion, element.getOp());
    758 }
    759 
    760 // This can assist with debugging the clip stack reduction code when the test below fails.
    761 static inline void print_clip(const SkClipStack::Element& element) {
    762     static const char* kOpStrs[] = {
    763         "DF",
    764         "IS",
    765         "UN",
    766         "XR",
    767         "RD",
    768         "RP",
    769     };
    770     if (SkClipStack::Element::kEmpty_Type != element.getType()) {
    771         const SkRect& bounds = element.getBounds();
    772         bool isRect = SkClipStack::Element::kRect_Type == element.getType();
    773         SkDebugf("%s %s %s [%f %f] x [%f %f]\n",
    774                  kOpStrs[element.getOp()],
    775                  (isRect ? "R" : "P"),
    776                  (element.isInverseFilled() ? "I" : " "),
    777                  bounds.fLeft, bounds.fRight, bounds.fTop, bounds.fBottom);
    778     } else {
    779         SkDebugf("EM\n");
    780     }
    781 }
    782 
    783 static void test_reduced_clip_stack(skiatest::Reporter* reporter) {
    784     // We construct random clip stacks, reduce them, and then rasterize both versions to verify that
    785     // they are equal.
    786 
    787     // All the clip elements will be contained within these bounds.
    788     static const SkRect kBounds = SkRect::MakeWH(100, 100);
    789 
    790     enum {
    791         kNumTests = 200,
    792         kMinElemsPerTest = 1,
    793         kMaxElemsPerTest = 50,
    794     };
    795 
    796     // min/max size of a clip element as a fraction of kBounds.
    797     static const SkScalar kMinElemSizeFrac = SK_Scalar1 / 5;
    798     static const SkScalar kMaxElemSizeFrac = SK_Scalar1;
    799 
    800     static const SkRegion::Op kOps[] = {
    801         SkRegion::kDifference_Op,
    802         SkRegion::kIntersect_Op,
    803         SkRegion::kUnion_Op,
    804         SkRegion::kXOR_Op,
    805         SkRegion::kReverseDifference_Op,
    806         SkRegion::kReplace_Op,
    807     };
    808 
    809     // Replace operations short-circuit the optimizer. We want to make sure that we test this code
    810     // path a little bit but we don't want it to prevent us from testing many longer traversals in
    811     // the optimizer.
    812     static const int kReplaceDiv = 4 * kMaxElemsPerTest;
    813 
    814     // We want to test inverse fills. However, they are quite rare in practice so don't over do it.
    815     static const SkScalar kFractionInverted = SK_Scalar1 / kMaxElemsPerTest;
    816 
    817     static const AddElementFunc kElementFuncs[] = {
    818         add_rect,
    819         add_round_rect,
    820         add_oval,
    821     };
    822 
    823     SkRandom r;
    824 
    825     for (int i = 0; i < kNumTests; ++i) {
    826         // Randomly generate a clip stack.
    827         SkClipStack stack;
    828         int numElems = r.nextRangeU(kMinElemsPerTest, kMaxElemsPerTest);
    829         for (int e = 0; e < numElems; ++e) {
    830             SkRegion::Op op = kOps[r.nextULessThan(SK_ARRAY_COUNT(kOps))];
    831             if (op == SkRegion::kReplace_Op) {
    832                 if (r.nextU() % kReplaceDiv) {
    833                     --e;
    834                     continue;
    835                 }
    836             }
    837 
    838             // saves can change the clip stack behavior when an element is added.
    839             bool doSave = r.nextBool();
    840 
    841             SkSize size = SkSize::Make(
    842                 SkScalarFloorToScalar(SkScalarMul(kBounds.width(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))),
    843                 SkScalarFloorToScalar(SkScalarMul(kBounds.height(), r.nextRangeScalar(kMinElemSizeFrac, kMaxElemSizeFrac))));
    844 
    845             SkPoint xy = {SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fLeft, kBounds.fRight - size.fWidth)),
    846                           SkScalarFloorToScalar(r.nextRangeScalar(kBounds.fTop, kBounds.fBottom - size.fHeight))};
    847 
    848             SkRect rect = SkRect::MakeXYWH(xy.fX, xy.fY, size.fWidth, size.fHeight);
    849 
    850             bool invert = r.nextBiasedBool(kFractionInverted);
    851             kElementFuncs[r.nextULessThan(SK_ARRAY_COUNT(kElementFuncs))](rect, invert, op, &stack);
    852             if (doSave) {
    853                 stack.save();
    854             }
    855         }
    856 
    857         SkRect inflatedBounds = kBounds;
    858         inflatedBounds.outset(kBounds.width() / 2, kBounds.height() / 2);
    859         SkIRect inflatedIBounds;
    860         inflatedBounds.roundOut(&inflatedIBounds);
    861 
    862         typedef GrReducedClip::ElementList ElementList;
    863         // Get the reduced version of the stack.
    864         ElementList reducedClips;
    865 
    866         GrReducedClip::InitialState initial;
    867         SkIRect tBounds;
    868         SkIRect* tightBounds = r.nextBool() ? &tBounds : NULL;
    869         GrReducedClip::ReduceClipStack(stack,
    870                                        inflatedIBounds,
    871                                        &reducedClips,
    872                                        &initial,
    873                                        tightBounds);
    874 
    875         // Build a new clip stack based on the reduced clip elements
    876         SkClipStack reducedStack;
    877         if (GrReducedClip::kAllOut_InitialState == initial) {
    878             // whether the result is bounded or not, the whole plane should start outside the clip.
    879             reducedStack.clipEmpty();
    880         }
    881         for (ElementList::Iter iter = reducedClips.headIter(); NULL != iter.get(); iter.next()) {
    882             add_elem_to_stack(*iter.get(), &reducedStack);
    883         }
    884 
    885         // GrReducedClipStack assumes that the final result is clipped to the returned bounds
    886         if (NULL != tightBounds) {
    887             reducedStack.clipDevRect(*tightBounds, SkRegion::kIntersect_Op);
    888         }
    889 
    890         // convert both the original stack and reduced stack to SkRegions and see if they're equal
    891         SkRegion region;
    892         SkRegion reducedRegion;
    893 
    894         region.setRect(inflatedIBounds);
    895         const SkClipStack::Element* element;
    896         SkClipStack::Iter iter(stack, SkClipStack::Iter::kBottom_IterStart);
    897         while ((element = iter.next())) {
    898             add_elem_to_region(*element, inflatedIBounds, &region);
    899         }
    900 
    901         reducedRegion.setRect(inflatedIBounds);
    902         iter.reset(reducedStack, SkClipStack::Iter::kBottom_IterStart);
    903         while ((element = iter.next())) {
    904             add_elem_to_region(*element, inflatedIBounds, &reducedRegion);
    905         }
    906 
    907         REPORTER_ASSERT(reporter, region == reducedRegion);
    908     }
    909 }
    910 
    911 #endif
    912 ///////////////////////////////////////////////////////////////////////////////////////////////////
    913 
    914 static void TestClipStack(skiatest::Reporter* reporter) {
    915     SkClipStack stack;
    916 
    917     REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
    918     assert_count(reporter, stack, 0);
    919 
    920     static const SkIRect gRects[] = {
    921         { 0, 0, 100, 100 },
    922         { 25, 25, 125, 125 },
    923         { 0, 0, 1000, 1000 },
    924         { 0, 0, 75, 75 }
    925     };
    926     for (size_t i = 0; i < SK_ARRAY_COUNT(gRects); i++) {
    927         stack.clipDevRect(gRects[i], SkRegion::kIntersect_Op);
    928     }
    929 
    930     // all of the above rects should have been intersected, leaving only 1 rect
    931     SkClipStack::B2TIter iter(stack);
    932     const SkClipStack::Element* element = iter.next();
    933     SkRect answer;
    934     answer.iset(25, 25, 75, 75);
    935 
    936     REPORTER_ASSERT(reporter, NULL != element);
    937     REPORTER_ASSERT(reporter, SkClipStack::Element::kRect_Type == element->getType());
    938     REPORTER_ASSERT(reporter, SkRegion::kIntersect_Op == element->getOp());
    939     REPORTER_ASSERT(reporter, element->getRect() == answer);
    940     // now check that we only had one in our iterator
    941     REPORTER_ASSERT(reporter, !iter.next());
    942 
    943     stack.reset();
    944     REPORTER_ASSERT(reporter, 0 == stack.getSaveCount());
    945     assert_count(reporter, stack, 0);
    946 
    947     test_assign_and_comparison(reporter);
    948     test_iterators(reporter);
    949     test_bounds(reporter, true);        // once with rects
    950     test_bounds(reporter, false);       // once with paths
    951     test_isWideOpen(reporter);
    952     test_rect_merging(reporter);
    953     test_rect_inverse_fill(reporter);
    954     test_quickContains(reporter);
    955 #if SK_SUPPORT_GPU
    956     test_reduced_clip_stack(reporter);
    957 #endif
    958 }
    959 
    960 #include "TestClassDef.h"
    961 DEFINE_TESTCLASS("ClipStack", TestClipStackClass, TestClipStack)
    962