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