blob: ca907a1f87b4289cc891480dd0ade81e4a939a22 [file] [log] [blame]
bsalomon47cc7692016-04-26 12:56:00 -07001/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include <initializer_list>
9#include <functional>
10#include "Test.h"
11#if SK_SUPPORT_GPU
12#include "GrShape.h"
bsalomon9fb42032016-05-13 09:23:38 -070013#include "SkCanvas.h"
bsalomon47cc7692016-04-26 12:56:00 -070014#include "SkDashPathEffect.h"
bsalomon9fb42032016-05-13 09:23:38 -070015#include "SkPath.h"
bsalomonee295642016-06-06 14:01:25 -070016#include "SkPathOps.h"
bsalomon9fb42032016-05-13 09:23:38 -070017#include "SkSurface.h"
bsalomon47cc7692016-04-26 12:56:00 -070018
bsalomon72dc51c2016-04-27 06:46:23 -070019using Key = SkTArray<uint32_t>;
20
21static bool make_key(Key* key, const GrShape& shape) {
22 int size = shape.unstyledKeySize();
23 if (size <= 0) {
24 key->reset(0);
25 return false;
26 }
27 SkASSERT(size);
28 key->reset(size);
29 shape.writeUnstyledKey(key->begin());
30 return true;
31}
32
bsalomonee295642016-06-06 14:01:25 -070033static bool paths_fill_same(const SkPath& a, const SkPath& b) {
34 SkPath pathXor;
35 Op(a, b, SkPathOp::kXOR_SkPathOp, &pathXor);
36 return pathXor.isEmpty();
37}
38
bsalomon9fb42032016-05-13 09:23:38 -070039static bool test_bounds_by_rasterizing(const SkPath& path, const SkRect& bounds) {
40 static constexpr int kRes = 2000;
41 // This tolerance is in units of 1/kRes fractions of the bounds width/height.
42 static constexpr int kTol = 0;
43 GR_STATIC_ASSERT(kRes % 4 == 0);
44 SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
45 sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
46 surface->getCanvas()->clear(0x0);
47 SkRect clip = SkRect::MakeXYWH(kRes/4, kRes/4, kRes/2, kRes/2);
48 SkMatrix matrix;
49 matrix.setRectToRect(bounds, clip, SkMatrix::kFill_ScaleToFit);
50 clip.outset(SkIntToScalar(kTol), SkIntToScalar(kTol));
51 surface->getCanvas()->clipRect(clip, SkRegion::kDifference_Op);
52 surface->getCanvas()->concat(matrix);
53 SkPaint whitePaint;
54 whitePaint.setColor(SK_ColorWHITE);
55 surface->getCanvas()->drawPath(path, whitePaint);
56 SkPixmap pixmap;
57 surface->getCanvas()->peekPixels(&pixmap);
58#if defined(SK_BUILD_FOR_WIN)
59 // The static constexpr version in #else causes cl.exe to crash.
60 const uint8_t* kZeros = reinterpret_cast<uint8_t*>(calloc(kRes, 1));
61#else
62 static constexpr uint8_t kZeros[kRes] = {0};
63#endif
64 for (int y = 0; y < kRes/4; ++y) {
65 const uint8_t* row = pixmap.addr8(0, y);
66 if (0 != memcmp(kZeros, row, kRes)) {
67 return false;
68 }
69 }
70#ifdef SK_BUILD_FOR_WIN
71 free(const_cast<uint8_t*>(kZeros));
72#endif
73 return true;
74}
bsalomon72dc51c2016-04-27 06:46:23 -070075
bsalomon9fb42032016-05-13 09:23:38 -070076namespace {
bsalomon47cc7692016-04-26 12:56:00 -070077class TestCase {
78public:
bsalomon72dc51c2016-04-27 06:46:23 -070079 template <typename GEO>
bsalomon97fd2d42016-05-09 13:02:01 -070080 TestCase(const GEO& geo, const SkPaint& paint, skiatest::Reporter* r,
81 SkScalar scale = SK_Scalar1) : fBase(geo, paint) {
82 this->init(r, scale);
bsalomon47cc7692016-04-26 12:56:00 -070083 }
84
bsalomon70493962016-06-10 08:05:14 -070085 TestCase(const GrShape& shape, skiatest::Reporter* r, SkScalar scale = SK_Scalar1)
86 : fBase(shape) {
87 this->init(r, scale);
88 }
89
bsalomon47cc7692016-04-26 12:56:00 -070090 struct SelfExpectations {
91 bool fPEHasEffect;
92 bool fPEHasValidKey;
93 bool fStrokeApplies;
94 };
95
96 void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const;
97
98 enum ComparisonExpecation {
99 kAllDifferent_ComparisonExpecation,
100 kSameUpToPE_ComparisonExpecation,
101 kSameUpToStroke_ComparisonExpecation,
102 kAllSame_ComparisonExpecation,
103 };
104
105 void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const;
106
bsalomon72dc51c2016-04-27 06:46:23 -0700107 const GrShape& baseShape() const { return fBase; }
108 const GrShape& appliedPathEffectShape() const { return fAppliedPE; }
109 const GrShape& appliedFullStyleShape() const { return fAppliedFull; }
110
111 // The returned array's count will be 0 if the key shape has no key.
112 const Key& baseKey() const { return fBaseKey; }
113 const Key& appliedPathEffectKey() const { return fAppliedPEKey; }
114 const Key& appliedFullStyleKey() const { return fAppliedFullKey; }
bsalomon409ed732016-04-27 12:36:02 -0700115 const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; }
bsalomon72dc51c2016-04-27 06:46:23 -0700116
bsalomon47cc7692016-04-26 12:56:00 -0700117private:
bsalomon9fb42032016-05-13 09:23:38 -0700118 static void CheckBounds(skiatest::Reporter* r, const GrShape& shape, const SkRect& bounds) {
119 SkPath path;
120 shape.asPath(&path);
121 // If the bounds are empty, the path ought to be as well.
122 if (bounds.isEmpty()) {
123 REPORTER_ASSERT(r, path.isEmpty());
124 return;
125 }
126 if (path.isEmpty()) {
127 return;
128 }
bsalomon70493962016-06-10 08:05:14 -0700129 // The bounds API explicitly calls out that it does not consider inverseness.
130 SkPath p = path;
131 p.setFillType(SkPath::ConvertToNonInverseFillType(path.getFillType()));
132 REPORTER_ASSERT(r, test_bounds_by_rasterizing(p, bounds));
bsalomon9fb42032016-05-13 09:23:38 -0700133 }
134
bsalomon97fd2d42016-05-09 13:02:01 -0700135 void init(skiatest::Reporter* r, SkScalar scale) {
136 fAppliedPE = fBase.applyStyle(GrStyle::Apply::kPathEffectOnly, scale);
137 fAppliedPEThenStroke = fAppliedPE.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec,
138 scale);
139 fAppliedFull = fBase.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale);
bsalomon47cc7692016-04-26 12:56:00 -0700140
bsalomon72dc51c2016-04-27 06:46:23 -0700141 make_key(&fBaseKey, fBase);
142 make_key(&fAppliedPEKey, fAppliedPE);
143 make_key(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
144 make_key(&fAppliedFullKey, fAppliedFull);
bsalomonfb083272016-05-04 08:27:41 -0700145
146 // Applying the path effect and then the stroke should always be the same as applying
147 // both in one go.
148 REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey);
149 SkPath a, b;
150 fAppliedPEThenStroke.asPath(&a);
151 fAppliedFull.asPath(&b);
bsalomonee295642016-06-06 14:01:25 -0700152 // If the output of the path effect is a rrect then it is possible for a and b to be
153 // different paths that fill identically. The reason is that fAppliedFull will do this:
154 // base -> apply path effect -> rrect_as_path -> stroke -> stroked_rrect_as_path
155 // fAppliedPEThenStroke will have converted the rrect_as_path back to a rrect. However,
156 // now that there is no longer a path effect, the direction and starting index get
157 // canonicalized before the stroke.
bsalomon70493962016-06-10 08:05:14 -0700158 if (fAppliedPE.asRRect(nullptr, nullptr, nullptr, nullptr)) {
bsalomonee295642016-06-06 14:01:25 -0700159 REPORTER_ASSERT(r, paths_fill_same(a, b));
160 } else {
161 REPORTER_ASSERT(r, a == b);
162 }
bsalomon7c73a532016-05-11 15:15:56 -0700163 REPORTER_ASSERT(r, fAppliedFull.isEmpty() == fAppliedPEThenStroke.isEmpty());
164
165 SkPath path;
166 fBase.asPath(&path);
167 REPORTER_ASSERT(r, path.isEmpty() == fBase.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700168 REPORTER_ASSERT(r, path.getSegmentMasks() == fBase.segmentMask());
bsalomon7c73a532016-05-11 15:15:56 -0700169 fAppliedPE.asPath(&path);
170 REPORTER_ASSERT(r, path.isEmpty() == fAppliedPE.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700171 REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedPE.segmentMask());
bsalomon7c73a532016-05-11 15:15:56 -0700172 fAppliedFull.asPath(&path);
173 REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700174 REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedFull.segmentMask());
bsalomonfb083272016-05-04 08:27:41 -0700175
bsalomon9fb42032016-05-13 09:23:38 -0700176 CheckBounds(r, fBase, fBase.bounds());
177 CheckBounds(r, fAppliedPE, fAppliedPE.bounds());
178 CheckBounds(r, fAppliedPEThenStroke, fAppliedPEThenStroke.bounds());
179 CheckBounds(r, fAppliedFull, fAppliedFull.bounds());
bsalomon0a0f67e2016-06-28 11:56:42 -0700180 SkRect styledBounds = fBase.styledBounds();
bsalomon9fb42032016-05-13 09:23:38 -0700181 CheckBounds(r, fAppliedFull, styledBounds);
bsalomon0a0f67e2016-06-28 11:56:42 -0700182 styledBounds = fAppliedPE.styledBounds();
bsalomon9fb42032016-05-13 09:23:38 -0700183 CheckBounds(r, fAppliedFull, styledBounds);
184
bsalomonfb083272016-05-04 08:27:41 -0700185 // Check that the same path is produced when style is applied by GrShape and GrStyle.
186 SkPath preStyle;
187 SkPath postPathEffect;
188 SkPath postAllStyle;
189
190 fBase.asPath(&preStyle);
bsalomon1a0b9ed2016-05-06 11:07:03 -0700191 SkStrokeRec postPEStrokeRec(SkStrokeRec::kFill_InitStyle);
bsalomon97fd2d42016-05-09 13:02:01 -0700192 if (fBase.style().applyPathEffectToPath(&postPathEffect, &postPEStrokeRec, preStyle,
193 scale)) {
bsalomon1a0b9ed2016-05-06 11:07:03 -0700194 // run postPathEffect through GrShape to get any geometry reductions that would have
195 // occurred to fAppliedPE.
196 GrShape(postPathEffect, GrStyle(postPEStrokeRec, nullptr)).asPath(&postPathEffect);
197
bsalomonfb083272016-05-04 08:27:41 -0700198 SkPath testPath;
199 fAppliedPE.asPath(&testPath);
200 REPORTER_ASSERT(r, testPath == postPathEffect);
bsalomon1a0b9ed2016-05-06 11:07:03 -0700201 REPORTER_ASSERT(r, postPEStrokeRec.hasEqualEffect(fAppliedPE.style().strokeRec()));
bsalomonfb083272016-05-04 08:27:41 -0700202 }
203 SkStrokeRec::InitStyle fillOrHairline;
bsalomon97fd2d42016-05-09 13:02:01 -0700204 if (fBase.style().applyToPath(&postAllStyle, &fillOrHairline, preStyle, scale)) {
bsalomonfb083272016-05-04 08:27:41 -0700205 SkPath testPath;
206 fAppliedFull.asPath(&testPath);
bsalomon1b28c1a2016-06-20 12:28:17 -0700207 if (fBase.style().hasPathEffect()) {
208 // Because GrShape always does two-stage application when there is a path effect
209 // there may be a reduction/canonicalization step between the path effect and
210 // strokerec not reflected in postAllStyle since it applied both the path effect
211 // and strokerec without analyzing the intermediate path.
212 REPORTER_ASSERT(r, paths_fill_same(postAllStyle, testPath));
213 } else {
214 // Make sure that postAllStyle sees any reductions/canonicalizations that GrShape
215 // would apply.
216 GrShape(postAllStyle, GrStyle(fillOrHairline)).asPath(&postAllStyle);
217 REPORTER_ASSERT(r, testPath == postAllStyle);
218 }
219
bsalomonfb083272016-05-04 08:27:41 -0700220 if (fillOrHairline == SkStrokeRec::kFill_InitStyle) {
221 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleFill());
222 } else {
223 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleHairline());
224 }
225 }
bsalomon47cc7692016-04-26 12:56:00 -0700226 }
227
228 GrShape fBase;
229 GrShape fAppliedPE;
230 GrShape fAppliedPEThenStroke;
231 GrShape fAppliedFull;
232
233 Key fBaseKey;
234 Key fAppliedPEKey;
235 Key fAppliedPEThenStrokeKey;
236 Key fAppliedFullKey;
bsalomon47cc7692016-04-26 12:56:00 -0700237};
238
239void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const {
bsalomon47cc7692016-04-26 12:56:00 -0700240 // The base's key should always be valid (unless the path is volatile)
bsalomon72dc51c2016-04-27 06:46:23 -0700241 REPORTER_ASSERT(reporter, fBaseKey.count());
bsalomon47cc7692016-04-26 12:56:00 -0700242 if (expectations.fPEHasEffect) {
243 REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700244 REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedPEKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700245 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700246 REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedFullKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700247 if (expectations.fStrokeApplies && expectations.fPEHasValidKey) {
248 REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700249 REPORTER_ASSERT(reporter, SkToBool(fAppliedFullKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700250 }
251 } else {
252 REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey);
bsalomonfb083272016-05-04 08:27:41 -0700253 SkPath a, b;
bsalomon72dc51c2016-04-27 06:46:23 -0700254 fBase.asPath(&a);
255 fAppliedPE.asPath(&b);
256 REPORTER_ASSERT(reporter, a == b);
bsalomon47cc7692016-04-26 12:56:00 -0700257 if (expectations.fStrokeApplies) {
258 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
259 } else {
260 REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey);
261 }
262 }
263}
264
bsalomonac5fcea2016-06-23 12:23:07 -0700265static bool can_interchange_winding_and_even_odd_fill(const GrShape& shape) {
266 SkPath path;
267 shape.asPath(&path);
268 if (shape.style().hasNonDashPathEffect()) {
269 return false;
270 }
271 const SkStrokeRec::Style strokeRecStyle = shape.style().strokeRec().getStyle();
272 return strokeRecStyle == SkStrokeRec::kStroke_Style ||
273 strokeRecStyle == SkStrokeRec::kHairline_Style ||
274 (shape.style().isSimpleFill() && path.isConvex());
275}
276
277static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrShape& b,
278 const Key& keyA, const Key& keyB) {
bsalomonee295642016-06-06 14:01:25 -0700279 // GrShape only respects the input winding direction and start point for rrect shapes
280 // when there is a path effect. Thus, if there are two GrShapes representing the same rrect
281 // but one has a path effect in its style and the other doesn't then asPath() and the unstyled
282 // key will differ. GrShape will have canonicalized the direction and start point for the shape
283 // without the path effect. If *both* have path effects then they should have both preserved
284 // the direction and starting point.
285
286 // The asRRect() output params are all initialized just to silence compiler warnings about
287 // uninitialized variables.
288 SkRRect rrectA = SkRRect::MakeEmpty(), rrectB = SkRRect::MakeEmpty();
289 SkPath::Direction dirA = SkPath::kCW_Direction, dirB = SkPath::kCW_Direction;
290 unsigned startA = ~0U, startB = ~0U;
bsalomon70493962016-06-10 08:05:14 -0700291 bool invertedA = true, invertedB = true;
bsalomonee295642016-06-06 14:01:25 -0700292
bsalomon70493962016-06-10 08:05:14 -0700293 bool aIsRRect = a.asRRect(&rrectA, &dirA, &startA, &invertedA);
294 bool bIsRRect = b.asRRect(&rrectB, &dirB, &startB, &invertedB);
bsalomonee295642016-06-06 14:01:25 -0700295 bool aHasPE = a.style().hasPathEffect();
296 bool bHasPE = b.style().hasPathEffect();
297 bool allowSameRRectButDiffStartAndDir = (aIsRRect && bIsRRect) && (aHasPE != bHasPE);
bsalomon425c27f2016-06-23 13:18:45 -0700298 // GrShape will close paths with simple fill style.
299 bool allowedClosednessDiff = (a.style().isSimpleFill() != b.style().isSimpleFill());
bsalomonee295642016-06-06 14:01:25 -0700300 SkPath pathA, pathB;
301 a.asPath(&pathA);
302 b.asPath(&pathB);
bsalomon70493962016-06-10 08:05:14 -0700303
bsalomonfd32df72016-06-14 14:37:21 -0700304 // Having a dash path effect can allow 'a' but not 'b' to turn a inverse fill type into a
305 // non-inverse fill type (or vice versa).
bsalomon70493962016-06-10 08:05:14 -0700306 bool ignoreInversenessDifference = false;
307 if (pathA.isInverseFillType() != pathB.isInverseFillType()) {
308 const GrShape* s1 = pathA.isInverseFillType() ? &a : &b;
309 const GrShape* s2 = pathA.isInverseFillType() ? &b : &a;
bsalomonfd32df72016-06-14 14:37:21 -0700310 bool canDropInverse1 = s1->style().isDashed();
311 bool canDropInverse2 = s2->style().isDashed();
312 ignoreInversenessDifference = (canDropInverse1 != canDropInverse2);
bsalomon70493962016-06-10 08:05:14 -0700313 }
bsalomona4817af2016-06-23 11:48:26 -0700314 bool ignoreWindingVsEvenOdd = false;
315 if (SkPath::ConvertToNonInverseFillType(pathA.getFillType()) !=
316 SkPath::ConvertToNonInverseFillType(pathB.getFillType())) {
bsalomonac5fcea2016-06-23 12:23:07 -0700317 bool aCanChange = can_interchange_winding_and_even_odd_fill(a);
318 bool bCanChange = can_interchange_winding_and_even_odd_fill(b);
bsalomona4817af2016-06-23 11:48:26 -0700319 if (aCanChange != bCanChange) {
320 ignoreWindingVsEvenOdd = true;
321 }
322 }
bsalomonee295642016-06-06 14:01:25 -0700323 if (allowSameRRectButDiffStartAndDir) {
324 REPORTER_ASSERT(r, rrectA == rrectB);
325 REPORTER_ASSERT(r, paths_fill_same(pathA, pathB));
bsalomon70493962016-06-10 08:05:14 -0700326 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
bsalomonee295642016-06-06 14:01:25 -0700327 } else {
bsalomon70493962016-06-10 08:05:14 -0700328 SkPath pA = pathA;
329 SkPath pB = pathB;
bsalomon425c27f2016-06-23 13:18:45 -0700330 REPORTER_ASSERT(r, a.inverseFilled() == pA.isInverseFillType());
331 REPORTER_ASSERT(r, b.inverseFilled() == pB.isInverseFillType());
bsalomon70493962016-06-10 08:05:14 -0700332 if (ignoreInversenessDifference) {
333 pA.setFillType(SkPath::ConvertToNonInverseFillType(pathA.getFillType()));
334 pB.setFillType(SkPath::ConvertToNonInverseFillType(pathB.getFillType()));
bsalomona4817af2016-06-23 11:48:26 -0700335 }
336 if (ignoreWindingVsEvenOdd) {
337 pA.setFillType(pA.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
338 : SkPath::kEvenOdd_FillType);
339 pB.setFillType(pB.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
340 : SkPath::kEvenOdd_FillType);
341 }
342 if (!ignoreInversenessDifference && !ignoreWindingVsEvenOdd) {
bsalomon70493962016-06-10 08:05:14 -0700343 REPORTER_ASSERT(r, keyA == keyB);
bsalomona4817af2016-06-23 11:48:26 -0700344 } else {
345 REPORTER_ASSERT(r, keyA != keyB);
bsalomon70493962016-06-10 08:05:14 -0700346 }
bsalomon425c27f2016-06-23 13:18:45 -0700347 if (allowedClosednessDiff) {
bsalomon93f66bc2016-06-21 08:35:49 -0700348 // GrShape will close paths with simple fill style. Make the non-filled path closed
349 // so that the comparision will succeed. Make sure both are closed before comparing.
350 pA.close();
351 pB.close();
352 }
bsalomon70493962016-06-10 08:05:14 -0700353 REPORTER_ASSERT(r, pA == pB);
bsalomonee295642016-06-06 14:01:25 -0700354 REPORTER_ASSERT(r, aIsRRect == bIsRRect);
355 if (aIsRRect) {
356 REPORTER_ASSERT(r, rrectA == rrectB);
357 REPORTER_ASSERT(r, dirA == dirB);
358 REPORTER_ASSERT(r, startA == startB);
bsalomon70493962016-06-10 08:05:14 -0700359 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
bsalomonee295642016-06-06 14:01:25 -0700360 }
361 }
362 REPORTER_ASSERT(r, a.isEmpty() == b.isEmpty());
bsalomon425c27f2016-06-23 13:18:45 -0700363 REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeClosed() == b.knownToBeClosed());
364 // closedness can affect convexity.
365 REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeConvex() == b.knownToBeConvex());
366 if (a.knownToBeConvex()) {
367 REPORTER_ASSERT(r, pathA.isConvex());
368 }
369 if (b.knownToBeConvex()) {
370 REPORTER_ASSERT(r, pathB.isConvex());
371 }
bsalomonee295642016-06-06 14:01:25 -0700372 REPORTER_ASSERT(r, a.bounds() == b.bounds());
bsalomon06115ee2016-06-07 06:28:51 -0700373 REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask());
bsalomon0a0f67e2016-06-28 11:56:42 -0700374 // Init these to suppress warnings.
375 SkPoint pts[4] {{0, 0,}, {0, 0}, {0, 0}, {0, 0}} ;
376 bool invertedLine[2] {true, true};
377 REPORTER_ASSERT(r, a.asLine(pts, &invertedLine[0]) == b.asLine(pts + 2, &invertedLine[1]));
bsalomon425c27f2016-06-23 13:18:45 -0700378 // mayBeInverseFilledAfterStyling() is allowed to differ if one has a arbitrary PE and the other
379 // doesn't (since the PE can set any fill type on its output path).
380 // Moreover, dash style explicitly ignores inverseness. So if one is dashed but not the other
381 // then they may disagree about inverseness.
382 if (a.style().hasNonDashPathEffect() == b.style().hasNonDashPathEffect() &&
383 a.style().isDashed() == b.style().isDashed()) {
384 REPORTER_ASSERT(r, a.mayBeInverseFilledAfterStyling() ==
385 b.mayBeInverseFilledAfterStyling());
386 }
bsalomon0a0f67e2016-06-28 11:56:42 -0700387 if (a.asLine(nullptr, nullptr)) {
bsalomon398e3f42016-06-13 10:22:48 -0700388 REPORTER_ASSERT(r, pts[2] == pts[0] && pts[3] == pts[1]);
bsalomon0a0f67e2016-06-28 11:56:42 -0700389 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedLine[0] == invertedLine[1]);
390 REPORTER_ASSERT(r, invertedLine[0] == a.inverseFilled());
391 REPORTER_ASSERT(r, invertedLine[1] == b.inverseFilled());
bsalomon398e3f42016-06-13 10:22:48 -0700392 }
bsalomon425c27f2016-06-23 13:18:45 -0700393 REPORTER_ASSERT(r, ignoreInversenessDifference || a.inverseFilled() == b.inverseFilled());
bsalomonee295642016-06-06 14:01:25 -0700394}
395
396void TestCase::compare(skiatest::Reporter* r, const TestCase& that,
bsalomon47cc7692016-04-26 12:56:00 -0700397 ComparisonExpecation expectation) const {
bsalomon72dc51c2016-04-27 06:46:23 -0700398 SkPath a, b;
bsalomon47cc7692016-04-26 12:56:00 -0700399 switch (expectation) {
400 case kAllDifferent_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700401 REPORTER_ASSERT(r, fBaseKey != that.fBaseKey);
402 REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
403 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700404 break;
405 case kSameUpToPE_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700406 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
407 REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
408 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700409 break;
410 case kSameUpToStroke_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700411 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
412 check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
413 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700414 break;
415 case kAllSame_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700416 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
417 check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
418 check_equivalence(r, fAppliedFull, that.fAppliedFull, fAppliedFullKey,
419 that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700420 break;
421 }
422}
423} // namespace
424
425static sk_sp<SkPathEffect> make_dash() {
426 static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f };
427 static const SkScalar kPhase = 0.75;
428 return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase);
429}
430
431static sk_sp<SkPathEffect> make_null_dash() {
432 static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0};
433 return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f);
434}
435
bsalomon72dc51c2016-04-27 06:46:23 -0700436template<typename GEO>
437static void test_basic(skiatest::Reporter* reporter, const GEO& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700438 sk_sp<SkPathEffect> dashPE = make_dash();
439
440 TestCase::SelfExpectations expectations;
441 SkPaint fill;
442
bsalomonfb083272016-05-04 08:27:41 -0700443 TestCase fillCase(geo, fill, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700444 expectations.fPEHasEffect = false;
445 expectations.fPEHasValidKey = false;
446 expectations.fStrokeApplies = false;
447 fillCase.testExpectations(reporter, expectations);
448 // Test that another GrShape instance built from the same primitive is the same.
bsalomonfb083272016-05-04 08:27:41 -0700449 TestCase(geo, fill, reporter).compare(reporter, fillCase,
450 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700451
452 SkPaint stroke2RoundBevel;
453 stroke2RoundBevel.setStyle(SkPaint::kStroke_Style);
454 stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap);
455 stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join);
456 stroke2RoundBevel.setStrokeWidth(2.f);
bsalomonfb083272016-05-04 08:27:41 -0700457 TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700458 expectations.fPEHasValidKey = true;
459 expectations.fPEHasEffect = false;
460 expectations.fStrokeApplies = true;
461 stroke2RoundBevelCase.testExpectations(reporter, expectations);
bsalomonfb083272016-05-04 08:27:41 -0700462 TestCase(geo, stroke2RoundBevel, reporter).compare(reporter, stroke2RoundBevelCase,
463 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700464
465 SkPaint stroke2RoundBevelDash = stroke2RoundBevel;
466 stroke2RoundBevelDash.setPathEffect(make_dash());
bsalomonfb083272016-05-04 08:27:41 -0700467 TestCase stroke2RoundBevelDashCase(geo, stroke2RoundBevelDash, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700468 expectations.fPEHasValidKey = true;
469 expectations.fPEHasEffect = true;
470 expectations.fStrokeApplies = true;
471 stroke2RoundBevelDashCase.testExpectations(reporter, expectations);
bsalomonfb083272016-05-04 08:27:41 -0700472 TestCase(geo, stroke2RoundBevelDash, reporter).compare(reporter, stroke2RoundBevelDashCase,
473 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700474
475 fillCase.compare(reporter, stroke2RoundBevelCase,
476 TestCase::kSameUpToStroke_ComparisonExpecation);
477 fillCase.compare(reporter, stroke2RoundBevelDashCase,
478 TestCase::kSameUpToPE_ComparisonExpecation);
479 stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
480 TestCase::kSameUpToPE_ComparisonExpecation);
bsalomon72dc51c2016-04-27 06:46:23 -0700481
bsalomonf0cf3552016-05-05 08:28:30 -0700482 // Stroke and fill cases
483 SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel;
484 stroke2RoundBevelAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
485 TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter);
486 expectations.fPEHasValidKey = true;
487 expectations.fPEHasEffect = false;
488 expectations.fStrokeApplies = true;
489 stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations);
490 TestCase(geo, stroke2RoundBevelAndFill, reporter).compare(reporter,
491 stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation);
492
493 SkPaint stroke2RoundBevelAndFillDash = stroke2RoundBevelDash;
494 stroke2RoundBevelAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
495 TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter);
496 expectations.fPEHasValidKey = true;
bsalomona0587862016-06-09 06:03:38 -0700497 expectations.fPEHasEffect = false;
bsalomonf0cf3552016-05-05 08:28:30 -0700498 expectations.fStrokeApplies = true;
499 stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations);
500 TestCase(geo, stroke2RoundBevelAndFillDash, reporter).compare(
501 reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation);
bsalomona0587862016-06-09 06:03:38 -0700502 stroke2RoundBevelAndFillDashCase.compare(reporter, stroke2RoundBevelAndFillCase,
503 TestCase::kAllSame_ComparisonExpecation);
bsalomonf0cf3552016-05-05 08:28:30 -0700504
bsalomon72dc51c2016-04-27 06:46:23 -0700505 SkPaint hairline;
506 hairline.setStyle(SkPaint::kStroke_Style);
507 hairline.setStrokeWidth(0.f);
bsalomonfb083272016-05-04 08:27:41 -0700508 TestCase hairlineCase(geo, hairline, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700509 // Since hairline style doesn't change the SkPath data, it is keyed identically to fill.
510 hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
bsalomon9ad5d7c2016-05-04 08:44:15 -0700511 REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline());
512 REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline());
513 REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline());
bsalomon47cc7692016-04-26 12:56:00 -0700514}
515
bsalomon97fd2d42016-05-09 13:02:01 -0700516template<typename GEO>
517static void test_scale(skiatest::Reporter* reporter, const GEO& geo) {
518 sk_sp<SkPathEffect> dashPE = make_dash();
519
520 static const SkScalar kS1 = 1.f;
521 static const SkScalar kS2 = 2.f;
522
523 SkPaint fill;
524 TestCase fillCase1(geo, fill, reporter, kS1);
525 TestCase fillCase2(geo, fill, reporter, kS2);
526 // Scale doesn't affect fills.
527 fillCase1.compare(reporter, fillCase2, TestCase::kAllSame_ComparisonExpecation);
528
529 SkPaint hairline;
530 hairline.setStyle(SkPaint::kStroke_Style);
531 hairline.setStrokeWidth(0.f);
532 TestCase hairlineCase1(geo, hairline, reporter, kS1);
533 TestCase hairlineCase2(geo, hairline, reporter, kS2);
534 // Scale doesn't affect hairlines.
535 hairlineCase1.compare(reporter, hairlineCase2, TestCase::kAllSame_ComparisonExpecation);
536
537 SkPaint stroke;
538 stroke.setStyle(SkPaint::kStroke_Style);
539 stroke.setStrokeWidth(2.f);
540 TestCase strokeCase1(geo, stroke, reporter, kS1);
541 TestCase strokeCase2(geo, stroke, reporter, kS2);
542 // Scale affects the stroke.
543 strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation);
544
545 SkPaint strokeDash = stroke;
546 strokeDash.setPathEffect(make_dash());
547 TestCase strokeDashCase1(geo, strokeDash, reporter, kS1);
548 TestCase strokeDashCase2(geo, strokeDash, reporter, kS2);
549 // Scale affects the dash and the stroke.
550 strokeDashCase1.compare(reporter, strokeDashCase2, TestCase::kSameUpToPE_ComparisonExpecation);
551
552 // Stroke and fill cases
553 SkPaint strokeAndFill = stroke;
554 strokeAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
555 TestCase strokeAndFillCase1(geo, strokeAndFill, reporter, kS1);
556 TestCase strokeAndFillCase2(geo, strokeAndFill, reporter, kS2);
bsalomona0587862016-06-09 06:03:38 -0700557 SkPaint strokeAndFillDash = strokeDash;
558 strokeAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
559 // Dash is ignored for stroke and fill
560 TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1);
561 TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2);
bsalomon97fd2d42016-05-09 13:02:01 -0700562 // Scale affects the stroke. Though, this can wind up creating a rect when the input is a rect.
563 // In that case we wind up with a pure geometry key and the geometries are the same.
564 SkRRect rrect;
bsalomon70493962016-06-10 08:05:14 -0700565 if (strokeAndFillCase1.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr, nullptr)) {
bsalomon97fd2d42016-05-09 13:02:01 -0700566 // We currently only expect to get here in the rect->rect case.
567 REPORTER_ASSERT(reporter, rrect.isRect());
bsalomonee295642016-06-06 14:01:25 -0700568 REPORTER_ASSERT(reporter,
bsalomon70493962016-06-10 08:05:14 -0700569 strokeAndFillCase1.baseShape().asRRect(&rrect, nullptr, nullptr, nullptr) &&
bsalomonee295642016-06-06 14:01:25 -0700570 rrect.isRect());
bsalomon97fd2d42016-05-09 13:02:01 -0700571 strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
572 TestCase::kAllSame_ComparisonExpecation);
573 } else {
574 strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
575 TestCase::kSameUpToStroke_ComparisonExpecation);
bsalomona0587862016-06-09 06:03:38 -0700576 strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2,
577 TestCase::kSameUpToStroke_ComparisonExpecation);
bsalomon97fd2d42016-05-09 13:02:01 -0700578 }
bsalomona0587862016-06-09 06:03:38 -0700579 strokeAndFillDashCase1.compare(reporter, strokeAndFillCase1,
580 TestCase::kAllSame_ComparisonExpecation);
581 strokeAndFillDashCase2.compare(reporter, strokeAndFillCase2,
582 TestCase::kAllSame_ComparisonExpecation);
bsalomon97fd2d42016-05-09 13:02:01 -0700583}
584
bsalomon72dc51c2016-04-27 06:46:23 -0700585template <typename GEO, typename T>
bsalomon06077562016-05-04 13:50:29 -0700586static void test_stroke_param_impl(skiatest::Reporter* reporter, const GEO& geo,
587 std::function<void(SkPaint*, T)> setter, T a, T b,
588 bool paramAffectsStroke,
589 bool paramAffectsDashAndStroke) {
590 // Set the stroke width so that we don't get hairline. However, call the setter afterward so
591 // that it can override the stroke width.
bsalomon47cc7692016-04-26 12:56:00 -0700592 SkPaint strokeA;
593 strokeA.setStyle(SkPaint::kStroke_Style);
594 strokeA.setStrokeWidth(2.f);
595 setter(&strokeA, a);
596 SkPaint strokeB;
597 strokeB.setStyle(SkPaint::kStroke_Style);
598 strokeB.setStrokeWidth(2.f);
599 setter(&strokeB, b);
600
bsalomonfb083272016-05-04 08:27:41 -0700601 TestCase strokeACase(geo, strokeA, reporter);
602 TestCase strokeBCase(geo, strokeB, reporter);
bsalomon06077562016-05-04 13:50:29 -0700603 if (paramAffectsStroke) {
604 strokeACase.compare(reporter, strokeBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
605 } else {
606 strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation);
607 }
bsalomon47cc7692016-04-26 12:56:00 -0700608
bsalomonf0cf3552016-05-05 08:28:30 -0700609 SkPaint strokeAndFillA = strokeA;
610 SkPaint strokeAndFillB = strokeB;
611 strokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style);
612 strokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style);
613 TestCase strokeAndFillACase(geo, strokeAndFillA, reporter);
614 TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter);
615 if (paramAffectsStroke) {
616 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
617 TestCase::kSameUpToStroke_ComparisonExpecation);
618 } else {
619 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
620 TestCase::kAllSame_ComparisonExpecation);
621 }
622
bsalomon47cc7692016-04-26 12:56:00 -0700623 // Make sure stroking params don't affect fill style.
624 SkPaint fillA = strokeA, fillB = strokeB;
625 fillA.setStyle(SkPaint::kFill_Style);
626 fillB.setStyle(SkPaint::kFill_Style);
bsalomonfb083272016-05-04 08:27:41 -0700627 TestCase fillACase(geo, fillA, reporter);
628 TestCase fillBCase(geo, fillB, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700629 fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
630
631 // Make sure just applying the dash but not stroke gives the same key for both stroking
632 // variations.
633 SkPaint dashA = strokeA, dashB = strokeB;
634 dashA.setPathEffect(make_dash());
635 dashB.setPathEffect(make_dash());
bsalomonfb083272016-05-04 08:27:41 -0700636 TestCase dashACase(geo, dashA, reporter);
637 TestCase dashBCase(geo, dashB, reporter);
bsalomon06077562016-05-04 13:50:29 -0700638 if (paramAffectsDashAndStroke) {
639 dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
640 } else {
641 dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation);
642 }
bsalomon47cc7692016-04-26 12:56:00 -0700643}
644
bsalomon06077562016-05-04 13:50:29 -0700645template <typename GEO, typename T>
646static void test_stroke_param(skiatest::Reporter* reporter, const GEO& geo,
647 std::function<void(SkPaint*, T)> setter, T a, T b) {
648 test_stroke_param_impl(reporter, geo, setter, a, b, true, true);
649};
650
651template <typename GEO>
652static void test_stroke_cap(skiatest::Reporter* reporter, const GEO& geo) {
653 GrShape shape(geo, GrStyle(SkStrokeRec::kHairline_InitStyle));
654 // The cap should only affect shapes that may be open.
655 bool affectsStroke = !shape.knownToBeClosed();
656 // Dashing adds ends that need caps.
657 bool affectsDashAndStroke = true;
658 test_stroke_param_impl<GEO, SkPaint::Cap>(
659 reporter,
660 geo,
661 [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
662 SkPaint::kButt_Cap, SkPaint::kRound_Cap,
663 affectsStroke,
664 affectsDashAndStroke);
665};
666
bsalomon72dc51c2016-04-27 06:46:23 -0700667template <typename GEO>
668static void test_miter_limit(skiatest::Reporter* reporter, const GEO& geo) {
bsalomon06077562016-05-04 13:50:29 -0700669 auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) {
670 p->setStrokeJoin(SkPaint::kMiter_Join);
671 p->setStrokeMiter(miter);
672 };
bsalomon47cc7692016-04-26 12:56:00 -0700673
bsalomon06077562016-05-04 13:50:29 -0700674 auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) {
675 p->setStrokeJoin(SkPaint::kRound_Join);
676 p->setStrokeMiter(miter);
677 };
bsalomon47cc7692016-04-26 12:56:00 -0700678
bsalomon06077562016-05-04 13:50:29 -0700679 // The miter limit should affect stroked and dashed-stroked cases when the join type is
680 // miter.
681 test_stroke_param_impl<GEO, SkScalar>(
682 reporter,
683 geo,
684 setMiterJoinAndLimit,
685 0.5f, 0.75f,
686 true,
687 true);
bsalomon47cc7692016-04-26 12:56:00 -0700688
bsalomon06077562016-05-04 13:50:29 -0700689 // The miter limit should not affect stroked and dashed-stroked cases when the join type is
690 // not miter.
691 test_stroke_param_impl<GEO, SkScalar>(
692 reporter,
693 geo,
694 setOtherJoinAndLimit,
695 0.5f, 0.75f,
696 false,
697 false);
bsalomon47cc7692016-04-26 12:56:00 -0700698}
699
bsalomon72dc51c2016-04-27 06:46:23 -0700700template<typename GEO>
701static void test_dash_fill(skiatest::Reporter* reporter, const GEO& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700702 // A dash with no stroke should have no effect
703 using DashFactoryFn = sk_sp<SkPathEffect>(*)();
704 for (DashFactoryFn md : {&make_dash, &make_null_dash}) {
705 SkPaint dashFill;
706 dashFill.setPathEffect((*md)());
bsalomonfb083272016-05-04 08:27:41 -0700707 TestCase dashFillCase(geo, dashFill, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700708
bsalomonfb083272016-05-04 08:27:41 -0700709 TestCase fillCase(geo, SkPaint(), reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700710 dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
711 }
712}
713
bsalomon72dc51c2016-04-27 06:46:23 -0700714template<typename GEO>
715void test_null_dash(skiatest::Reporter* reporter, const GEO& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700716 SkPaint fill;
717 SkPaint stroke;
718 stroke.setStyle(SkPaint::kStroke_Style);
719 stroke.setStrokeWidth(1.f);
720 SkPaint dash;
721 dash.setStyle(SkPaint::kStroke_Style);
722 dash.setStrokeWidth(1.f);
723 dash.setPathEffect(make_dash());
724 SkPaint nullDash;
725 nullDash.setStyle(SkPaint::kStroke_Style);
726 nullDash.setStrokeWidth(1.f);
727 nullDash.setPathEffect(make_null_dash());
728
bsalomonfb083272016-05-04 08:27:41 -0700729 TestCase fillCase(geo, fill, reporter);
730 TestCase strokeCase(geo, stroke, reporter);
731 TestCase dashCase(geo, dash, reporter);
732 TestCase nullDashCase(geo, nullDash, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700733
734 nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation);
735 nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation);
736 nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation);
737}
738
bsalomon72dc51c2016-04-27 06:46:23 -0700739template <typename GEO>
740void test_path_effect_makes_rrect(skiatest::Reporter* reporter, const GEO& geo) {
741 /**
742 * This path effect takes any input path and turns it into a rrect. It passes through stroke
743 * info.
744 */
745 class RRectPathEffect : SkPathEffect {
746 public:
747 static const SkRRect& RRect() {
748 static const SkRRect kRRect = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 3, 5);
749 return kRRect;
750 }
751
752 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
753 const SkRect* cullR) const override {
754 dst->reset();
755 dst->addRRect(RRect());
756 return true;
757 }
758 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
759 *dst = RRect().getBounds();
760 }
761 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new RRectPathEffect); }
762 Factory getFactory() const override { return nullptr; }
763 void toString(SkString*) const override {}
764 private:
765 RRectPathEffect() {}
766 };
767
768 SkPaint fill;
bsalomonfb083272016-05-04 08:27:41 -0700769 TestCase fillGeoCase(geo, fill, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700770
771 SkPaint pe;
772 pe.setPathEffect(RRectPathEffect::Make());
bsalomonfb083272016-05-04 08:27:41 -0700773 TestCase geoPECase(geo, pe, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700774
775 SkPaint peStroke;
776 peStroke.setPathEffect(RRectPathEffect::Make());
777 peStroke.setStrokeWidth(2.f);
778 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -0700779 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700780
781 fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation);
782 fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation);
783 geoPECase.compare(reporter, geoPEStrokeCase,
784 TestCase::kSameUpToStroke_ComparisonExpecation);
785
bsalomonfb083272016-05-04 08:27:41 -0700786 TestCase rrectFillCase(RRectPathEffect::RRect(), fill, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700787 SkPaint stroke = peStroke;
788 stroke.setPathEffect(nullptr);
bsalomonfb083272016-05-04 08:27:41 -0700789 TestCase rrectStrokeCase(RRectPathEffect::RRect(), stroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700790
791 SkRRect rrect;
792 // Applying the path effect should make a SkRRect shape. There is no further stroking in the
793 // geoPECase, so the full style should be the same as just the PE.
bsalomon70493962016-06-10 08:05:14 -0700794 REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().asRRect(&rrect, nullptr, nullptr,
795 nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -0700796 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
797 REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == rrectFillCase.baseKey());
798
bsalomon70493962016-06-10 08:05:14 -0700799 REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr,
800 nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -0700801 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
802 REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == rrectFillCase.baseKey());
803
804 // In the PE+stroke case applying the full style should be the same as just stroking the rrect.
bsalomon70493962016-06-10 08:05:14 -0700805 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().asRRect(&rrect, nullptr,
806 nullptr, nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -0700807 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
808 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == rrectFillCase.baseKey());
809
bsalomon70493962016-06-10 08:05:14 -0700810 REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().asRRect(&rrect, nullptr,
811 nullptr, nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -0700812 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() ==
813 rrectStrokeCase.appliedFullStyleKey());
814}
815
816template <typename GEO>
817void test_unknown_path_effect(skiatest::Reporter* reporter, const GEO& geo) {
818 /**
819 * This path effect just adds two lineTos to the input path.
820 */
821 class AddLineTosPathEffect : SkPathEffect {
822 public:
823 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
824 const SkRect* cullR) const override {
825 *dst = src;
826 dst->lineTo(0, 0);
827 dst->lineTo(10, 10);
828 return true;
829 }
830 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
831 *dst = src;
832 dst->growToInclude(0, 0);
833 dst->growToInclude(10, 10);
834 }
835 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new AddLineTosPathEffect); }
836 Factory getFactory() const override { return nullptr; }
837 void toString(SkString*) const override {}
838 private:
839 AddLineTosPathEffect() {}
840 };
841
bsalomon9ad5d7c2016-05-04 08:44:15 -0700842 // This path effect should make the keys invalid when it is applied. We only produce a path
bsalomon72dc51c2016-04-27 06:46:23 -0700843 // effect key for dash path effects. So the only way another arbitrary path effect can produce
844 // a styled result with a key is to produce a non-path shape that has a purely geometric key.
845 SkPaint peStroke;
846 peStroke.setPathEffect(AddLineTosPathEffect::Make());
847 peStroke.setStrokeWidth(2.f);
848 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -0700849 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700850 TestCase::SelfExpectations expectations;
851 expectations.fPEHasEffect = true;
852 expectations.fPEHasValidKey = false;
853 expectations.fStrokeApplies = true;
854 geoPEStrokeCase.testExpectations(reporter, expectations);
855}
856
bsalomon9ad5d7c2016-05-04 08:44:15 -0700857template <typename GEO>
858void test_make_hairline_path_effect(skiatest::Reporter* reporter, const GEO& geo, bool isNonPath) {
859 /**
860 * This path effect just changes the stroke rec to hairline.
861 */
862 class MakeHairlinePathEffect : SkPathEffect {
863 public:
864 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* strokeRec,
865 const SkRect* cullR) const override {
866 *dst = src;
867 strokeRec->setHairlineStyle();
868 return true;
869 }
bsalomon70493962016-06-10 08:05:14 -0700870 void computeFastBounds(SkRect* dst, const SkRect& src) const override { *dst = src; }
bsalomon9ad5d7c2016-05-04 08:44:15 -0700871 static sk_sp<SkPathEffect> Make() {
872 return sk_sp<SkPathEffect>(new MakeHairlinePathEffect);
873 }
874 Factory getFactory() const override { return nullptr; }
875 void toString(SkString*) const override {}
876 private:
877 MakeHairlinePathEffect() {}
878 };
879
880 SkPaint fill;
881 SkPaint pe;
882 pe.setPathEffect(MakeHairlinePathEffect::Make());
883
884 TestCase peCase(geo, pe, reporter);
885
bsalomonee295642016-06-06 14:01:25 -0700886 SkPath a, b, c;
bsalomon9ad5d7c2016-05-04 08:44:15 -0700887 peCase.baseShape().asPath(&a);
888 peCase.appliedPathEffectShape().asPath(&b);
bsalomonee295642016-06-06 14:01:25 -0700889 peCase.appliedFullStyleShape().asPath(&c);
bsalomon9ad5d7c2016-05-04 08:44:15 -0700890 if (isNonPath) {
bsalomonee295642016-06-06 14:01:25 -0700891 // RRect types can have a change in start index or direction after the PE is applied. This
892 // is because once the PE is applied, GrShape may canonicalize the dir and index since it
893 // is not germane to the styling any longer.
894 // Instead we just check that the paths would fill the same both before and after styling.
895 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
896 REPORTER_ASSERT(reporter, paths_fill_same(a, c));
bsalomon9ad5d7c2016-05-04 08:44:15 -0700897 } else {
bsalomona4817af2016-06-23 11:48:26 -0700898 // The base shape cannot perform canonicalization on the path's fill type because of an
899 // unknown path effect. However, after the path effect is applied the resulting hairline
900 // shape will canonicalize the path fill type since hairlines (and stroking in general)
901 // don't distinguish between even/odd and non-zero winding.
902 a.setFillType(b.getFillType());
bsalomonee295642016-06-06 14:01:25 -0700903 REPORTER_ASSERT(reporter, a == b);
904 REPORTER_ASSERT(reporter, a == c);
bsalomon9ad5d7c2016-05-04 08:44:15 -0700905 REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey().empty());
906 REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey().empty());
907 }
bsalomonee295642016-06-06 14:01:25 -0700908 REPORTER_ASSERT(reporter, peCase.appliedPathEffectShape().style().isSimpleHairline());
909 REPORTER_ASSERT(reporter, peCase.appliedFullStyleShape().style().isSimpleHairline());
bsalomon9ad5d7c2016-05-04 08:44:15 -0700910}
911
bsalomon4eeccc92016-04-27 13:30:25 -0700912/**
913 * isNonPath indicates whether the initial shape made from the path is expected to be recognized
914 * as a simpler shape type (e.g. rrect)
915 */
916void test_volatile_path(skiatest::Reporter* reporter, const SkPath& path,
917 bool isNonPath) {
918 SkPath vPath(path);
919 vPath.setIsVolatile(true);
920
921 SkPaint dashAndStroke;
922 dashAndStroke.setPathEffect(make_dash());
923 dashAndStroke.setStrokeWidth(2.f);
924 dashAndStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -0700925 TestCase volatileCase(vPath, dashAndStroke, reporter);
bsalomon4eeccc92016-04-27 13:30:25 -0700926 // We expect a shape made from a volatile path to have a key iff the shape is recognized
927 // as a specialized geometry.
928 if (isNonPath) {
929 REPORTER_ASSERT(reporter, SkToBool(volatileCase.baseKey().count()));
930 // In this case all the keys should be identical to the non-volatile case.
bsalomonfb083272016-05-04 08:27:41 -0700931 TestCase nonVolatileCase(path, dashAndStroke, reporter);
bsalomon4eeccc92016-04-27 13:30:25 -0700932 volatileCase.compare(reporter, nonVolatileCase, TestCase::kAllSame_ComparisonExpecation);
933 } else {
934 // None of the keys should be valid.
935 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.baseKey().count()));
936 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectKey().count()));
937 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedFullStyleKey().count()));
938 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectThenStrokeKey().count()));
939 }
940}
941
bsalomon409ed732016-04-27 12:36:02 -0700942template <typename GEO>
943void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const GEO& geo) {
944 /**
945 * This path effect returns an empty path.
946 */
947 class EmptyPathEffect : SkPathEffect {
948 public:
949 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
950 const SkRect* cullR) const override {
951 dst->reset();
952 return true;
953 }
954 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
955 dst->setEmpty();
956 }
957 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new EmptyPathEffect); }
958 Factory getFactory() const override { return nullptr; }
959 void toString(SkString*) const override {}
960 private:
961 EmptyPathEffect() {}
962 };
963
964 SkPath emptyPath;
965 GrShape emptyShape(emptyPath);
966 Key emptyKey;
967 make_key(&emptyKey, emptyShape);
bsalomon7c73a532016-05-11 15:15:56 -0700968 REPORTER_ASSERT(reporter, emptyShape.isEmpty());
bsalomon409ed732016-04-27 12:36:02 -0700969
970 SkPaint pe;
971 pe.setPathEffect(EmptyPathEffect::Make());
bsalomonfb083272016-05-04 08:27:41 -0700972 TestCase geoCase(geo, pe, reporter);
bsalomon409ed732016-04-27 12:36:02 -0700973 REPORTER_ASSERT(reporter, geoCase.appliedFullStyleKey() == emptyKey);
974 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectKey() == emptyKey);
975 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectThenStrokeKey() == emptyKey);
bsalomon7c73a532016-05-11 15:15:56 -0700976 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectShape().isEmpty());
977 REPORTER_ASSERT(reporter, geoCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -0700978
979 SkPaint peStroke;
980 peStroke.setPathEffect(EmptyPathEffect::Make());
981 peStroke.setStrokeWidth(2.f);
982 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -0700983 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon409ed732016-04-27 12:36:02 -0700984 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == emptyKey);
985 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == emptyKey);
986 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey);
bsalomon7c73a532016-05-11 15:15:56 -0700987 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty());
988 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -0700989}
990
bsalomond6723842016-06-07 12:20:15 -0700991template <typename GEO>
992void test_path_effect_fails(skiatest::Reporter* reporter, const GEO& geo) {
993 /**
994 * This path effect returns an empty path.
995 */
996 class FailurePathEffect : SkPathEffect {
997 public:
998 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
999 const SkRect* cullR) const override {
1000 return false;
1001 }
1002 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1003 *dst = src;
1004 }
1005 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new FailurePathEffect); }
1006 Factory getFactory() const override { return nullptr; }
1007 void toString(SkString*) const override {}
1008 private:
1009 FailurePathEffect() {}
1010 };
1011
1012 SkPaint fill;
1013 TestCase fillCase(geo, fill, reporter);
1014
1015 SkPaint pe;
1016 pe.setPathEffect(FailurePathEffect::Make());
1017 TestCase peCase(geo, pe, reporter);
1018
1019 SkPaint stroke;
1020 stroke.setStrokeWidth(2.f);
1021 stroke.setStyle(SkPaint::kStroke_Style);
1022 TestCase strokeCase(geo, stroke, reporter);
1023
1024 SkPaint peStroke = stroke;
1025 peStroke.setPathEffect(FailurePathEffect::Make());
1026 TestCase peStrokeCase(geo, peStroke, reporter);
1027
1028 // In general the path effect failure can cause some of the TestCase::compare() tests to fail
1029 // for at least two reasons: 1) We will initially treat the shape as unkeyable because of the
1030 // path effect, but then when the path effect fails we can key it. 2) GrShape will change its
1031 // mind about whether a unclosed rect is actually rect. The path effect initially bars us from
1032 // closing it but after the effect fails we can (for the fill+pe case). This causes different
1033 // routes through GrShape to have equivalent but different representations of the path (closed
1034 // or not) but that fill the same.
1035 SkPath a;
1036 SkPath b;
1037 fillCase.appliedPathEffectShape().asPath(&a);
1038 peCase.appliedPathEffectShape().asPath(&b);
1039 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1040
1041 fillCase.appliedFullStyleShape().asPath(&a);
1042 peCase.appliedFullStyleShape().asPath(&b);
1043 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1044
1045 strokeCase.appliedPathEffectShape().asPath(&a);
1046 peStrokeCase.appliedPathEffectShape().asPath(&b);
1047 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1048
1049 strokeCase.appliedFullStyleShape().asPath(&a);
1050 peStrokeCase.appliedFullStyleShape().asPath(&b);
1051 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1052}
1053
bsalomon409ed732016-04-27 12:36:02 -07001054void test_empty_shape(skiatest::Reporter* reporter) {
1055 SkPath emptyPath;
1056 SkPaint fill;
bsalomonfb083272016-05-04 08:27:41 -07001057 TestCase fillEmptyCase(emptyPath, fill, reporter);
bsalomon7c73a532016-05-11 15:15:56 -07001058 REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty());
1059 REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty());
1060 REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001061
1062 Key emptyKey(fillEmptyCase.baseKey());
1063 REPORTER_ASSERT(reporter, emptyKey.count());
1064 TestCase::SelfExpectations expectations;
1065 expectations.fStrokeApplies = false;
1066 expectations.fPEHasEffect = false;
1067 // This will test whether applying style preserves emptiness
1068 fillEmptyCase.testExpectations(reporter, expectations);
1069
1070 // Stroking an empty path should have no effect
1071 SkPath emptyPath2;
1072 SkPaint stroke;
1073 stroke.setStrokeWidth(2.f);
1074 stroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001075 TestCase strokeEmptyCase(emptyPath2, stroke, reporter);
bsalomon409ed732016-04-27 12:36:02 -07001076 strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
1077
1078 // Dashing and stroking an empty path should have no effect
1079 SkPath emptyPath3;
1080 SkPaint dashAndStroke;
1081 dashAndStroke.setPathEffect(make_dash());
1082 dashAndStroke.setStrokeWidth(2.f);
1083 dashAndStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001084 TestCase dashAndStrokeEmptyCase(emptyPath3, dashAndStroke, reporter);
bsalomon409ed732016-04-27 12:36:02 -07001085 dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase,
1086 TestCase::kAllSame_ComparisonExpecation);
bsalomon5e410b42016-04-28 09:30:46 -07001087
1088 // A shape made from an empty rrect should behave the same as an empty path.
1089 SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty());
1090 REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type);
bsalomonfb083272016-05-04 08:27:41 -07001091 TestCase dashAndStrokeEmptyRRectCase(emptyRRect, dashAndStroke, reporter);
bsalomon5e410b42016-04-28 09:30:46 -07001092 dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
1093 TestCase::kAllSame_ComparisonExpecation);
1094
1095 // Same for a rect.
1096 SkRect emptyRect = SkRect::MakeEmpty();
bsalomonfb083272016-05-04 08:27:41 -07001097 TestCase dashAndStrokeEmptyRectCase(emptyRect, dashAndStroke, reporter);
bsalomon5e410b42016-04-28 09:30:46 -07001098 dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase,
1099 TestCase::kAllSame_ComparisonExpecation);
bsalomon409ed732016-04-27 12:36:02 -07001100}
1101
bsalomon70493962016-06-10 08:05:14 -07001102// rect and oval types have rrect start indices that collapse to the same point. Here we select the
1103// canonical point in these cases.
1104unsigned canonicalize_rrect_start(int s, const SkRRect& rrect) {
1105 switch (rrect.getType()) {
1106 case SkRRect::kRect_Type:
1107 return (s + 1) & 0b110;
1108 case SkRRect::kOval_Type:
1109 return s & 0b110;
1110 default:
1111 return s;
1112 }
1113}
1114
1115void test_rrect(skiatest::Reporter* r, const SkRRect& rrect) {
bsalomoncadb5a22016-06-10 18:28:06 -07001116 enum Style {
bsalomon70493962016-06-10 08:05:14 -07001117 kFill,
1118 kStroke,
1119 kHairline,
1120 kStrokeAndFill
1121 };
1122
1123 // SkStrokeRec has no default cons., so init with kFill before calling the setters below.
1124 SkStrokeRec strokeRecs[4] { SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle,
1125 SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle};
1126 strokeRecs[kFill].setFillStyle();
1127 strokeRecs[kStroke].setStrokeStyle(2.f);
1128 strokeRecs[kHairline].setHairlineStyle();
1129 strokeRecs[kStrokeAndFill].setStrokeStyle(3.f, true);
1130 sk_sp<SkPathEffect> dashEffect = make_dash();
1131
bsalomoncadb5a22016-06-10 18:28:06 -07001132 static constexpr Style kStyleCnt = static_cast<Style>(SK_ARRAY_COUNT(strokeRecs));
1133
1134 auto index = [](bool inverted,
1135 SkPath::Direction dir,
1136 unsigned start,
1137 Style style,
1138 bool dash) -> int {
1139 return inverted * (2 * 8 * kStyleCnt * 2) +
1140 dir * ( 8 * kStyleCnt * 2) +
1141 start * ( kStyleCnt * 2) +
1142 style * ( 2) +
1143 dash;
1144 };
1145 static const SkPath::Direction kSecondDirection = static_cast<SkPath::Direction>(1);
1146 const int cnt = index(true, kSecondDirection, 7, static_cast<Style>(kStyleCnt - 1), true) + 1;
1147 SkAutoTArray<GrShape> shapes(cnt);
1148 for (bool inverted : {false, true}) {
1149 for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
1150 for (unsigned start = 0; start < 8; ++start) {
1151 for (Style style : {kFill, kStroke, kHairline, kStrokeAndFill}) {
1152 for (bool dash : {false, true}) {
bsalomon70493962016-06-10 08:05:14 -07001153 SkPathEffect* pe = dash ? dashEffect.get() : nullptr;
bsalomoncadb5a22016-06-10 18:28:06 -07001154 shapes[index(inverted, dir, start, style, dash)] =
1155 GrShape(rrect, dir, start, SkToBool(inverted),
bsalomon70493962016-06-10 08:05:14 -07001156 GrStyle(strokeRecs[style], pe));
1157 }
1158 }
1159 }
1160 }
1161 }
1162
bsalomonfd32df72016-06-14 14:37:21 -07001163 // Get the keys for some example shape instances that we'll use for comparision against the
1164 // rest.
1165 static constexpr SkPath::Direction kExamplesDir = SkPath::kCW_Direction;
1166 static constexpr unsigned kExamplesStart = 0;
1167 const GrShape& exampleFillCase = shapes[index(false, kExamplesDir, kExamplesStart, kFill,
1168 false)];
bsalomon70493962016-06-10 08:05:14 -07001169 Key exampleFillCaseKey;
1170 make_key(&exampleFillCaseKey, exampleFillCase);
1171
bsalomonfd32df72016-06-14 14:37:21 -07001172 const GrShape& exampleStrokeAndFillCase = shapes[index(false, kExamplesDir, kExamplesStart,
1173 kStrokeAndFill, false)];
bsalomon70493962016-06-10 08:05:14 -07001174 Key exampleStrokeAndFillCaseKey;
1175 make_key(&exampleStrokeAndFillCaseKey, exampleStrokeAndFillCase);
1176
bsalomonfd32df72016-06-14 14:37:21 -07001177 const GrShape& exampleInvFillCase = shapes[index(true, kExamplesDir, kExamplesStart, kFill,
1178 false)];
bsalomon70493962016-06-10 08:05:14 -07001179 Key exampleInvFillCaseKey;
1180 make_key(&exampleInvFillCaseKey, exampleInvFillCase);
1181
bsalomonfd32df72016-06-14 14:37:21 -07001182 const GrShape& exampleInvStrokeAndFillCase = shapes[index(true, kExamplesDir, kExamplesStart,
1183 kStrokeAndFill, false)];
bsalomon70493962016-06-10 08:05:14 -07001184 Key exampleInvStrokeAndFillCaseKey;
1185 make_key(&exampleInvStrokeAndFillCaseKey, exampleInvStrokeAndFillCase);
1186
bsalomonfd32df72016-06-14 14:37:21 -07001187 const GrShape& exampleStrokeCase = shapes[index(false, kExamplesDir, kExamplesStart, kStroke,
1188 false)];
bsalomon70493962016-06-10 08:05:14 -07001189 Key exampleStrokeCaseKey;
1190 make_key(&exampleStrokeCaseKey, exampleStrokeCase);
1191
bsalomonfd32df72016-06-14 14:37:21 -07001192 const GrShape& exampleInvStrokeCase = shapes[index(true, kExamplesDir, kExamplesStart, kStroke,
1193 false)];
1194 Key exampleInvStrokeCaseKey;
1195 make_key(&exampleInvStrokeCaseKey, exampleInvStrokeCase);
1196
1197 const GrShape& exampleHairlineCase = shapes[index(false, kExamplesDir, kExamplesStart,
1198 kHairline, false)];
bsalomon70493962016-06-10 08:05:14 -07001199 Key exampleHairlineCaseKey;
1200 make_key(&exampleHairlineCaseKey, exampleHairlineCase);
1201
bsalomonfd32df72016-06-14 14:37:21 -07001202 const GrShape& exampleInvHairlineCase = shapes[index(true, kExamplesDir, kExamplesStart,
1203 kHairline, false)];
1204 Key exampleInvHairlineCaseKey;
1205 make_key(&exampleInvHairlineCaseKey, exampleInvHairlineCase);
1206
bsalomon70493962016-06-10 08:05:14 -07001207 // These are dummy initializations to suppress warnings.
bsalomoncadb5a22016-06-10 18:28:06 -07001208 SkRRect queryRR = SkRRect::MakeEmpty();
1209 SkPath::Direction queryDir = SkPath::kCW_Direction;
1210 unsigned queryStart = ~0U;
1211 bool queryInverted = true;
bsalomon70493962016-06-10 08:05:14 -07001212
bsalomoncadb5a22016-06-10 18:28:06 -07001213 REPORTER_ASSERT(r, exampleFillCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
1214 REPORTER_ASSERT(r, queryRR == rrect);
1215 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1216 REPORTER_ASSERT(r, 0 == queryStart);
1217 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001218
bsalomoncadb5a22016-06-10 18:28:06 -07001219 REPORTER_ASSERT(r, exampleInvFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1220 &queryInverted));
1221 REPORTER_ASSERT(r, queryRR == rrect);
1222 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1223 REPORTER_ASSERT(r, 0 == queryStart);
1224 REPORTER_ASSERT(r, queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001225
bsalomoncadb5a22016-06-10 18:28:06 -07001226 REPORTER_ASSERT(r, exampleStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1227 &queryInverted));
1228 REPORTER_ASSERT(r, queryRR == rrect);
1229 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1230 REPORTER_ASSERT(r, 0 == queryStart);
1231 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001232
bsalomoncadb5a22016-06-10 18:28:06 -07001233 REPORTER_ASSERT(r, exampleInvStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1234 &queryInverted));
1235 REPORTER_ASSERT(r, queryRR == rrect);
1236 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1237 REPORTER_ASSERT(r, 0 == queryStart);
1238 REPORTER_ASSERT(r, queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001239
bsalomoncadb5a22016-06-10 18:28:06 -07001240 REPORTER_ASSERT(r, exampleHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1241 &queryInverted));
1242 REPORTER_ASSERT(r, queryRR == rrect);
1243 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1244 REPORTER_ASSERT(r, 0 == queryStart);
1245 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001246
bsalomonfd32df72016-06-14 14:37:21 -07001247 REPORTER_ASSERT(r, exampleInvHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1248 &queryInverted));
1249 REPORTER_ASSERT(r, queryRR == rrect);
1250 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1251 REPORTER_ASSERT(r, 0 == queryStart);
1252 REPORTER_ASSERT(r, queryInverted);
1253
bsalomoncadb5a22016-06-10 18:28:06 -07001254 REPORTER_ASSERT(r, exampleStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
1255 REPORTER_ASSERT(r, queryRR == rrect);
1256 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1257 REPORTER_ASSERT(r, 0 == queryStart);
1258 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001259
bsalomonfd32df72016-06-14 14:37:21 -07001260 REPORTER_ASSERT(r, exampleInvStrokeCase.asRRect(&queryRR, &queryDir, &queryStart,
1261 &queryInverted));
1262 REPORTER_ASSERT(r, queryRR == rrect);
1263 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1264 REPORTER_ASSERT(r, 0 == queryStart);
1265 REPORTER_ASSERT(r, queryInverted);
1266
bsalomon70493962016-06-10 08:05:14 -07001267 // Remember that the key reflects the geometry before styling is applied.
1268 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvFillCaseKey);
1269 REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeAndFillCaseKey);
1270 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeAndFillCaseKey);
1271 REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001272 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001273 REPORTER_ASSERT(r, exampleFillCaseKey == exampleHairlineCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001274 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvHairlineCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001275 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvFillCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001276 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvStrokeCaseKey);
1277 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvHairlineCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001278
bsalomoncadb5a22016-06-10 18:28:06 -07001279 for (bool inverted : {false, true}) {
1280 for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
1281 for (unsigned start = 0; start < 8; ++start) {
1282 for (bool dash : {false, true}) {
1283 const GrShape& fillCase = shapes[index(inverted, dir, start, kFill, dash)];
bsalomon70493962016-06-10 08:05:14 -07001284 Key fillCaseKey;
1285 make_key(&fillCaseKey, fillCase);
1286
bsalomoncadb5a22016-06-10 18:28:06 -07001287 const GrShape& strokeAndFillCase = shapes[index(inverted, dir, start,
1288 kStrokeAndFill, dash)];
bsalomon70493962016-06-10 08:05:14 -07001289 Key strokeAndFillCaseKey;
1290 make_key(&strokeAndFillCaseKey, strokeAndFillCase);
1291
1292 // Both fill and stroke-and-fill shapes must respect the inverseness and both
1293 // ignore dashing.
1294 REPORTER_ASSERT(r, !fillCase.style().pathEffect());
1295 REPORTER_ASSERT(r, !strokeAndFillCase.style().pathEffect());
1296 TestCase a(fillCase, r);
1297 TestCase b(inverted ? exampleInvFillCase : exampleFillCase, r);
1298 TestCase c(strokeAndFillCase, r);
1299 TestCase d(inverted ? exampleInvStrokeAndFillCase
1300 : exampleStrokeAndFillCase, r);
1301 a.compare(r, b, TestCase::kAllSame_ComparisonExpecation);
1302 c.compare(r, d, TestCase::kAllSame_ComparisonExpecation);
1303
bsalomoncadb5a22016-06-10 18:28:06 -07001304 const GrShape& strokeCase = shapes[index(inverted, dir, start, kStroke, dash)];
1305 const GrShape& hairlineCase = shapes[index(inverted, dir, start, kHairline,
1306 dash)];
bsalomon70493962016-06-10 08:05:14 -07001307
1308 TestCase e(strokeCase, r);
bsalomon70493962016-06-10 08:05:14 -07001309 TestCase g(hairlineCase, r);
bsalomon70493962016-06-10 08:05:14 -07001310
bsalomonfd32df72016-06-14 14:37:21 -07001311 // Both hairline and stroke shapes must respect the dashing.
bsalomon70493962016-06-10 08:05:14 -07001312 if (dash) {
bsalomonfd32df72016-06-14 14:37:21 -07001313 // Dashing always ignores the inverseness. skbug.com/5421
1314 TestCase f(exampleStrokeCase, r);
1315 TestCase h(exampleHairlineCase, r);
bsalomoncadb5a22016-06-10 18:28:06 -07001316 unsigned expectedStart = canonicalize_rrect_start(start, rrect);
bsalomon70493962016-06-10 08:05:14 -07001317 REPORTER_ASSERT(r, strokeCase.style().pathEffect());
1318 REPORTER_ASSERT(r, hairlineCase.style().pathEffect());
1319
bsalomoncadb5a22016-06-10 18:28:06 -07001320 REPORTER_ASSERT(r, strokeCase.asRRect(&queryRR, &queryDir, &queryStart,
1321 &queryInverted));
1322 REPORTER_ASSERT(r, queryRR == rrect);
1323 REPORTER_ASSERT(r, queryDir == dir);
1324 REPORTER_ASSERT(r, queryStart == expectedStart);
1325 REPORTER_ASSERT(r, !queryInverted);
1326 REPORTER_ASSERT(r, hairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1327 &queryInverted));
1328 REPORTER_ASSERT(r, queryRR == rrect);
1329 REPORTER_ASSERT(r, queryDir == dir);
1330 REPORTER_ASSERT(r, queryStart == expectedStart);
1331 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001332
1333 // The pre-style case for the dash will match the non-dash example iff the
1334 // dir and start match (dir=cw, start=0).
bsalomoncadb5a22016-06-10 18:28:06 -07001335 if (0 == expectedStart && SkPath::kCW_Direction == dir) {
bsalomon70493962016-06-10 08:05:14 -07001336 e.compare(r, f, TestCase::kSameUpToPE_ComparisonExpecation);
1337 g.compare(r, h, TestCase::kSameUpToPE_ComparisonExpecation);
1338 } else {
1339 e.compare(r, f, TestCase::kAllDifferent_ComparisonExpecation);
1340 g.compare(r, h, TestCase::kAllDifferent_ComparisonExpecation);
1341 }
1342 } else {
bsalomonfd32df72016-06-14 14:37:21 -07001343 TestCase f(inverted ? exampleInvStrokeCase : exampleStrokeCase, r);
1344 TestCase h(inverted ? exampleInvHairlineCase : exampleHairlineCase, r);
bsalomon70493962016-06-10 08:05:14 -07001345 REPORTER_ASSERT(r, !strokeCase.style().pathEffect());
1346 REPORTER_ASSERT(r, !hairlineCase.style().pathEffect());
1347 e.compare(r, f, TestCase::kAllSame_ComparisonExpecation);
1348 g.compare(r, h, TestCase::kAllSame_ComparisonExpecation);
1349 }
1350 }
1351 }
1352 }
1353 }
1354}
1355
bsalomon0a0f67e2016-06-28 11:56:42 -07001356void test_lines(skiatest::Reporter* r) {
1357 static constexpr SkPoint kA { 1, 1};
1358 static constexpr SkPoint kB { 5, -9};
1359 static constexpr SkPoint kC {-3, 17};
1360
1361 SkPath lineAB;
1362 lineAB.moveTo(kA);
1363 lineAB.lineTo(kB);
1364
1365 SkPath lineBA;
1366 lineBA.moveTo(kB);
1367 lineBA.lineTo(kA);
1368
1369 SkPath lineAC;
1370 lineAC.moveTo(kB);
1371 lineAC.lineTo(kC);
1372
1373 SkPath invLineAB = lineAB;
1374 invLineAB.setFillType(SkPath::kInverseEvenOdd_FillType);
1375
1376 SkPaint fill;
1377 SkPaint stroke;
1378 stroke.setStyle(SkPaint::kStroke_Style);
1379 stroke.setStrokeWidth(2.f);
1380 SkPaint hairline;
1381 hairline.setStyle(SkPaint::kStroke_Style);
1382 hairline.setStrokeWidth(0.f);
1383 SkPaint dash = stroke;
1384 dash.setPathEffect(make_dash());
1385
1386 TestCase fillAB(lineAB, fill, r);
1387 TestCase fillEmpty(SkPath(), fill, r);
1388 fillAB.compare(r, fillEmpty, TestCase::kAllSame_ComparisonExpecation);
1389 REPORTER_ASSERT(r, !fillAB.baseShape().asLine(nullptr, nullptr));
1390
1391 TestCase strokeAB(lineAB, stroke, r);
1392 TestCase strokeBA(lineBA, stroke, r);
1393 TestCase strokeAC(lineAC, stroke, r);
1394
1395 TestCase hairlineAB(lineAB, hairline, r);
1396 TestCase hairlineBA(lineBA, hairline, r);
1397 TestCase hairlineAC(lineAC, hairline, r);
1398
1399 TestCase dashAB(lineAB, dash, r);
1400 TestCase dashBA(lineBA, dash, r);
1401 TestCase dashAC(lineAC, dash, r);
1402
1403 strokeAB.compare(r, fillAB, TestCase::kAllDifferent_ComparisonExpecation);
1404
1405 strokeAB.compare(r, strokeBA, TestCase::kAllSame_ComparisonExpecation);
1406 strokeAB.compare(r, strokeAC, TestCase::kAllDifferent_ComparisonExpecation);
1407
1408 hairlineAB.compare(r, hairlineBA, TestCase::kAllSame_ComparisonExpecation);
1409 hairlineAB.compare(r, hairlineAC, TestCase::kAllDifferent_ComparisonExpecation);
1410
1411 dashAB.compare(r, dashBA, TestCase::kAllDifferent_ComparisonExpecation);
1412 dashAB.compare(r, dashAC, TestCase::kAllDifferent_ComparisonExpecation);
1413
1414 strokeAB.compare(r, hairlineAB, TestCase::kSameUpToStroke_ComparisonExpecation);
1415
1416 // One of dashAB or dashBA should have the same line as strokeAB. It depends upon how
1417 // GrShape canonicalizes line endpoints (when it can, i.e. when not dashed).
1418 bool canonicalizeAsAB;
1419 SkPoint canonicalPts[2] {kA, kB};
1420 // Init these to suppress warnings.
1421 bool inverted = true;
1422 SkPoint pts[2] {{0, 0}, {0, 0}};
1423 REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted);
1424 if (pts[0] == kA && pts[1] == kB) {
1425 canonicalizeAsAB = true;
1426 } else if (pts[1] == kA && pts[0] == kB) {
1427 canonicalizeAsAB = false;
1428 SkTSwap(canonicalPts[0], canonicalPts[1]);
1429 } else {
1430 ERRORF(r, "Should return pts (a,b) or (b, a)");
1431 return;
1432 };
1433
1434 strokeAB.compare(r, canonicalizeAsAB ? dashAB : dashBA,
1435 TestCase::kSameUpToPE_ComparisonExpecation);
1436 REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted &&
1437 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1438 REPORTER_ASSERT(r, hairlineAB.baseShape().asLine(pts, &inverted) && !inverted &&
1439 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1440 REPORTER_ASSERT(r, dashAB.baseShape().asLine(pts, &inverted) && !inverted &&
1441 pts[0] == kA && pts[1] == kB);
1442 REPORTER_ASSERT(r, dashBA.baseShape().asLine(pts, &inverted) && !inverted &&
1443 pts[0] == kB && pts[1] == kA);
1444
1445
1446 TestCase strokeInvAB(invLineAB, stroke, r);
1447 TestCase hairlineInvAB(invLineAB, hairline, r);
1448 TestCase dashInvAB(invLineAB, dash, r);
1449 strokeInvAB.compare(r, strokeAB, TestCase::kAllDifferent_ComparisonExpecation);
1450 hairlineInvAB.compare(r, hairlineAB, TestCase::kAllDifferent_ComparisonExpecation);
1451 // Dashing ignores inverse.
1452 dashInvAB.compare(r, dashAB, TestCase::kAllSame_ComparisonExpecation);
1453
1454 REPORTER_ASSERT(r, strokeInvAB.baseShape().asLine(pts, &inverted) && inverted &&
1455 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1456 REPORTER_ASSERT(r, hairlineInvAB.baseShape().asLine(pts, &inverted) && inverted &&
1457 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1458 // Dashing ignores inverse.
1459 REPORTER_ASSERT(r, dashInvAB.baseShape().asLine(pts, &inverted) && !inverted &&
1460 pts[0] == kA && pts[1] == kB);
1461
1462}
1463
bsalomon47cc7692016-04-26 12:56:00 -07001464DEF_TEST(GrShape, reporter) {
bsalomonee295642016-06-06 14:01:25 -07001465 for (auto r : { SkRect::MakeWH(10, 20),
1466 SkRect::MakeWH(-10, -20),
1467 SkRect::MakeWH(-10, 20),
1468 SkRect::MakeWH(10, -20)}) {
1469 test_basic(reporter, r);
1470 test_scale(reporter, r);
1471 test_dash_fill(reporter, r);
1472 test_null_dash(reporter, r);
1473 // Test modifying various stroke params.
1474 test_stroke_param<SkRect, SkScalar>(
1475 reporter, r,
1476 [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
1477 SkIntToScalar(2), SkIntToScalar(4));
1478 test_stroke_param<SkRect, SkPaint::Join>(
1479 reporter, r,
1480 [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
1481 SkPaint::kMiter_Join, SkPaint::kRound_Join);
1482 test_stroke_cap(reporter, r);
1483 test_miter_limit(reporter, r);
1484 test_path_effect_makes_rrect(reporter, r);
1485 test_unknown_path_effect(reporter, r);
1486 test_path_effect_makes_empty_shape(reporter, r);
bsalomond6723842016-06-07 12:20:15 -07001487 test_path_effect_fails(reporter, r);
bsalomonee295642016-06-06 14:01:25 -07001488 test_make_hairline_path_effect(reporter, r, true);
bsalomon398e3f42016-06-13 10:22:48 -07001489 GrShape shape(r);
bsalomon0a0f67e2016-06-28 11:56:42 -07001490 REPORTER_ASSERT(reporter, !shape.asLine(nullptr, nullptr));
bsalomonee295642016-06-06 14:01:25 -07001491 }
bsalomon47cc7692016-04-26 12:56:00 -07001492
1493 for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)),
bsalomonee295642016-06-06 14:01:25 -07001494 SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4),
1495 SkRRect::MakeOval(SkRect::MakeWH(20, 20))}) {
bsalomon47cc7692016-04-26 12:56:00 -07001496 test_basic(reporter, rr);
bsalomon70493962016-06-10 08:05:14 -07001497 test_rrect(reporter, rr);
bsalomon97fd2d42016-05-09 13:02:01 -07001498 test_scale(reporter, rr);
bsalomon47cc7692016-04-26 12:56:00 -07001499 test_dash_fill(reporter, rr);
1500 test_null_dash(reporter, rr);
1501 // Test modifying various stroke params.
bsalomon72dc51c2016-04-27 06:46:23 -07001502 test_stroke_param<SkRRect, SkScalar>(
bsalomon47cc7692016-04-26 12:56:00 -07001503 reporter, rr,
1504 [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
1505 SkIntToScalar(2), SkIntToScalar(4));
bsalomon72dc51c2016-04-27 06:46:23 -07001506 test_stroke_param<SkRRect, SkPaint::Join>(
bsalomon47cc7692016-04-26 12:56:00 -07001507 reporter, rr,
1508 [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
1509 SkPaint::kMiter_Join, SkPaint::kRound_Join);
bsalomon06077562016-05-04 13:50:29 -07001510 test_stroke_cap(reporter, rr);
bsalomon47cc7692016-04-26 12:56:00 -07001511 test_miter_limit(reporter, rr);
bsalomon72dc51c2016-04-27 06:46:23 -07001512 test_path_effect_makes_rrect(reporter, rr);
1513 test_unknown_path_effect(reporter, rr);
bsalomon409ed732016-04-27 12:36:02 -07001514 test_path_effect_makes_empty_shape(reporter, rr);
bsalomond6723842016-06-07 12:20:15 -07001515 test_path_effect_fails(reporter, rr);
bsalomon9ad5d7c2016-05-04 08:44:15 -07001516 test_make_hairline_path_effect(reporter, rr, true);
bsalomon398e3f42016-06-13 10:22:48 -07001517 GrShape shape(rr);
bsalomon0a0f67e2016-06-28 11:56:42 -07001518 REPORTER_ASSERT(reporter, !shape.asLine(nullptr, nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001519 }
1520
1521 struct TestPath {
bsalomon93f66bc2016-06-21 08:35:49 -07001522 TestPath(const SkPath& path, bool isRRectFill, bool isRRectStroke, bool isLine,
1523 const SkRRect& rrect)
bsalomon72dc51c2016-04-27 06:46:23 -07001524 : fPath(path)
1525 , fIsRRectForFill(isRRectFill)
1526 , fIsRRectForStroke(isRRectStroke)
bsalomon398e3f42016-06-13 10:22:48 -07001527 , fIsLine(isLine)
bsalomon72dc51c2016-04-27 06:46:23 -07001528 , fRRect(rrect) {}
1529 SkPath fPath;
1530 bool fIsRRectForFill;
1531 bool fIsRRectForStroke;
bsalomon398e3f42016-06-13 10:22:48 -07001532 bool fIsLine;
bsalomon72dc51c2016-04-27 06:46:23 -07001533 SkRRect fRRect;
1534 };
1535 SkTArray<TestPath> paths;
1536
1537 SkPath circlePath;
1538 circlePath.addCircle(10, 10, 10);
bsalomon398e3f42016-06-13 10:22:48 -07001539 paths.emplace_back(circlePath, true, true, false, SkRRect::MakeOval(SkRect::MakeWH(20,20)));
bsalomon72dc51c2016-04-27 06:46:23 -07001540
1541 SkPath rectPath;
1542 rectPath.addRect(SkRect::MakeWH(10, 10));
bsalomon398e3f42016-06-13 10:22:48 -07001543 paths.emplace_back(rectPath, true, true, false, SkRRect::MakeRect(SkRect::MakeWH(10, 10)));
bsalomon72dc51c2016-04-27 06:46:23 -07001544
1545 SkPath openRectPath;
1546 openRectPath.moveTo(0, 0);
1547 openRectPath.lineTo(10, 0);
1548 openRectPath.lineTo(10, 10);
1549 openRectPath.lineTo(0, 10);
bsalomon398e3f42016-06-13 10:22:48 -07001550 paths.emplace_back(openRectPath, true, false, false, SkRRect::MakeRect(SkRect::MakeWH(10, 10)));
bsalomon72dc51c2016-04-27 06:46:23 -07001551
1552 SkPath quadPath;
1553 quadPath.quadTo(10, 10, 5, 8);
bsalomon398e3f42016-06-13 10:22:48 -07001554 paths.emplace_back(quadPath, false, false, false, SkRRect());
1555
1556 SkPath linePath;
1557 linePath.lineTo(10, 10);
1558 paths.emplace_back(linePath, false, false, true, SkRRect());
bsalomon72dc51c2016-04-27 06:46:23 -07001559
1560 for (auto testPath : paths) {
bsalomon70493962016-06-10 08:05:14 -07001561 for (bool inverseFill : {false, true}) {
1562 if (inverseFill) {
1563 if (testPath.fPath.getFillType() == SkPath::kEvenOdd_FillType) {
1564 testPath.fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
1565 } else {
1566 SkASSERT(testPath.fPath.getFillType() == SkPath::kWinding_FillType);
1567 testPath.fPath.setFillType(SkPath::kInverseWinding_FillType);
1568 }
1569 }
1570 const SkPath& path = testPath.fPath;
1571 // These tests all assume that the original GrShape for fill and stroke will be the
1572 // same.
1573 // However, that is not the case in special cases (e.g. an unclosed rect becomes a RRect
bsalomon0a0f67e2016-06-28 11:56:42 -07001574 // GrShape with a fill style but becomes a Path GrShape when stroked). Similarly, a path
1575 // that is a line becomes empty when filled but is special-cased as a line when stroked.
1576 if (testPath.fIsRRectForFill == testPath.fIsRRectForStroke && !testPath.fIsLine) {
bsalomon70493962016-06-10 08:05:14 -07001577 test_basic(reporter, path);
1578 test_null_dash(reporter, path);
1579 test_path_effect_makes_rrect(reporter, path);
1580 }
1581 test_scale(reporter, path);
1582 // This test uses a stroking paint, hence use of fIsRRectForStroke
bsalomon0a0f67e2016-06-28 11:56:42 -07001583 test_volatile_path(reporter, path, testPath.fIsRRectForStroke || testPath.fIsLine);
bsalomon70493962016-06-10 08:05:14 -07001584 test_dash_fill(reporter, path);
1585 // Test modifying various stroke params.
1586 test_stroke_param<SkPath, SkScalar>(
1587 reporter, path,
1588 [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
1589 SkIntToScalar(2), SkIntToScalar(4));
1590 test_stroke_param<SkPath, SkPaint::Join>(
1591 reporter, path,
1592 [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
1593 SkPaint::kMiter_Join, SkPaint::kRound_Join);
1594 test_stroke_cap(reporter, path);
1595 test_miter_limit(reporter, path);
1596 test_unknown_path_effect(reporter, path);
1597 test_path_effect_makes_empty_shape(reporter, path);
1598 test_path_effect_fails(reporter, path);
bsalomon0a0f67e2016-06-28 11:56:42 -07001599 test_make_hairline_path_effect(reporter, path, testPath.fIsRRectForStroke ||
1600 testPath.fIsLine);
bsalomon72dc51c2016-04-27 06:46:23 -07001601 }
bsalomon70493962016-06-10 08:05:14 -07001602 }
bsalomonfd32df72016-06-14 14:37:21 -07001603
bsalomon70493962016-06-10 08:05:14 -07001604 for (auto testPath : paths) {
1605 const SkPath& path = testPath.fPath;
bsalomon72dc51c2016-04-27 06:46:23 -07001606
1607 SkPaint fillPaint;
bsalomonfb083272016-05-04 08:27:41 -07001608 TestCase fillPathCase(path, fillPaint, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001609 SkRRect rrect;
1610 REPORTER_ASSERT(reporter, testPath.fIsRRectForFill ==
bsalomon70493962016-06-10 08:05:14 -07001611 fillPathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
1612 nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001613 if (testPath.fIsRRectForFill) {
bsalomon70493962016-06-10 08:05:14 -07001614 TestCase fillPathCase2(testPath.fPath, fillPaint, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001615 REPORTER_ASSERT(reporter, rrect == testPath.fRRect);
bsalomonfb083272016-05-04 08:27:41 -07001616 TestCase fillRRectCase(rrect, fillPaint, reporter);
bsalomon70493962016-06-10 08:05:14 -07001617 fillPathCase2.compare(reporter, fillRRectCase,
1618 TestCase::kAllSame_ComparisonExpecation);
bsalomon72dc51c2016-04-27 06:46:23 -07001619 }
bsalomon72dc51c2016-04-27 06:46:23 -07001620 SkPaint strokePaint;
1621 strokePaint.setStrokeWidth(3.f);
1622 strokePaint.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001623 TestCase strokePathCase(path, strokePaint, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001624 REPORTER_ASSERT(reporter, testPath.fIsRRectForStroke ==
bsalomon70493962016-06-10 08:05:14 -07001625 strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
1626 nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001627 if (testPath.fIsRRectForStroke) {
1628 REPORTER_ASSERT(reporter, rrect == testPath.fRRect);
bsalomonfb083272016-05-04 08:27:41 -07001629 TestCase strokeRRectCase(rrect, strokePaint, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001630 strokePathCase.compare(reporter, strokeRRectCase,
bsalomonee295642016-06-06 14:01:25 -07001631 TestCase::kAllSame_ComparisonExpecation);
bsalomon72dc51c2016-04-27 06:46:23 -07001632 }
bsalomon47cc7692016-04-26 12:56:00 -07001633 }
bsalomon409ed732016-04-27 12:36:02 -07001634
bsalomon4eeccc92016-04-27 13:30:25 -07001635 // Test a volatile empty path.
1636 test_volatile_path(reporter, SkPath(), true);
1637
bsalomon409ed732016-04-27 12:36:02 -07001638 test_empty_shape(reporter);
bsalomon0a0f67e2016-06-28 11:56:42 -07001639
1640 test_lines(reporter);
bsalomon47cc7692016-04-26 12:56:00 -07001641}
1642
1643#endif