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