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