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