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