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