Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2018 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 "PathOpsExtendedTest.h"
      9 #include "PathOpsThreadedCommon.h"
     10 #include "Test.h"
     11 
     12 static SkPath build_squircle(SkPath::Verb verb, const SkRect& rect, SkPath::Direction dir) {
     13     SkPath path;
     14     bool reverse = SkPath::kCCW_Direction == dir;
     15     switch (verb) {
     16         case SkPath::kLine_Verb:
     17             path.addRect(rect, dir);
     18             reverse = false;
     19             break;
     20         case SkPath::kQuad_Verb:
     21             path.moveTo(rect.centerX(), rect.fTop);
     22             path.quadTo(rect.fRight, rect.fTop, rect.fRight, rect.centerY());
     23             path.quadTo(rect.fRight, rect.fBottom, rect.centerX(), rect.fBottom);
     24             path.quadTo(rect.fLeft, rect.fBottom, rect.fLeft, rect.centerY());
     25             path.quadTo(rect.fLeft, rect.fTop, rect.centerX(), rect.fTop);
     26             break;
     27         case SkPath::kConic_Verb:
     28             path.addCircle(rect.centerX(), rect.centerY(), rect.width() / 2, dir);
     29             reverse = false;
     30             break;
     31         case SkPath::kCubic_Verb: {
     32             SkScalar aX14 = rect.fLeft + rect.width() * 1 / 4;
     33             SkScalar aX34 = rect.fLeft + rect.width() * 3 / 4;
     34             SkScalar aY14 = rect.fTop + rect.height() * 1 / 4;
     35             SkScalar aY34 = rect.fTop + rect.height() * 3 / 4;
     36             path.moveTo(rect.centerX(), rect.fTop);
     37             path.cubicTo(aX34, rect.fTop, rect.fRight, aY14, rect.fRight, rect.centerY());
     38             path.cubicTo(rect.fRight, aY34, aX34, rect.fBottom, rect.centerX(), rect.fBottom);
     39             path.cubicTo(aX14, rect.fBottom, rect.fLeft, aY34, rect.fLeft, rect.centerY());
     40             path.cubicTo(rect.fLeft, aY14, aX14, rect.fTop, rect.centerX(), rect.fTop);
     41             } break;
     42         default:
     43             SkASSERT(0);
     44     }
     45     if (reverse) {
     46         SkPath temp;
     47         temp.reverseAddPath(path);
     48         path.swap(temp);
     49     }
     50     return path;
     51 }
     52 
     53 DEF_TEST(PathOpsAsWinding, reporter) {
     54     SkPath test, result;
     55     test.addRect({1, 2, 3, 4});
     56     // if test is winding
     57     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     58     REPORTER_ASSERT(reporter, test == result);
     59     // if test is empty
     60     test.reset();
     61     test.setFillType(SkPath::kEvenOdd_FillType);
     62     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     63     REPORTER_ASSERT(reporter, result.isEmpty());
     64     REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
     65     // if test is convex
     66     test.addCircle(5, 5, 10);
     67     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     68     REPORTER_ASSERT(reporter, result.isConvex());
     69     test.setFillType(SkPath::kWinding_FillType);
     70     REPORTER_ASSERT(reporter, test == result);
     71     // if test has infinity
     72     test.reset();
     73     test.addRect({1, 2, 3, SK_ScalarInfinity});
     74     test.setFillType(SkPath::kEvenOdd_FillType);
     75     REPORTER_ASSERT(reporter, !AsWinding(test, &result));
     76     // if test has only one contour
     77     test.reset();
     78     SkPoint ell[] = {{0, 0}, {4, 0}, {4, 1}, {1, 1}, {1, 4}, {0, 4}};
     79     test.addPoly(ell, SK_ARRAY_COUNT(ell), true);
     80     test.setFillType(SkPath::kEvenOdd_FillType);
     81     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     82     REPORTER_ASSERT(reporter, !result.isConvex());
     83     test.setFillType(SkPath::kWinding_FillType);
     84     REPORTER_ASSERT(reporter, test == result);
     85     // test two contours that do not overlap or share bounds
     86     test.addRect({5, 2, 6, 3});
     87     test.setFillType(SkPath::kEvenOdd_FillType);
     88     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     89     REPORTER_ASSERT(reporter, !result.isConvex());
     90     test.setFillType(SkPath::kWinding_FillType);
     91     REPORTER_ASSERT(reporter, test == result);
     92     // test two contours that do not overlap but share bounds
     93     test.reset();
     94     test.addPoly(ell, SK_ARRAY_COUNT(ell), true);
     95     test.addRect({2, 2, 3, 3});
     96     test.setFillType(SkPath::kEvenOdd_FillType);
     97     REPORTER_ASSERT(reporter, AsWinding(test, &result));
     98     REPORTER_ASSERT(reporter, !result.isConvex());
     99     test.setFillType(SkPath::kWinding_FillType);
    100     REPORTER_ASSERT(reporter, test == result);
    101     // test two contours that partially overlap
    102     test.reset();
    103     test.addRect({0, 0, 3, 3});
    104     test.addRect({1, 1, 4, 4});
    105     test.setFillType(SkPath::kEvenOdd_FillType);
    106     REPORTER_ASSERT(reporter, AsWinding(test, &result));
    107     REPORTER_ASSERT(reporter, !result.isConvex());
    108     test.setFillType(SkPath::kWinding_FillType);
    109     REPORTER_ASSERT(reporter, test == result);
    110     // test that result may be input
    111     SkPath copy = test;
    112     test.setFillType(SkPath::kEvenOdd_FillType);
    113     REPORTER_ASSERT(reporter, AsWinding(test, &test));
    114     REPORTER_ASSERT(reporter, !test.isConvex());
    115     REPORTER_ASSERT(reporter, test == copy);
    116     // test a in b, b in a, cw/ccw
    117     constexpr SkRect rectA = {0, 0, 3, 3};
    118     constexpr SkRect rectB = {1, 1, 2, 2};
    119     const std::initializer_list<SkPoint> revBccw = {{1, 2}, {2, 2}, {2, 1}, {1, 1}};
    120     const std::initializer_list<SkPoint> revBcw  = {{2, 1}, {2, 2}, {1, 2}, {1, 1}};
    121     for (bool aFirst : {false, true}) {
    122         for (auto dirA : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
    123             for (auto dirB : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
    124                 test.reset();
    125                 test.setFillType(SkPath::kEvenOdd_FillType);
    126                 if (aFirst) {
    127                     test.addRect(rectA, dirA);
    128                     test.addRect(rectB, dirB);
    129                 } else {
    130                     test.addRect(rectB, dirB);
    131                     test.addRect(rectA, dirA);
    132                 }
    133                 SkPath original = test;
    134                 REPORTER_ASSERT(reporter, AsWinding(test, &result));
    135                 REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
    136                 test.reset();
    137                 if (aFirst) {
    138                     test.addRect(rectA, dirA);
    139                 }
    140                 if (dirA != dirB) {
    141                     test.addRect(rectB, dirB);
    142                 } else {
    143                     test.addPoly(SkPath::kCW_Direction == dirA ? revBccw : revBcw, true);
    144                 }
    145                 if (!aFirst) {
    146                     test.addRect(rectA, dirA);
    147                 }
    148                 REPORTER_ASSERT(reporter, test == result);
    149                 // test that result may be input
    150                 REPORTER_ASSERT(reporter, AsWinding(original, &original));
    151                 REPORTER_ASSERT(reporter, original.getFillType() == SkPath::kWinding_FillType);
    152                 REPORTER_ASSERT(reporter, original == result);
    153             }
    154         }
    155     }
    156     // Test curve types with donuts. Create a donut with outer and hole in all directions.
    157     // After converting to winding, all donuts should have a hole in the middle.
    158     for (bool aFirst : {false, true}) {
    159         for (auto dirA : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
    160             for (auto dirB : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
    161                 for (auto curveA : { SkPath::kLine_Verb, SkPath::kQuad_Verb,
    162                                      SkPath::kConic_Verb, SkPath::kCubic_Verb } ) {
    163                     SkPath pathA = build_squircle(curveA, rectA, dirA);
    164                     for (auto curveB : { SkPath::kLine_Verb, SkPath::kQuad_Verb,
    165                                      SkPath::kConic_Verb, SkPath::kCubic_Verb } ) {
    166                         test = aFirst ? pathA : SkPath();
    167                         test.addPath(build_squircle(curveB, rectB, dirB));
    168                         if (!aFirst) {
    169                             test.addPath(pathA);
    170                         }
    171                         test.setFillType(SkPath::kEvenOdd_FillType);
    172                         REPORTER_ASSERT(reporter, AsWinding(test, &result));
    173                        REPORTER_ASSERT(reporter, result.getFillType() == SkPath::kWinding_FillType);
    174                         for (SkScalar x = rectA.fLeft - 1; x <= rectA.fRight + 1; ++x) {
    175                             for (SkScalar y = rectA.fTop - 1; y <= rectA.fBottom + 1; ++y) {
    176                                 bool evenOddContains = test.contains(x, y);
    177                                 bool windingContains = result.contains(x, y);
    178                                 REPORTER_ASSERT(reporter, evenOddContains == windingContains);
    179                             }
    180                         }
    181                     }
    182                 }
    183             }
    184         }
    185     }
    186 }
    187