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