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