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