GrShapeTest.cpp revision ac5fcea9c3ec32a87bfd8cb96531e82097a1d861
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    static constexpr int kRes = 2000;
41    // This tolerance is in units of 1/kRes fractions of the bounds width/height.
42    static constexpr int kTol = 0;
43    GR_STATIC_ASSERT(kRes % 4 == 0);
44    SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
45    sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
46    surface->getCanvas()->clear(0x0);
47    SkRect clip = SkRect::MakeXYWH(kRes/4, kRes/4, kRes/2, kRes/2);
48    SkMatrix matrix;
49    matrix.setRectToRect(bounds, clip, SkMatrix::kFill_ScaleToFit);
50    clip.outset(SkIntToScalar(kTol), SkIntToScalar(kTol));
51    surface->getCanvas()->clipRect(clip, SkRegion::kDifference_Op);
52    surface->getCanvas()->concat(matrix);
53    SkPaint whitePaint;
54    whitePaint.setColor(SK_ColorWHITE);
55    surface->getCanvas()->drawPath(path, whitePaint);
56    SkPixmap pixmap;
57    surface->getCanvas()->peekPixels(&pixmap);
58#if defined(SK_BUILD_FOR_WIN)
59    // The static constexpr version in #else causes cl.exe to crash.
60    const uint8_t* kZeros = reinterpret_cast<uint8_t*>(calloc(kRes, 1));
61#else
62    static constexpr uint8_t kZeros[kRes] = {0};
63#endif
64    for (int y = 0; y < kRes/4; ++y) {
65        const uint8_t* row = pixmap.addr8(0, y);
66        if (0 != memcmp(kZeros, row, kRes)) {
67            return false;
68        }
69    }
70#ifdef SK_BUILD_FOR_WIN
71    free(const_cast<uint8_t*>(kZeros));
72#endif
73    return true;
74}
75
76namespace {
77class TestCase {
78public:
79    template <typename GEO>
80    TestCase(const GEO& geo, const SkPaint& paint, skiatest::Reporter* r,
81             SkScalar scale = SK_Scalar1) : fBase(geo, paint) {
82        this->init(r, scale);
83    }
84
85    TestCase(const GrShape& shape, skiatest::Reporter* r, SkScalar scale = SK_Scalar1)
86        : fBase(shape) {
87        this->init(r, scale);
88    }
89
90    struct SelfExpectations {
91        bool fPEHasEffect;
92        bool fPEHasValidKey;
93        bool fStrokeApplies;
94    };
95
96    void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const;
97
98    enum ComparisonExpecation {
99        kAllDifferent_ComparisonExpecation,
100        kSameUpToPE_ComparisonExpecation,
101        kSameUpToStroke_ComparisonExpecation,
102        kAllSame_ComparisonExpecation,
103    };
104
105    void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const;
106
107    const GrShape& baseShape() const { return fBase; }
108    const GrShape& appliedPathEffectShape() const { return fAppliedPE; }
109    const GrShape& appliedFullStyleShape() const { return fAppliedFull; }
110
111    // The returned array's count will be 0 if the key shape has no key.
112    const Key& baseKey() const { return fBaseKey; }
113    const Key& appliedPathEffectKey() const { return fAppliedPEKey; }
114    const Key& appliedFullStyleKey() const { return fAppliedFullKey; }
115    const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; }
116
117private:
118    static void CheckBounds(skiatest::Reporter* r, const GrShape& shape, const SkRect& bounds) {
119        SkPath path;
120        shape.asPath(&path);
121        // If the bounds are empty, the path ought to be as well.
122        if (bounds.isEmpty()) {
123            REPORTER_ASSERT(r, path.isEmpty());
124            return;
125        }
126        if (path.isEmpty()) {
127            return;
128        }
129        // The bounds API explicitly calls out that it does not consider inverseness.
130        SkPath p = path;
131        p.setFillType(SkPath::ConvertToNonInverseFillType(path.getFillType()));
132        REPORTER_ASSERT(r, test_bounds_by_rasterizing(p, bounds));
133    }
134
135    void init(skiatest::Reporter* r, SkScalar scale) {
136        fAppliedPE           = fBase.applyStyle(GrStyle::Apply::kPathEffectOnly, scale);
137        fAppliedPEThenStroke = fAppliedPE.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec,
138                                                     scale);
139        fAppliedFull         = fBase.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale);
140
141        make_key(&fBaseKey, fBase);
142        make_key(&fAppliedPEKey, fAppliedPE);
143        make_key(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
144        make_key(&fAppliedFullKey, fAppliedFull);
145
146        // Applying the path effect and then the stroke should always be the same as applying
147        // both in one go.
148        REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey);
149        SkPath a, b;
150        fAppliedPEThenStroke.asPath(&a);
151        fAppliedFull.asPath(&b);
152        // If the output of the path effect is a rrect then it is possible for a and b to be
153        // different paths that fill identically. The reason is that fAppliedFull will do this:
154        // base -> apply path effect -> rrect_as_path -> stroke -> stroked_rrect_as_path
155        // fAppliedPEThenStroke will have converted the rrect_as_path back to a rrect. However,
156        // now that there is no longer a path effect, the direction and starting index get
157        // canonicalized before the stroke.
158        if (fAppliedPE.asRRect(nullptr, nullptr, nullptr, nullptr)) {
159            REPORTER_ASSERT(r, paths_fill_same(a, b));
160        } else {
161            REPORTER_ASSERT(r, a == b);
162        }
163        REPORTER_ASSERT(r, fAppliedFull.isEmpty() == fAppliedPEThenStroke.isEmpty());
164
165        SkPath path;
166        fBase.asPath(&path);
167        REPORTER_ASSERT(r, path.isEmpty() == fBase.isEmpty());
168        REPORTER_ASSERT(r, path.getSegmentMasks() == fBase.segmentMask());
169        fAppliedPE.asPath(&path);
170        REPORTER_ASSERT(r, path.isEmpty() == fAppliedPE.isEmpty());
171        REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedPE.segmentMask());
172        fAppliedFull.asPath(&path);
173        REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull.isEmpty());
174        REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedFull.segmentMask());
175
176        CheckBounds(r, fBase, fBase.bounds());
177        CheckBounds(r, fAppliedPE, fAppliedPE.bounds());
178        CheckBounds(r, fAppliedPEThenStroke, fAppliedPEThenStroke.bounds());
179        CheckBounds(r, fAppliedFull, fAppliedFull.bounds());
180        SkRect styledBounds;
181        fBase.styledBounds(&styledBounds);
182        CheckBounds(r, fAppliedFull, styledBounds);
183        fAppliedPE.styledBounds(&styledBounds);
184        CheckBounds(r, fAppliedFull, styledBounds);
185
186        // Check that the same path is produced when style is applied by GrShape and GrStyle.
187        SkPath preStyle;
188        SkPath postPathEffect;
189        SkPath postAllStyle;
190
191        fBase.asPath(&preStyle);
192        SkStrokeRec postPEStrokeRec(SkStrokeRec::kFill_InitStyle);
193        if (fBase.style().applyPathEffectToPath(&postPathEffect, &postPEStrokeRec, preStyle,
194                                                scale)) {
195            // run postPathEffect through GrShape to get any geometry reductions that would have
196            // occurred to fAppliedPE.
197            GrShape(postPathEffect, GrStyle(postPEStrokeRec, nullptr)).asPath(&postPathEffect);
198
199            SkPath testPath;
200            fAppliedPE.asPath(&testPath);
201            REPORTER_ASSERT(r, testPath == postPathEffect);
202            REPORTER_ASSERT(r, postPEStrokeRec.hasEqualEffect(fAppliedPE.style().strokeRec()));
203        }
204        SkStrokeRec::InitStyle fillOrHairline;
205        if (fBase.style().applyToPath(&postAllStyle, &fillOrHairline, preStyle, scale)) {
206            SkPath testPath;
207            fAppliedFull.asPath(&testPath);
208            if (fBase.style().hasPathEffect()) {
209                // Because GrShape always does two-stage application when there is a path effect
210                // there may be a reduction/canonicalization step between the path effect and
211                // strokerec not reflected in postAllStyle since it applied both the path effect
212                // and strokerec without analyzing the intermediate path.
213                REPORTER_ASSERT(r, paths_fill_same(postAllStyle, testPath));
214            } else {
215                // Make sure that postAllStyle sees any reductions/canonicalizations that GrShape
216                // would apply.
217                GrShape(postAllStyle, GrStyle(fillOrHairline)).asPath(&postAllStyle);
218                REPORTER_ASSERT(r, testPath == postAllStyle);
219            }
220
221            if (fillOrHairline == SkStrokeRec::kFill_InitStyle) {
222                REPORTER_ASSERT(r, fAppliedFull.style().isSimpleFill());
223            } else {
224                REPORTER_ASSERT(r, fAppliedFull.style().isSimpleHairline());
225            }
226        }
227    }
228
229    GrShape fBase;
230    GrShape fAppliedPE;
231    GrShape fAppliedPEThenStroke;
232    GrShape fAppliedFull;
233
234    Key fBaseKey;
235    Key fAppliedPEKey;
236    Key fAppliedPEThenStrokeKey;
237    Key fAppliedFullKey;
238};
239
240void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const {
241    // The base's key should always be valid (unless the path is volatile)
242    REPORTER_ASSERT(reporter, fBaseKey.count());
243    if (expectations.fPEHasEffect) {
244        REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey);
245        REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedPEKey.count()));
246        REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
247        REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedFullKey.count()));
248        if (expectations.fStrokeApplies && expectations.fPEHasValidKey) {
249            REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey);
250            REPORTER_ASSERT(reporter, SkToBool(fAppliedFullKey.count()));
251        }
252    } else {
253        REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey);
254        SkPath a, b;
255        fBase.asPath(&a);
256        fAppliedPE.asPath(&b);
257        REPORTER_ASSERT(reporter, a == b);
258        if (expectations.fStrokeApplies) {
259            REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
260        } else {
261            REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey);
262        }
263    }
264}
265
266static bool can_interchange_winding_and_even_odd_fill(const GrShape& shape) {
267    SkPath path;
268    shape.asPath(&path);
269    if (shape.style().hasNonDashPathEffect()) {
270        return false;
271    }
272    const SkStrokeRec::Style strokeRecStyle = shape.style().strokeRec().getStyle();
273    return strokeRecStyle == SkStrokeRec::kStroke_Style ||
274           strokeRecStyle == SkStrokeRec::kHairline_Style ||
275           (shape.style().isSimpleFill() && path.isConvex());
276}
277
278static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrShape& b,
279                              const Key& keyA, const Key& keyB) {
280    // GrShape only respects the input winding direction and start point for rrect shapes
281    // when there is a path effect. Thus, if there are two GrShapes representing the same rrect
282    // but one has a path effect in its style and the other doesn't then asPath() and the unstyled
283    // key will differ. GrShape will have canonicalized the direction and start point for the shape
284    // without the path effect. If *both* have path effects then they should have both preserved
285    // the direction and starting point.
286
287    // The asRRect() output params are all initialized just to silence compiler warnings about
288    // uninitialized variables.
289    SkRRect rrectA = SkRRect::MakeEmpty(), rrectB = SkRRect::MakeEmpty();
290    SkPath::Direction dirA = SkPath::kCW_Direction, dirB = SkPath::kCW_Direction;
291    unsigned startA = ~0U, startB = ~0U;
292    bool invertedA = true, invertedB = true;
293
294    bool aIsRRect = a.asRRect(&rrectA, &dirA, &startA, &invertedA);
295    bool bIsRRect = b.asRRect(&rrectB, &dirB, &startB, &invertedB);
296    bool aHasPE = a.style().hasPathEffect();
297    bool bHasPE = b.style().hasPathEffect();
298    bool allowSameRRectButDiffStartAndDir = (aIsRRect && bIsRRect) && (aHasPE != bHasPE);
299
300    SkPath pathA, pathB;
301    a.asPath(&pathA);
302    b.asPath(&pathB);
303
304    // Having a dash path effect can allow 'a' but not 'b' to turn a inverse fill type into a
305    // non-inverse fill type  (or vice versa).
306    bool ignoreInversenessDifference = false;
307    if (pathA.isInverseFillType() != pathB.isInverseFillType()) {
308        const GrShape* s1 = pathA.isInverseFillType() ? &a : &b;
309        const GrShape* s2 = pathA.isInverseFillType() ? &b : &a;
310        bool canDropInverse1 = s1->style().isDashed();
311        bool canDropInverse2 = s2->style().isDashed();
312        ignoreInversenessDifference = (canDropInverse1 != canDropInverse2);
313    }
314    bool ignoreWindingVsEvenOdd = false;
315    if (SkPath::ConvertToNonInverseFillType(pathA.getFillType()) !=
316        SkPath::ConvertToNonInverseFillType(pathB.getFillType())) {
317        bool aCanChange = can_interchange_winding_and_even_odd_fill(a);
318        bool bCanChange = can_interchange_winding_and_even_odd_fill(b);
319        if (aCanChange != bCanChange) {
320            ignoreWindingVsEvenOdd = true;
321        }
322    }
323    if (allowSameRRectButDiffStartAndDir) {
324        REPORTER_ASSERT(r, rrectA == rrectB);
325        REPORTER_ASSERT(r, paths_fill_same(pathA, pathB));
326        REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
327    } else {
328        SkPath pA = pathA;
329        SkPath pB = pathB;
330        if (ignoreInversenessDifference) {
331            pA.setFillType(SkPath::ConvertToNonInverseFillType(pathA.getFillType()));
332            pB.setFillType(SkPath::ConvertToNonInverseFillType(pathB.getFillType()));
333        }
334        if (ignoreWindingVsEvenOdd) {
335            pA.setFillType(pA.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
336                                                  : SkPath::kEvenOdd_FillType);
337            pB.setFillType(pB.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
338                                                  : SkPath::kEvenOdd_FillType);
339        }
340        if (!ignoreInversenessDifference && !ignoreWindingVsEvenOdd) {
341            REPORTER_ASSERT(r, keyA == keyB);
342        } else {
343            REPORTER_ASSERT(r, keyA != keyB);
344        }
345        if (a.style().isSimpleFill() != b.style().isSimpleFill()) {
346            // GrShape will close paths with simple fill style. Make the non-filled path closed
347            // so that the comparision will succeed. Make sure both are closed before comparing.
348            pA.close();
349            pB.close();
350        }
351        REPORTER_ASSERT(r, pA == pB);
352        REPORTER_ASSERT(r, aIsRRect == bIsRRect);
353        if (aIsRRect) {
354            REPORTER_ASSERT(r, rrectA == rrectB);
355            REPORTER_ASSERT(r, dirA == dirB);
356            REPORTER_ASSERT(r, startA == startB);
357            REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
358        }
359    }
360    REPORTER_ASSERT(r, a.isEmpty() == b.isEmpty());
361    REPORTER_ASSERT(r, a.knownToBeClosed() == b.knownToBeClosed());
362    REPORTER_ASSERT(r, a.bounds() == b.bounds());
363    REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask());
364    SkPoint pts[4];
365    REPORTER_ASSERT(r, a.asLine(pts) == b.asLine(pts + 2));
366    if (a.asLine(pts)) {
367        REPORTER_ASSERT(r, pts[2] == pts[0] && pts[3] == pts[1]);
368    }
369}
370
371void TestCase::compare(skiatest::Reporter* r, const TestCase& that,
372                       ComparisonExpecation expectation) const {
373    SkPath a, b;
374    switch (expectation) {
375        case kAllDifferent_ComparisonExpecation:
376            REPORTER_ASSERT(r, fBaseKey != that.fBaseKey);
377            REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
378            REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
379            break;
380        case kSameUpToPE_ComparisonExpecation:
381            check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
382            REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
383            REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
384            break;
385        case kSameUpToStroke_ComparisonExpecation:
386            check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
387            check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
388            REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
389            break;
390        case kAllSame_ComparisonExpecation:
391            check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
392            check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
393            check_equivalence(r, fAppliedFull, that.fAppliedFull, fAppliedFullKey,
394                              that.fAppliedFullKey);
395            break;
396    }
397}
398}  // namespace
399
400static sk_sp<SkPathEffect> make_dash() {
401    static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f };
402    static const SkScalar kPhase = 0.75;
403    return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase);
404}
405
406static sk_sp<SkPathEffect> make_null_dash() {
407    static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0};
408    return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f);
409}
410
411template<typename GEO>
412static void test_basic(skiatest::Reporter* reporter, const GEO& geo) {
413    sk_sp<SkPathEffect> dashPE = make_dash();
414
415    TestCase::SelfExpectations expectations;
416    SkPaint fill;
417
418    TestCase fillCase(geo, fill, reporter);
419    expectations.fPEHasEffect = false;
420    expectations.fPEHasValidKey = false;
421    expectations.fStrokeApplies = false;
422    fillCase.testExpectations(reporter, expectations);
423    // Test that another GrShape instance built from the same primitive is the same.
424    TestCase(geo, fill, reporter).compare(reporter, fillCase,
425                                          TestCase::kAllSame_ComparisonExpecation);
426
427    SkPaint stroke2RoundBevel;
428    stroke2RoundBevel.setStyle(SkPaint::kStroke_Style);
429    stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap);
430    stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join);
431    stroke2RoundBevel.setStrokeWidth(2.f);
432    TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter);
433    expectations.fPEHasValidKey = true;
434    expectations.fPEHasEffect = false;
435    expectations.fStrokeApplies = true;
436    stroke2RoundBevelCase.testExpectations(reporter, expectations);
437    TestCase(geo, stroke2RoundBevel, reporter).compare(reporter, stroke2RoundBevelCase,
438                                                       TestCase::kAllSame_ComparisonExpecation);
439
440    SkPaint stroke2RoundBevelDash = stroke2RoundBevel;
441    stroke2RoundBevelDash.setPathEffect(make_dash());
442    TestCase stroke2RoundBevelDashCase(geo, stroke2RoundBevelDash, reporter);
443    expectations.fPEHasValidKey = true;
444    expectations.fPEHasEffect = true;
445    expectations.fStrokeApplies = true;
446    stroke2RoundBevelDashCase.testExpectations(reporter, expectations);
447    TestCase(geo, stroke2RoundBevelDash, reporter).compare(reporter, stroke2RoundBevelDashCase,
448                                                           TestCase::kAllSame_ComparisonExpecation);
449
450    fillCase.compare(reporter, stroke2RoundBevelCase,
451                     TestCase::kSameUpToStroke_ComparisonExpecation);
452    fillCase.compare(reporter, stroke2RoundBevelDashCase,
453                     TestCase::kSameUpToPE_ComparisonExpecation);
454    stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
455                                  TestCase::kSameUpToPE_ComparisonExpecation);
456
457    // Stroke and fill cases
458    SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel;
459    stroke2RoundBevelAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
460    TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter);
461    expectations.fPEHasValidKey = true;
462    expectations.fPEHasEffect = false;
463    expectations.fStrokeApplies = true;
464    stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations);
465    TestCase(geo, stroke2RoundBevelAndFill, reporter).compare(reporter,
466            stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation);
467
468    SkPaint stroke2RoundBevelAndFillDash = stroke2RoundBevelDash;
469    stroke2RoundBevelAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
470    TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter);
471    expectations.fPEHasValidKey = true;
472    expectations.fPEHasEffect = false;
473    expectations.fStrokeApplies = true;
474    stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations);
475    TestCase(geo, stroke2RoundBevelAndFillDash, reporter).compare(
476        reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation);
477    stroke2RoundBevelAndFillDashCase.compare(reporter, stroke2RoundBevelAndFillCase,
478                                             TestCase::kAllSame_ComparisonExpecation);
479
480    SkPaint hairline;
481    hairline.setStyle(SkPaint::kStroke_Style);
482    hairline.setStrokeWidth(0.f);
483    TestCase hairlineCase(geo, hairline, reporter);
484    // Since hairline style doesn't change the SkPath data, it is keyed identically to fill.
485    hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
486    REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline());
487    REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline());
488    REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline());
489}
490
491template<typename GEO>
492static void test_scale(skiatest::Reporter* reporter, const GEO& geo) {
493    sk_sp<SkPathEffect> dashPE = make_dash();
494
495    static const SkScalar kS1 = 1.f;
496    static const SkScalar kS2 = 2.f;
497
498    SkPaint fill;
499    TestCase fillCase1(geo, fill, reporter, kS1);
500    TestCase fillCase2(geo, fill, reporter, kS2);
501    // Scale doesn't affect fills.
502    fillCase1.compare(reporter, fillCase2, TestCase::kAllSame_ComparisonExpecation);
503
504    SkPaint hairline;
505    hairline.setStyle(SkPaint::kStroke_Style);
506    hairline.setStrokeWidth(0.f);
507    TestCase hairlineCase1(geo, hairline, reporter, kS1);
508    TestCase hairlineCase2(geo, hairline, reporter, kS2);
509    // Scale doesn't affect hairlines.
510    hairlineCase1.compare(reporter, hairlineCase2, TestCase::kAllSame_ComparisonExpecation);
511
512    SkPaint stroke;
513    stroke.setStyle(SkPaint::kStroke_Style);
514    stroke.setStrokeWidth(2.f);
515    TestCase strokeCase1(geo, stroke, reporter, kS1);
516    TestCase strokeCase2(geo, stroke, reporter, kS2);
517    // Scale affects the stroke.
518    strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation);
519
520    SkPaint strokeDash = stroke;
521    strokeDash.setPathEffect(make_dash());
522    TestCase strokeDashCase1(geo, strokeDash, reporter, kS1);
523    TestCase strokeDashCase2(geo, strokeDash, reporter, kS2);
524    // Scale affects the dash and the stroke.
525    strokeDashCase1.compare(reporter, strokeDashCase2,  TestCase::kSameUpToPE_ComparisonExpecation);
526
527    // Stroke and fill cases
528    SkPaint strokeAndFill = stroke;
529    strokeAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
530    TestCase strokeAndFillCase1(geo, strokeAndFill, reporter, kS1);
531    TestCase strokeAndFillCase2(geo, strokeAndFill, reporter, kS2);
532    SkPaint strokeAndFillDash = strokeDash;
533    strokeAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
534    // Dash is ignored for stroke and fill
535    TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1);
536    TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2);
537    // Scale affects the stroke. Though, this can wind up creating a rect when the input is a rect.
538    // In that case we wind up with a pure geometry key and the geometries are the same.
539    SkRRect rrect;
540    if (strokeAndFillCase1.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr, nullptr)) {
541        // We currently only expect to get here in the rect->rect case.
542        REPORTER_ASSERT(reporter, rrect.isRect());
543        REPORTER_ASSERT(reporter,
544                        strokeAndFillCase1.baseShape().asRRect(&rrect, nullptr, nullptr, nullptr) &&
545                        rrect.isRect());
546        strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
547                                   TestCase::kAllSame_ComparisonExpecation);
548    } else {
549        strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
550                                   TestCase::kSameUpToStroke_ComparisonExpecation);
551        strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2,
552                                       TestCase::kSameUpToStroke_ComparisonExpecation);
553    }
554    strokeAndFillDashCase1.compare(reporter, strokeAndFillCase1,
555                                   TestCase::kAllSame_ComparisonExpecation);
556    strokeAndFillDashCase2.compare(reporter, strokeAndFillCase2,
557                                   TestCase::kAllSame_ComparisonExpecation);
558}
559
560template <typename GEO, typename T>
561static void test_stroke_param_impl(skiatest::Reporter* reporter, const GEO& geo,
562                                   std::function<void(SkPaint*, T)> setter, T a, T b,
563                                   bool paramAffectsStroke,
564                                   bool paramAffectsDashAndStroke) {
565    // Set the stroke width so that we don't get hairline. However, call the setter afterward so
566    // that it can override the stroke width.
567    SkPaint strokeA;
568    strokeA.setStyle(SkPaint::kStroke_Style);
569    strokeA.setStrokeWidth(2.f);
570    setter(&strokeA, a);
571    SkPaint strokeB;
572    strokeB.setStyle(SkPaint::kStroke_Style);
573    strokeB.setStrokeWidth(2.f);
574    setter(&strokeB, b);
575
576    TestCase strokeACase(geo, strokeA, reporter);
577    TestCase strokeBCase(geo, strokeB, reporter);
578    if (paramAffectsStroke) {
579        strokeACase.compare(reporter, strokeBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
580    } else {
581        strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation);
582    }
583
584    SkPaint strokeAndFillA = strokeA;
585    SkPaint strokeAndFillB = strokeB;
586    strokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style);
587    strokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style);
588    TestCase strokeAndFillACase(geo, strokeAndFillA, reporter);
589    TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter);
590    if (paramAffectsStroke) {
591        strokeAndFillACase.compare(reporter, strokeAndFillBCase,
592                                   TestCase::kSameUpToStroke_ComparisonExpecation);
593    } else {
594        strokeAndFillACase.compare(reporter, strokeAndFillBCase,
595                                   TestCase::kAllSame_ComparisonExpecation);
596    }
597
598    // Make sure stroking params don't affect fill style.
599    SkPaint fillA = strokeA, fillB = strokeB;
600    fillA.setStyle(SkPaint::kFill_Style);
601    fillB.setStyle(SkPaint::kFill_Style);
602    TestCase fillACase(geo, fillA, reporter);
603    TestCase fillBCase(geo, fillB, reporter);
604    fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
605
606    // Make sure just applying the dash but not stroke gives the same key for both stroking
607    // variations.
608    SkPaint dashA = strokeA, dashB = strokeB;
609    dashA.setPathEffect(make_dash());
610    dashB.setPathEffect(make_dash());
611    TestCase dashACase(geo, dashA, reporter);
612    TestCase dashBCase(geo, dashB, reporter);
613    if (paramAffectsDashAndStroke) {
614        dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
615    } else {
616        dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation);
617    }
618}
619
620template <typename GEO, typename T>
621static void test_stroke_param(skiatest::Reporter* reporter, const GEO& geo,
622                              std::function<void(SkPaint*, T)> setter, T a, T b) {
623    test_stroke_param_impl(reporter, geo, setter, a, b, true, true);
624};
625
626template <typename GEO>
627static void test_stroke_cap(skiatest::Reporter* reporter, const GEO& geo) {
628    GrShape shape(geo, GrStyle(SkStrokeRec::kHairline_InitStyle));
629    // The cap should only affect shapes that may be open.
630    bool affectsStroke = !shape.knownToBeClosed();
631    // Dashing adds ends that need caps.
632    bool affectsDashAndStroke = true;
633    test_stroke_param_impl<GEO, SkPaint::Cap>(
634        reporter,
635        geo,
636        [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
637        SkPaint::kButt_Cap, SkPaint::kRound_Cap,
638        affectsStroke,
639        affectsDashAndStroke);
640};
641
642template <typename GEO>
643static void test_miter_limit(skiatest::Reporter* reporter, const GEO& geo) {
644    auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) {
645        p->setStrokeJoin(SkPaint::kMiter_Join);
646        p->setStrokeMiter(miter);
647    };
648
649    auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) {
650        p->setStrokeJoin(SkPaint::kRound_Join);
651        p->setStrokeMiter(miter);
652    };
653
654    // The miter limit should affect stroked and dashed-stroked cases when the join type is
655    // miter.
656    test_stroke_param_impl<GEO, SkScalar>(
657        reporter,
658        geo,
659        setMiterJoinAndLimit,
660        0.5f, 0.75f,
661        true,
662        true);
663
664    // The miter limit should not affect stroked and dashed-stroked cases when the join type is
665    // not miter.
666    test_stroke_param_impl<GEO, SkScalar>(
667        reporter,
668        geo,
669        setOtherJoinAndLimit,
670        0.5f, 0.75f,
671        false,
672        false);
673}
674
675template<typename GEO>
676static void test_dash_fill(skiatest::Reporter* reporter, const GEO& geo) {
677    // A dash with no stroke should have no effect
678    using DashFactoryFn = sk_sp<SkPathEffect>(*)();
679    for (DashFactoryFn md : {&make_dash, &make_null_dash}) {
680        SkPaint dashFill;
681        dashFill.setPathEffect((*md)());
682        TestCase dashFillCase(geo, dashFill, reporter);
683
684        TestCase fillCase(geo, SkPaint(), reporter);
685        dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
686    }
687}
688
689template<typename GEO>
690void test_null_dash(skiatest::Reporter* reporter, const GEO& geo) {
691    SkPaint fill;
692    SkPaint stroke;
693    stroke.setStyle(SkPaint::kStroke_Style);
694    stroke.setStrokeWidth(1.f);
695    SkPaint dash;
696    dash.setStyle(SkPaint::kStroke_Style);
697    dash.setStrokeWidth(1.f);
698    dash.setPathEffect(make_dash());
699    SkPaint nullDash;
700    nullDash.setStyle(SkPaint::kStroke_Style);
701    nullDash.setStrokeWidth(1.f);
702    nullDash.setPathEffect(make_null_dash());
703
704    TestCase fillCase(geo, fill, reporter);
705    TestCase strokeCase(geo, stroke, reporter);
706    TestCase dashCase(geo, dash, reporter);
707    TestCase nullDashCase(geo, nullDash, reporter);
708
709    nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation);
710    nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation);
711    nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation);
712}
713
714template <typename GEO>
715void test_path_effect_makes_rrect(skiatest::Reporter* reporter, const GEO& geo) {
716    /**
717     * This path effect takes any input path and turns it into a rrect. It passes through stroke
718     * info.
719     */
720    class RRectPathEffect : SkPathEffect {
721    public:
722        static const SkRRect& RRect() {
723            static const SkRRect kRRect = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 3, 5);
724            return kRRect;
725        }
726
727        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
728                        const SkRect* cullR) const override {
729            dst->reset();
730            dst->addRRect(RRect());
731            return true;
732        }
733        void computeFastBounds(SkRect* dst, const SkRect& src) const override {
734            *dst = RRect().getBounds();
735        }
736        static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new RRectPathEffect); }
737        Factory getFactory() const override { return nullptr; }
738        void toString(SkString*) const override {}
739    private:
740        RRectPathEffect() {}
741    };
742
743    SkPaint fill;
744    TestCase fillGeoCase(geo, fill, reporter);
745
746    SkPaint pe;
747    pe.setPathEffect(RRectPathEffect::Make());
748    TestCase geoPECase(geo, pe, reporter);
749
750    SkPaint peStroke;
751    peStroke.setPathEffect(RRectPathEffect::Make());
752    peStroke.setStrokeWidth(2.f);
753    peStroke.setStyle(SkPaint::kStroke_Style);
754    TestCase geoPEStrokeCase(geo, peStroke, reporter);
755
756    fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation);
757    fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation);
758    geoPECase.compare(reporter, geoPEStrokeCase,
759                      TestCase::kSameUpToStroke_ComparisonExpecation);
760
761    TestCase rrectFillCase(RRectPathEffect::RRect(), fill, reporter);
762    SkPaint stroke = peStroke;
763    stroke.setPathEffect(nullptr);
764    TestCase rrectStrokeCase(RRectPathEffect::RRect(), stroke, reporter);
765
766    SkRRect rrect;
767    // Applying the path effect should make a SkRRect shape. There is no further stroking in the
768    // geoPECase, so the full style should be the same as just the PE.
769    REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().asRRect(&rrect, nullptr, nullptr,
770                                                                         nullptr));
771    REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
772    REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == rrectFillCase.baseKey());
773
774    REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr,
775                                                                        nullptr));
776    REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
777    REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == rrectFillCase.baseKey());
778
779    // In the PE+stroke case applying the full style should be the same as just stroking the rrect.
780    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().asRRect(&rrect, nullptr,
781                                                                               nullptr, nullptr));
782    REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
783    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == rrectFillCase.baseKey());
784
785    REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().asRRect(&rrect, nullptr,
786                                                                               nullptr, nullptr));
787    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() ==
788                              rrectStrokeCase.appliedFullStyleKey());
789}
790
791template <typename GEO>
792void test_unknown_path_effect(skiatest::Reporter* reporter, const GEO& geo) {
793    /**
794     * This path effect just adds two lineTos to the input path.
795     */
796    class AddLineTosPathEffect : SkPathEffect {
797    public:
798        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
799                        const SkRect* cullR) const override {
800            *dst = src;
801            dst->lineTo(0, 0);
802            dst->lineTo(10, 10);
803            return true;
804        }
805        void computeFastBounds(SkRect* dst, const SkRect& src) const override {
806            *dst = src;
807            dst->growToInclude(0, 0);
808            dst->growToInclude(10, 10);
809        }
810        static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new AddLineTosPathEffect); }
811        Factory getFactory() const override { return nullptr; }
812        void toString(SkString*) const override {}
813    private:
814        AddLineTosPathEffect() {}
815    };
816
817     // This path effect should make the keys invalid when it is applied. We only produce a path
818     // effect key for dash path effects. So the only way another arbitrary path effect can produce
819     // a styled result with a key is to produce a non-path shape that has a purely geometric key.
820    SkPaint peStroke;
821    peStroke.setPathEffect(AddLineTosPathEffect::Make());
822    peStroke.setStrokeWidth(2.f);
823    peStroke.setStyle(SkPaint::kStroke_Style);
824    TestCase geoPEStrokeCase(geo, peStroke, reporter);
825    TestCase::SelfExpectations expectations;
826    expectations.fPEHasEffect = true;
827    expectations.fPEHasValidKey = false;
828    expectations.fStrokeApplies = true;
829    geoPEStrokeCase.testExpectations(reporter, expectations);
830}
831
832template <typename GEO>
833void test_make_hairline_path_effect(skiatest::Reporter* reporter, const GEO& geo, bool isNonPath) {
834    /**
835     * This path effect just changes the stroke rec to hairline.
836     */
837    class MakeHairlinePathEffect : SkPathEffect {
838    public:
839        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* strokeRec,
840                        const SkRect* cullR) const override {
841            *dst = src;
842            strokeRec->setHairlineStyle();
843            return true;
844        }
845        void computeFastBounds(SkRect* dst, const SkRect& src) const override { *dst = src; }
846        static sk_sp<SkPathEffect> Make() {
847            return sk_sp<SkPathEffect>(new MakeHairlinePathEffect);
848        }
849        Factory getFactory() const override { return nullptr; }
850        void toString(SkString*) const override {}
851    private:
852        MakeHairlinePathEffect() {}
853    };
854
855    SkPaint fill;
856    SkPaint pe;
857    pe.setPathEffect(MakeHairlinePathEffect::Make());
858
859    TestCase peCase(geo, pe, reporter);
860
861    SkPath a, b, c;
862    peCase.baseShape().asPath(&a);
863    peCase.appliedPathEffectShape().asPath(&b);
864    peCase.appliedFullStyleShape().asPath(&c);
865    if (isNonPath) {
866        // RRect types can have a change in start index or direction after the PE is applied. This
867        // is because once the PE is applied, GrShape may canonicalize the dir and index since it
868        // is not germane to the styling any longer.
869        // Instead we just check that the paths would fill the same both before and after styling.
870        REPORTER_ASSERT(reporter, paths_fill_same(a, b));
871        REPORTER_ASSERT(reporter, paths_fill_same(a, c));
872    } else {
873        // The base shape cannot perform canonicalization on the path's fill type because of an
874        // unknown path effect. However, after the path effect is applied the resulting hairline
875        // shape will canonicalize the path fill type since hairlines (and stroking in general)
876        // don't distinguish between even/odd and non-zero winding.
877        a.setFillType(b.getFillType());
878        REPORTER_ASSERT(reporter, a == b);
879        REPORTER_ASSERT(reporter, a == c);
880        REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey().empty());
881        REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey().empty());
882    }
883    REPORTER_ASSERT(reporter, peCase.appliedPathEffectShape().style().isSimpleHairline());
884    REPORTER_ASSERT(reporter, peCase.appliedFullStyleShape().style().isSimpleHairline());
885}
886
887/**
888 * isNonPath indicates whether the initial shape made from the path is expected to be recognized
889 * as a simpler shape type (e.g. rrect)
890 */
891void test_volatile_path(skiatest::Reporter* reporter, const SkPath& path,
892                        bool isNonPath) {
893    SkPath vPath(path);
894    vPath.setIsVolatile(true);
895
896    SkPaint dashAndStroke;
897    dashAndStroke.setPathEffect(make_dash());
898    dashAndStroke.setStrokeWidth(2.f);
899    dashAndStroke.setStyle(SkPaint::kStroke_Style);
900    TestCase volatileCase(vPath, dashAndStroke, reporter);
901    // We expect a shape made from a volatile path to have a key iff the shape is recognized
902    // as a specialized geometry.
903    if (isNonPath) {
904        REPORTER_ASSERT(reporter, SkToBool(volatileCase.baseKey().count()));
905        // In this case all the keys should be identical to the non-volatile case.
906        TestCase nonVolatileCase(path, dashAndStroke, reporter);
907        volatileCase.compare(reporter, nonVolatileCase, TestCase::kAllSame_ComparisonExpecation);
908    } else {
909        // None of the keys should be valid.
910        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.baseKey().count()));
911        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectKey().count()));
912        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedFullStyleKey().count()));
913        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectThenStrokeKey().count()));
914    }
915}
916
917template <typename GEO>
918void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const GEO& geo) {
919    /**
920     * This path effect returns an empty path.
921     */
922    class EmptyPathEffect : SkPathEffect {
923    public:
924        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
925                        const SkRect* cullR) const override {
926            dst->reset();
927            return true;
928        }
929        void computeFastBounds(SkRect* dst, const SkRect& src) const override {
930            dst->setEmpty();
931        }
932        static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new EmptyPathEffect); }
933        Factory getFactory() const override { return nullptr; }
934        void toString(SkString*) const override {}
935    private:
936        EmptyPathEffect() {}
937    };
938
939    SkPath emptyPath;
940    GrShape emptyShape(emptyPath);
941    Key emptyKey;
942    make_key(&emptyKey, emptyShape);
943    REPORTER_ASSERT(reporter, emptyShape.isEmpty());
944
945    SkPaint pe;
946    pe.setPathEffect(EmptyPathEffect::Make());
947    TestCase geoCase(geo, pe, reporter);
948    REPORTER_ASSERT(reporter, geoCase.appliedFullStyleKey() == emptyKey);
949    REPORTER_ASSERT(reporter, geoCase.appliedPathEffectKey() == emptyKey);
950    REPORTER_ASSERT(reporter, geoCase.appliedPathEffectThenStrokeKey() == emptyKey);
951    REPORTER_ASSERT(reporter, geoCase.appliedPathEffectShape().isEmpty());
952    REPORTER_ASSERT(reporter, geoCase.appliedFullStyleShape().isEmpty());
953
954    SkPaint peStroke;
955    peStroke.setPathEffect(EmptyPathEffect::Make());
956    peStroke.setStrokeWidth(2.f);
957    peStroke.setStyle(SkPaint::kStroke_Style);
958    TestCase geoPEStrokeCase(geo, peStroke, reporter);
959    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == emptyKey);
960    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == emptyKey);
961    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey);
962    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty());
963    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty());
964}
965
966template <typename GEO>
967void test_path_effect_fails(skiatest::Reporter* reporter, const GEO& geo) {
968    /**
969     * This path effect returns an empty path.
970     */
971    class FailurePathEffect : SkPathEffect {
972    public:
973        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
974                        const SkRect* cullR) const override {
975            return false;
976        }
977        void computeFastBounds(SkRect* dst, const SkRect& src) const override {
978            *dst = src;
979        }
980        static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new FailurePathEffect); }
981        Factory getFactory() const override { return nullptr; }
982        void toString(SkString*) const override {}
983    private:
984        FailurePathEffect() {}
985    };
986
987    SkPaint fill;
988    TestCase fillCase(geo, fill, reporter);
989
990    SkPaint pe;
991    pe.setPathEffect(FailurePathEffect::Make());
992    TestCase peCase(geo, pe, reporter);
993
994    SkPaint stroke;
995    stroke.setStrokeWidth(2.f);
996    stroke.setStyle(SkPaint::kStroke_Style);
997    TestCase strokeCase(geo, stroke, reporter);
998
999    SkPaint peStroke = stroke;
1000    peStroke.setPathEffect(FailurePathEffect::Make());
1001    TestCase peStrokeCase(geo, peStroke, reporter);
1002
1003    // In general the path effect failure can cause some of the TestCase::compare() tests to fail
1004    // for at least two reasons: 1) We will initially treat the shape as unkeyable because of the
1005    // path effect, but then when the path effect fails we can key it. 2) GrShape will change its
1006    // mind about whether a unclosed rect is actually rect. The path effect initially bars us from
1007    // closing it but after the effect fails we can (for the fill+pe case). This causes different
1008    // routes through GrShape to have equivalent but different representations of the path (closed
1009    // or not) but that fill the same.
1010    SkPath a;
1011    SkPath b;
1012    fillCase.appliedPathEffectShape().asPath(&a);
1013    peCase.appliedPathEffectShape().asPath(&b);
1014    REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1015
1016    fillCase.appliedFullStyleShape().asPath(&a);
1017    peCase.appliedFullStyleShape().asPath(&b);
1018    REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1019
1020    strokeCase.appliedPathEffectShape().asPath(&a);
1021    peStrokeCase.appliedPathEffectShape().asPath(&b);
1022    REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1023
1024    strokeCase.appliedFullStyleShape().asPath(&a);
1025    peStrokeCase.appliedFullStyleShape().asPath(&b);
1026    REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1027}
1028
1029void test_empty_shape(skiatest::Reporter* reporter) {
1030    SkPath emptyPath;
1031    SkPaint fill;
1032    TestCase fillEmptyCase(emptyPath, fill, reporter);
1033    REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty());
1034    REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty());
1035    REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty());
1036
1037    Key emptyKey(fillEmptyCase.baseKey());
1038    REPORTER_ASSERT(reporter, emptyKey.count());
1039    TestCase::SelfExpectations expectations;
1040    expectations.fStrokeApplies = false;
1041    expectations.fPEHasEffect = false;
1042    // This will test whether applying style preserves emptiness
1043    fillEmptyCase.testExpectations(reporter, expectations);
1044
1045    // Stroking an empty path should have no effect
1046    SkPath emptyPath2;
1047    SkPaint stroke;
1048    stroke.setStrokeWidth(2.f);
1049    stroke.setStyle(SkPaint::kStroke_Style);
1050    TestCase strokeEmptyCase(emptyPath2, stroke, reporter);
1051    strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
1052
1053    // Dashing and stroking an empty path should have no effect
1054    SkPath emptyPath3;
1055    SkPaint dashAndStroke;
1056    dashAndStroke.setPathEffect(make_dash());
1057    dashAndStroke.setStrokeWidth(2.f);
1058    dashAndStroke.setStyle(SkPaint::kStroke_Style);
1059    TestCase dashAndStrokeEmptyCase(emptyPath3, dashAndStroke, reporter);
1060    dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase,
1061                                   TestCase::kAllSame_ComparisonExpecation);
1062
1063    // A shape made from an empty rrect should behave the same as an empty path.
1064    SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty());
1065    REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type);
1066    TestCase dashAndStrokeEmptyRRectCase(emptyRRect, dashAndStroke, reporter);
1067    dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
1068                                        TestCase::kAllSame_ComparisonExpecation);
1069
1070    // Same for a rect.
1071    SkRect emptyRect = SkRect::MakeEmpty();
1072    TestCase dashAndStrokeEmptyRectCase(emptyRect, dashAndStroke, reporter);
1073    dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase,
1074                                       TestCase::kAllSame_ComparisonExpecation);
1075}
1076
1077// rect and oval types have rrect start indices that collapse to the same point. Here we select the
1078// canonical point in these cases.
1079unsigned canonicalize_rrect_start(int s, const SkRRect& rrect) {
1080    switch (rrect.getType()) {
1081        case SkRRect::kRect_Type:
1082            return (s + 1) & 0b110;
1083        case SkRRect::kOval_Type:
1084            return s & 0b110;
1085        default:
1086            return s;
1087    }
1088}
1089
1090void test_rrect(skiatest::Reporter* r, const SkRRect& rrect) {
1091    enum Style {
1092        kFill,
1093        kStroke,
1094        kHairline,
1095        kStrokeAndFill
1096    };
1097
1098    // SkStrokeRec has no default cons., so init with kFill before calling the setters below.
1099    SkStrokeRec strokeRecs[4] { SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle,
1100                                SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle};
1101    strokeRecs[kFill].setFillStyle();
1102    strokeRecs[kStroke].setStrokeStyle(2.f);
1103    strokeRecs[kHairline].setHairlineStyle();
1104    strokeRecs[kStrokeAndFill].setStrokeStyle(3.f, true);
1105    sk_sp<SkPathEffect> dashEffect = make_dash();
1106
1107    static constexpr Style kStyleCnt = static_cast<Style>(SK_ARRAY_COUNT(strokeRecs));
1108
1109    auto index = [](bool inverted,
1110                    SkPath::Direction dir,
1111                    unsigned start,
1112                    Style style,
1113                    bool dash) -> int {
1114        return inverted * (2 * 8 * kStyleCnt * 2) +
1115               dir      * (    8 * kStyleCnt * 2) +
1116               start    * (        kStyleCnt * 2) +
1117               style    * (                    2) +
1118               dash;
1119    };
1120    static const SkPath::Direction kSecondDirection = static_cast<SkPath::Direction>(1);
1121    const int cnt = index(true, kSecondDirection, 7, static_cast<Style>(kStyleCnt - 1), true) + 1;
1122    SkAutoTArray<GrShape> shapes(cnt);
1123    for (bool inverted : {false, true}) {
1124        for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
1125            for (unsigned start = 0; start < 8; ++start) {
1126                for (Style style : {kFill, kStroke, kHairline, kStrokeAndFill}) {
1127                    for (bool dash : {false, true}) {
1128                        SkPathEffect* pe = dash ? dashEffect.get() : nullptr;
1129                        shapes[index(inverted, dir, start, style, dash)] =
1130                                GrShape(rrect, dir, start, SkToBool(inverted),
1131                                        GrStyle(strokeRecs[style], pe));
1132                    }
1133                }
1134            }
1135        }
1136    }
1137
1138    // Get the keys for some example shape instances that we'll use for comparision against the
1139    // rest.
1140    static constexpr SkPath::Direction kExamplesDir = SkPath::kCW_Direction;
1141    static constexpr unsigned kExamplesStart = 0;
1142    const GrShape& exampleFillCase = shapes[index(false, kExamplesDir, kExamplesStart, kFill,
1143                                                  false)];
1144    Key exampleFillCaseKey;
1145    make_key(&exampleFillCaseKey, exampleFillCase);
1146
1147    const GrShape& exampleStrokeAndFillCase = shapes[index(false, kExamplesDir, kExamplesStart,
1148                                                           kStrokeAndFill, false)];
1149    Key exampleStrokeAndFillCaseKey;
1150    make_key(&exampleStrokeAndFillCaseKey, exampleStrokeAndFillCase);
1151
1152    const GrShape& exampleInvFillCase = shapes[index(true, kExamplesDir, kExamplesStart, kFill,
1153                                                     false)];
1154    Key exampleInvFillCaseKey;
1155    make_key(&exampleInvFillCaseKey, exampleInvFillCase);
1156
1157    const GrShape& exampleInvStrokeAndFillCase = shapes[index(true, kExamplesDir, kExamplesStart,
1158                                                              kStrokeAndFill, false)];
1159    Key exampleInvStrokeAndFillCaseKey;
1160    make_key(&exampleInvStrokeAndFillCaseKey, exampleInvStrokeAndFillCase);
1161
1162    const GrShape& exampleStrokeCase = shapes[index(false, kExamplesDir, kExamplesStart, kStroke,
1163                                                    false)];
1164    Key exampleStrokeCaseKey;
1165    make_key(&exampleStrokeCaseKey, exampleStrokeCase);
1166
1167    const GrShape& exampleInvStrokeCase = shapes[index(true, kExamplesDir, kExamplesStart, kStroke,
1168                                                       false)];
1169    Key exampleInvStrokeCaseKey;
1170    make_key(&exampleInvStrokeCaseKey, exampleInvStrokeCase);
1171
1172    const GrShape& exampleHairlineCase = shapes[index(false, kExamplesDir, kExamplesStart,
1173                                                      kHairline, false)];
1174    Key exampleHairlineCaseKey;
1175    make_key(&exampleHairlineCaseKey, exampleHairlineCase);
1176
1177    const GrShape& exampleInvHairlineCase = shapes[index(true, kExamplesDir, kExamplesStart,
1178                                                         kHairline, false)];
1179    Key exampleInvHairlineCaseKey;
1180    make_key(&exampleInvHairlineCaseKey, exampleInvHairlineCase);
1181
1182    // These are dummy initializations to suppress warnings.
1183    SkRRect queryRR = SkRRect::MakeEmpty();
1184    SkPath::Direction queryDir = SkPath::kCW_Direction;
1185    unsigned queryStart = ~0U;
1186    bool queryInverted = true;
1187
1188    REPORTER_ASSERT(r, exampleFillCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
1189    REPORTER_ASSERT(r, queryRR == rrect);
1190    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1191    REPORTER_ASSERT(r, 0 == queryStart);
1192    REPORTER_ASSERT(r, !queryInverted);
1193
1194    REPORTER_ASSERT(r, exampleInvFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1195                                                  &queryInverted));
1196    REPORTER_ASSERT(r, queryRR == rrect);
1197    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1198    REPORTER_ASSERT(r, 0 == queryStart);
1199    REPORTER_ASSERT(r, queryInverted);
1200
1201    REPORTER_ASSERT(r, exampleStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1202                                                        &queryInverted));
1203    REPORTER_ASSERT(r, queryRR == rrect);
1204    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1205    REPORTER_ASSERT(r, 0 == queryStart);
1206    REPORTER_ASSERT(r, !queryInverted);
1207
1208    REPORTER_ASSERT(r, exampleInvStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1209                                                           &queryInverted));
1210    REPORTER_ASSERT(r, queryRR == rrect);
1211    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1212    REPORTER_ASSERT(r, 0 == queryStart);
1213    REPORTER_ASSERT(r, queryInverted);
1214
1215    REPORTER_ASSERT(r, exampleHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1216                                                   &queryInverted));
1217    REPORTER_ASSERT(r, queryRR == rrect);
1218    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1219    REPORTER_ASSERT(r, 0 == queryStart);
1220    REPORTER_ASSERT(r, !queryInverted);
1221
1222    REPORTER_ASSERT(r, exampleInvHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1223                                                      &queryInverted));
1224    REPORTER_ASSERT(r, queryRR == rrect);
1225    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1226    REPORTER_ASSERT(r, 0 == queryStart);
1227    REPORTER_ASSERT(r, queryInverted);
1228
1229    REPORTER_ASSERT(r, exampleStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
1230    REPORTER_ASSERT(r, queryRR == rrect);
1231    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1232    REPORTER_ASSERT(r, 0 == queryStart);
1233    REPORTER_ASSERT(r, !queryInverted);
1234
1235    REPORTER_ASSERT(r, exampleInvStrokeCase.asRRect(&queryRR, &queryDir, &queryStart,
1236                                                    &queryInverted));
1237    REPORTER_ASSERT(r, queryRR == rrect);
1238    REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1239    REPORTER_ASSERT(r, 0 == queryStart);
1240    REPORTER_ASSERT(r, queryInverted);
1241
1242    // Remember that the key reflects the geometry before styling is applied.
1243    REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvFillCaseKey);
1244    REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeAndFillCaseKey);
1245    REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeAndFillCaseKey);
1246    REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeCaseKey);
1247    REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeCaseKey);
1248    REPORTER_ASSERT(r, exampleFillCaseKey == exampleHairlineCaseKey);
1249    REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvHairlineCaseKey);
1250    REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvFillCaseKey);
1251    REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvStrokeCaseKey);
1252    REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvHairlineCaseKey);
1253
1254    for (bool inverted : {false, true}) {
1255        for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
1256            for (unsigned start = 0; start < 8; ++start) {
1257                for (bool dash : {false, true}) {
1258                    const GrShape& fillCase = shapes[index(inverted, dir, start, kFill, dash)];
1259                    Key fillCaseKey;
1260                    make_key(&fillCaseKey, fillCase);
1261
1262                    const GrShape& strokeAndFillCase = shapes[index(inverted, dir, start,
1263                                                                    kStrokeAndFill, dash)];
1264                    Key strokeAndFillCaseKey;
1265                    make_key(&strokeAndFillCaseKey, strokeAndFillCase);
1266
1267                    // Both fill and stroke-and-fill shapes must respect the inverseness and both
1268                    // ignore dashing.
1269                    REPORTER_ASSERT(r, !fillCase.style().pathEffect());
1270                    REPORTER_ASSERT(r, !strokeAndFillCase.style().pathEffect());
1271                    TestCase a(fillCase, r);
1272                    TestCase b(inverted ? exampleInvFillCase : exampleFillCase, r);
1273                    TestCase c(strokeAndFillCase, r);
1274                    TestCase d(inverted ? exampleInvStrokeAndFillCase
1275                                        : exampleStrokeAndFillCase, r);
1276                    a.compare(r, b, TestCase::kAllSame_ComparisonExpecation);
1277                    c.compare(r, d, TestCase::kAllSame_ComparisonExpecation);
1278
1279                    const GrShape& strokeCase = shapes[index(inverted, dir, start, kStroke, dash)];
1280                    const GrShape& hairlineCase = shapes[index(inverted, dir, start, kHairline,
1281                                                               dash)];
1282
1283                    TestCase e(strokeCase, r);
1284                    TestCase g(hairlineCase, r);
1285
1286                    // Both hairline and stroke shapes must respect the dashing.
1287                    if (dash) {
1288                        // Dashing always ignores the inverseness. skbug.com/5421
1289                        TestCase f(exampleStrokeCase, r);
1290                        TestCase h(exampleHairlineCase, r);
1291                        unsigned expectedStart = canonicalize_rrect_start(start, rrect);
1292                        REPORTER_ASSERT(r, strokeCase.style().pathEffect());
1293                        REPORTER_ASSERT(r, hairlineCase.style().pathEffect());
1294
1295                        REPORTER_ASSERT(r, strokeCase.asRRect(&queryRR, &queryDir, &queryStart,
1296                                                              &queryInverted));
1297                        REPORTER_ASSERT(r, queryRR == rrect);
1298                        REPORTER_ASSERT(r, queryDir == dir);
1299                        REPORTER_ASSERT(r, queryStart == expectedStart);
1300                        REPORTER_ASSERT(r, !queryInverted);
1301                        REPORTER_ASSERT(r, hairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1302                                                                &queryInverted));
1303                        REPORTER_ASSERT(r, queryRR == rrect);
1304                        REPORTER_ASSERT(r, queryDir == dir);
1305                        REPORTER_ASSERT(r, queryStart == expectedStart);
1306                        REPORTER_ASSERT(r, !queryInverted);
1307
1308                        // The pre-style case for the dash will match the non-dash example iff the
1309                        // dir and start match (dir=cw, start=0).
1310                        if (0 == expectedStart && SkPath::kCW_Direction == dir) {
1311                            e.compare(r, f, TestCase::kSameUpToPE_ComparisonExpecation);
1312                            g.compare(r, h, TestCase::kSameUpToPE_ComparisonExpecation);
1313                        } else {
1314                            e.compare(r, f, TestCase::kAllDifferent_ComparisonExpecation);
1315                            g.compare(r, h, TestCase::kAllDifferent_ComparisonExpecation);
1316                        }
1317                    } else {
1318                        TestCase f(inverted ? exampleInvStrokeCase : exampleStrokeCase, r);
1319                        TestCase h(inverted ? exampleInvHairlineCase : exampleHairlineCase, r);
1320                        REPORTER_ASSERT(r, !strokeCase.style().pathEffect());
1321                        REPORTER_ASSERT(r, !hairlineCase.style().pathEffect());
1322                        e.compare(r, f, TestCase::kAllSame_ComparisonExpecation);
1323                        g.compare(r, h, TestCase::kAllSame_ComparisonExpecation);
1324                    }
1325                }
1326            }
1327        }
1328    }
1329}
1330
1331DEF_TEST(GrShape, reporter) {
1332    for (auto r : { SkRect::MakeWH(10, 20),
1333                    SkRect::MakeWH(-10, -20),
1334                    SkRect::MakeWH(-10, 20),
1335                    SkRect::MakeWH(10, -20)}) {
1336        test_basic(reporter, r);
1337        test_scale(reporter, r);
1338        test_dash_fill(reporter, r);
1339        test_null_dash(reporter, r);
1340        // Test modifying various stroke params.
1341        test_stroke_param<SkRect, SkScalar>(
1342                reporter, r,
1343                [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
1344                SkIntToScalar(2), SkIntToScalar(4));
1345        test_stroke_param<SkRect, SkPaint::Join>(
1346                reporter, r,
1347                [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
1348                SkPaint::kMiter_Join, SkPaint::kRound_Join);
1349        test_stroke_cap(reporter, r);
1350        test_miter_limit(reporter, r);
1351        test_path_effect_makes_rrect(reporter, r);
1352        test_unknown_path_effect(reporter, r);
1353        test_path_effect_makes_empty_shape(reporter, r);
1354        test_path_effect_fails(reporter, r);
1355        test_make_hairline_path_effect(reporter, r, true);
1356        GrShape shape(r);
1357        REPORTER_ASSERT(reporter, !shape.asLine(nullptr));
1358    }
1359
1360    for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)),
1361                     SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4),
1362                     SkRRect::MakeOval(SkRect::MakeWH(20, 20))}) {
1363        test_basic(reporter, rr);
1364        test_rrect(reporter, rr);
1365        test_scale(reporter, rr);
1366        test_dash_fill(reporter, rr);
1367        test_null_dash(reporter, rr);
1368        // Test modifying various stroke params.
1369        test_stroke_param<SkRRect, SkScalar>(
1370                          reporter, rr,
1371                          [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
1372                          SkIntToScalar(2), SkIntToScalar(4));
1373        test_stroke_param<SkRRect, SkPaint::Join>(
1374                          reporter, rr,
1375                          [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
1376                          SkPaint::kMiter_Join, SkPaint::kRound_Join);
1377        test_stroke_cap(reporter, rr);
1378        test_miter_limit(reporter, rr);
1379        test_path_effect_makes_rrect(reporter, rr);
1380        test_unknown_path_effect(reporter, rr);
1381        test_path_effect_makes_empty_shape(reporter, rr);
1382        test_path_effect_fails(reporter, rr);
1383        test_make_hairline_path_effect(reporter, rr, true);
1384        GrShape shape(rr);
1385        REPORTER_ASSERT(reporter, !shape.asLine(nullptr));
1386    }
1387
1388    struct TestPath {
1389        TestPath(const SkPath& path, bool isRRectFill, bool isRRectStroke, bool isLine,
1390                 const SkRRect& rrect)
1391            : fPath(path)
1392            , fIsRRectForFill(isRRectFill)
1393            , fIsRRectForStroke(isRRectStroke)
1394            , fIsLine(isLine)
1395            , fRRect(rrect) {}
1396        SkPath  fPath;
1397        bool    fIsRRectForFill;
1398        bool    fIsRRectForStroke;
1399        bool    fIsLine;
1400        SkRRect fRRect;
1401    };
1402    SkTArray<TestPath> paths;
1403
1404    SkPath circlePath;
1405    circlePath.addCircle(10, 10, 10);
1406    paths.emplace_back(circlePath, true, true, false, SkRRect::MakeOval(SkRect::MakeWH(20,20)));
1407
1408    SkPath rectPath;
1409    rectPath.addRect(SkRect::MakeWH(10, 10));
1410    paths.emplace_back(rectPath, true, true, false, SkRRect::MakeRect(SkRect::MakeWH(10, 10)));
1411
1412    SkPath openRectPath;
1413    openRectPath.moveTo(0, 0);
1414    openRectPath.lineTo(10, 0);
1415    openRectPath.lineTo(10, 10);
1416    openRectPath.lineTo(0, 10);
1417    paths.emplace_back(openRectPath, true, false, false, SkRRect::MakeRect(SkRect::MakeWH(10, 10)));
1418
1419    SkPath quadPath;
1420    quadPath.quadTo(10, 10, 5, 8);
1421    paths.emplace_back(quadPath, false, false, false, SkRRect());
1422
1423    SkPath linePath;
1424    linePath.lineTo(10, 10);
1425    paths.emplace_back(linePath, false, false, true, SkRRect());
1426
1427    for (auto testPath : paths) {
1428        for (bool inverseFill : {false, true}) {
1429            if (inverseFill) {
1430                if (testPath.fPath.getFillType() == SkPath::kEvenOdd_FillType) {
1431                    testPath.fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1432                } else {
1433                    SkASSERT(testPath.fPath.getFillType() == SkPath::kWinding_FillType);
1434                    testPath.fPath.setFillType(SkPath::kInverseWinding_FillType);
1435                }
1436            }
1437            const SkPath& path = testPath.fPath;
1438            // These tests all assume that the original GrShape for fill and stroke will be the
1439            // same.
1440            // However, that is not the case in special cases (e.g. an unclosed rect becomes a RRect
1441            // GrShape with a fill style but becomes a Path GrShape when stroked).
1442            if (testPath.fIsRRectForFill == testPath.fIsRRectForStroke) {
1443                test_basic(reporter, path);
1444                test_null_dash(reporter, path);
1445                test_path_effect_makes_rrect(reporter, path);
1446            }
1447            test_scale(reporter, path);
1448            // This test uses a stroking paint, hence use of fIsRRectForStroke
1449            test_volatile_path(reporter, path, testPath.fIsRRectForStroke);
1450            test_dash_fill(reporter, path);
1451            // Test modifying various stroke params.
1452            test_stroke_param<SkPath, SkScalar>(
1453                reporter, path,
1454                [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
1455                SkIntToScalar(2), SkIntToScalar(4));
1456            test_stroke_param<SkPath, SkPaint::Join>(
1457                reporter, path,
1458                [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
1459                SkPaint::kMiter_Join, SkPaint::kRound_Join);
1460            test_stroke_cap(reporter, path);
1461            test_miter_limit(reporter, path);
1462            test_unknown_path_effect(reporter, path);
1463            test_path_effect_makes_empty_shape(reporter, path);
1464            test_path_effect_fails(reporter, path);
1465            test_make_hairline_path_effect(reporter, path, testPath.fIsRRectForStroke);
1466        }
1467    }
1468
1469    for (auto testPath : paths) {
1470        const SkPath& path = testPath.fPath;
1471
1472        SkPaint fillPaint;
1473        TestCase fillPathCase(path, fillPaint, reporter);
1474        SkRRect rrect;
1475        REPORTER_ASSERT(reporter, testPath.fIsRRectForFill ==
1476                                  fillPathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
1477                                                                   nullptr));
1478        if (testPath.fIsRRectForFill) {
1479            TestCase fillPathCase2(testPath.fPath, fillPaint, reporter);
1480            REPORTER_ASSERT(reporter, rrect == testPath.fRRect);
1481            TestCase fillRRectCase(rrect, fillPaint, reporter);
1482            fillPathCase2.compare(reporter, fillRRectCase,
1483                                  TestCase::kAllSame_ComparisonExpecation);
1484        }
1485        SkPaint strokePaint;
1486        strokePaint.setStrokeWidth(3.f);
1487        strokePaint.setStyle(SkPaint::kStroke_Style);
1488        TestCase strokePathCase(path, strokePaint, reporter);
1489        REPORTER_ASSERT(reporter, testPath.fIsRRectForStroke ==
1490                                  strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
1491                                                                     nullptr));
1492        if (testPath.fIsRRectForStroke) {
1493            REPORTER_ASSERT(reporter, rrect == testPath.fRRect);
1494            TestCase strokeRRectCase(rrect, strokePaint, reporter);
1495            strokePathCase.compare(reporter, strokeRRectCase,
1496                                   TestCase::kAllSame_ComparisonExpecation);
1497        }
1498        REPORTER_ASSERT(reporter, testPath.fIsLine == fillPathCase.baseShape().asLine(nullptr));
1499        REPORTER_ASSERT(reporter, testPath.fIsLine == strokePathCase.baseShape().asLine(nullptr));
1500    }
1501
1502    // Test a volatile empty path.
1503    test_volatile_path(reporter, SkPath(), true);
1504
1505    test_empty_shape(reporter);
1506}
1507
1508#endif
1509