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