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