GrShapeTest.cpp revision 06115ee4300ef6756729dfbcb3e2fc70ebf0413a
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    struct SelfExpectations {
86        bool fPEHasEffect;
87        bool fPEHasValidKey;
88        bool fStrokeApplies;
89    };
90
91    void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const;
92
93    enum ComparisonExpecation {
94        kAllDifferent_ComparisonExpecation,
95        kSameUpToPE_ComparisonExpecation,
96        kSameUpToStroke_ComparisonExpecation,
97        kAllSame_ComparisonExpecation,
98    };
99
100    void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const;
101
102    const GrShape& baseShape() const { return fBase; }
103    const GrShape& appliedPathEffectShape() const { return fAppliedPE; }
104    const GrShape& appliedFullStyleShape() const { return fAppliedFull; }
105
106    // The returned array's count will be 0 if the key shape has no key.
107    const Key& baseKey() const { return fBaseKey; }
108    const Key& appliedPathEffectKey() const { return fAppliedPEKey; }
109    const Key& appliedFullStyleKey() const { return fAppliedFullKey; }
110    const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; }
111
112private:
113    static void CheckBounds(skiatest::Reporter* r, const GrShape& shape, const SkRect& bounds) {
114        SkPath path;
115        shape.asPath(&path);
116        // If the bounds are empty, the path ought to be as well.
117        if (bounds.isEmpty()) {
118            REPORTER_ASSERT(r, path.isEmpty());
119            return;
120        }
121        if (path.isEmpty()) {
122            return;
123        }
124        REPORTER_ASSERT(r, test_bounds_by_rasterizing(path, bounds));
125    }
126
127    void init(skiatest::Reporter* r, SkScalar scale) {
128        fAppliedPE           = fBase.applyStyle(GrStyle::Apply::kPathEffectOnly, scale);
129        fAppliedPEThenStroke = fAppliedPE.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec,
130                                                     scale);
131        fAppliedFull         = fBase.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale);
132
133        make_key(&fBaseKey, fBase);
134        make_key(&fAppliedPEKey, fAppliedPE);
135        make_key(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
136        make_key(&fAppliedFullKey, fAppliedFull);
137
138        // Applying the path effect and then the stroke should always be the same as applying
139        // both in one go.
140        REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey);
141        SkPath a, b;
142        fAppliedPEThenStroke.asPath(&a);
143        fAppliedFull.asPath(&b);
144        // If the output of the path effect is a rrect then it is possible for a and b to be
145        // different paths that fill identically. The reason is that fAppliedFull will do this:
146        // base -> apply path effect -> rrect_as_path -> stroke -> stroked_rrect_as_path
147        // fAppliedPEThenStroke will have converted the rrect_as_path back to a rrect. However,
148        // now that there is no longer a path effect, the direction and starting index get
149        // canonicalized before the stroke.
150        if (fAppliedPE.asRRect(nullptr, nullptr, nullptr)) {
151            REPORTER_ASSERT(r, paths_fill_same(a, b));
152        } else {
153            REPORTER_ASSERT(r, a == b);
154        }
155        REPORTER_ASSERT(r, fAppliedFull.isEmpty() == fAppliedPEThenStroke.isEmpty());
156
157        SkPath path;
158        fBase.asPath(&path);
159        REPORTER_ASSERT(r, path.isEmpty() == fBase.isEmpty());
160        REPORTER_ASSERT(r, path.getSegmentMasks() == fBase.segmentMask());
161        fAppliedPE.asPath(&path);
162        REPORTER_ASSERT(r, path.isEmpty() == fAppliedPE.isEmpty());
163        REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedPE.segmentMask());
164        fAppliedFull.asPath(&path);
165        REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull.isEmpty());
166        REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedFull.segmentMask());
167
168        CheckBounds(r, fBase, fBase.bounds());
169        CheckBounds(r, fAppliedPE, fAppliedPE.bounds());
170        CheckBounds(r, fAppliedPEThenStroke, fAppliedPEThenStroke.bounds());
171        CheckBounds(r, fAppliedFull, fAppliedFull.bounds());
172        SkRect styledBounds;
173        fBase.styledBounds(&styledBounds);
174        CheckBounds(r, fAppliedFull, styledBounds);
175        fAppliedPE.styledBounds(&styledBounds);
176        CheckBounds(r, fAppliedFull, styledBounds);
177
178        // Check that the same path is produced when style is applied by GrShape and GrStyle.
179        SkPath preStyle;
180        SkPath postPathEffect;
181        SkPath postAllStyle;
182
183        fBase.asPath(&preStyle);
184        SkStrokeRec postPEStrokeRec(SkStrokeRec::kFill_InitStyle);
185        if (fBase.style().applyPathEffectToPath(&postPathEffect, &postPEStrokeRec, preStyle,
186                                                scale)) {
187            // run postPathEffect through GrShape to get any geometry reductions that would have
188            // occurred to fAppliedPE.
189            GrShape(postPathEffect, GrStyle(postPEStrokeRec, nullptr)).asPath(&postPathEffect);
190
191            SkPath testPath;
192            fAppliedPE.asPath(&testPath);
193            REPORTER_ASSERT(r, testPath == postPathEffect);
194            REPORTER_ASSERT(r, postPEStrokeRec.hasEqualEffect(fAppliedPE.style().strokeRec()));
195        }
196        SkStrokeRec::InitStyle fillOrHairline;
197        if (fBase.style().applyToPath(&postAllStyle, &fillOrHairline, preStyle, scale)) {
198            // run postPathEffect through GrShape to get any reductions that would have occurred
199            // to fAppliedFull.
200            GrShape(postAllStyle, GrStyle(fillOrHairline)).asPath(&postAllStyle);
201
202            SkPath testPath;
203            fAppliedFull.asPath(&testPath);
204            REPORTER_ASSERT(r, testPath == postAllStyle);
205            if (fillOrHairline == SkStrokeRec::kFill_InitStyle) {
206                REPORTER_ASSERT(r, fAppliedFull.style().isSimpleFill());
207            } else {
208                REPORTER_ASSERT(r, fAppliedFull.style().isSimpleHairline());
209            }
210        }
211    }
212
213    GrShape fBase;
214    GrShape fAppliedPE;
215    GrShape fAppliedPEThenStroke;
216    GrShape fAppliedFull;
217
218    Key fBaseKey;
219    Key fAppliedPEKey;
220    Key fAppliedPEThenStrokeKey;
221    Key fAppliedFullKey;
222};
223
224void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const {
225    // The base's key should always be valid (unless the path is volatile)
226    REPORTER_ASSERT(reporter, fBaseKey.count());
227    if (expectations.fPEHasEffect) {
228        REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey);
229        REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedPEKey.count()));
230        REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
231        REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedFullKey.count()));
232        if (expectations.fStrokeApplies && expectations.fPEHasValidKey) {
233            REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey);
234            REPORTER_ASSERT(reporter, SkToBool(fAppliedFullKey.count()));
235        }
236    } else {
237        REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey);
238        SkPath a, b;
239        fBase.asPath(&a);
240        fAppliedPE.asPath(&b);
241        REPORTER_ASSERT(reporter, a == b);
242        if (expectations.fStrokeApplies) {
243            REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
244        } else {
245            REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey);
246        }
247    }
248}
249
250void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrShape& b,
251                       const Key& keyA, const Key& keyB) {
252    // GrShape only respects the input winding direction and start point for rrect shapes
253    // when there is a path effect. Thus, if there are two GrShapes representing the same rrect
254    // but one has a path effect in its style and the other doesn't then asPath() and the unstyled
255    // key will differ. GrShape will have canonicalized the direction and start point for the shape
256    // without the path effect. If *both* have path effects then they should have both preserved
257    // the direction and starting point.
258
259    // The asRRect() output params are all initialized just to silence compiler warnings about
260    // uninitialized variables.
261    SkRRect rrectA = SkRRect::MakeEmpty(), rrectB = SkRRect::MakeEmpty();
262    SkPath::Direction dirA = SkPath::kCW_Direction, dirB = SkPath::kCW_Direction;
263    unsigned startA = ~0U, startB = ~0U;
264
265    bool aIsRRect = a.asRRect(&rrectA, &dirA, &startA);
266    bool bIsRRect = b.asRRect(&rrectB, &dirB, &startB);
267    bool aHasPE = a.style().hasPathEffect();
268    bool bHasPE = b.style().hasPathEffect();
269    bool allowSameRRectButDiffStartAndDir = (aIsRRect && bIsRRect) && (aHasPE != bHasPE);
270    SkPath pathA, pathB;
271    a.asPath(&pathA);
272    b.asPath(&pathB);
273    if (allowSameRRectButDiffStartAndDir) {
274        REPORTER_ASSERT(r, rrectA == rrectB);
275        REPORTER_ASSERT(r, paths_fill_same(pathA, pathB));
276    } else {
277        REPORTER_ASSERT(r, pathA == pathB);
278        REPORTER_ASSERT(r, keyA == keyB);
279        REPORTER_ASSERT(r, aIsRRect == bIsRRect);
280        if (aIsRRect) {
281            REPORTER_ASSERT(r, rrectA == rrectB);
282            REPORTER_ASSERT(r, dirA == dirB);
283            REPORTER_ASSERT(r, startA == startB);
284        }
285    }
286    REPORTER_ASSERT(r, a.isEmpty() == b.isEmpty());
287    REPORTER_ASSERT(r, a.knownToBeClosed() == b.knownToBeClosed());
288    REPORTER_ASSERT(r, a.bounds() == b.bounds());
289    REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask());
290}
291
292void TestCase::compare(skiatest::Reporter* r, const TestCase& that,
293                       ComparisonExpecation expectation) const {
294    SkPath a, b;
295    switch (expectation) {
296        case kAllDifferent_ComparisonExpecation:
297            REPORTER_ASSERT(r, fBaseKey != that.fBaseKey);
298            REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
299            REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
300            break;
301        case kSameUpToPE_ComparisonExpecation:
302            check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
303            REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
304            REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
305            break;
306        case kSameUpToStroke_ComparisonExpecation:
307            check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
308            check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
309            REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
310            break;
311        case kAllSame_ComparisonExpecation:
312            check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
313            check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
314            check_equivalence(r, fAppliedFull, that.fAppliedFull, fAppliedFullKey,
315                              that.fAppliedFullKey);
316            break;
317    }
318}
319}  // namespace
320
321static sk_sp<SkPathEffect> make_dash() {
322    static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f };
323    static const SkScalar kPhase = 0.75;
324    return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase);
325}
326
327static sk_sp<SkPathEffect> make_null_dash() {
328    static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0};
329    return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f);
330}
331
332template<typename GEO>
333static void test_basic(skiatest::Reporter* reporter, const GEO& geo) {
334    sk_sp<SkPathEffect> dashPE = make_dash();
335
336    TestCase::SelfExpectations expectations;
337    SkPaint fill;
338
339    TestCase fillCase(geo, fill, reporter);
340    expectations.fPEHasEffect = false;
341    expectations.fPEHasValidKey = false;
342    expectations.fStrokeApplies = false;
343    fillCase.testExpectations(reporter, expectations);
344    // Test that another GrShape instance built from the same primitive is the same.
345    TestCase(geo, fill, reporter).compare(reporter, fillCase,
346                                          TestCase::kAllSame_ComparisonExpecation);
347
348    SkPaint stroke2RoundBevel;
349    stroke2RoundBevel.setStyle(SkPaint::kStroke_Style);
350    stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap);
351    stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join);
352    stroke2RoundBevel.setStrokeWidth(2.f);
353    TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter);
354    expectations.fPEHasValidKey = true;
355    expectations.fPEHasEffect = false;
356    expectations.fStrokeApplies = true;
357    stroke2RoundBevelCase.testExpectations(reporter, expectations);
358    TestCase(geo, stroke2RoundBevel, reporter).compare(reporter, stroke2RoundBevelCase,
359                                                       TestCase::kAllSame_ComparisonExpecation);
360
361    SkPaint stroke2RoundBevelDash = stroke2RoundBevel;
362    stroke2RoundBevelDash.setPathEffect(make_dash());
363    TestCase stroke2RoundBevelDashCase(geo, stroke2RoundBevelDash, reporter);
364    expectations.fPEHasValidKey = true;
365    expectations.fPEHasEffect = true;
366    expectations.fStrokeApplies = true;
367    stroke2RoundBevelDashCase.testExpectations(reporter, expectations);
368    TestCase(geo, stroke2RoundBevelDash, reporter).compare(reporter, stroke2RoundBevelDashCase,
369                                                           TestCase::kAllSame_ComparisonExpecation);
370
371    fillCase.compare(reporter, stroke2RoundBevelCase,
372                     TestCase::kSameUpToStroke_ComparisonExpecation);
373    fillCase.compare(reporter, stroke2RoundBevelDashCase,
374                     TestCase::kSameUpToPE_ComparisonExpecation);
375    stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
376                                  TestCase::kSameUpToPE_ComparisonExpecation);
377
378    // Stroke and fill cases
379    SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel;
380    stroke2RoundBevelAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
381    TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter);
382    expectations.fPEHasValidKey = true;
383    expectations.fPEHasEffect = false;
384    expectations.fStrokeApplies = true;
385    stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations);
386    TestCase(geo, stroke2RoundBevelAndFill, reporter).compare(reporter,
387            stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation);
388
389    SkPaint stroke2RoundBevelAndFillDash = stroke2RoundBevelDash;
390    stroke2RoundBevelAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
391    TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter);
392    expectations.fPEHasValidKey = true;
393    expectations.fPEHasEffect = true;
394    expectations.fStrokeApplies = true;
395    stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations);
396    TestCase(geo, stroke2RoundBevelAndFillDash, reporter).compare(
397        reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation);
398
399    stroke2RoundBevelAndFillCase.compare(reporter, stroke2RoundBevelCase,
400                                         TestCase::kSameUpToStroke_ComparisonExpecation);
401    stroke2RoundBevelAndFillDashCase.compare(reporter, stroke2RoundBevelDashCase,
402                                             TestCase::kSameUpToStroke_ComparisonExpecation);
403    stroke2RoundBevelAndFillCase.compare(reporter, stroke2RoundBevelAndFillDashCase,
404                                         TestCase::kSameUpToPE_ComparisonExpecation);
405
406    SkPaint hairline;
407    hairline.setStyle(SkPaint::kStroke_Style);
408    hairline.setStrokeWidth(0.f);
409    TestCase hairlineCase(geo, hairline, reporter);
410    // Since hairline style doesn't change the SkPath data, it is keyed identically to fill.
411    hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
412    REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline());
413    REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline());
414    REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline());
415}
416
417template<typename GEO>
418static void test_scale(skiatest::Reporter* reporter, const GEO& geo) {
419    sk_sp<SkPathEffect> dashPE = make_dash();
420
421    static const SkScalar kS1 = 1.f;
422    static const SkScalar kS2 = 2.f;
423
424    SkPaint fill;
425    TestCase fillCase1(geo, fill, reporter, kS1);
426    TestCase fillCase2(geo, fill, reporter, kS2);
427    // Scale doesn't affect fills.
428    fillCase1.compare(reporter, fillCase2, TestCase::kAllSame_ComparisonExpecation);
429
430    SkPaint hairline;
431    hairline.setStyle(SkPaint::kStroke_Style);
432    hairline.setStrokeWidth(0.f);
433    TestCase hairlineCase1(geo, hairline, reporter, kS1);
434    TestCase hairlineCase2(geo, hairline, reporter, kS2);
435    // Scale doesn't affect hairlines.
436    hairlineCase1.compare(reporter, hairlineCase2, TestCase::kAllSame_ComparisonExpecation);
437
438    SkPaint stroke;
439    stroke.setStyle(SkPaint::kStroke_Style);
440    stroke.setStrokeWidth(2.f);
441    TestCase strokeCase1(geo, stroke, reporter, kS1);
442    TestCase strokeCase2(geo, stroke, reporter, kS2);
443    // Scale affects the stroke.
444    strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation);
445
446    SkPaint strokeDash = stroke;
447    strokeDash.setPathEffect(make_dash());
448    TestCase strokeDashCase1(geo, strokeDash, reporter, kS1);
449    TestCase strokeDashCase2(geo, strokeDash, reporter, kS2);
450    // Scale affects the dash and the stroke.
451    strokeDashCase1.compare(reporter, strokeDashCase2,  TestCase::kSameUpToPE_ComparisonExpecation);
452
453    // Stroke and fill cases
454    SkPaint strokeAndFill = stroke;
455    strokeAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
456    TestCase strokeAndFillCase1(geo, strokeAndFill, reporter, kS1);
457    TestCase strokeAndFillCase2(geo, strokeAndFill, reporter, kS2);
458    // Scale affects the stroke. Though, this can wind up creating a rect when the input is a rect.
459    // In that case we wind up with a pure geometry key and the geometries are the same.
460    SkRRect rrect;
461    if (strokeAndFillCase1.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr)) {
462        // We currently only expect to get here in the rect->rect case.
463        REPORTER_ASSERT(reporter, rrect.isRect());
464        REPORTER_ASSERT(reporter,
465                        strokeAndFillCase1.baseShape().asRRect(&rrect, nullptr, nullptr) &&
466                        rrect.isRect());
467        strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
468                                   TestCase::kAllSame_ComparisonExpecation);
469    } else {
470        strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
471                                   TestCase::kSameUpToStroke_ComparisonExpecation);
472    }
473
474    SkPaint strokeAndFillDash = strokeDash;
475    strokeAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
476    TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1);
477    TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2);
478    // Scale affects the path effect and stroke.
479    strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2,
480                               TestCase::kSameUpToPE_ComparisonExpecation);
481}
482
483template <typename GEO, typename T>
484static void test_stroke_param_impl(skiatest::Reporter* reporter, const GEO& geo,
485                                   std::function<void(SkPaint*, T)> setter, T a, T b,
486                                   bool paramAffectsStroke,
487                                   bool paramAffectsDashAndStroke) {
488    // Set the stroke width so that we don't get hairline. However, call the setter afterward so
489    // that it can override the stroke width.
490    SkPaint strokeA;
491    strokeA.setStyle(SkPaint::kStroke_Style);
492    strokeA.setStrokeWidth(2.f);
493    setter(&strokeA, a);
494    SkPaint strokeB;
495    strokeB.setStyle(SkPaint::kStroke_Style);
496    strokeB.setStrokeWidth(2.f);
497    setter(&strokeB, b);
498
499    TestCase strokeACase(geo, strokeA, reporter);
500    TestCase strokeBCase(geo, strokeB, reporter);
501    if (paramAffectsStroke) {
502        strokeACase.compare(reporter, strokeBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
503    } else {
504        strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation);
505    }
506
507    SkPaint strokeAndFillA = strokeA;
508    SkPaint strokeAndFillB = strokeB;
509    strokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style);
510    strokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style);
511    TestCase strokeAndFillACase(geo, strokeAndFillA, reporter);
512    TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter);
513    if (paramAffectsStroke) {
514        strokeAndFillACase.compare(reporter, strokeAndFillBCase,
515                                   TestCase::kSameUpToStroke_ComparisonExpecation);
516    } else {
517        strokeAndFillACase.compare(reporter, strokeAndFillBCase,
518                                   TestCase::kAllSame_ComparisonExpecation);
519    }
520
521    // Make sure stroking params don't affect fill style.
522    SkPaint fillA = strokeA, fillB = strokeB;
523    fillA.setStyle(SkPaint::kFill_Style);
524    fillB.setStyle(SkPaint::kFill_Style);
525    TestCase fillACase(geo, fillA, reporter);
526    TestCase fillBCase(geo, fillB, reporter);
527    fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
528
529    // Make sure just applying the dash but not stroke gives the same key for both stroking
530    // variations.
531    SkPaint dashA = strokeA, dashB = strokeB;
532    dashA.setPathEffect(make_dash());
533    dashB.setPathEffect(make_dash());
534    TestCase dashACase(geo, dashA, reporter);
535    TestCase dashBCase(geo, dashB, reporter);
536    if (paramAffectsDashAndStroke) {
537        dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
538    } else {
539        dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation);
540    }
541
542    SkPaint dashStrokeAndFillA = dashA, dashStrokeAndFillB = dashB;
543    dashStrokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style);
544    dashStrokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style);
545    TestCase dashStrokeAndFillACase(geo, dashStrokeAndFillA, reporter);
546    TestCase dashStrokeAndFillBCase(geo, dashStrokeAndFillB, reporter);
547    if (paramAffectsDashAndStroke) {
548        dashStrokeAndFillACase.compare(reporter, dashStrokeAndFillBCase,
549                                       TestCase::kSameUpToStroke_ComparisonExpecation);
550    } else {
551        dashStrokeAndFillACase.compare(reporter, dashStrokeAndFillBCase,
552                                       TestCase::kAllSame_ComparisonExpecation);
553    }
554}
555
556template <typename GEO, typename T>
557static void test_stroke_param(skiatest::Reporter* reporter, const GEO& geo,
558                              std::function<void(SkPaint*, T)> setter, T a, T b) {
559    test_stroke_param_impl(reporter, geo, setter, a, b, true, true);
560};
561
562template <typename GEO>
563static void test_stroke_cap(skiatest::Reporter* reporter, const GEO& geo) {
564    GrShape shape(geo, GrStyle(SkStrokeRec::kHairline_InitStyle));
565    // The cap should only affect shapes that may be open.
566    bool affectsStroke = !shape.knownToBeClosed();
567    // Dashing adds ends that need caps.
568    bool affectsDashAndStroke = true;
569    test_stroke_param_impl<GEO, SkPaint::Cap>(
570        reporter,
571        geo,
572        [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
573        SkPaint::kButt_Cap, SkPaint::kRound_Cap,
574        affectsStroke,
575        affectsDashAndStroke);
576};
577
578template <typename GEO>
579static void test_miter_limit(skiatest::Reporter* reporter, const GEO& geo) {
580    auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) {
581        p->setStrokeJoin(SkPaint::kMiter_Join);
582        p->setStrokeMiter(miter);
583    };
584
585    auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) {
586        p->setStrokeJoin(SkPaint::kRound_Join);
587        p->setStrokeMiter(miter);
588    };
589
590    // The miter limit should affect stroked and dashed-stroked cases when the join type is
591    // miter.
592    test_stroke_param_impl<GEO, SkScalar>(
593        reporter,
594        geo,
595        setMiterJoinAndLimit,
596        0.5f, 0.75f,
597        true,
598        true);
599
600    // The miter limit should not affect stroked and dashed-stroked cases when the join type is
601    // not miter.
602    test_stroke_param_impl<GEO, SkScalar>(
603        reporter,
604        geo,
605        setOtherJoinAndLimit,
606        0.5f, 0.75f,
607        false,
608        false);
609}
610
611template<typename GEO>
612static void test_dash_fill(skiatest::Reporter* reporter, const GEO& geo) {
613    // A dash with no stroke should have no effect
614    using DashFactoryFn = sk_sp<SkPathEffect>(*)();
615    for (DashFactoryFn md : {&make_dash, &make_null_dash}) {
616        SkPaint dashFill;
617        dashFill.setPathEffect((*md)());
618        TestCase dashFillCase(geo, dashFill, reporter);
619
620        TestCase fillCase(geo, SkPaint(), reporter);
621        dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
622    }
623}
624
625template<typename GEO>
626void test_null_dash(skiatest::Reporter* reporter, const GEO& geo) {
627    SkPaint fill;
628    SkPaint stroke;
629    stroke.setStyle(SkPaint::kStroke_Style);
630    stroke.setStrokeWidth(1.f);
631    SkPaint dash;
632    dash.setStyle(SkPaint::kStroke_Style);
633    dash.setStrokeWidth(1.f);
634    dash.setPathEffect(make_dash());
635    SkPaint nullDash;
636    nullDash.setStyle(SkPaint::kStroke_Style);
637    nullDash.setStrokeWidth(1.f);
638    nullDash.setPathEffect(make_null_dash());
639
640    TestCase fillCase(geo, fill, reporter);
641    TestCase strokeCase(geo, stroke, reporter);
642    TestCase dashCase(geo, dash, reporter);
643    TestCase nullDashCase(geo, nullDash, reporter);
644
645    nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation);
646    nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation);
647    nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation);
648}
649
650template <typename GEO>
651void test_path_effect_makes_rrect(skiatest::Reporter* reporter, const GEO& geo) {
652    /**
653     * This path effect takes any input path and turns it into a rrect. It passes through stroke
654     * info.
655     */
656    class RRectPathEffect : SkPathEffect {
657    public:
658        static const SkRRect& RRect() {
659            static const SkRRect kRRect = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 3, 5);
660            return kRRect;
661        }
662
663        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
664                        const SkRect* cullR) const override {
665            dst->reset();
666            dst->addRRect(RRect());
667            return true;
668        }
669        void computeFastBounds(SkRect* dst, const SkRect& src) const override {
670            *dst = RRect().getBounds();
671        }
672        static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new RRectPathEffect); }
673        Factory getFactory() const override { return nullptr; }
674        void toString(SkString*) const override {}
675    private:
676        RRectPathEffect() {}
677    };
678
679    SkPaint fill;
680    TestCase fillGeoCase(geo, fill, reporter);
681
682    SkPaint pe;
683    pe.setPathEffect(RRectPathEffect::Make());
684    TestCase geoPECase(geo, pe, reporter);
685
686    SkPaint peStroke;
687    peStroke.setPathEffect(RRectPathEffect::Make());
688    peStroke.setStrokeWidth(2.f);
689    peStroke.setStyle(SkPaint::kStroke_Style);
690    TestCase geoPEStrokeCase(geo, peStroke, reporter);
691
692    fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation);
693    fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation);
694    geoPECase.compare(reporter, geoPEStrokeCase,
695                      TestCase::kSameUpToStroke_ComparisonExpecation);
696
697    TestCase rrectFillCase(RRectPathEffect::RRect(), fill, reporter);
698    SkPaint stroke = peStroke;
699    stroke.setPathEffect(nullptr);
700    TestCase rrectStrokeCase(RRectPathEffect::RRect(), stroke, reporter);
701
702    SkRRect rrect;
703    // Applying the path effect should make a SkRRect shape. There is no further stroking in the
704    // geoPECase, so the full style should be the same as just the PE.
705    REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().asRRect(&rrect, nullptr, nullptr));
706    REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
707    REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == rrectFillCase.baseKey());
708
709    REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr));
710    REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
711    REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == rrectFillCase.baseKey());
712
713    // In the PE+stroke case applying the full style should be the same as just stroking the rrect.
714    REPORTER_ASSERT(reporter,
715                    geoPEStrokeCase.appliedPathEffectShape().asRRect(&rrect, nullptr, nullptr));
716    REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
717    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == rrectFillCase.baseKey());
718
719    REPORTER_ASSERT(reporter,
720                    !geoPEStrokeCase.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr));
721    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() ==
722                              rrectStrokeCase.appliedFullStyleKey());
723}
724
725template <typename GEO>
726void test_unknown_path_effect(skiatest::Reporter* reporter, const GEO& geo) {
727    /**
728     * This path effect just adds two lineTos to the input path.
729     */
730    class AddLineTosPathEffect : SkPathEffect {
731    public:
732        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
733                        const SkRect* cullR) const override {
734            *dst = src;
735            dst->lineTo(0, 0);
736            dst->lineTo(10, 10);
737            return true;
738        }
739        void computeFastBounds(SkRect* dst, const SkRect& src) const override {
740            *dst = src;
741            dst->growToInclude(0, 0);
742            dst->growToInclude(10, 10);
743        }
744        static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new AddLineTosPathEffect); }
745        Factory getFactory() const override { return nullptr; }
746        void toString(SkString*) const override {}
747    private:
748        AddLineTosPathEffect() {}
749    };
750
751     // This path effect should make the keys invalid when it is applied. We only produce a path
752     // effect key for dash path effects. So the only way another arbitrary path effect can produce
753     // a styled result with a key is to produce a non-path shape that has a purely geometric key.
754    SkPaint peStroke;
755    peStroke.setPathEffect(AddLineTosPathEffect::Make());
756    peStroke.setStrokeWidth(2.f);
757    peStroke.setStyle(SkPaint::kStroke_Style);
758    TestCase geoPEStrokeCase(geo, peStroke, reporter);
759    TestCase::SelfExpectations expectations;
760    expectations.fPEHasEffect = true;
761    expectations.fPEHasValidKey = false;
762    expectations.fStrokeApplies = true;
763    geoPEStrokeCase.testExpectations(reporter, expectations);
764}
765
766template <typename GEO>
767void test_make_hairline_path_effect(skiatest::Reporter* reporter, const GEO& geo, bool isNonPath) {
768    /**
769     * This path effect just changes the stroke rec to hairline.
770     */
771    class MakeHairlinePathEffect : SkPathEffect {
772    public:
773        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* strokeRec,
774                        const SkRect* cullR) const override {
775            *dst = src;
776            strokeRec->setHairlineStyle();
777            return true;
778        }
779        void computeFastBounds(SkRect* dst, const SkRect& src) const override {  *dst = src; }
780        static sk_sp<SkPathEffect> Make() {
781            return sk_sp<SkPathEffect>(new MakeHairlinePathEffect);
782        }
783        Factory getFactory() const override { return nullptr; }
784        void toString(SkString*) const override {}
785    private:
786        MakeHairlinePathEffect() {}
787    };
788
789    SkPaint fill;
790    SkPaint pe;
791    pe.setPathEffect(MakeHairlinePathEffect::Make());
792
793    TestCase peCase(geo, pe, reporter);
794
795    SkPath a, b, c;
796    peCase.baseShape().asPath(&a);
797    peCase.appliedPathEffectShape().asPath(&b);
798    peCase.appliedFullStyleShape().asPath(&c);
799    if (isNonPath) {
800        // RRect types can have a change in start index or direction after the PE is applied. This
801        // is because once the PE is applied, GrShape may canonicalize the dir and index since it
802        // is not germane to the styling any longer.
803        // Instead we just check that the paths would fill the same both before and after styling.
804        REPORTER_ASSERT(reporter, paths_fill_same(a, b));
805        REPORTER_ASSERT(reporter, paths_fill_same(a, c));
806    } else {
807        REPORTER_ASSERT(reporter, a == b);
808        REPORTER_ASSERT(reporter, a == c);
809        REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey().empty());
810        REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey().empty());
811    }
812    REPORTER_ASSERT(reporter, peCase.appliedPathEffectShape().style().isSimpleHairline());
813    REPORTER_ASSERT(reporter, peCase.appliedFullStyleShape().style().isSimpleHairline());
814}
815
816/**
817 * isNonPath indicates whether the initial shape made from the path is expected to be recognized
818 * as a simpler shape type (e.g. rrect)
819 */
820void test_volatile_path(skiatest::Reporter* reporter, const SkPath& path,
821                        bool isNonPath) {
822    SkPath vPath(path);
823    vPath.setIsVolatile(true);
824
825    SkPaint dashAndStroke;
826    dashAndStroke.setPathEffect(make_dash());
827    dashAndStroke.setStrokeWidth(2.f);
828    dashAndStroke.setStyle(SkPaint::kStroke_Style);
829    TestCase volatileCase(vPath, dashAndStroke, reporter);
830    // We expect a shape made from a volatile path to have a key iff the shape is recognized
831    // as a specialized geometry.
832    if (isNonPath) {
833        REPORTER_ASSERT(reporter, SkToBool(volatileCase.baseKey().count()));
834        // In this case all the keys should be identical to the non-volatile case.
835        TestCase nonVolatileCase(path, dashAndStroke, reporter);
836        volatileCase.compare(reporter, nonVolatileCase, TestCase::kAllSame_ComparisonExpecation);
837    } else {
838        // None of the keys should be valid.
839        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.baseKey().count()));
840        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectKey().count()));
841        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedFullStyleKey().count()));
842        REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectThenStrokeKey().count()));
843    }
844}
845
846template <typename GEO>
847void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const GEO& geo) {
848    /**
849     * This path effect returns an empty path.
850     */
851    class EmptyPathEffect : SkPathEffect {
852    public:
853        bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
854                        const SkRect* cullR) const override {
855            dst->reset();
856            return true;
857        }
858        void computeFastBounds(SkRect* dst, const SkRect& src) const override {
859            dst->setEmpty();
860        }
861        static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new EmptyPathEffect); }
862        Factory getFactory() const override { return nullptr; }
863        void toString(SkString*) const override {}
864    private:
865        EmptyPathEffect() {}
866    };
867
868    SkPath emptyPath;
869    GrShape emptyShape(emptyPath);
870    Key emptyKey;
871    make_key(&emptyKey, emptyShape);
872    REPORTER_ASSERT(reporter, emptyShape.isEmpty());
873
874    SkPaint pe;
875    pe.setPathEffect(EmptyPathEffect::Make());
876    TestCase geoCase(geo, pe, reporter);
877    REPORTER_ASSERT(reporter, geoCase.appliedFullStyleKey() == emptyKey);
878    REPORTER_ASSERT(reporter, geoCase.appliedPathEffectKey() == emptyKey);
879    REPORTER_ASSERT(reporter, geoCase.appliedPathEffectThenStrokeKey() == emptyKey);
880    REPORTER_ASSERT(reporter, geoCase.appliedPathEffectShape().isEmpty());
881    REPORTER_ASSERT(reporter, geoCase.appliedFullStyleShape().isEmpty());
882
883    SkPaint peStroke;
884    peStroke.setPathEffect(EmptyPathEffect::Make());
885    peStroke.setStrokeWidth(2.f);
886    peStroke.setStyle(SkPaint::kStroke_Style);
887    TestCase geoPEStrokeCase(geo, peStroke, reporter);
888    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == emptyKey);
889    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == emptyKey);
890    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey);
891    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty());
892    REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty());
893}
894
895void test_empty_shape(skiatest::Reporter* reporter) {
896    SkPath emptyPath;
897    SkPaint fill;
898    TestCase fillEmptyCase(emptyPath, fill, reporter);
899    REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty());
900    REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty());
901    REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty());
902
903    Key emptyKey(fillEmptyCase.baseKey());
904    REPORTER_ASSERT(reporter, emptyKey.count());
905    TestCase::SelfExpectations expectations;
906    expectations.fStrokeApplies = false;
907    expectations.fPEHasEffect = false;
908    // This will test whether applying style preserves emptiness
909    fillEmptyCase.testExpectations(reporter, expectations);
910
911    // Stroking an empty path should have no effect
912    SkPath emptyPath2;
913    SkPaint stroke;
914    stroke.setStrokeWidth(2.f);
915    stroke.setStyle(SkPaint::kStroke_Style);
916    TestCase strokeEmptyCase(emptyPath2, stroke, reporter);
917    strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
918
919    // Dashing and stroking an empty path should have no effect
920    SkPath emptyPath3;
921    SkPaint dashAndStroke;
922    dashAndStroke.setPathEffect(make_dash());
923    dashAndStroke.setStrokeWidth(2.f);
924    dashAndStroke.setStyle(SkPaint::kStroke_Style);
925    TestCase dashAndStrokeEmptyCase(emptyPath3, dashAndStroke, reporter);
926    dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase,
927                                   TestCase::kAllSame_ComparisonExpecation);
928
929    // A shape made from an empty rrect should behave the same as an empty path.
930    SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty());
931    REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type);
932    TestCase dashAndStrokeEmptyRRectCase(emptyRRect, dashAndStroke, reporter);
933    dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
934                                        TestCase::kAllSame_ComparisonExpecation);
935
936    // Same for a rect.
937    SkRect emptyRect = SkRect::MakeEmpty();
938    TestCase dashAndStrokeEmptyRectCase(emptyRect, dashAndStroke, reporter);
939    dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase,
940                                       TestCase::kAllSame_ComparisonExpecation);
941}
942
943DEF_TEST(GrShape, reporter) {
944    for (auto r : { SkRect::MakeWH(10, 20),
945                    SkRect::MakeWH(-10, -20),
946                    SkRect::MakeWH(-10, 20),
947                    SkRect::MakeWH(10, -20)}) {
948        test_basic(reporter, r);
949        test_scale(reporter, r);
950        test_dash_fill(reporter, r);
951        test_null_dash(reporter, r);
952        // Test modifying various stroke params.
953        test_stroke_param<SkRect, SkScalar>(
954                reporter, r,
955                [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
956                SkIntToScalar(2), SkIntToScalar(4));
957        test_stroke_param<SkRect, SkPaint::Join>(
958                reporter, r,
959                [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
960                SkPaint::kMiter_Join, SkPaint::kRound_Join);
961        test_stroke_cap(reporter, r);
962        test_miter_limit(reporter, r);
963        test_path_effect_makes_rrect(reporter, r);
964        test_unknown_path_effect(reporter, r);
965        test_path_effect_makes_empty_shape(reporter, r);
966        test_make_hairline_path_effect(reporter, r, true);
967    }
968
969    for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)),
970                     SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4),
971                     SkRRect::MakeOval(SkRect::MakeWH(20, 20))}) {
972        test_basic(reporter, rr);
973        test_scale(reporter, rr);
974        test_dash_fill(reporter, rr);
975        test_null_dash(reporter, rr);
976        // Test modifying various stroke params.
977        test_stroke_param<SkRRect, SkScalar>(
978                          reporter, rr,
979                          [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
980                          SkIntToScalar(2), SkIntToScalar(4));
981        test_stroke_param<SkRRect, SkPaint::Join>(
982                          reporter, rr,
983                          [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
984                          SkPaint::kMiter_Join, SkPaint::kRound_Join);
985        test_stroke_cap(reporter, rr);
986        test_miter_limit(reporter, rr);
987        test_path_effect_makes_rrect(reporter, rr);
988        test_unknown_path_effect(reporter, rr);
989        test_path_effect_makes_empty_shape(reporter, rr);
990        test_make_hairline_path_effect(reporter, rr, true);
991    }
992
993    struct TestPath {
994        TestPath(const SkPath& path, bool isRRectFill, bool isRRectStroke ,const SkRRect& rrect)
995            : fPath(path)
996            , fIsRRectForFill(isRRectFill)
997            , fIsRRectForStroke(isRRectStroke)
998            , fRRect(rrect) {}
999        SkPath  fPath;
1000        bool    fIsRRectForFill;
1001        bool    fIsRRectForStroke;
1002        SkRRect fRRect;
1003    };
1004    SkTArray<TestPath> paths;
1005
1006    SkPath circlePath;
1007    circlePath.addCircle(10, 10, 10);
1008    paths.emplace_back(circlePath, true, true, SkRRect::MakeOval(SkRect::MakeWH(20,20)));
1009
1010    SkPath rectPath;
1011    rectPath.addRect(SkRect::MakeWH(10, 10));
1012    paths.emplace_back(rectPath, true, true, SkRRect::MakeRect(SkRect::MakeWH(10, 10)));
1013
1014    SkPath openRectPath;
1015    openRectPath.moveTo(0, 0);
1016    openRectPath.lineTo(10, 0);
1017    openRectPath.lineTo(10, 10);
1018    openRectPath.lineTo(0, 10);
1019    paths.emplace_back(openRectPath, true, false, SkRRect::MakeRect(SkRect::MakeWH(10, 10)));
1020
1021    SkPath quadPath;
1022    quadPath.quadTo(10, 10, 5, 8);
1023    paths.emplace_back(quadPath, false, false, SkRRect());
1024
1025    for (auto testPath : paths) {
1026        const SkPath& path = testPath.fPath;
1027        // These tests all assume that the original GrShape for fill and stroke will be the same.
1028        // However, that is not the case in special cases (e.g. a unclosed rect becomes a RRect
1029        // GrShape with a fill style but becomes a Path GrShape when stroked).
1030        if (testPath.fIsRRectForFill == testPath.fIsRRectForStroke) {
1031            test_basic(reporter, path);
1032            test_null_dash(reporter, path);
1033            test_path_effect_makes_rrect(reporter, path);
1034        }
1035        test_scale(reporter, path);
1036        // This test uses a stroking paint, hence use of fIsRRectForStroke
1037        test_volatile_path(reporter, path, testPath.fIsRRectForStroke);
1038        test_dash_fill(reporter, path);
1039        // Test modifying various stroke params.
1040        test_stroke_param<SkPath, SkScalar>(
1041            reporter, path,
1042            [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
1043            SkIntToScalar(2), SkIntToScalar(4));
1044        test_stroke_param<SkPath, SkPaint::Join>(
1045            reporter, path,
1046            [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
1047            SkPaint::kMiter_Join, SkPaint::kRound_Join);
1048        test_stroke_cap(reporter, path);
1049        test_miter_limit(reporter, path);
1050        test_unknown_path_effect(reporter, path);
1051        test_path_effect_makes_empty_shape(reporter, path);
1052        test_make_hairline_path_effect(reporter, path, testPath.fIsRRectForStroke);
1053
1054        SkPaint fillPaint;
1055        TestCase fillPathCase(path, fillPaint, reporter);
1056        SkRRect rrect;
1057        REPORTER_ASSERT(reporter, testPath.fIsRRectForFill ==
1058                                  fillPathCase.baseShape().asRRect(&rrect, nullptr, nullptr));
1059        if (testPath.fIsRRectForFill) {
1060            TestCase fillPathCase2(path, fillPaint, reporter);
1061            REPORTER_ASSERT(reporter, rrect == testPath.fRRect);
1062            TestCase fillRRectCase(rrect, fillPaint, reporter);
1063            fillPathCase2.compare(reporter, fillRRectCase, TestCase::kAllSame_ComparisonExpecation);
1064        }
1065
1066        SkPaint strokePaint;
1067        strokePaint.setStrokeWidth(3.f);
1068        strokePaint.setStyle(SkPaint::kStroke_Style);
1069        TestCase strokePathCase(path, strokePaint, reporter);
1070        REPORTER_ASSERT(reporter, testPath.fIsRRectForStroke ==
1071                                  strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr));
1072        if (testPath.fIsRRectForStroke) {
1073            REPORTER_ASSERT(reporter, rrect == testPath.fRRect);
1074            TestCase strokeRRectCase(rrect, strokePaint, reporter);
1075            strokePathCase.compare(reporter, strokeRRectCase,
1076                                   TestCase::kAllSame_ComparisonExpecation);
1077        }
1078    }
1079
1080    // Test a volatile empty path.
1081    test_volatile_path(reporter, SkPath(), true);
1082
1083    test_empty_shape(reporter);
1084}
1085
1086#endif
1087