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