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