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