Home | History | Annotate | Download | only in tests
      1 /*
      2  * Copyright 2016 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 <initializer_list>
      9 #include <functional>
     10 #include "Test.h"
     11 #if SK_SUPPORT_GPU
     12 #include "GrShape.h"
     13 #include "SkCanvas.h"
     14 #include "SkDashPathEffect.h"
     15 #include "SkPath.h"
     16 #include "SkPathOps.h"
     17 #include "SkSurface.h"
     18 #include "SkClipOpPriv.h"
     19 
     20 using Key = SkTArray<uint32_t>;
     21 
     22 static bool make_key(Key* key, const GrShape& shape) {
     23     int size = shape.unstyledKeySize();
     24     if (size <= 0) {
     25         key->reset(0);
     26         return false;
     27     }
     28     SkASSERT(size);
     29     key->reset(size);
     30     shape.writeUnstyledKey(key->begin());
     31     return true;
     32 }
     33 
     34 static bool paths_fill_same(const SkPath& a, const SkPath& b) {
     35     SkPath pathXor;
     36     Op(a, b, SkPathOp::kXOR_SkPathOp, &pathXor);
     37     return pathXor.isEmpty();
     38 }
     39 
     40 static bool test_bounds_by_rasterizing(const SkPath& path, const SkRect& bounds) {
     41     // We test the bounds by rasterizing the path into a kRes by kRes grid. The bounds is
     42     // mapped to the range kRes/4 to 3*kRes/4 in x and y. A difference clip is used to avoid
     43     // rendering within the bounds (with a tolerance). Then we render the path and check that
     44     // everything got clipped out.
     45     static constexpr int kRes = 2000;
     46     // This tolerance is in units of 1/kRes fractions of the bounds width/height.
     47     static constexpr int kTol = 0;
     48     GR_STATIC_ASSERT(kRes % 4 == 0);
     49     SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
     50     sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
     51     surface->getCanvas()->clear(0x0);
     52     SkRect clip = SkRect::MakeXYWH(kRes/4, kRes/4, kRes/2, kRes/2);
     53     SkMatrix matrix;
     54     matrix.setRectToRect(bounds, clip, SkMatrix::kFill_ScaleToFit);
     55     clip.outset(SkIntToScalar(kTol), SkIntToScalar(kTol));
     56     surface->getCanvas()->clipRect(clip, kDifference_SkClipOp);
     57     surface->getCanvas()->concat(matrix);
     58     SkPaint whitePaint;
     59     whitePaint.setColor(SK_ColorWHITE);
     60     surface->getCanvas()->drawPath(path, whitePaint);
     61     SkPixmap pixmap;
     62     surface->getCanvas()->peekPixels(&pixmap);
     63 #if defined(SK_BUILD_FOR_WIN)
     64     // The static constexpr version in #else causes cl.exe to crash.
     65     const uint8_t* kZeros = reinterpret_cast<uint8_t*>(calloc(kRes, 1));
     66 #else
     67     static constexpr uint8_t kZeros[kRes] = {0};
     68 #endif
     69     for (int y = 0; y < kRes; ++y) {
     70         const uint8_t* row = pixmap.addr8(0, y);
     71         if (0 != memcmp(kZeros, row, kRes)) {
     72             return false;
     73         }
     74     }
     75 #ifdef SK_BUILD_FOR_WIN
     76     free(const_cast<uint8_t*>(kZeros));
     77 #endif
     78     return true;
     79 }
     80 
     81 namespace {
     82 /**
     83  * Geo is a factory for creating a GrShape from another representation. It also answers some
     84  * questions about expected behavior for GrShape given the inputs.
     85  */
     86 class Geo {
     87 public:
     88     virtual ~Geo() {}
     89     virtual GrShape makeShape(const SkPaint&) const = 0;
     90     virtual SkPath path() const = 0;
     91     // These functions allow tests to check for special cases where style gets
     92     // applied by GrShape in its constructor (without calling GrShape::applyStyle).
     93     // These unfortunately rely on knowing details of GrShape's implementation.
     94     // These predicates are factored out here to avoid littering the rest of the
     95     // test code with GrShape implementation details.
     96     virtual bool fillChangesGeom() const { return false; }
     97     virtual bool strokeIsConvertedToFill() const { return false; }
     98     virtual bool strokeAndFillIsConvertedToFill(const SkPaint&) const { return false; }
     99     // Is this something we expect GrShape to recognize as something simpler than a path.
    100     virtual bool isNonPath(const SkPaint& paint) const { return true; }
    101 };
    102 
    103 class RectGeo : public Geo {
    104 public:
    105     RectGeo(const SkRect& rect) : fRect(rect) {}
    106 
    107     SkPath path() const override {
    108         SkPath path;
    109         path.addRect(fRect);
    110         return path;
    111     }
    112 
    113     GrShape makeShape(const SkPaint& paint) const override {
    114         return GrShape(fRect, paint);
    115     }
    116 
    117     bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
    118         SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
    119         // Converted to an outset rectangle.
    120         return paint.getStrokeJoin() == SkPaint::kMiter_Join &&
    121                paint.getStrokeMiter() >= SK_ScalarSqrt2;
    122     }
    123 
    124 private:
    125     SkRect fRect;
    126 };
    127 
    128 class RRectGeo : public Geo {
    129 public:
    130     RRectGeo(const SkRRect& rrect) : fRRect(rrect) {}
    131 
    132     GrShape makeShape(const SkPaint& paint) const override {
    133         return GrShape(fRRect, paint);
    134     }
    135 
    136     SkPath path() const override {
    137         SkPath path;
    138         path.addRRect(fRRect);
    139         return path;
    140     }
    141 
    142     bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
    143         SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
    144         if (fRRect.isRect()) {
    145             return RectGeo(fRRect.rect()).strokeAndFillIsConvertedToFill(paint);
    146         }
    147         return false;
    148     }
    149 
    150 private:
    151     SkRRect fRRect;
    152 };
    153 
    154 class PathGeo : public Geo {
    155 public:
    156     enum class Invert { kNo, kYes };
    157 
    158     PathGeo(const SkPath& path, Invert invert) : fPath(path)  {
    159         SkASSERT(!path.isInverseFillType());
    160         if (Invert::kYes == invert) {
    161             if (fPath.getFillType() == SkPath::kEvenOdd_FillType) {
    162                 fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
    163             } else {
    164                 SkASSERT(fPath.getFillType() == SkPath::kWinding_FillType);
    165                 fPath.setFillType(SkPath::kInverseWinding_FillType);
    166             }
    167         }
    168     }
    169 
    170     GrShape makeShape(const SkPaint& paint) const override {
    171         return GrShape(fPath, paint);
    172     }
    173 
    174     SkPath path() const override { return fPath; }
    175 
    176     bool fillChangesGeom() const override {
    177         // unclosed rects get closed. Lines get turned into empty geometry
    178         return this->isUnclosedRect() || (fPath.isLine(nullptr) && !fPath.isInverseFillType());
    179     }
    180 
    181     bool strokeIsConvertedToFill() const override {
    182         return this->isAxisAlignedLine();
    183     }
    184 
    185     bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
    186         SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
    187         if (this->isAxisAlignedLine()) {
    188             // The fill is ignored (zero area) and the stroke is converted to a rrect.
    189             return true;
    190         }
    191         SkRect rect;
    192         unsigned start;
    193         SkPath::Direction dir;
    194         if (SkPathPriv::IsSimpleClosedRect(fPath, &rect, &dir, &start)) {
    195             return RectGeo(rect).strokeAndFillIsConvertedToFill(paint);
    196         }
    197         return false;
    198     }
    199 
    200     bool isNonPath(const SkPaint& paint) const override {
    201         return fPath.isLine(nullptr) || fPath.isEmpty();
    202     }
    203 
    204 private:
    205     bool isAxisAlignedLine() const {
    206         SkPoint pts[2];
    207         if (!fPath.isLine(pts)) {
    208             return false;
    209         }
    210         return pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY;
    211     }
    212 
    213     bool isUnclosedRect() const {
    214         bool closed;
    215         return fPath.isRect(nullptr, &closed, nullptr) && !closed;
    216     }
    217 
    218     SkPath fPath;
    219 };
    220 
    221 class RRectPathGeo : public PathGeo {
    222 public:
    223     enum class RRectForStroke { kNo, kYes };
    224 
    225     RRectPathGeo(const SkPath& path, const SkRRect& equivalentRRect, RRectForStroke rrectForStroke,
    226                  Invert invert)
    227             : PathGeo(path, invert)
    228             , fRRect(equivalentRRect)
    229             , fRRectForStroke(rrectForStroke) {}
    230 
    231     RRectPathGeo(const SkPath& path, const SkRect& equivalentRect, RRectForStroke rrectForStroke,
    232                  Invert invert)
    233             : RRectPathGeo(path, SkRRect::MakeRect(equivalentRect), rrectForStroke, invert) {}
    234 
    235     bool isNonPath(const SkPaint& paint) const override {
    236         if (SkPaint::kFill_Style == paint.getStyle() || RRectForStroke::kYes == fRRectForStroke) {
    237             return true;
    238         }
    239         return false;
    240     }
    241 
    242     const SkRRect& rrect() const { return fRRect; }
    243 
    244 private:
    245     SkRRect         fRRect;
    246     RRectForStroke  fRRectForStroke;
    247 };
    248 
    249 class TestCase {
    250 public:
    251     TestCase(const Geo& geo, const SkPaint& paint, skiatest::Reporter* r,
    252              SkScalar scale = SK_Scalar1) : fBase(geo.makeShape(paint)) {
    253         this->init(r, scale);
    254     }
    255 
    256     template<typename... ShapeArgs>
    257     TestCase(skiatest::Reporter* r, ShapeArgs... shapeArgs)
    258             : fBase(shapeArgs...) {
    259         this->init(r, SK_Scalar1);
    260     }
    261 
    262     TestCase(const GrShape& shape, skiatest::Reporter* r, SkScalar scale = SK_Scalar1)
    263         : fBase(shape) {
    264         this->init(r, scale);
    265     }
    266 
    267     struct SelfExpectations {
    268         bool fPEHasEffect;
    269         bool fPEHasValidKey;
    270         bool fStrokeApplies;
    271     };
    272 
    273     void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const;
    274 
    275     enum ComparisonExpecation {
    276         kAllDifferent_ComparisonExpecation,
    277         kSameUpToPE_ComparisonExpecation,
    278         kSameUpToStroke_ComparisonExpecation,
    279         kAllSame_ComparisonExpecation,
    280     };
    281 
    282     void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const;
    283 
    284     const GrShape& baseShape() const { return fBase; }
    285     const GrShape& appliedPathEffectShape() const { return fAppliedPE; }
    286     const GrShape& appliedFullStyleShape() const { return fAppliedFull; }
    287 
    288     // The returned array's count will be 0 if the key shape has no key.
    289     const Key& baseKey() const { return fBaseKey; }
    290     const Key& appliedPathEffectKey() const { return fAppliedPEKey; }
    291     const Key& appliedFullStyleKey() const { return fAppliedFullKey; }
    292     const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; }
    293 
    294 private:
    295     static void CheckBounds(skiatest::Reporter* r, const GrShape& shape, const SkRect& bounds) {
    296         SkPath path;
    297         shape.asPath(&path);
    298         // If the bounds are empty, the path ought to be as well.
    299         if (bounds.fLeft > bounds.fRight || bounds.fTop > bounds.fBottom) {
    300             REPORTER_ASSERT(r, path.isEmpty());
    301             return;
    302         }
    303         if (path.isEmpty()) {
    304             return;
    305         }
    306         // The bounds API explicitly calls out that it does not consider inverseness.
    307         SkPath p = path;
    308         p.setFillType(SkPath::ConvertToNonInverseFillType(path.getFillType()));
    309         REPORTER_ASSERT(r, test_bounds_by_rasterizing(p, bounds));
    310     }
    311 
    312     void init(skiatest::Reporter* r, SkScalar scale) {
    313         fAppliedPE           = fBase.applyStyle(GrStyle::Apply::kPathEffectOnly, scale);
    314         fAppliedPEThenStroke = fAppliedPE.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec,
    315                                                      scale);
    316         fAppliedFull         = fBase.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale);
    317 
    318         make_key(&fBaseKey, fBase);
    319         make_key(&fAppliedPEKey, fAppliedPE);
    320         make_key(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
    321         make_key(&fAppliedFullKey, fAppliedFull);
    322 
    323         // Applying the path effect and then the stroke should always be the same as applying
    324         // both in one go.
    325         REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey);
    326         SkPath a, b;
    327         fAppliedPEThenStroke.asPath(&a);
    328         fAppliedFull.asPath(&b);
    329         // If the output of the path effect is a rrect then it is possible for a and b to be
    330         // different paths that fill identically. The reason is that fAppliedFull will do this:
    331         // base -> apply path effect -> rrect_as_path -> stroke -> stroked_rrect_as_path
    332         // fAppliedPEThenStroke will have converted the rrect_as_path back to a rrect. However,
    333         // now that there is no longer a path effect, the direction and starting index get
    334         // canonicalized before the stroke.
    335         if (fAppliedPE.asRRect(nullptr, nullptr, nullptr, nullptr)) {
    336             REPORTER_ASSERT(r, paths_fill_same(a, b));
    337         } else {
    338             REPORTER_ASSERT(r, a == b);
    339         }
    340         REPORTER_ASSERT(r, fAppliedFull.isEmpty() == fAppliedPEThenStroke.isEmpty());
    341 
    342         SkPath path;
    343         fBase.asPath(&path);
    344         REPORTER_ASSERT(r, path.isEmpty() == fBase.isEmpty());
    345         REPORTER_ASSERT(r, path.getSegmentMasks() == fBase.segmentMask());
    346         fAppliedPE.asPath(&path);
    347         REPORTER_ASSERT(r, path.isEmpty() == fAppliedPE.isEmpty());
    348         REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedPE.segmentMask());
    349         fAppliedFull.asPath(&path);
    350         REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull.isEmpty());
    351         REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedFull.segmentMask());
    352 
    353         CheckBounds(r, fBase, fBase.bounds());
    354         CheckBounds(r, fAppliedPE, fAppliedPE.bounds());
    355         CheckBounds(r, fAppliedPEThenStroke, fAppliedPEThenStroke.bounds());
    356         CheckBounds(r, fAppliedFull, fAppliedFull.bounds());
    357         SkRect styledBounds = fBase.styledBounds();
    358         CheckBounds(r, fAppliedFull, styledBounds);
    359         styledBounds = fAppliedPE.styledBounds();
    360         CheckBounds(r, fAppliedFull, styledBounds);
    361 
    362         // Check that the same path is produced when style is applied by GrShape and GrStyle.
    363         SkPath preStyle;
    364         SkPath postPathEffect;
    365         SkPath postAllStyle;
    366 
    367         fBase.asPath(&preStyle);
    368         SkStrokeRec postPEStrokeRec(SkStrokeRec::kFill_InitStyle);
    369         if (fBase.style().applyPathEffectToPath(&postPathEffect, &postPEStrokeRec, preStyle,
    370                                                 scale)) {
    371             // run postPathEffect through GrShape to get any geometry reductions that would have
    372             // occurred to fAppliedPE.
    373             GrShape(postPathEffect, GrStyle(postPEStrokeRec, nullptr)).asPath(&postPathEffect);
    374 
    375             SkPath testPath;
    376             fAppliedPE.asPath(&testPath);
    377             REPORTER_ASSERT(r, testPath == postPathEffect);
    378             REPORTER_ASSERT(r, postPEStrokeRec.hasEqualEffect(fAppliedPE.style().strokeRec()));
    379         }
    380         SkStrokeRec::InitStyle fillOrHairline;
    381         if (fBase.style().applyToPath(&postAllStyle, &fillOrHairline, preStyle, scale)) {
    382             SkPath testPath;
    383             fAppliedFull.asPath(&testPath);
    384             if (fBase.style().hasPathEffect()) {
    385                 // Because GrShape always does two-stage application when there is a path effect
    386                 // there may be a reduction/canonicalization step between the path effect and
    387                 // strokerec not reflected in postAllStyle since it applied both the path effect
    388                 // and strokerec without analyzing the intermediate path.
    389                 REPORTER_ASSERT(r, paths_fill_same(postAllStyle, testPath));
    390             } else {
    391                 // Make sure that postAllStyle sees any reductions/canonicalizations that GrShape
    392                 // would apply.
    393                 GrShape(postAllStyle, GrStyle(fillOrHairline)).asPath(&postAllStyle);
    394                 REPORTER_ASSERT(r, testPath == postAllStyle);
    395             }
    396 
    397             if (fillOrHairline == SkStrokeRec::kFill_InitStyle) {
    398                 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleFill());
    399             } else {
    400                 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleHairline());
    401             }
    402         }
    403     }
    404 
    405     GrShape fBase;
    406     GrShape fAppliedPE;
    407     GrShape fAppliedPEThenStroke;
    408     GrShape fAppliedFull;
    409 
    410     Key fBaseKey;
    411     Key fAppliedPEKey;
    412     Key fAppliedPEThenStrokeKey;
    413     Key fAppliedFullKey;
    414 };
    415 
    416 void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const {
    417     // The base's key should always be valid (unless the path is volatile)
    418     REPORTER_ASSERT(reporter, fBaseKey.count());
    419     if (expectations.fPEHasEffect) {
    420         REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey);
    421         REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedPEKey.count()));
    422         REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
    423         REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedFullKey.count()));
    424         if (expectations.fStrokeApplies && expectations.fPEHasValidKey) {
    425             REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey);
    426             REPORTER_ASSERT(reporter, SkToBool(fAppliedFullKey.count()));
    427         }
    428     } else {
    429         REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey);
    430         SkPath a, b;
    431         fBase.asPath(&a);
    432         fAppliedPE.asPath(&b);
    433         REPORTER_ASSERT(reporter, a == b);
    434         if (expectations.fStrokeApplies) {
    435             REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
    436         } else {
    437             REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey);
    438         }
    439     }
    440 }
    441 
    442 static bool can_interchange_winding_and_even_odd_fill(const GrShape& shape) {
    443     SkPath path;
    444     shape.asPath(&path);
    445     if (shape.style().hasNonDashPathEffect()) {
    446         return false;
    447     }
    448     const SkStrokeRec::Style strokeRecStyle = shape.style().strokeRec().getStyle();
    449     return strokeRecStyle == SkStrokeRec::kStroke_Style ||
    450            strokeRecStyle == SkStrokeRec::kHairline_Style ||
    451            (shape.style().isSimpleFill() && path.isConvex());
    452 }
    453 
    454 static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrShape& b,
    455                               const Key& keyA, const Key& keyB) {
    456     // GrShape only respects the input winding direction and start point for rrect shapes
    457     // when there is a path effect. Thus, if there are two GrShapes representing the same rrect
    458     // but one has a path effect in its style and the other doesn't then asPath() and the unstyled
    459     // key will differ. GrShape will have canonicalized the direction and start point for the shape
    460     // without the path effect. If *both* have path effects then they should have both preserved
    461     // the direction and starting point.
    462 
    463     // The asRRect() output params are all initialized just to silence compiler warnings about
    464     // uninitialized variables.
    465     SkRRect rrectA = SkRRect::MakeEmpty(), rrectB = SkRRect::MakeEmpty();
    466     SkPath::Direction dirA = SkPath::kCW_Direction, dirB = SkPath::kCW_Direction;
    467     unsigned startA = ~0U, startB = ~0U;
    468     bool invertedA = true, invertedB = true;
    469 
    470     bool aIsRRect = a.asRRect(&rrectA, &dirA, &startA, &invertedA);
    471     bool bIsRRect = b.asRRect(&rrectB, &dirB, &startB, &invertedB);
    472     bool aHasPE = a.style().hasPathEffect();
    473     bool bHasPE = b.style().hasPathEffect();
    474     bool allowSameRRectButDiffStartAndDir = (aIsRRect && bIsRRect) && (aHasPE != bHasPE);
    475     // GrShape will close paths with simple fill style.
    476     bool allowedClosednessDiff = (a.style().isSimpleFill() != b.style().isSimpleFill());
    477     SkPath pathA, pathB;
    478     a.asPath(&pathA);
    479     b.asPath(&pathB);
    480 
    481     // Having a dash path effect can allow 'a' but not 'b' to turn a inverse fill type into a
    482     // non-inverse fill type  (or vice versa).
    483     bool ignoreInversenessDifference = false;
    484     if (pathA.isInverseFillType() != pathB.isInverseFillType()) {
    485         const GrShape* s1 = pathA.isInverseFillType() ? &a : &b;
    486         const GrShape* s2 = pathA.isInverseFillType() ? &b : &a;
    487         bool canDropInverse1 = s1->style().isDashed();
    488         bool canDropInverse2 = s2->style().isDashed();
    489         ignoreInversenessDifference = (canDropInverse1 != canDropInverse2);
    490     }
    491     bool ignoreWindingVsEvenOdd = false;
    492     if (SkPath::ConvertToNonInverseFillType(pathA.getFillType()) !=
    493         SkPath::ConvertToNonInverseFillType(pathB.getFillType())) {
    494         bool aCanChange = can_interchange_winding_and_even_odd_fill(a);
    495         bool bCanChange = can_interchange_winding_and_even_odd_fill(b);
    496         if (aCanChange != bCanChange) {
    497             ignoreWindingVsEvenOdd = true;
    498         }
    499     }
    500     if (allowSameRRectButDiffStartAndDir) {
    501         REPORTER_ASSERT(r, rrectA == rrectB);
    502         REPORTER_ASSERT(r, paths_fill_same(pathA, pathB));
    503         REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
    504     } else {
    505         SkPath pA = pathA;
    506         SkPath pB = pathB;
    507         REPORTER_ASSERT(r, a.inverseFilled() == pA.isInverseFillType());
    508         REPORTER_ASSERT(r, b.inverseFilled() == pB.isInverseFillType());
    509         if (ignoreInversenessDifference) {
    510             pA.setFillType(SkPath::ConvertToNonInverseFillType(pathA.getFillType()));
    511             pB.setFillType(SkPath::ConvertToNonInverseFillType(pathB.getFillType()));
    512         }
    513         if (ignoreWindingVsEvenOdd) {
    514             pA.setFillType(pA.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
    515                                                   : SkPath::kEvenOdd_FillType);
    516             pB.setFillType(pB.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
    517                                                   : SkPath::kEvenOdd_FillType);
    518         }
    519         if (!ignoreInversenessDifference && !ignoreWindingVsEvenOdd) {
    520             REPORTER_ASSERT(r, keyA == keyB);
    521         } else {
    522             REPORTER_ASSERT(r, keyA != keyB);
    523         }
    524         if (allowedClosednessDiff) {
    525             // GrShape will close paths with simple fill style. Make the non-filled path closed
    526             // so that the comparision will succeed. Make sure both are closed before comparing.
    527             pA.close();
    528             pB.close();
    529         }
    530         REPORTER_ASSERT(r, pA == pB);
    531         REPORTER_ASSERT(r, aIsRRect == bIsRRect);
    532         if (aIsRRect) {
    533             REPORTER_ASSERT(r, rrectA == rrectB);
    534             REPORTER_ASSERT(r, dirA == dirB);
    535             REPORTER_ASSERT(r, startA == startB);
    536             REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
    537         }
    538     }
    539     REPORTER_ASSERT(r, a.isEmpty() == b.isEmpty());
    540     REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeClosed() == b.knownToBeClosed());
    541     // closedness can affect convexity.
    542     REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeConvex() == b.knownToBeConvex());
    543     if (a.knownToBeConvex()) {
    544         REPORTER_ASSERT(r, pathA.isConvex());
    545     }
    546     if (b.knownToBeConvex()) {
    547         REPORTER_ASSERT(r, pathB.isConvex());
    548     }
    549     REPORTER_ASSERT(r, a.bounds() == b.bounds());
    550     REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask());
    551     // Init these to suppress warnings.
    552     SkPoint pts[4] {{0, 0,}, {0, 0}, {0, 0}, {0, 0}} ;
    553     bool invertedLine[2] {true, true};
    554     REPORTER_ASSERT(r, a.asLine(pts, &invertedLine[0]) == b.asLine(pts + 2, &invertedLine[1]));
    555     // mayBeInverseFilledAfterStyling() is allowed to differ if one has a arbitrary PE and the other
    556     // doesn't (since the PE can set any fill type on its output path).
    557     // Moreover, dash style explicitly ignores inverseness. So if one is dashed but not the other
    558     // then they may disagree about inverseness.
    559     if (a.style().hasNonDashPathEffect() == b.style().hasNonDashPathEffect() &&
    560         a.style().isDashed() == b.style().isDashed()) {
    561         REPORTER_ASSERT(r, a.mayBeInverseFilledAfterStyling() ==
    562                            b.mayBeInverseFilledAfterStyling());
    563     }
    564     if (a.asLine(nullptr, nullptr)) {
    565         REPORTER_ASSERT(r, pts[2] == pts[0] && pts[3] == pts[1]);
    566         REPORTER_ASSERT(r, ignoreInversenessDifference || invertedLine[0] == invertedLine[1]);
    567         REPORTER_ASSERT(r, invertedLine[0] == a.inverseFilled());
    568         REPORTER_ASSERT(r, invertedLine[1] == b.inverseFilled());
    569     }
    570     REPORTER_ASSERT(r, ignoreInversenessDifference || a.inverseFilled() == b.inverseFilled());
    571 }
    572 
    573 void TestCase::compare(skiatest::Reporter* r, const TestCase& that,
    574                        ComparisonExpecation expectation) const {
    575     SkPath a, b;
    576     switch (expectation) {
    577         case kAllDifferent_ComparisonExpecation:
    578             REPORTER_ASSERT(r, fBaseKey != that.fBaseKey);
    579             REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
    580             REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
    581             break;
    582         case kSameUpToPE_ComparisonExpecation:
    583             check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
    584             REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
    585             REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
    586             break;
    587         case kSameUpToStroke_ComparisonExpecation:
    588             check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
    589             check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
    590             REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
    591             break;
    592         case kAllSame_ComparisonExpecation:
    593             check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
    594             check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
    595             check_equivalence(r, fAppliedFull, that.fAppliedFull, fAppliedFullKey,
    596                               that.fAppliedFullKey);
    597             break;
    598     }
    599 }
    600 }  // namespace
    601 
    602 static sk_sp<SkPathEffect> make_dash() {
    603     static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f };
    604     static const SkScalar kPhase = 0.75;
    605     return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase);
    606 }
    607 
    608 static sk_sp<SkPathEffect> make_null_dash() {
    609     static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0};
    610     return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f);
    611 }
    612 
    613 static void test_basic(skiatest::Reporter* reporter, const Geo& geo) {
    614     sk_sp<SkPathEffect> dashPE = make_dash();
    615 
    616     TestCase::SelfExpectations expectations;
    617     SkPaint fill;
    618 
    619     TestCase fillCase(geo, fill, reporter);
    620     expectations.fPEHasEffect = false;
    621     expectations.fPEHasValidKey = false;
    622     expectations.fStrokeApplies = false;
    623     fillCase.testExpectations(reporter, expectations);
    624     // Test that another GrShape instance built from the same primitive is the same.
    625     TestCase(geo, fill, reporter).compare(reporter, fillCase,
    626                                           TestCase::kAllSame_ComparisonExpecation);
    627 
    628     SkPaint stroke2RoundBevel;
    629     stroke2RoundBevel.setStyle(SkPaint::kStroke_Style);
    630     stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap);
    631     stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join);
    632     stroke2RoundBevel.setStrokeWidth(2.f);
    633     TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter);
    634     expectations.fPEHasValidKey = true;
    635     expectations.fPEHasEffect = false;
    636     expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
    637     stroke2RoundBevelCase.testExpectations(reporter, expectations);
    638     TestCase(geo, stroke2RoundBevel, reporter).compare(reporter, stroke2RoundBevelCase,
    639                                                        TestCase::kAllSame_ComparisonExpecation);
    640 
    641     SkPaint stroke2RoundBevelDash = stroke2RoundBevel;
    642     stroke2RoundBevelDash.setPathEffect(make_dash());
    643     TestCase stroke2RoundBevelDashCase(geo, stroke2RoundBevelDash, reporter);
    644     expectations.fPEHasValidKey = true;
    645     expectations.fPEHasEffect = true;
    646     expectations.fStrokeApplies = true;
    647     stroke2RoundBevelDashCase.testExpectations(reporter, expectations);
    648     TestCase(geo, stroke2RoundBevelDash, reporter).compare(reporter, stroke2RoundBevelDashCase,
    649                                                            TestCase::kAllSame_ComparisonExpecation);
    650 
    651     if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) {
    652         fillCase.compare(reporter, stroke2RoundBevelCase,
    653                          TestCase::kAllDifferent_ComparisonExpecation);
    654         fillCase.compare(reporter, stroke2RoundBevelDashCase,
    655                          TestCase::kAllDifferent_ComparisonExpecation);
    656     } else {
    657         fillCase.compare(reporter, stroke2RoundBevelCase,
    658                          TestCase::kSameUpToStroke_ComparisonExpecation);
    659         fillCase.compare(reporter, stroke2RoundBevelDashCase,
    660                          TestCase::kSameUpToPE_ComparisonExpecation);
    661     }
    662     if (geo.strokeIsConvertedToFill()) {
    663         stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
    664                                       TestCase::kAllDifferent_ComparisonExpecation);
    665     } else {
    666         stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
    667                                       TestCase::kSameUpToPE_ComparisonExpecation);
    668     }
    669 
    670     // Stroke and fill cases
    671     SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel;
    672     stroke2RoundBevelAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
    673     TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter);
    674     expectations.fPEHasValidKey = true;
    675     expectations.fPEHasEffect = false;
    676     expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
    677     stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations);
    678     TestCase(geo, stroke2RoundBevelAndFill, reporter).compare(reporter,
    679             stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation);
    680 
    681     SkPaint stroke2RoundBevelAndFillDash = stroke2RoundBevelDash;
    682     stroke2RoundBevelAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
    683     TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter);
    684     expectations.fPEHasValidKey = true;
    685     expectations.fPEHasEffect = false;
    686     expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
    687     stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations);
    688     TestCase(geo, stroke2RoundBevelAndFillDash, reporter).compare(
    689         reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation);
    690     stroke2RoundBevelAndFillDashCase.compare(reporter, stroke2RoundBevelAndFillCase,
    691                                              TestCase::kAllSame_ComparisonExpecation);
    692 
    693     SkPaint hairline;
    694     hairline.setStyle(SkPaint::kStroke_Style);
    695     hairline.setStrokeWidth(0.f);
    696     TestCase hairlineCase(geo, hairline, reporter);
    697     // Since hairline style doesn't change the SkPath data, it is keyed identically to fill (except
    698     // in the line and unclosed rect cases).
    699     if (geo.fillChangesGeom()) {
    700         hairlineCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation);
    701     } else {
    702         hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
    703     }
    704     REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline());
    705     REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline());
    706     REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline());
    707 
    708 }
    709 
    710 static void test_scale(skiatest::Reporter* reporter, const Geo& geo) {
    711     sk_sp<SkPathEffect> dashPE = make_dash();
    712 
    713     static const SkScalar kS1 = 1.f;
    714     static const SkScalar kS2 = 2.f;
    715 
    716     SkPaint fill;
    717     TestCase fillCase1(geo, fill, reporter, kS1);
    718     TestCase fillCase2(geo, fill, reporter, kS2);
    719     // Scale doesn't affect fills.
    720     fillCase1.compare(reporter, fillCase2, TestCase::kAllSame_ComparisonExpecation);
    721 
    722     SkPaint hairline;
    723     hairline.setStyle(SkPaint::kStroke_Style);
    724     hairline.setStrokeWidth(0.f);
    725     TestCase hairlineCase1(geo, hairline, reporter, kS1);
    726     TestCase hairlineCase2(geo, hairline, reporter, kS2);
    727     // Scale doesn't affect hairlines.
    728     hairlineCase1.compare(reporter, hairlineCase2, TestCase::kAllSame_ComparisonExpecation);
    729 
    730     SkPaint stroke;
    731     stroke.setStyle(SkPaint::kStroke_Style);
    732     stroke.setStrokeWidth(2.f);
    733     TestCase strokeCase1(geo, stroke, reporter, kS1);
    734     TestCase strokeCase2(geo, stroke, reporter, kS2);
    735     // Scale affects the stroke
    736     if (geo.strokeIsConvertedToFill()) {
    737         REPORTER_ASSERT(reporter, !strokeCase1.baseShape().style().applies());
    738         strokeCase1.compare(reporter, strokeCase2, TestCase::kAllSame_ComparisonExpecation);
    739     } else {
    740         strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation);
    741     }
    742 
    743     SkPaint strokeDash = stroke;
    744     strokeDash.setPathEffect(make_dash());
    745     TestCase strokeDashCase1(geo, strokeDash, reporter, kS1);
    746     TestCase strokeDashCase2(geo, strokeDash, reporter, kS2);
    747     // Scale affects the dash and the stroke.
    748     strokeDashCase1.compare(reporter, strokeDashCase2,
    749                             TestCase::kSameUpToPE_ComparisonExpecation);
    750 
    751     // Stroke and fill cases
    752     SkPaint strokeAndFill = stroke;
    753     strokeAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
    754     TestCase strokeAndFillCase1(geo, strokeAndFill, reporter, kS1);
    755     TestCase strokeAndFillCase2(geo, strokeAndFill, reporter, kS2);
    756     SkPaint strokeAndFillDash = strokeDash;
    757     strokeAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
    758     // Dash is ignored for stroke and fill
    759     TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1);
    760     TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2);
    761     // Scale affects the stroke, but check to make sure this didn't become a simpler shape (e.g.
    762     // stroke-and-filled rect can become a rect), in which case the scale shouldn't matter and the
    763     // geometries should agree.
    764     if (geo.strokeAndFillIsConvertedToFill(strokeAndFillDash)) {
    765         REPORTER_ASSERT(reporter, !strokeAndFillCase1.baseShape().style().applies());
    766         strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
    767                                    TestCase::kAllSame_ComparisonExpecation);
    768         strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2,
    769                                        TestCase::kAllSame_ComparisonExpecation);
    770     } else {
    771         strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
    772                                    TestCase::kSameUpToStroke_ComparisonExpecation);
    773     }
    774     strokeAndFillDashCase1.compare(reporter, strokeAndFillCase1,
    775                                    TestCase::kAllSame_ComparisonExpecation);
    776     strokeAndFillDashCase2.compare(reporter, strokeAndFillCase2,
    777                                    TestCase::kAllSame_ComparisonExpecation);
    778 }
    779 
    780 template <typename T>
    781 static void test_stroke_param_impl(skiatest::Reporter* reporter, const Geo& geo,
    782                                    std::function<void(SkPaint*, T)> setter, T a, T b,
    783                                    bool paramAffectsStroke,
    784                                    bool paramAffectsDashAndStroke) {
    785     // Set the stroke width so that we don't get hairline. However, call the setter afterward so
    786     // that it can override the stroke width.
    787     SkPaint strokeA;
    788     strokeA.setStyle(SkPaint::kStroke_Style);
    789     strokeA.setStrokeWidth(2.f);
    790     setter(&strokeA, a);
    791     SkPaint strokeB;
    792     strokeB.setStyle(SkPaint::kStroke_Style);
    793     strokeB.setStrokeWidth(2.f);
    794     setter(&strokeB, b);
    795 
    796     TestCase strokeACase(geo, strokeA, reporter);
    797     TestCase strokeBCase(geo, strokeB, reporter);
    798     if (paramAffectsStroke) {
    799         // If stroking is immediately incorporated into a geometric transformation then the base
    800         // shapes will differ.
    801         if (geo.strokeIsConvertedToFill()) {
    802             strokeACase.compare(reporter, strokeBCase,
    803                                 TestCase::kAllDifferent_ComparisonExpecation);
    804         } else {
    805             strokeACase.compare(reporter, strokeBCase,
    806                                 TestCase::kSameUpToStroke_ComparisonExpecation);
    807         }
    808     } else {
    809         strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation);
    810     }
    811 
    812     SkPaint strokeAndFillA = strokeA;
    813     SkPaint strokeAndFillB = strokeB;
    814     strokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style);
    815     strokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style);
    816     TestCase strokeAndFillACase(geo, strokeAndFillA, reporter);
    817     TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter);
    818     if (paramAffectsStroke) {
    819         // If stroking is immediately incorporated into a geometric transformation then the base
    820         // shapes will differ.
    821         if (geo.strokeAndFillIsConvertedToFill(strokeAndFillA) ||
    822             geo.strokeAndFillIsConvertedToFill(strokeAndFillB)) {
    823             strokeAndFillACase.compare(reporter, strokeAndFillBCase,
    824                                        TestCase::kAllDifferent_ComparisonExpecation);
    825         } else {
    826             strokeAndFillACase.compare(reporter, strokeAndFillBCase,
    827                                        TestCase::kSameUpToStroke_ComparisonExpecation);
    828         }
    829     } else {
    830         strokeAndFillACase.compare(reporter, strokeAndFillBCase,
    831                                    TestCase::kAllSame_ComparisonExpecation);
    832     }
    833 
    834     // Make sure stroking params don't affect fill style.
    835     SkPaint fillA = strokeA, fillB = strokeB;
    836     fillA.setStyle(SkPaint::kFill_Style);
    837     fillB.setStyle(SkPaint::kFill_Style);
    838     TestCase fillACase(geo, fillA, reporter);
    839     TestCase fillBCase(geo, fillB, reporter);
    840     fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
    841 
    842     // Make sure just applying the dash but not stroke gives the same key for both stroking
    843     // variations.
    844     SkPaint dashA = strokeA, dashB = strokeB;
    845     dashA.setPathEffect(make_dash());
    846     dashB.setPathEffect(make_dash());
    847     TestCase dashACase(geo, dashA, reporter);
    848     TestCase dashBCase(geo, dashB, reporter);
    849     if (paramAffectsDashAndStroke) {
    850         dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
    851     } else {
    852         dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation);
    853     }
    854 }
    855 
    856 template <typename T>
    857 static void test_stroke_param(skiatest::Reporter* reporter, const Geo& geo,
    858                               std::function<void(SkPaint*, T)> setter, T a, T b) {
    859     test_stroke_param_impl(reporter, geo, setter, a, b, true, true);
    860 };
    861 
    862 static void test_stroke_cap(skiatest::Reporter* reporter, const Geo& geo) {
    863     SkPaint hairline;
    864     hairline.setStrokeWidth(0);
    865     hairline.setStyle(SkPaint::kStroke_Style);
    866     GrShape shape = geo.makeShape(hairline);
    867     // The cap should only affect shapes that may be open.
    868     bool affectsStroke = !shape.knownToBeClosed();
    869     // Dashing adds ends that need caps.
    870     bool affectsDashAndStroke = true;
    871     test_stroke_param_impl<SkPaint::Cap>(
    872         reporter,
    873         geo,
    874         [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
    875         SkPaint::kButt_Cap, SkPaint::kRound_Cap,
    876         affectsStroke,
    877         affectsDashAndStroke);
    878 };
    879 
    880 static bool shape_known_not_to_have_joins(const GrShape& shape) {
    881     return shape.asLine(nullptr, nullptr) || shape.isEmpty();
    882 }
    883 
    884 static void test_stroke_join(skiatest::Reporter* reporter, const Geo& geo) {
    885     SkPaint hairline;
    886     hairline.setStrokeWidth(0);
    887     hairline.setStyle(SkPaint::kStroke_Style);
    888     GrShape shape = geo.makeShape(hairline);
    889     // GrShape recognizes certain types don't have joins and will prevent the join type from
    890     // affecting the style key.
    891     // Dashing doesn't add additional joins. However, GrShape currently loses track of this
    892     // after applying the dash.
    893     bool affectsStroke = !shape_known_not_to_have_joins(shape);
    894     test_stroke_param_impl<SkPaint::Join>(
    895             reporter,
    896             geo,
    897             [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
    898             SkPaint::kRound_Join, SkPaint::kBevel_Join,
    899             affectsStroke, true);
    900 };
    901 
    902 static void test_miter_limit(skiatest::Reporter* reporter, const Geo& geo) {
    903     auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) {
    904         p->setStrokeJoin(SkPaint::kMiter_Join);
    905         p->setStrokeMiter(miter);
    906     };
    907 
    908     auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) {
    909         p->setStrokeJoin(SkPaint::kRound_Join);
    910         p->setStrokeMiter(miter);
    911     };
    912 
    913     SkPaint hairline;
    914     hairline.setStrokeWidth(0);
    915     hairline.setStyle(SkPaint::kStroke_Style);
    916     GrShape shape = geo.makeShape(hairline);
    917     bool mayHaveJoins = !shape_known_not_to_have_joins(shape);
    918 
    919     // The miter limit should affect stroked and dashed-stroked cases when the join type is
    920     // miter.
    921     test_stroke_param_impl<SkScalar>(
    922         reporter,
    923         geo,
    924         setMiterJoinAndLimit,
    925         0.5f, 0.75f,
    926         mayHaveJoins,
    927         true);
    928 
    929     // The miter limit should not affect stroked and dashed-stroked cases when the join type is
    930     // not miter.
    931     test_stroke_param_impl<SkScalar>(
    932         reporter,
    933         geo,
    934         setOtherJoinAndLimit,
    935         0.5f, 0.75f,
    936         false,
    937         false);
    938 }
    939 
    940 static void test_dash_fill(skiatest::Reporter* reporter, const Geo& geo) {
    941     // A dash with no stroke should have no effect
    942     using DashFactoryFn = sk_sp<SkPathEffect>(*)();
    943     for (DashFactoryFn md : {&make_dash, &make_null_dash}) {
    944         SkPaint dashFill;
    945         dashFill.setPathEffect((*md)());
    946         TestCase dashFillCase(geo, dashFill, reporter);
    947 
    948         TestCase fillCase(geo, SkPaint(), reporter);
    949         dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
    950     }
    951 }
    952 
    953 void test_null_dash(skiatest::Reporter* reporter, const Geo& geo) {
    954     SkPaint fill;
    955     SkPaint stroke;
    956     stroke.setStyle(SkPaint::kStroke_Style);
    957     stroke.setStrokeWidth(1.f);
    958     SkPaint dash;
    959     dash.setStyle(SkPaint::kStroke_Style);
    960     dash.setStrokeWidth(1.f);
    961     dash.setPathEffect(make_dash());
    962     SkPaint nullDash;
    963     nullDash.setStyle(SkPaint::kStroke_Style);
    964     nullDash.setStrokeWidth(1.f);
    965     nullDash.setPathEffect(make_null_dash());
    966 
    967     TestCase fillCase(geo, fill, reporter);
    968     TestCase strokeCase(geo, stroke, reporter);
    969     TestCase dashCase(geo, dash, reporter);
    970     TestCase nullDashCase(geo, nullDash, reporter);
    971 
    972     // We expect the null dash to be ignored so nullDashCase should match strokeCase, always.
    973     nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation);
    974     // Check whether the fillCase or strokeCase/nullDashCase would undergo a geometric tranformation
    975     // on construction in order to determine how to compare the fill and stroke.
    976     if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) {
    977         nullDashCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation);
    978     } else {
    979         nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation);
    980     }
    981     // In the null dash case we may immediately convert to a fill, but not for the normal dash case.
    982     if (geo.strokeIsConvertedToFill()) {
    983         nullDashCase.compare(reporter, dashCase, TestCase::kAllDifferent_ComparisonExpecation);
    984     } else {
    985         nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation);
    986     }
    987 }
    988 
    989 void test_path_effect_makes_rrect(skiatest::Reporter* reporter, const Geo& geo) {
    990     /**
    991      * This path effect takes any input path and turns it into a rrect. It passes through stroke
    992      * info.
    993      */
    994     class RRectPathEffect : SkPathEffect {
    995     public:
    996         static const SkRRect& RRect() {
    997             static const SkRRect kRRect = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 3, 5);
    998             return kRRect;
    999         }
   1000 
   1001         bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
   1002                         const SkRect* cullR) const override {
   1003             dst->reset();
   1004             dst->addRRect(RRect());
   1005             return true;
   1006         }
   1007         void computeFastBounds(SkRect* dst, const SkRect& src) const override {
   1008             *dst = RRect().getBounds();
   1009         }
   1010         static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new RRectPathEffect); }
   1011         Factory getFactory() const override { return nullptr; }
   1012         void toString(SkString*) const override {}
   1013     private:
   1014         RRectPathEffect() {}
   1015     };
   1016 
   1017     SkPaint fill;
   1018     TestCase fillGeoCase(geo, fill, reporter);
   1019 
   1020     SkPaint pe;
   1021     pe.setPathEffect(RRectPathEffect::Make());
   1022     TestCase geoPECase(geo, pe, reporter);
   1023 
   1024     SkPaint peStroke;
   1025     peStroke.setPathEffect(RRectPathEffect::Make());
   1026     peStroke.setStrokeWidth(2.f);
   1027     peStroke.setStyle(SkPaint::kStroke_Style);
   1028     TestCase geoPEStrokeCase(geo, peStroke, reporter);
   1029 
   1030     // Check whether constructing the filled case would cause the base shape to have a different
   1031     // geometry (because of a geometric transformation upon initial GrShape construction).
   1032     if (geo.fillChangesGeom()) {
   1033         fillGeoCase.compare(reporter, geoPECase, TestCase::kAllDifferent_ComparisonExpecation);
   1034         fillGeoCase.compare(reporter, geoPEStrokeCase,
   1035                             TestCase::kAllDifferent_ComparisonExpecation);
   1036     } else {
   1037         fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation);
   1038         fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation);
   1039     }
   1040     geoPECase.compare(reporter, geoPEStrokeCase,
   1041                       TestCase::kSameUpToStroke_ComparisonExpecation);
   1042 
   1043     TestCase rrectFillCase(reporter, RRectPathEffect::RRect(), fill);
   1044     SkPaint stroke = peStroke;
   1045     stroke.setPathEffect(nullptr);
   1046     TestCase rrectStrokeCase(reporter, RRectPathEffect::RRect(), stroke);
   1047 
   1048     SkRRect rrect;
   1049     // Applying the path effect should make a SkRRect shape. There is no further stroking in the
   1050     // geoPECase, so the full style should be the same as just the PE.
   1051     REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().asRRect(&rrect, nullptr, nullptr,
   1052                                                                          nullptr));
   1053     REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
   1054     REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == rrectFillCase.baseKey());
   1055 
   1056     REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr,
   1057                                                                         nullptr));
   1058     REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
   1059     REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == rrectFillCase.baseKey());
   1060 
   1061     // In the PE+stroke case applying the full style should be the same as just stroking the rrect.
   1062     REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().asRRect(&rrect, nullptr,
   1063                                                                                nullptr, nullptr));
   1064     REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
   1065     REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == rrectFillCase.baseKey());
   1066 
   1067     REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().asRRect(&rrect, nullptr,
   1068                                                                                nullptr, nullptr));
   1069     REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() ==
   1070                               rrectStrokeCase.appliedFullStyleKey());
   1071 }
   1072 
   1073 void test_unknown_path_effect(skiatest::Reporter* reporter, const Geo& geo) {
   1074     /**
   1075      * This path effect just adds two lineTos to the input path.
   1076      */
   1077     class AddLineTosPathEffect : SkPathEffect {
   1078     public:
   1079         bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
   1080                         const SkRect* cullR) const override {
   1081             *dst = src;
   1082             // To avoid triggering data-based keying of paths with few verbs we add many segments.
   1083             for (int i = 0; i < 100; ++i) {
   1084                 dst->lineTo(SkIntToScalar(i), SkIntToScalar(i));
   1085             }
   1086             return true;
   1087         }
   1088         void computeFastBounds(SkRect* dst, const SkRect& src) const override {
   1089             *dst = src;
   1090             dst->growToInclude(0, 0);
   1091             dst->growToInclude(100, 100);
   1092         }
   1093         static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new AddLineTosPathEffect); }
   1094         Factory getFactory() const override { return nullptr; }
   1095         void toString(SkString*) const override {}
   1096     private:
   1097         AddLineTosPathEffect() {}
   1098     };
   1099 
   1100      // This path effect should make the keys invalid when it is applied. We only produce a path
   1101      // effect key for dash path effects. So the only way another arbitrary path effect can produce
   1102      // a styled result with a key is to produce a non-path shape that has a purely geometric key.
   1103     SkPaint peStroke;
   1104     peStroke.setPathEffect(AddLineTosPathEffect::Make());
   1105     peStroke.setStrokeWidth(2.f);
   1106     peStroke.setStyle(SkPaint::kStroke_Style);
   1107     TestCase geoPEStrokeCase(geo, peStroke, reporter);
   1108     TestCase::SelfExpectations expectations;
   1109     expectations.fPEHasEffect = true;
   1110     expectations.fPEHasValidKey = false;
   1111     expectations.fStrokeApplies = true;
   1112     geoPEStrokeCase.testExpectations(reporter, expectations);
   1113 }
   1114 
   1115 void test_make_hairline_path_effect(skiatest::Reporter* reporter, const Geo& geo) {
   1116     /**
   1117      * This path effect just changes the stroke rec to hairline.
   1118      */
   1119     class MakeHairlinePathEffect : SkPathEffect {
   1120     public:
   1121         bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* strokeRec,
   1122                         const SkRect* cullR) const override {
   1123             *dst = src;
   1124             strokeRec->setHairlineStyle();
   1125             return true;
   1126         }
   1127         void computeFastBounds(SkRect* dst, const SkRect& src) const override { *dst = src; }
   1128         static sk_sp<SkPathEffect> Make() {
   1129             return sk_sp<SkPathEffect>(new MakeHairlinePathEffect);
   1130         }
   1131         Factory getFactory() const override { return nullptr; }
   1132         void toString(SkString*) const override {}
   1133     private:
   1134         MakeHairlinePathEffect() {}
   1135     };
   1136 
   1137     SkPaint fill;
   1138     SkPaint pe;
   1139     pe.setPathEffect(MakeHairlinePathEffect::Make());
   1140 
   1141     TestCase peCase(geo, pe, reporter);
   1142 
   1143     SkPath a, b, c;
   1144     peCase.baseShape().asPath(&a);
   1145     peCase.appliedPathEffectShape().asPath(&b);
   1146     peCase.appliedFullStyleShape().asPath(&c);
   1147     if (geo.isNonPath(pe)) {
   1148         // RRect types can have a change in start index or direction after the PE is applied. This
   1149         // is because once the PE is applied, GrShape may canonicalize the dir and index since it
   1150         // is not germane to the styling any longer.
   1151         // Instead we just check that the paths would fill the same both before and after styling.
   1152         REPORTER_ASSERT(reporter, paths_fill_same(a, b));
   1153         REPORTER_ASSERT(reporter, paths_fill_same(a, c));
   1154     } else {
   1155         // The base shape cannot perform canonicalization on the path's fill type because of an
   1156         // unknown path effect. However, after the path effect is applied the resulting hairline
   1157         // shape will canonicalize the path fill type since hairlines (and stroking in general)
   1158         // don't distinguish between even/odd and non-zero winding.
   1159         a.setFillType(b.getFillType());
   1160         REPORTER_ASSERT(reporter, a == b);
   1161         REPORTER_ASSERT(reporter, a == c);
   1162         // If the resulting path is small enough then it will have a key.
   1163         REPORTER_ASSERT(reporter, paths_fill_same(a, b));
   1164         REPORTER_ASSERT(reporter, paths_fill_same(a, c));
   1165         REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey().empty());
   1166         REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey().empty());
   1167     }
   1168     REPORTER_ASSERT(reporter, peCase.appliedPathEffectShape().style().isSimpleHairline());
   1169     REPORTER_ASSERT(reporter, peCase.appliedFullStyleShape().style().isSimpleHairline());
   1170 }
   1171 
   1172 void test_volatile_path(skiatest::Reporter* reporter, const Geo& geo) {
   1173     SkPath vPath = geo.path();
   1174     vPath.setIsVolatile(true);
   1175 
   1176     SkPaint dashAndStroke;
   1177     dashAndStroke.setPathEffect(make_dash());
   1178     dashAndStroke.setStrokeWidth(2.f);
   1179     dashAndStroke.setStyle(SkPaint::kStroke_Style);
   1180     TestCase volatileCase(reporter, vPath, dashAndStroke);
   1181     // We expect a shape made from a volatile path to have a key iff the shape is recognized
   1182     // as a specialized geometry.
   1183     if (geo.isNonPath(dashAndStroke)) {
   1184         REPORTER_ASSERT(reporter, SkToBool(volatileCase.baseKey().count()));
   1185         // In this case all the keys should be identical to the non-volatile case.
   1186         TestCase nonVolatileCase(reporter, geo.path(), dashAndStroke);
   1187         volatileCase.compare(reporter, nonVolatileCase, TestCase::kAllSame_ComparisonExpecation);
   1188     } else {
   1189         // None of the keys should be valid.
   1190         REPORTER_ASSERT(reporter, !SkToBool(volatileCase.baseKey().count()));
   1191         REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectKey().count()));
   1192         REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedFullStyleKey().count()));
   1193         REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectThenStrokeKey().count()));
   1194     }
   1195 }
   1196 
   1197 void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const Geo& geo) {
   1198     /**
   1199      * This path effect returns an empty path.
   1200      */
   1201     class EmptyPathEffect : SkPathEffect {
   1202     public:
   1203         bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
   1204                         const SkRect* cullR) const override {
   1205             dst->reset();
   1206             return true;
   1207         }
   1208         void computeFastBounds(SkRect* dst, const SkRect& src) const override {
   1209             dst->setEmpty();
   1210         }
   1211         static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new EmptyPathEffect); }
   1212         Factory getFactory() const override { return nullptr; }
   1213         void toString(SkString*) const override {}
   1214     private:
   1215         EmptyPathEffect() {}
   1216     };
   1217 
   1218     SkPath emptyPath;
   1219     GrShape emptyShape(emptyPath);
   1220     Key emptyKey;
   1221     make_key(&emptyKey, emptyShape);
   1222     REPORTER_ASSERT(reporter, emptyShape.isEmpty());
   1223 
   1224     SkPaint pe;
   1225     pe.setPathEffect(EmptyPathEffect::Make());
   1226     TestCase geoCase(geo, pe, reporter);
   1227     REPORTER_ASSERT(reporter, geoCase.appliedFullStyleKey() == emptyKey);
   1228     REPORTER_ASSERT(reporter, geoCase.appliedPathEffectKey() == emptyKey);
   1229     REPORTER_ASSERT(reporter, geoCase.appliedPathEffectThenStrokeKey() == emptyKey);
   1230     REPORTER_ASSERT(reporter, geoCase.appliedPathEffectShape().isEmpty());
   1231     REPORTER_ASSERT(reporter, geoCase.appliedFullStyleShape().isEmpty());
   1232 
   1233     SkPaint peStroke;
   1234     peStroke.setPathEffect(EmptyPathEffect::Make());
   1235     peStroke.setStrokeWidth(2.f);
   1236     peStroke.setStyle(SkPaint::kStroke_Style);
   1237     TestCase geoPEStrokeCase(geo, peStroke, reporter);
   1238     REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == emptyKey);
   1239     REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == emptyKey);
   1240     REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey);
   1241     REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty());
   1242     REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty());
   1243 }
   1244 
   1245 void test_path_effect_fails(skiatest::Reporter* reporter, const Geo& geo) {
   1246     /**
   1247      * This path effect always fails to apply.
   1248      */
   1249     class FailurePathEffect : SkPathEffect {
   1250     public:
   1251         bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
   1252                         const SkRect* cullR) const override {
   1253             return false;
   1254         }
   1255         void computeFastBounds(SkRect* dst, const SkRect& src) const override {
   1256             *dst = src;
   1257         }
   1258         static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new FailurePathEffect); }
   1259         Factory getFactory() const override { return nullptr; }
   1260         void toString(SkString*) const override {}
   1261     private:
   1262         FailurePathEffect() {}
   1263     };
   1264 
   1265     SkPaint fill;
   1266     TestCase fillCase(geo, fill, reporter);
   1267 
   1268     SkPaint pe;
   1269     pe.setPathEffect(FailurePathEffect::Make());
   1270     TestCase peCase(geo, pe, reporter);
   1271 
   1272     SkPaint stroke;
   1273     stroke.setStrokeWidth(2.f);
   1274     stroke.setStyle(SkPaint::kStroke_Style);
   1275     TestCase strokeCase(geo, stroke, reporter);
   1276 
   1277     SkPaint peStroke = stroke;
   1278     peStroke.setPathEffect(FailurePathEffect::Make());
   1279     TestCase peStrokeCase(geo, peStroke, reporter);
   1280 
   1281     // In general the path effect failure can cause some of the TestCase::compare() tests to fail
   1282     // for at least two reasons: 1) We will initially treat the shape as unkeyable because of the
   1283     // path effect, but then when the path effect fails we can key it. 2) GrShape will change its
   1284     // mind about whether a unclosed rect is actually rect. The path effect initially bars us from
   1285     // closing it but after the effect fails we can (for the fill+pe case). This causes different
   1286     // routes through GrShape to have equivalent but different representations of the path (closed
   1287     // or not) but that fill the same.
   1288     SkPath a;
   1289     SkPath b;
   1290     fillCase.appliedPathEffectShape().asPath(&a);
   1291     peCase.appliedPathEffectShape().asPath(&b);
   1292     REPORTER_ASSERT(reporter, paths_fill_same(a, b));
   1293 
   1294     fillCase.appliedFullStyleShape().asPath(&a);
   1295     peCase.appliedFullStyleShape().asPath(&b);
   1296     REPORTER_ASSERT(reporter, paths_fill_same(a, b));
   1297 
   1298     strokeCase.appliedPathEffectShape().asPath(&a);
   1299     peStrokeCase.appliedPathEffectShape().asPath(&b);
   1300     REPORTER_ASSERT(reporter, paths_fill_same(a, b));
   1301 
   1302     strokeCase.appliedFullStyleShape().asPath(&a);
   1303     peStrokeCase.appliedFullStyleShape().asPath(&b);
   1304     REPORTER_ASSERT(reporter, paths_fill_same(a, b));
   1305 }
   1306 
   1307 void test_empty_shape(skiatest::Reporter* reporter) {
   1308     SkPath emptyPath;
   1309     SkPaint fill;
   1310     TestCase fillEmptyCase(reporter, emptyPath, fill);
   1311     REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty());
   1312     REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty());
   1313     REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty());
   1314 
   1315     Key emptyKey(fillEmptyCase.baseKey());
   1316     REPORTER_ASSERT(reporter, emptyKey.count());
   1317     TestCase::SelfExpectations expectations;
   1318     expectations.fStrokeApplies = false;
   1319     expectations.fPEHasEffect = false;
   1320     // This will test whether applying style preserves emptiness
   1321     fillEmptyCase.testExpectations(reporter, expectations);
   1322 
   1323     // Stroking an empty path should have no effect
   1324     SkPath emptyPath2;
   1325     SkPaint stroke;
   1326     stroke.setStrokeWidth(2.f);
   1327     stroke.setStyle(SkPaint::kStroke_Style);
   1328     TestCase strokeEmptyCase(reporter, emptyPath2, stroke);
   1329     strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
   1330 
   1331     // Dashing and stroking an empty path should have no effect
   1332     SkPath emptyPath3;
   1333     SkPaint dashAndStroke;
   1334     dashAndStroke.setPathEffect(make_dash());
   1335     dashAndStroke.setStrokeWidth(2.f);
   1336     dashAndStroke.setStyle(SkPaint::kStroke_Style);
   1337     TestCase dashAndStrokeEmptyCase(reporter, emptyPath3, dashAndStroke);
   1338     dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase,
   1339                                    TestCase::kAllSame_ComparisonExpecation);
   1340 
   1341     // A shape made from an empty rrect should behave the same as an empty path.
   1342     SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty());
   1343     REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type);
   1344     TestCase dashAndStrokeEmptyRRectCase(reporter, emptyRRect, dashAndStroke);
   1345     dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
   1346                                         TestCase::kAllSame_ComparisonExpecation);
   1347 
   1348     // Same for a rect.
   1349     SkRect emptyRect = SkRect::MakeEmpty();
   1350     TestCase dashAndStrokeEmptyRectCase(reporter, emptyRect, dashAndStroke);
   1351     dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase,
   1352                                        TestCase::kAllSame_ComparisonExpecation);
   1353 }
   1354 
   1355 // rect and oval types have rrect start indices that collapse to the same point. Here we select the
   1356 // canonical point in these cases.
   1357 unsigned canonicalize_rrect_start(int s, const SkRRect& rrect) {
   1358     switch (rrect.getType()) {
   1359         case SkRRect::kRect_Type:
   1360             return (s + 1) & 0b110;
   1361         case SkRRect::kOval_Type:
   1362             return s & 0b110;
   1363         default:
   1364             return s;
   1365     }
   1366 }
   1367 
   1368 void test_rrect(skiatest::Reporter* r, const SkRRect& rrect) {
   1369     enum Style {
   1370         kFill,
   1371         kStroke,
   1372         kHairline,
   1373         kStrokeAndFill
   1374     };
   1375 
   1376     // SkStrokeRec has no default cons., so init with kFill before calling the setters below.
   1377     SkStrokeRec strokeRecs[4] { SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle,
   1378                                 SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle};
   1379     strokeRecs[kFill].setFillStyle();
   1380     strokeRecs[kStroke].setStrokeStyle(2.f);
   1381     strokeRecs[kHairline].setHairlineStyle();
   1382     strokeRecs[kStrokeAndFill].setStrokeStyle(3.f, true);
   1383     // Use a bevel join to avoid complications of stroke+filled rects becoming filled rects before
   1384     // applyStyle() is called.
   1385     strokeRecs[kStrokeAndFill].setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 1.f);
   1386     sk_sp<SkPathEffect> dashEffect = make_dash();
   1387 
   1388     static constexpr Style kStyleCnt = static_cast<Style>(SK_ARRAY_COUNT(strokeRecs));
   1389 
   1390     auto index = [](bool inverted,
   1391                     SkPath::Direction dir,
   1392                     unsigned start,
   1393                     Style style,
   1394                     bool dash) -> int {
   1395         return inverted * (2 * 8 * kStyleCnt * 2) +
   1396                dir      * (    8 * kStyleCnt * 2) +
   1397                start    * (        kStyleCnt * 2) +
   1398                style    * (                    2) +
   1399                dash;
   1400     };
   1401     static const SkPath::Direction kSecondDirection = static_cast<SkPath::Direction>(1);
   1402     const int cnt = index(true, kSecondDirection, 7, static_cast<Style>(kStyleCnt - 1), true) + 1;
   1403     SkAutoTArray<GrShape> shapes(cnt);
   1404     for (bool inverted : {false, true}) {
   1405         for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
   1406             for (unsigned start = 0; start < 8; ++start) {
   1407                 for (Style style : {kFill, kStroke, kHairline, kStrokeAndFill}) {
   1408                     for (bool dash : {false, true}) {
   1409                         sk_sp<SkPathEffect> pe = dash ? dashEffect : nullptr;
   1410                         shapes[index(inverted, dir, start, style, dash)] =
   1411                                 GrShape(rrect, dir, start, SkToBool(inverted),
   1412                                         GrStyle(strokeRecs[style], std::move(pe)));
   1413                     }
   1414                 }
   1415             }
   1416         }
   1417     }
   1418 
   1419     // Get the keys for some example shape instances that we'll use for comparision against the
   1420     // rest.
   1421     static constexpr SkPath::Direction kExamplesDir = SkPath::kCW_Direction;
   1422     static constexpr unsigned kExamplesStart = 0;
   1423     const GrShape& exampleFillCase = shapes[index(false, kExamplesDir, kExamplesStart, kFill,
   1424                                                   false)];
   1425     Key exampleFillCaseKey;
   1426     make_key(&exampleFillCaseKey, exampleFillCase);
   1427 
   1428     const GrShape& exampleStrokeAndFillCase = shapes[index(false, kExamplesDir, kExamplesStart,
   1429                                                            kStrokeAndFill, false)];
   1430     Key exampleStrokeAndFillCaseKey;
   1431     make_key(&exampleStrokeAndFillCaseKey, exampleStrokeAndFillCase);
   1432 
   1433     const GrShape& exampleInvFillCase = shapes[index(true, kExamplesDir, kExamplesStart, kFill,
   1434                                                      false)];
   1435     Key exampleInvFillCaseKey;
   1436     make_key(&exampleInvFillCaseKey, exampleInvFillCase);
   1437 
   1438     const GrShape& exampleInvStrokeAndFillCase = shapes[index(true, kExamplesDir, kExamplesStart,
   1439                                                               kStrokeAndFill, false)];
   1440     Key exampleInvStrokeAndFillCaseKey;
   1441     make_key(&exampleInvStrokeAndFillCaseKey, exampleInvStrokeAndFillCase);
   1442 
   1443     const GrShape& exampleStrokeCase = shapes[index(false, kExamplesDir, kExamplesStart, kStroke,
   1444                                                     false)];
   1445     Key exampleStrokeCaseKey;
   1446     make_key(&exampleStrokeCaseKey, exampleStrokeCase);
   1447 
   1448     const GrShape& exampleInvStrokeCase = shapes[index(true, kExamplesDir, kExamplesStart, kStroke,
   1449                                                        false)];
   1450     Key exampleInvStrokeCaseKey;
   1451     make_key(&exampleInvStrokeCaseKey, exampleInvStrokeCase);
   1452 
   1453     const GrShape& exampleHairlineCase = shapes[index(false, kExamplesDir, kExamplesStart,
   1454                                                       kHairline, false)];
   1455     Key exampleHairlineCaseKey;
   1456     make_key(&exampleHairlineCaseKey, exampleHairlineCase);
   1457 
   1458     const GrShape& exampleInvHairlineCase = shapes[index(true, kExamplesDir, kExamplesStart,
   1459                                                          kHairline, false)];
   1460     Key exampleInvHairlineCaseKey;
   1461     make_key(&exampleInvHairlineCaseKey, exampleInvHairlineCase);
   1462 
   1463     // These are dummy initializations to suppress warnings.
   1464     SkRRect queryRR = SkRRect::MakeEmpty();
   1465     SkPath::Direction queryDir = SkPath::kCW_Direction;
   1466     unsigned queryStart = ~0U;
   1467     bool queryInverted = true;
   1468 
   1469     REPORTER_ASSERT(r, exampleFillCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
   1470     REPORTER_ASSERT(r, queryRR == rrect);
   1471     REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
   1472     REPORTER_ASSERT(r, 0 == queryStart);
   1473     REPORTER_ASSERT(r, !queryInverted);
   1474 
   1475     REPORTER_ASSERT(r, exampleInvFillCase.asRRect(&queryRR, &queryDir, &queryStart,
   1476                                                   &queryInverted));
   1477     REPORTER_ASSERT(r, queryRR == rrect);
   1478     REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
   1479     REPORTER_ASSERT(r, 0 == queryStart);
   1480     REPORTER_ASSERT(r, queryInverted);
   1481 
   1482     REPORTER_ASSERT(r, exampleStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
   1483                                                         &queryInverted));
   1484     REPORTER_ASSERT(r, queryRR == rrect);
   1485     REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
   1486     REPORTER_ASSERT(r, 0 == queryStart);
   1487     REPORTER_ASSERT(r, !queryInverted);
   1488 
   1489     REPORTER_ASSERT(r, exampleInvStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
   1490                                                            &queryInverted));
   1491     REPORTER_ASSERT(r, queryRR == rrect);
   1492     REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
   1493     REPORTER_ASSERT(r, 0 == queryStart);
   1494     REPORTER_ASSERT(r, queryInverted);
   1495 
   1496     REPORTER_ASSERT(r, exampleHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
   1497                                                    &queryInverted));
   1498     REPORTER_ASSERT(r, queryRR == rrect);
   1499     REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
   1500     REPORTER_ASSERT(r, 0 == queryStart);
   1501     REPORTER_ASSERT(r, !queryInverted);
   1502 
   1503     REPORTER_ASSERT(r, exampleInvHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
   1504                                                       &queryInverted));
   1505     REPORTER_ASSERT(r, queryRR == rrect);
   1506     REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
   1507     REPORTER_ASSERT(r, 0 == queryStart);
   1508     REPORTER_ASSERT(r, queryInverted);
   1509 
   1510     REPORTER_ASSERT(r, exampleStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
   1511     REPORTER_ASSERT(r, queryRR == rrect);
   1512     REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
   1513     REPORTER_ASSERT(r, 0 == queryStart);
   1514     REPORTER_ASSERT(r, !queryInverted);
   1515 
   1516     REPORTER_ASSERT(r, exampleInvStrokeCase.asRRect(&queryRR, &queryDir, &queryStart,
   1517                                                     &queryInverted));
   1518     REPORTER_ASSERT(r, queryRR == rrect);
   1519     REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
   1520     REPORTER_ASSERT(r, 0 == queryStart);
   1521     REPORTER_ASSERT(r, queryInverted);
   1522 
   1523     // Remember that the key reflects the geometry before styling is applied.
   1524     REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvFillCaseKey);
   1525     REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeAndFillCaseKey);
   1526     REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeAndFillCaseKey);
   1527     REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeCaseKey);
   1528     REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeCaseKey);
   1529     REPORTER_ASSERT(r, exampleFillCaseKey == exampleHairlineCaseKey);
   1530     REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvHairlineCaseKey);
   1531     REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvFillCaseKey);
   1532     REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvStrokeCaseKey);
   1533     REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvHairlineCaseKey);
   1534 
   1535     for (bool inverted : {false, true}) {
   1536         for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
   1537             for (unsigned start = 0; start < 8; ++start) {
   1538                 for (bool dash : {false, true}) {
   1539                     const GrShape& fillCase = shapes[index(inverted, dir, start, kFill, dash)];
   1540                     Key fillCaseKey;
   1541                     make_key(&fillCaseKey, fillCase);
   1542 
   1543                     const GrShape& strokeAndFillCase = shapes[index(inverted, dir, start,
   1544                                                                     kStrokeAndFill, dash)];
   1545                     Key strokeAndFillCaseKey;
   1546                     make_key(&strokeAndFillCaseKey, strokeAndFillCase);
   1547 
   1548                     // Both fill and stroke-and-fill shapes must respect the inverseness and both
   1549                     // ignore dashing.
   1550                     REPORTER_ASSERT(r, !fillCase.style().pathEffect());
   1551                     REPORTER_ASSERT(r, !strokeAndFillCase.style().pathEffect());
   1552                     TestCase a(fillCase, r);
   1553                     TestCase b(inverted ? exampleInvFillCase : exampleFillCase, r);
   1554                     TestCase c(strokeAndFillCase, r);
   1555                     TestCase d(inverted ? exampleInvStrokeAndFillCase
   1556                                         : exampleStrokeAndFillCase, r);
   1557                     a.compare(r, b, TestCase::kAllSame_ComparisonExpecation);
   1558                     c.compare(r, d, TestCase::kAllSame_ComparisonExpecation);
   1559 
   1560                     const GrShape& strokeCase = shapes[index(inverted, dir, start, kStroke, dash)];
   1561                     const GrShape& hairlineCase = shapes[index(inverted, dir, start, kHairline,
   1562                                                                dash)];
   1563 
   1564                     TestCase e(strokeCase, r);
   1565                     TestCase g(hairlineCase, r);
   1566 
   1567                     // Both hairline and stroke shapes must respect the dashing.
   1568                     if (dash) {
   1569                         // Dashing always ignores the inverseness. skbug.com/5421
   1570                         TestCase f(exampleStrokeCase, r);
   1571                         TestCase h(exampleHairlineCase, r);
   1572                         unsigned expectedStart = canonicalize_rrect_start(start, rrect);
   1573                         REPORTER_ASSERT(r, strokeCase.style().pathEffect());
   1574                         REPORTER_ASSERT(r, hairlineCase.style().pathEffect());
   1575 
   1576                         REPORTER_ASSERT(r, strokeCase.asRRect(&queryRR, &queryDir, &queryStart,
   1577                                                               &queryInverted));
   1578                         REPORTER_ASSERT(r, queryRR == rrect);
   1579                         REPORTER_ASSERT(r, queryDir == dir);
   1580                         REPORTER_ASSERT(r, queryStart == expectedStart);
   1581                         REPORTER_ASSERT(r, !queryInverted);
   1582                         REPORTER_ASSERT(r, hairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
   1583                                                                 &queryInverted));
   1584                         REPORTER_ASSERT(r, queryRR == rrect);
   1585                         REPORTER_ASSERT(r, queryDir == dir);
   1586                         REPORTER_ASSERT(r, queryStart == expectedStart);
   1587                         REPORTER_ASSERT(r, !queryInverted);
   1588 
   1589                         // The pre-style case for the dash will match the non-dash example iff the
   1590                         // dir and start match (dir=cw, start=0).
   1591                         if (0 == expectedStart && SkPath::kCW_Direction == dir) {
   1592                             e.compare(r, f, TestCase::kSameUpToPE_ComparisonExpecation);
   1593                             g.compare(r, h, TestCase::kSameUpToPE_ComparisonExpecation);
   1594                         } else {
   1595                             e.compare(r, f, TestCase::kAllDifferent_ComparisonExpecation);
   1596                             g.compare(r, h, TestCase::kAllDifferent_ComparisonExpecation);
   1597                         }
   1598                     } else {
   1599                         TestCase f(inverted ? exampleInvStrokeCase : exampleStrokeCase, r);
   1600                         TestCase h(inverted ? exampleInvHairlineCase : exampleHairlineCase, r);
   1601                         REPORTER_ASSERT(r, !strokeCase.style().pathEffect());
   1602                         REPORTER_ASSERT(r, !hairlineCase.style().pathEffect());
   1603                         e.compare(r, f, TestCase::kAllSame_ComparisonExpecation);
   1604                         g.compare(r, h, TestCase::kAllSame_ComparisonExpecation);
   1605                     }
   1606                 }
   1607             }
   1608         }
   1609     }
   1610 }
   1611 
   1612 void test_lines(skiatest::Reporter* r) {
   1613     static constexpr SkPoint kA { 1,  1};
   1614     static constexpr SkPoint kB { 5, -9};
   1615     static constexpr SkPoint kC {-3, 17};
   1616 
   1617     SkPath lineAB;
   1618     lineAB.moveTo(kA);
   1619     lineAB.lineTo(kB);
   1620 
   1621     SkPath lineBA;
   1622     lineBA.moveTo(kB);
   1623     lineBA.lineTo(kA);
   1624 
   1625     SkPath lineAC;
   1626     lineAC.moveTo(kB);
   1627     lineAC.lineTo(kC);
   1628 
   1629     SkPath invLineAB = lineAB;
   1630     invLineAB.setFillType(SkPath::kInverseEvenOdd_FillType);
   1631 
   1632     SkPaint fill;
   1633     SkPaint stroke;
   1634     stroke.setStyle(SkPaint::kStroke_Style);
   1635     stroke.setStrokeWidth(2.f);
   1636     SkPaint hairline;
   1637     hairline.setStyle(SkPaint::kStroke_Style);
   1638     hairline.setStrokeWidth(0.f);
   1639     SkPaint dash = stroke;
   1640     dash.setPathEffect(make_dash());
   1641 
   1642     TestCase fillAB(r, lineAB, fill);
   1643     TestCase fillEmpty(r, SkPath(), fill);
   1644     fillAB.compare(r, fillEmpty, TestCase::kAllSame_ComparisonExpecation);
   1645     REPORTER_ASSERT(r, !fillAB.baseShape().asLine(nullptr, nullptr));
   1646 
   1647     TestCase strokeAB(r, lineAB, stroke);
   1648     TestCase strokeBA(r, lineBA, stroke);
   1649     TestCase strokeAC(r, lineAC, stroke);
   1650 
   1651     TestCase hairlineAB(r, lineAB, hairline);
   1652     TestCase hairlineBA(r, lineBA, hairline);
   1653     TestCase hairlineAC(r, lineAC, hairline);
   1654 
   1655     TestCase dashAB(r, lineAB, dash);
   1656     TestCase dashBA(r, lineBA, dash);
   1657     TestCase dashAC(r, lineAC, dash);
   1658 
   1659     strokeAB.compare(r, fillAB, TestCase::kAllDifferent_ComparisonExpecation);
   1660 
   1661     strokeAB.compare(r, strokeBA, TestCase::kAllSame_ComparisonExpecation);
   1662     strokeAB.compare(r, strokeAC, TestCase::kAllDifferent_ComparisonExpecation);
   1663 
   1664     hairlineAB.compare(r, hairlineBA, TestCase::kAllSame_ComparisonExpecation);
   1665     hairlineAB.compare(r, hairlineAC, TestCase::kAllDifferent_ComparisonExpecation);
   1666 
   1667     dashAB.compare(r, dashBA, TestCase::kAllDifferent_ComparisonExpecation);
   1668     dashAB.compare(r, dashAC, TestCase::kAllDifferent_ComparisonExpecation);
   1669 
   1670     strokeAB.compare(r, hairlineAB, TestCase::kSameUpToStroke_ComparisonExpecation);
   1671 
   1672     // One of dashAB or dashBA should have the same line as strokeAB. It depends upon how
   1673     // GrShape canonicalizes line endpoints (when it can, i.e. when not dashed).
   1674     bool canonicalizeAsAB;
   1675     SkPoint canonicalPts[2] {kA, kB};
   1676     // Init these to suppress warnings.
   1677     bool inverted = true;
   1678     SkPoint pts[2] {{0, 0}, {0, 0}};
   1679     REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted);
   1680     if (pts[0] == kA && pts[1] == kB) {
   1681         canonicalizeAsAB = true;
   1682     } else if (pts[1] == kA && pts[0] == kB) {
   1683         canonicalizeAsAB = false;
   1684         SkTSwap(canonicalPts[0], canonicalPts[1]);
   1685     } else {
   1686         ERRORF(r, "Should return pts (a,b) or (b, a)");
   1687         return;
   1688     };
   1689 
   1690     strokeAB.compare(r, canonicalizeAsAB ? dashAB : dashBA,
   1691                      TestCase::kSameUpToPE_ComparisonExpecation);
   1692     REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted &&
   1693                        pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
   1694     REPORTER_ASSERT(r, hairlineAB.baseShape().asLine(pts, &inverted) && !inverted &&
   1695                        pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
   1696     REPORTER_ASSERT(r, dashAB.baseShape().asLine(pts, &inverted) && !inverted &&
   1697                        pts[0] == kA && pts[1] == kB);
   1698     REPORTER_ASSERT(r, dashBA.baseShape().asLine(pts, &inverted) && !inverted &&
   1699                        pts[0] == kB && pts[1] == kA);
   1700 
   1701 
   1702     TestCase strokeInvAB(r, invLineAB, stroke);
   1703     TestCase hairlineInvAB(r, invLineAB, hairline);
   1704     TestCase dashInvAB(r, invLineAB, dash);
   1705     strokeInvAB.compare(r, strokeAB, TestCase::kAllDifferent_ComparisonExpecation);
   1706     hairlineInvAB.compare(r, hairlineAB, TestCase::kAllDifferent_ComparisonExpecation);
   1707     // Dashing ignores inverse.
   1708     dashInvAB.compare(r, dashAB, TestCase::kAllSame_ComparisonExpecation);
   1709 
   1710     REPORTER_ASSERT(r, strokeInvAB.baseShape().asLine(pts, &inverted) && inverted &&
   1711                        pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
   1712     REPORTER_ASSERT(r, hairlineInvAB.baseShape().asLine(pts, &inverted) && inverted &&
   1713                        pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
   1714     // Dashing ignores inverse.
   1715     REPORTER_ASSERT(r, dashInvAB.baseShape().asLine(pts, &inverted) && !inverted &&
   1716                        pts[0] == kA && pts[1] == kB);
   1717 
   1718 }
   1719 
   1720 static void test_stroked_lines(skiatest::Reporter* r) {
   1721     // Paints to try
   1722     SkPaint buttCap;
   1723     buttCap.setStyle(SkPaint::kStroke_Style);
   1724     buttCap.setStrokeWidth(4);
   1725     buttCap.setStrokeCap(SkPaint::kButt_Cap);
   1726 
   1727     SkPaint squareCap = buttCap;
   1728     squareCap.setStrokeCap(SkPaint::kSquare_Cap);
   1729 
   1730     SkPaint roundCap = buttCap;
   1731     roundCap.setStrokeCap(SkPaint::kRound_Cap);
   1732 
   1733     // vertical
   1734     SkPath linePath;
   1735     linePath.moveTo(4, 4);
   1736     linePath.lineTo(4, 5);
   1737 
   1738     SkPaint fill;
   1739 
   1740     TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 4, 6, 5), fill),
   1741                                            TestCase::kAllSame_ComparisonExpecation);
   1742 
   1743     TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 7), fill),
   1744                                              TestCase::kAllSame_ComparisonExpecation);
   1745 
   1746     TestCase(r, linePath, roundCap).compare(r,
   1747         TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 7), 2, 2), fill),
   1748         TestCase::kAllSame_ComparisonExpecation);
   1749 
   1750     // horizontal
   1751     linePath.reset();
   1752     linePath.moveTo(4, 4);
   1753     linePath.lineTo(5, 4);
   1754 
   1755     TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(4, 2, 5, 6), fill),
   1756                                            TestCase::kAllSame_ComparisonExpecation);
   1757     TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 7, 6), fill),
   1758                                              TestCase::kAllSame_ComparisonExpecation);
   1759     TestCase(r, linePath, roundCap).compare(r,
   1760          TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 7, 6), 2, 2), fill),
   1761          TestCase::kAllSame_ComparisonExpecation);
   1762 
   1763     // point
   1764     linePath.reset();
   1765     linePath.moveTo(4, 4);
   1766     linePath.lineTo(4, 4);
   1767 
   1768     TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeEmpty(), fill),
   1769                                            TestCase::kAllSame_ComparisonExpecation);
   1770     TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 6), fill),
   1771                                              TestCase::kAllSame_ComparisonExpecation);
   1772     TestCase(r, linePath, roundCap).compare(r,
   1773         TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 6), 2, 2), fill),
   1774         TestCase::kAllSame_ComparisonExpecation);
   1775 }
   1776 
   1777 static void test_short_path_keys(skiatest::Reporter* r) {
   1778     SkPaint paints[4];
   1779     paints[1].setStyle(SkPaint::kStroke_Style);
   1780     paints[1].setStrokeWidth(5.f);
   1781     paints[2].setStyle(SkPaint::kStroke_Style);
   1782     paints[2].setStrokeWidth(0.f);
   1783     paints[3].setStyle(SkPaint::kStrokeAndFill_Style);
   1784     paints[3].setStrokeWidth(5.f);
   1785 
   1786     auto compare = [r, &paints] (const SkPath& pathA, const SkPath& pathB,
   1787                                  TestCase::ComparisonExpecation expectation) {
   1788         SkPath volatileA = pathA;
   1789         SkPath volatileB = pathB;
   1790         volatileA.setIsVolatile(true);
   1791         volatileB.setIsVolatile(true);
   1792         for (const SkPaint& paint : paints) {
   1793             REPORTER_ASSERT(r, !GrShape(volatileA, paint).hasUnstyledKey());
   1794             REPORTER_ASSERT(r, !GrShape(volatileB, paint).hasUnstyledKey());
   1795             for (PathGeo::Invert invert : {PathGeo::Invert::kNo, PathGeo::Invert::kYes}) {
   1796                 TestCase caseA(PathGeo(pathA, invert), paint, r);
   1797                 TestCase caseB(PathGeo(pathB, invert), paint, r);
   1798                 caseA.compare(r, caseB, expectation);
   1799             }
   1800         }
   1801     };
   1802 
   1803     SkPath pathA;
   1804     SkPath pathB;
   1805 
   1806     // Two identical paths
   1807     pathA.lineTo(10.f, 10.f);
   1808     pathA.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
   1809 
   1810     pathB.lineTo(10.f, 10.f);
   1811     pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
   1812     compare(pathA, pathB, TestCase::kAllSame_ComparisonExpecation);
   1813 
   1814     // Give path b a different point
   1815     pathB.reset();
   1816     pathB.lineTo(10.f, 10.f);
   1817     pathB.conicTo(21.f, 20.f, 20.f, 30.f, 0.7f);
   1818     compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
   1819 
   1820     // Give path b a different conic weight
   1821     pathB.reset();
   1822     pathB.lineTo(10.f, 10.f);
   1823     pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f);
   1824     compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
   1825 
   1826     // Give path b an extra lineTo verb
   1827     pathB.reset();
   1828     pathB.lineTo(10.f, 10.f);
   1829     pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f);
   1830     pathB.lineTo(50.f, 50.f);
   1831     compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
   1832 
   1833     // Give path b a close
   1834     pathB.reset();
   1835     pathB.lineTo(10.f, 10.f);
   1836     pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
   1837     pathB.close();
   1838     compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
   1839 }
   1840 
   1841 DEF_TEST(GrShape, reporter) {
   1842     SkTArray<std::unique_ptr<Geo>> geos;
   1843     SkTArray<std::unique_ptr<RRectPathGeo>> rrectPathGeos;
   1844 
   1845     for (auto r : { SkRect::MakeWH(10, 20),
   1846                     SkRect::MakeWH(-10, -20),
   1847                     SkRect::MakeWH(-10, 20),
   1848                     SkRect::MakeWH(10, -20)}) {
   1849         geos.emplace_back(new RectGeo(r));
   1850         SkPath rectPath;
   1851         rectPath.addRect(r);
   1852         geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
   1853                                            PathGeo::Invert::kNo));
   1854         geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
   1855                                            PathGeo::Invert::kYes));
   1856         rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
   1857                                                     PathGeo::Invert::kNo));
   1858     }
   1859     for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)),
   1860                      SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4),
   1861                      SkRRect::MakeOval(SkRect::MakeWH(20, 20))}) {
   1862         geos.emplace_back(new RRectGeo(rr));
   1863         test_rrect(reporter, rr);
   1864         SkPath rectPath;
   1865         rectPath.addRRect(rr);
   1866         geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
   1867                                            PathGeo::Invert::kNo));
   1868         geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
   1869                                            PathGeo::Invert::kYes));
   1870         rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, rr,
   1871                                                     RRectPathGeo::RRectForStroke::kYes,
   1872                                                     PathGeo::Invert::kNo));
   1873     }
   1874 
   1875     SkPath openRectPath;
   1876     openRectPath.moveTo(0, 0);
   1877     openRectPath.lineTo(10, 0);
   1878     openRectPath.lineTo(10, 10);
   1879     openRectPath.lineTo(0, 10);
   1880     geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
   1881                                        RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kNo));
   1882     geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
   1883                                        RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kYes));
   1884     rrectPathGeos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
   1885                                                 RRectPathGeo::RRectForStroke::kNo,
   1886                                                 PathGeo::Invert::kNo));
   1887 
   1888     SkPath quadPath;
   1889     quadPath.quadTo(10, 10, 5, 8);
   1890     geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kNo));
   1891     geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kYes));
   1892 
   1893     SkPath linePath;
   1894     linePath.lineTo(10, 10);
   1895     geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kNo));
   1896     geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kYes));
   1897 
   1898     // Horizontal and vertical paths become rrects when stroked.
   1899     SkPath vLinePath;
   1900     vLinePath.lineTo(0, 10);
   1901     geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kNo));
   1902     geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kYes));
   1903 
   1904     SkPath hLinePath;
   1905     hLinePath.lineTo(10, 0);
   1906     geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kNo));
   1907     geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kYes));
   1908 
   1909     for (int i = 0; i < geos.count(); ++i) {
   1910         test_basic(reporter, *geos[i]);
   1911         test_scale(reporter, *geos[i]);
   1912         test_dash_fill(reporter, *geos[i]);
   1913         test_null_dash(reporter, *geos[i]);
   1914         // Test modifying various stroke params.
   1915         test_stroke_param<SkScalar>(
   1916                 reporter, *geos[i],
   1917                 [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
   1918                 SkIntToScalar(2), SkIntToScalar(4));
   1919         test_stroke_join(reporter, *geos[i]);
   1920         test_stroke_cap(reporter, *geos[i]);
   1921         test_miter_limit(reporter, *geos[i]);
   1922         test_path_effect_makes_rrect(reporter, *geos[i]);
   1923         test_unknown_path_effect(reporter, *geos[i]);
   1924         test_path_effect_makes_empty_shape(reporter, *geos[i]);
   1925         test_path_effect_fails(reporter, *geos[i]);
   1926         test_make_hairline_path_effect(reporter, *geos[i]);
   1927         test_volatile_path(reporter, *geos[i]);
   1928     }
   1929 
   1930     for (int i = 0; i < rrectPathGeos.count(); ++i) {
   1931         const RRectPathGeo& rrgeo = *rrectPathGeos[i];
   1932         SkPaint fillPaint;
   1933         TestCase fillPathCase(reporter, rrgeo.path(), fillPaint);
   1934         SkRRect rrect;
   1935         REPORTER_ASSERT(reporter, rrgeo.isNonPath(fillPaint) ==
   1936                                   fillPathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
   1937                                                                    nullptr));
   1938         if (rrgeo.isNonPath(fillPaint)) {
   1939             TestCase fillPathCase2(reporter, rrgeo.path(), fillPaint);
   1940             REPORTER_ASSERT(reporter, rrect == rrgeo.rrect());
   1941             TestCase fillRRectCase(reporter, rrect, fillPaint);
   1942             fillPathCase2.compare(reporter, fillRRectCase,
   1943                                   TestCase::kAllSame_ComparisonExpecation);
   1944         }
   1945         SkPaint strokePaint;
   1946         strokePaint.setStrokeWidth(3.f);
   1947         strokePaint.setStyle(SkPaint::kStroke_Style);
   1948         TestCase strokePathCase(reporter, rrgeo.path(), strokePaint);
   1949         if (rrgeo.isNonPath(strokePaint)) {
   1950             REPORTER_ASSERT(reporter, strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
   1951                                                                          nullptr));
   1952             REPORTER_ASSERT(reporter, rrect == rrgeo.rrect());
   1953             TestCase strokeRRectCase(reporter, rrect, strokePaint);
   1954             strokePathCase.compare(reporter, strokeRRectCase,
   1955                                    TestCase::kAllSame_ComparisonExpecation);
   1956         }
   1957     }
   1958 
   1959     // Test a volatile empty path.
   1960     test_volatile_path(reporter, PathGeo(SkPath(), PathGeo::Invert::kNo));
   1961 
   1962     test_empty_shape(reporter);
   1963 
   1964     test_lines(reporter);
   1965 
   1966     test_stroked_lines(reporter);
   1967 
   1968     test_short_path_keys(reporter);
   1969 }
   1970 
   1971 #endif
   1972