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