blob: 6e1901ff0229cd1f9b7bf22e9a233fafa2431451 [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) {
bsalomon164fd9f2016-08-26 06:45:06 -070040 // We test the bounds by rasterizing the path into a kRes by kRes grid. The bounds is
41 // mapped to the range kRes/4 to 3*kRes/4 in x and y. A difference clip is used to avoid
42 // rendering within the bounds (with a tolerance). Then we render the path and check that
43 // everything got clipped out.
bsalomon9fb42032016-05-13 09:23:38 -070044 static constexpr int kRes = 2000;
45 // This tolerance is in units of 1/kRes fractions of the bounds width/height.
46 static constexpr int kTol = 0;
47 GR_STATIC_ASSERT(kRes % 4 == 0);
48 SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
49 sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
50 surface->getCanvas()->clear(0x0);
51 SkRect clip = SkRect::MakeXYWH(kRes/4, kRes/4, kRes/2, kRes/2);
52 SkMatrix matrix;
53 matrix.setRectToRect(bounds, clip, SkMatrix::kFill_ScaleToFit);
54 clip.outset(SkIntToScalar(kTol), SkIntToScalar(kTol));
55 surface->getCanvas()->clipRect(clip, SkRegion::kDifference_Op);
56 surface->getCanvas()->concat(matrix);
57 SkPaint whitePaint;
58 whitePaint.setColor(SK_ColorWHITE);
59 surface->getCanvas()->drawPath(path, whitePaint);
60 SkPixmap pixmap;
61 surface->getCanvas()->peekPixels(&pixmap);
62#if defined(SK_BUILD_FOR_WIN)
63 // The static constexpr version in #else causes cl.exe to crash.
64 const uint8_t* kZeros = reinterpret_cast<uint8_t*>(calloc(kRes, 1));
65#else
66 static constexpr uint8_t kZeros[kRes] = {0};
67#endif
bsalomon164fd9f2016-08-26 06:45:06 -070068 for (int y = 0; y < kRes; ++y) {
bsalomon9fb42032016-05-13 09:23:38 -070069 const uint8_t* row = pixmap.addr8(0, y);
70 if (0 != memcmp(kZeros, row, kRes)) {
71 return false;
72 }
73 }
74#ifdef SK_BUILD_FOR_WIN
75 free(const_cast<uint8_t*>(kZeros));
76#endif
77 return true;
78}
bsalomon72dc51c2016-04-27 06:46:23 -070079
bsalomon9fb42032016-05-13 09:23:38 -070080namespace {
bsalomona395f7c2016-08-24 17:47:40 -070081/**
82 * Geo is a factory for creating a GrShape from another representation. It also answers some
83 * questions about expected behavior for GrShape given the inputs.
84 */
85class Geo {
86public:
87 virtual ~Geo() {};
88 virtual GrShape makeShape(const SkPaint&) const = 0;
89 virtual SkPath path() const = 0;
90 // These functions allow tests to check for special cases where style gets
91 // applied by GrShape in its constructor (without calling GrShape::applyStyle).
92 // These unfortunately rely on knowing details of GrShape's implementation.
93 // These predicates are factored out here to avoid littering the rest of the
94 // test code with GrShape implementation details.
95 virtual bool fillChangesGeom() const { return false; }
96 virtual bool strokeIsConvertedToFill() const { return false; }
97 virtual bool strokeAndFillIsConvertedToFill(const SkPaint&) const { return false; }
98 // Is this something we expect GrShape to recognize as something simpler than a path.
99 virtual bool isNonPath(const SkPaint& paint) const { return true; }
100};
101
102class RectGeo : public Geo {
103public:
104 RectGeo(const SkRect& rect) : fRect(rect) {}
105
106 SkPath path() const override {
107 SkPath path;
108 path.addRect(fRect);
109 return path;
110 }
111
112 GrShape makeShape(const SkPaint& paint) const override {
113 return GrShape(fRect, paint);
114 }
115
116 bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
117 SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
118 // Converted to an outset rectangle.
119 return paint.getStrokeJoin() == SkPaint::kMiter_Join &&
120 paint.getStrokeMiter() >= SK_ScalarSqrt2;
121 }
122
123private:
124 SkRect fRect;
125};
126
127class RRectGeo : public Geo {
128public:
129 RRectGeo(const SkRRect& rrect) : fRRect(rrect) {}
130
131 GrShape makeShape(const SkPaint& paint) const override {
132 return GrShape(fRRect, paint);
133 }
134
135 SkPath path() const override {
136 SkPath path;
137 path.addRRect(fRRect);
138 return path;
139 }
140
141 bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
142 SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
143 if (fRRect.isRect()) {
144 return RectGeo(fRRect.rect()).strokeAndFillIsConvertedToFill(paint);
145 }
146 return false;
147 }
148
149private:
150 SkRRect fRRect;
151};
152
153class PathGeo : public Geo {
154public:
155 enum class Invert { kNo, kYes };
156
157 PathGeo(const SkPath& path, Invert invert) : fPath(path) {
158 SkASSERT(!path.isInverseFillType());
159 if (Invert::kYes == invert) {
160 if (fPath.getFillType() == SkPath::kEvenOdd_FillType) {
161 fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
162 } else {
163 SkASSERT(fPath.getFillType() == SkPath::kWinding_FillType);
164 fPath.setFillType(SkPath::kInverseWinding_FillType);
165 }
166 }
167 }
168
169 GrShape makeShape(const SkPaint& paint) const override {
170 return GrShape(fPath, paint);
171 }
172
173 SkPath path() const override { return fPath; }
174
175 bool fillChangesGeom() const override {
176 // unclosed rects get closed. Lines get turned into empty geometry
177 return this->isUnclosedRect() || (fPath.isLine(nullptr) && !fPath.isInverseFillType());
178 }
179
180 bool strokeIsConvertedToFill() const override {
181 return this->isAxisAlignedLine();
182 }
183
184 bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
185 SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
186 if (this->isAxisAlignedLine()) {
187 // The fill is ignored (zero area) and the stroke is converted to a rrect.
188 return true;
189 }
190 SkRect rect;
191 unsigned start;
192 SkPath::Direction dir;
193 if (SkPathPriv::IsSimpleClosedRect(fPath, &rect, &dir, &start)) {
194 return RectGeo(rect).strokeAndFillIsConvertedToFill(paint);
195 }
196 return false;
197 }
198
199 bool isNonPath(const SkPaint& paint) const override {
200 return fPath.isLine(nullptr) || fPath.isEmpty();
201 }
202
203private:
204 bool isAxisAlignedLine() const {
205 SkPoint pts[2];
206 if (!fPath.isLine(pts)) {
207 return false;
208 }
209 return pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY;
210 }
211
212 bool isUnclosedRect() const {
213 bool closed;
214 return fPath.isRect(nullptr, &closed, nullptr) && !closed;
215 }
216
217 SkPath fPath;
218};
219
220class RRectPathGeo : public PathGeo {
221public:
222 enum class RRectForStroke { kNo, kYes };
223
224 RRectPathGeo(const SkPath& path, const SkRRect& equivalentRRect, RRectForStroke rrectForStroke,
225 Invert invert)
226 : PathGeo(path, invert)
227 , fRRect(equivalentRRect)
228 , fRRectForStroke(rrectForStroke) {}
229
230 RRectPathGeo(const SkPath& path, const SkRect& equivalentRect, RRectForStroke rrectForStroke,
231 Invert invert)
232 : RRectPathGeo(path, SkRRect::MakeRect(equivalentRect), rrectForStroke, invert) {}
233
234 bool isNonPath(const SkPaint& paint) const override {
235 if (SkPaint::kFill_Style == paint.getStyle() || RRectForStroke::kYes == fRRectForStroke) {
236 return true;
237 }
238 return false;
239 }
240
241 const SkRRect& rrect() const { return fRRect; }
242
243private:
244 SkRRect fRRect;
245 RRectForStroke fRRectForStroke;
246};
247
bsalomon47cc7692016-04-26 12:56:00 -0700248class TestCase {
249public:
bsalomona395f7c2016-08-24 17:47:40 -0700250 TestCase(const Geo& geo, const SkPaint& paint, skiatest::Reporter* r,
251 SkScalar scale = SK_Scalar1) : fBase(geo.makeShape(paint)) {
bsalomon97fd2d42016-05-09 13:02:01 -0700252 this->init(r, scale);
bsalomon47cc7692016-04-26 12:56:00 -0700253 }
254
bsalomona395f7c2016-08-24 17:47:40 -0700255 template<typename... ShapeArgs>
256 TestCase(skiatest::Reporter* r, ShapeArgs... shapeArgs)
257 : fBase(shapeArgs...) {
258 this->init(r, SK_Scalar1);
259 }
260
bsalomon70493962016-06-10 08:05:14 -0700261 TestCase(const GrShape& shape, skiatest::Reporter* r, SkScalar scale = SK_Scalar1)
262 : fBase(shape) {
263 this->init(r, scale);
264 }
265
bsalomon47cc7692016-04-26 12:56:00 -0700266 struct SelfExpectations {
267 bool fPEHasEffect;
268 bool fPEHasValidKey;
269 bool fStrokeApplies;
270 };
271
272 void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const;
273
274 enum ComparisonExpecation {
275 kAllDifferent_ComparisonExpecation,
276 kSameUpToPE_ComparisonExpecation,
277 kSameUpToStroke_ComparisonExpecation,
278 kAllSame_ComparisonExpecation,
279 };
280
281 void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const;
282
bsalomon72dc51c2016-04-27 06:46:23 -0700283 const GrShape& baseShape() const { return fBase; }
284 const GrShape& appliedPathEffectShape() const { return fAppliedPE; }
285 const GrShape& appliedFullStyleShape() const { return fAppliedFull; }
286
287 // The returned array's count will be 0 if the key shape has no key.
288 const Key& baseKey() const { return fBaseKey; }
289 const Key& appliedPathEffectKey() const { return fAppliedPEKey; }
290 const Key& appliedFullStyleKey() const { return fAppliedFullKey; }
bsalomon409ed732016-04-27 12:36:02 -0700291 const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; }
bsalomon72dc51c2016-04-27 06:46:23 -0700292
bsalomon47cc7692016-04-26 12:56:00 -0700293private:
bsalomon9fb42032016-05-13 09:23:38 -0700294 static void CheckBounds(skiatest::Reporter* r, const GrShape& shape, const SkRect& bounds) {
295 SkPath path;
296 shape.asPath(&path);
297 // If the bounds are empty, the path ought to be as well.
bsalomon0ae36a22016-07-18 07:31:13 -0700298 if (bounds.fLeft > bounds.fRight || bounds.fTop > bounds.fBottom) {
bsalomon9fb42032016-05-13 09:23:38 -0700299 REPORTER_ASSERT(r, path.isEmpty());
300 return;
301 }
302 if (path.isEmpty()) {
303 return;
304 }
bsalomon70493962016-06-10 08:05:14 -0700305 // The bounds API explicitly calls out that it does not consider inverseness.
306 SkPath p = path;
307 p.setFillType(SkPath::ConvertToNonInverseFillType(path.getFillType()));
308 REPORTER_ASSERT(r, test_bounds_by_rasterizing(p, bounds));
bsalomon9fb42032016-05-13 09:23:38 -0700309 }
310
bsalomon97fd2d42016-05-09 13:02:01 -0700311 void init(skiatest::Reporter* r, SkScalar scale) {
312 fAppliedPE = fBase.applyStyle(GrStyle::Apply::kPathEffectOnly, scale);
313 fAppliedPEThenStroke = fAppliedPE.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec,
314 scale);
315 fAppliedFull = fBase.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale);
bsalomon47cc7692016-04-26 12:56:00 -0700316
bsalomon72dc51c2016-04-27 06:46:23 -0700317 make_key(&fBaseKey, fBase);
318 make_key(&fAppliedPEKey, fAppliedPE);
319 make_key(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
320 make_key(&fAppliedFullKey, fAppliedFull);
bsalomonfb083272016-05-04 08:27:41 -0700321
322 // Applying the path effect and then the stroke should always be the same as applying
323 // both in one go.
324 REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey);
325 SkPath a, b;
326 fAppliedPEThenStroke.asPath(&a);
327 fAppliedFull.asPath(&b);
bsalomonee295642016-06-06 14:01:25 -0700328 // If the output of the path effect is a rrect then it is possible for a and b to be
329 // different paths that fill identically. The reason is that fAppliedFull will do this:
330 // base -> apply path effect -> rrect_as_path -> stroke -> stroked_rrect_as_path
331 // fAppliedPEThenStroke will have converted the rrect_as_path back to a rrect. However,
332 // now that there is no longer a path effect, the direction and starting index get
333 // canonicalized before the stroke.
bsalomon70493962016-06-10 08:05:14 -0700334 if (fAppliedPE.asRRect(nullptr, nullptr, nullptr, nullptr)) {
bsalomonee295642016-06-06 14:01:25 -0700335 REPORTER_ASSERT(r, paths_fill_same(a, b));
336 } else {
337 REPORTER_ASSERT(r, a == b);
338 }
bsalomon7c73a532016-05-11 15:15:56 -0700339 REPORTER_ASSERT(r, fAppliedFull.isEmpty() == fAppliedPEThenStroke.isEmpty());
340
341 SkPath path;
342 fBase.asPath(&path);
343 REPORTER_ASSERT(r, path.isEmpty() == fBase.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700344 REPORTER_ASSERT(r, path.getSegmentMasks() == fBase.segmentMask());
bsalomon7c73a532016-05-11 15:15:56 -0700345 fAppliedPE.asPath(&path);
346 REPORTER_ASSERT(r, path.isEmpty() == fAppliedPE.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700347 REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedPE.segmentMask());
bsalomon7c73a532016-05-11 15:15:56 -0700348 fAppliedFull.asPath(&path);
349 REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700350 REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedFull.segmentMask());
bsalomonfb083272016-05-04 08:27:41 -0700351
bsalomon9fb42032016-05-13 09:23:38 -0700352 CheckBounds(r, fBase, fBase.bounds());
353 CheckBounds(r, fAppliedPE, fAppliedPE.bounds());
354 CheckBounds(r, fAppliedPEThenStroke, fAppliedPEThenStroke.bounds());
355 CheckBounds(r, fAppliedFull, fAppliedFull.bounds());
bsalomon0a0f67e2016-06-28 11:56:42 -0700356 SkRect styledBounds = fBase.styledBounds();
bsalomon9fb42032016-05-13 09:23:38 -0700357 CheckBounds(r, fAppliedFull, styledBounds);
bsalomon0a0f67e2016-06-28 11:56:42 -0700358 styledBounds = fAppliedPE.styledBounds();
bsalomon9fb42032016-05-13 09:23:38 -0700359 CheckBounds(r, fAppliedFull, styledBounds);
360
bsalomonfb083272016-05-04 08:27:41 -0700361 // Check that the same path is produced when style is applied by GrShape and GrStyle.
362 SkPath preStyle;
363 SkPath postPathEffect;
364 SkPath postAllStyle;
365
366 fBase.asPath(&preStyle);
bsalomon1a0b9ed2016-05-06 11:07:03 -0700367 SkStrokeRec postPEStrokeRec(SkStrokeRec::kFill_InitStyle);
bsalomon97fd2d42016-05-09 13:02:01 -0700368 if (fBase.style().applyPathEffectToPath(&postPathEffect, &postPEStrokeRec, preStyle,
369 scale)) {
bsalomon1a0b9ed2016-05-06 11:07:03 -0700370 // run postPathEffect through GrShape to get any geometry reductions that would have
371 // occurred to fAppliedPE.
372 GrShape(postPathEffect, GrStyle(postPEStrokeRec, nullptr)).asPath(&postPathEffect);
373
bsalomonfb083272016-05-04 08:27:41 -0700374 SkPath testPath;
375 fAppliedPE.asPath(&testPath);
376 REPORTER_ASSERT(r, testPath == postPathEffect);
bsalomon1a0b9ed2016-05-06 11:07:03 -0700377 REPORTER_ASSERT(r, postPEStrokeRec.hasEqualEffect(fAppliedPE.style().strokeRec()));
bsalomonfb083272016-05-04 08:27:41 -0700378 }
379 SkStrokeRec::InitStyle fillOrHairline;
bsalomon97fd2d42016-05-09 13:02:01 -0700380 if (fBase.style().applyToPath(&postAllStyle, &fillOrHairline, preStyle, scale)) {
bsalomonfb083272016-05-04 08:27:41 -0700381 SkPath testPath;
382 fAppliedFull.asPath(&testPath);
bsalomon1b28c1a2016-06-20 12:28:17 -0700383 if (fBase.style().hasPathEffect()) {
384 // Because GrShape always does two-stage application when there is a path effect
385 // there may be a reduction/canonicalization step between the path effect and
386 // strokerec not reflected in postAllStyle since it applied both the path effect
387 // and strokerec without analyzing the intermediate path.
388 REPORTER_ASSERT(r, paths_fill_same(postAllStyle, testPath));
389 } else {
390 // Make sure that postAllStyle sees any reductions/canonicalizations that GrShape
391 // would apply.
392 GrShape(postAllStyle, GrStyle(fillOrHairline)).asPath(&postAllStyle);
393 REPORTER_ASSERT(r, testPath == postAllStyle);
394 }
395
bsalomonfb083272016-05-04 08:27:41 -0700396 if (fillOrHairline == SkStrokeRec::kFill_InitStyle) {
397 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleFill());
398 } else {
399 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleHairline());
400 }
401 }
bsalomon47cc7692016-04-26 12:56:00 -0700402 }
403
404 GrShape fBase;
405 GrShape fAppliedPE;
406 GrShape fAppliedPEThenStroke;
407 GrShape fAppliedFull;
408
409 Key fBaseKey;
410 Key fAppliedPEKey;
411 Key fAppliedPEThenStrokeKey;
412 Key fAppliedFullKey;
bsalomon47cc7692016-04-26 12:56:00 -0700413};
414
415void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const {
bsalomon47cc7692016-04-26 12:56:00 -0700416 // The base's key should always be valid (unless the path is volatile)
bsalomon72dc51c2016-04-27 06:46:23 -0700417 REPORTER_ASSERT(reporter, fBaseKey.count());
bsalomon47cc7692016-04-26 12:56:00 -0700418 if (expectations.fPEHasEffect) {
419 REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700420 REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedPEKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700421 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700422 REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedFullKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700423 if (expectations.fStrokeApplies && expectations.fPEHasValidKey) {
424 REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700425 REPORTER_ASSERT(reporter, SkToBool(fAppliedFullKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700426 }
427 } else {
428 REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey);
bsalomonfb083272016-05-04 08:27:41 -0700429 SkPath a, b;
bsalomon72dc51c2016-04-27 06:46:23 -0700430 fBase.asPath(&a);
431 fAppliedPE.asPath(&b);
432 REPORTER_ASSERT(reporter, a == b);
bsalomon47cc7692016-04-26 12:56:00 -0700433 if (expectations.fStrokeApplies) {
434 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
435 } else {
436 REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey);
437 }
438 }
439}
440
bsalomonac5fcea2016-06-23 12:23:07 -0700441static bool can_interchange_winding_and_even_odd_fill(const GrShape& shape) {
442 SkPath path;
443 shape.asPath(&path);
444 if (shape.style().hasNonDashPathEffect()) {
445 return false;
446 }
447 const SkStrokeRec::Style strokeRecStyle = shape.style().strokeRec().getStyle();
448 return strokeRecStyle == SkStrokeRec::kStroke_Style ||
449 strokeRecStyle == SkStrokeRec::kHairline_Style ||
450 (shape.style().isSimpleFill() && path.isConvex());
451}
452
453static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrShape& b,
454 const Key& keyA, const Key& keyB) {
bsalomonee295642016-06-06 14:01:25 -0700455 // GrShape only respects the input winding direction and start point for rrect shapes
456 // when there is a path effect. Thus, if there are two GrShapes representing the same rrect
457 // but one has a path effect in its style and the other doesn't then asPath() and the unstyled
458 // key will differ. GrShape will have canonicalized the direction and start point for the shape
459 // without the path effect. If *both* have path effects then they should have both preserved
460 // the direction and starting point.
461
462 // The asRRect() output params are all initialized just to silence compiler warnings about
463 // uninitialized variables.
464 SkRRect rrectA = SkRRect::MakeEmpty(), rrectB = SkRRect::MakeEmpty();
465 SkPath::Direction dirA = SkPath::kCW_Direction, dirB = SkPath::kCW_Direction;
466 unsigned startA = ~0U, startB = ~0U;
bsalomon70493962016-06-10 08:05:14 -0700467 bool invertedA = true, invertedB = true;
bsalomonee295642016-06-06 14:01:25 -0700468
bsalomon70493962016-06-10 08:05:14 -0700469 bool aIsRRect = a.asRRect(&rrectA, &dirA, &startA, &invertedA);
470 bool bIsRRect = b.asRRect(&rrectB, &dirB, &startB, &invertedB);
bsalomonee295642016-06-06 14:01:25 -0700471 bool aHasPE = a.style().hasPathEffect();
472 bool bHasPE = b.style().hasPathEffect();
473 bool allowSameRRectButDiffStartAndDir = (aIsRRect && bIsRRect) && (aHasPE != bHasPE);
bsalomon425c27f2016-06-23 13:18:45 -0700474 // GrShape will close paths with simple fill style.
475 bool allowedClosednessDiff = (a.style().isSimpleFill() != b.style().isSimpleFill());
bsalomonee295642016-06-06 14:01:25 -0700476 SkPath pathA, pathB;
477 a.asPath(&pathA);
478 b.asPath(&pathB);
bsalomon70493962016-06-10 08:05:14 -0700479
bsalomonfd32df72016-06-14 14:37:21 -0700480 // Having a dash path effect can allow 'a' but not 'b' to turn a inverse fill type into a
481 // non-inverse fill type (or vice versa).
bsalomon70493962016-06-10 08:05:14 -0700482 bool ignoreInversenessDifference = false;
483 if (pathA.isInverseFillType() != pathB.isInverseFillType()) {
484 const GrShape* s1 = pathA.isInverseFillType() ? &a : &b;
485 const GrShape* s2 = pathA.isInverseFillType() ? &b : &a;
bsalomonfd32df72016-06-14 14:37:21 -0700486 bool canDropInverse1 = s1->style().isDashed();
487 bool canDropInverse2 = s2->style().isDashed();
488 ignoreInversenessDifference = (canDropInverse1 != canDropInverse2);
bsalomon70493962016-06-10 08:05:14 -0700489 }
bsalomona4817af2016-06-23 11:48:26 -0700490 bool ignoreWindingVsEvenOdd = false;
491 if (SkPath::ConvertToNonInverseFillType(pathA.getFillType()) !=
492 SkPath::ConvertToNonInverseFillType(pathB.getFillType())) {
bsalomonac5fcea2016-06-23 12:23:07 -0700493 bool aCanChange = can_interchange_winding_and_even_odd_fill(a);
494 bool bCanChange = can_interchange_winding_and_even_odd_fill(b);
bsalomona4817af2016-06-23 11:48:26 -0700495 if (aCanChange != bCanChange) {
496 ignoreWindingVsEvenOdd = true;
497 }
498 }
bsalomonee295642016-06-06 14:01:25 -0700499 if (allowSameRRectButDiffStartAndDir) {
500 REPORTER_ASSERT(r, rrectA == rrectB);
501 REPORTER_ASSERT(r, paths_fill_same(pathA, pathB));
bsalomon70493962016-06-10 08:05:14 -0700502 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
bsalomonee295642016-06-06 14:01:25 -0700503 } else {
bsalomon70493962016-06-10 08:05:14 -0700504 SkPath pA = pathA;
505 SkPath pB = pathB;
bsalomon425c27f2016-06-23 13:18:45 -0700506 REPORTER_ASSERT(r, a.inverseFilled() == pA.isInverseFillType());
507 REPORTER_ASSERT(r, b.inverseFilled() == pB.isInverseFillType());
bsalomon70493962016-06-10 08:05:14 -0700508 if (ignoreInversenessDifference) {
509 pA.setFillType(SkPath::ConvertToNonInverseFillType(pathA.getFillType()));
510 pB.setFillType(SkPath::ConvertToNonInverseFillType(pathB.getFillType()));
bsalomona4817af2016-06-23 11:48:26 -0700511 }
512 if (ignoreWindingVsEvenOdd) {
513 pA.setFillType(pA.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
514 : SkPath::kEvenOdd_FillType);
515 pB.setFillType(pB.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
516 : SkPath::kEvenOdd_FillType);
517 }
518 if (!ignoreInversenessDifference && !ignoreWindingVsEvenOdd) {
bsalomon70493962016-06-10 08:05:14 -0700519 REPORTER_ASSERT(r, keyA == keyB);
bsalomona4817af2016-06-23 11:48:26 -0700520 } else {
521 REPORTER_ASSERT(r, keyA != keyB);
bsalomon70493962016-06-10 08:05:14 -0700522 }
bsalomon425c27f2016-06-23 13:18:45 -0700523 if (allowedClosednessDiff) {
bsalomon93f66bc2016-06-21 08:35:49 -0700524 // GrShape will close paths with simple fill style. Make the non-filled path closed
525 // so that the comparision will succeed. Make sure both are closed before comparing.
526 pA.close();
527 pB.close();
528 }
bsalomon70493962016-06-10 08:05:14 -0700529 REPORTER_ASSERT(r, pA == pB);
bsalomonee295642016-06-06 14:01:25 -0700530 REPORTER_ASSERT(r, aIsRRect == bIsRRect);
531 if (aIsRRect) {
532 REPORTER_ASSERT(r, rrectA == rrectB);
533 REPORTER_ASSERT(r, dirA == dirB);
534 REPORTER_ASSERT(r, startA == startB);
bsalomon70493962016-06-10 08:05:14 -0700535 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
bsalomonee295642016-06-06 14:01:25 -0700536 }
537 }
538 REPORTER_ASSERT(r, a.isEmpty() == b.isEmpty());
bsalomon425c27f2016-06-23 13:18:45 -0700539 REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeClosed() == b.knownToBeClosed());
540 // closedness can affect convexity.
541 REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeConvex() == b.knownToBeConvex());
542 if (a.knownToBeConvex()) {
543 REPORTER_ASSERT(r, pathA.isConvex());
544 }
545 if (b.knownToBeConvex()) {
546 REPORTER_ASSERT(r, pathB.isConvex());
547 }
bsalomonee295642016-06-06 14:01:25 -0700548 REPORTER_ASSERT(r, a.bounds() == b.bounds());
bsalomon06115ee2016-06-07 06:28:51 -0700549 REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask());
bsalomon0a0f67e2016-06-28 11:56:42 -0700550 // Init these to suppress warnings.
551 SkPoint pts[4] {{0, 0,}, {0, 0}, {0, 0}, {0, 0}} ;
552 bool invertedLine[2] {true, true};
553 REPORTER_ASSERT(r, a.asLine(pts, &invertedLine[0]) == b.asLine(pts + 2, &invertedLine[1]));
bsalomon425c27f2016-06-23 13:18:45 -0700554 // mayBeInverseFilledAfterStyling() is allowed to differ if one has a arbitrary PE and the other
555 // doesn't (since the PE can set any fill type on its output path).
556 // Moreover, dash style explicitly ignores inverseness. So if one is dashed but not the other
557 // then they may disagree about inverseness.
558 if (a.style().hasNonDashPathEffect() == b.style().hasNonDashPathEffect() &&
559 a.style().isDashed() == b.style().isDashed()) {
560 REPORTER_ASSERT(r, a.mayBeInverseFilledAfterStyling() ==
561 b.mayBeInverseFilledAfterStyling());
562 }
bsalomon0a0f67e2016-06-28 11:56:42 -0700563 if (a.asLine(nullptr, nullptr)) {
bsalomon398e3f42016-06-13 10:22:48 -0700564 REPORTER_ASSERT(r, pts[2] == pts[0] && pts[3] == pts[1]);
bsalomon0a0f67e2016-06-28 11:56:42 -0700565 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedLine[0] == invertedLine[1]);
566 REPORTER_ASSERT(r, invertedLine[0] == a.inverseFilled());
567 REPORTER_ASSERT(r, invertedLine[1] == b.inverseFilled());
bsalomon398e3f42016-06-13 10:22:48 -0700568 }
bsalomon425c27f2016-06-23 13:18:45 -0700569 REPORTER_ASSERT(r, ignoreInversenessDifference || a.inverseFilled() == b.inverseFilled());
bsalomonee295642016-06-06 14:01:25 -0700570}
571
572void TestCase::compare(skiatest::Reporter* r, const TestCase& that,
bsalomon47cc7692016-04-26 12:56:00 -0700573 ComparisonExpecation expectation) const {
bsalomon72dc51c2016-04-27 06:46:23 -0700574 SkPath a, b;
bsalomon47cc7692016-04-26 12:56:00 -0700575 switch (expectation) {
576 case kAllDifferent_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700577 REPORTER_ASSERT(r, fBaseKey != that.fBaseKey);
578 REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
579 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700580 break;
581 case kSameUpToPE_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700582 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
583 REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
584 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700585 break;
586 case kSameUpToStroke_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700587 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
588 check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
589 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700590 break;
591 case kAllSame_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700592 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
593 check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
594 check_equivalence(r, fAppliedFull, that.fAppliedFull, fAppliedFullKey,
595 that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700596 break;
597 }
598}
599} // namespace
600
601static sk_sp<SkPathEffect> make_dash() {
602 static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f };
603 static const SkScalar kPhase = 0.75;
604 return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase);
605}
606
607static sk_sp<SkPathEffect> make_null_dash() {
608 static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0};
609 return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f);
610}
611
bsalomona395f7c2016-08-24 17:47:40 -0700612static void test_basic(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700613 sk_sp<SkPathEffect> dashPE = make_dash();
614
615 TestCase::SelfExpectations expectations;
616 SkPaint fill;
617
bsalomonfb083272016-05-04 08:27:41 -0700618 TestCase fillCase(geo, fill, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700619 expectations.fPEHasEffect = false;
620 expectations.fPEHasValidKey = false;
621 expectations.fStrokeApplies = false;
622 fillCase.testExpectations(reporter, expectations);
623 // Test that another GrShape instance built from the same primitive is the same.
bsalomonfb083272016-05-04 08:27:41 -0700624 TestCase(geo, fill, reporter).compare(reporter, fillCase,
625 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700626
627 SkPaint stroke2RoundBevel;
628 stroke2RoundBevel.setStyle(SkPaint::kStroke_Style);
629 stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap);
630 stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join);
631 stroke2RoundBevel.setStrokeWidth(2.f);
bsalomonfb083272016-05-04 08:27:41 -0700632 TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700633 expectations.fPEHasValidKey = true;
634 expectations.fPEHasEffect = false;
bsalomona395f7c2016-08-24 17:47:40 -0700635 expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
bsalomon47cc7692016-04-26 12:56:00 -0700636 stroke2RoundBevelCase.testExpectations(reporter, expectations);
bsalomonfb083272016-05-04 08:27:41 -0700637 TestCase(geo, stroke2RoundBevel, reporter).compare(reporter, stroke2RoundBevelCase,
638 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700639
640 SkPaint stroke2RoundBevelDash = stroke2RoundBevel;
641 stroke2RoundBevelDash.setPathEffect(make_dash());
bsalomonfb083272016-05-04 08:27:41 -0700642 TestCase stroke2RoundBevelDashCase(geo, stroke2RoundBevelDash, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700643 expectations.fPEHasValidKey = true;
644 expectations.fPEHasEffect = true;
645 expectations.fStrokeApplies = true;
646 stroke2RoundBevelDashCase.testExpectations(reporter, expectations);
bsalomonfb083272016-05-04 08:27:41 -0700647 TestCase(geo, stroke2RoundBevelDash, reporter).compare(reporter, stroke2RoundBevelDashCase,
648 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700649
bsalomona395f7c2016-08-24 17:47:40 -0700650 if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700651 fillCase.compare(reporter, stroke2RoundBevelCase,
652 TestCase::kAllDifferent_ComparisonExpecation);
653 fillCase.compare(reporter, stroke2RoundBevelDashCase,
654 TestCase::kAllDifferent_ComparisonExpecation);
655 } else {
656 fillCase.compare(reporter, stroke2RoundBevelCase,
657 TestCase::kSameUpToStroke_ComparisonExpecation);
658 fillCase.compare(reporter, stroke2RoundBevelDashCase,
659 TestCase::kSameUpToPE_ComparisonExpecation);
660 }
bsalomona395f7c2016-08-24 17:47:40 -0700661 if (geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700662 stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
663 TestCase::kAllDifferent_ComparisonExpecation);
664 } else {
665 stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
666 TestCase::kSameUpToPE_ComparisonExpecation);
667 }
bsalomon72dc51c2016-04-27 06:46:23 -0700668
bsalomonf0cf3552016-05-05 08:28:30 -0700669 // Stroke and fill cases
670 SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel;
671 stroke2RoundBevelAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
672 TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter);
673 expectations.fPEHasValidKey = true;
674 expectations.fPEHasEffect = false;
bsalomona395f7c2016-08-24 17:47:40 -0700675 expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
bsalomonf0cf3552016-05-05 08:28:30 -0700676 stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations);
677 TestCase(geo, stroke2RoundBevelAndFill, reporter).compare(reporter,
678 stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation);
679
680 SkPaint stroke2RoundBevelAndFillDash = stroke2RoundBevelDash;
681 stroke2RoundBevelAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
682 TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter);
683 expectations.fPEHasValidKey = true;
bsalomona0587862016-06-09 06:03:38 -0700684 expectations.fPEHasEffect = false;
bsalomona395f7c2016-08-24 17:47:40 -0700685 expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
bsalomonf0cf3552016-05-05 08:28:30 -0700686 stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations);
687 TestCase(geo, stroke2RoundBevelAndFillDash, reporter).compare(
688 reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation);
bsalomona0587862016-06-09 06:03:38 -0700689 stroke2RoundBevelAndFillDashCase.compare(reporter, stroke2RoundBevelAndFillCase,
690 TestCase::kAllSame_ComparisonExpecation);
bsalomonf0cf3552016-05-05 08:28:30 -0700691
bsalomon72dc51c2016-04-27 06:46:23 -0700692 SkPaint hairline;
693 hairline.setStyle(SkPaint::kStroke_Style);
694 hairline.setStrokeWidth(0.f);
bsalomonfb083272016-05-04 08:27:41 -0700695 TestCase hairlineCase(geo, hairline, reporter);
bsalomon487f8d32016-07-20 07:15:44 -0700696 // Since hairline style doesn't change the SkPath data, it is keyed identically to fill (except
697 // in the line and unclosed rect cases).
bsalomona395f7c2016-08-24 17:47:40 -0700698 if (geo.fillChangesGeom()) {
bsalomon487f8d32016-07-20 07:15:44 -0700699 hairlineCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation);
700 } else {
701 hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
702 }
bsalomon9ad5d7c2016-05-04 08:44:15 -0700703 REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline());
704 REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline());
705 REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline());
bsalomon47cc7692016-04-26 12:56:00 -0700706
bsalomon0ae36a22016-07-18 07:31:13 -0700707}
708
bsalomona395f7c2016-08-24 17:47:40 -0700709static void test_scale(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon97fd2d42016-05-09 13:02:01 -0700710 sk_sp<SkPathEffect> dashPE = make_dash();
711
712 static const SkScalar kS1 = 1.f;
713 static const SkScalar kS2 = 2.f;
714
715 SkPaint fill;
716 TestCase fillCase1(geo, fill, reporter, kS1);
717 TestCase fillCase2(geo, fill, reporter, kS2);
718 // Scale doesn't affect fills.
719 fillCase1.compare(reporter, fillCase2, TestCase::kAllSame_ComparisonExpecation);
720
721 SkPaint hairline;
722 hairline.setStyle(SkPaint::kStroke_Style);
723 hairline.setStrokeWidth(0.f);
724 TestCase hairlineCase1(geo, hairline, reporter, kS1);
725 TestCase hairlineCase2(geo, hairline, reporter, kS2);
726 // Scale doesn't affect hairlines.
727 hairlineCase1.compare(reporter, hairlineCase2, TestCase::kAllSame_ComparisonExpecation);
728
729 SkPaint stroke;
730 stroke.setStyle(SkPaint::kStroke_Style);
731 stroke.setStrokeWidth(2.f);
732 TestCase strokeCase1(geo, stroke, reporter, kS1);
733 TestCase strokeCase2(geo, stroke, reporter, kS2);
bsalomon0ae36a22016-07-18 07:31:13 -0700734 // Scale affects the stroke
bsalomona395f7c2016-08-24 17:47:40 -0700735 if (geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700736 REPORTER_ASSERT(reporter, !strokeCase1.baseShape().style().applies());
bsalomon0ae36a22016-07-18 07:31:13 -0700737 strokeCase1.compare(reporter, strokeCase2, TestCase::kAllSame_ComparisonExpecation);
738 } else {
739 strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation);
740 }
bsalomon97fd2d42016-05-09 13:02:01 -0700741
742 SkPaint strokeDash = stroke;
743 strokeDash.setPathEffect(make_dash());
744 TestCase strokeDashCase1(geo, strokeDash, reporter, kS1);
745 TestCase strokeDashCase2(geo, strokeDash, reporter, kS2);
746 // Scale affects the dash and the stroke.
bsalomon487f8d32016-07-20 07:15:44 -0700747 strokeDashCase1.compare(reporter, strokeDashCase2,
748 TestCase::kSameUpToPE_ComparisonExpecation);
bsalomon97fd2d42016-05-09 13:02:01 -0700749
750 // Stroke and fill cases
751 SkPaint strokeAndFill = stroke;
752 strokeAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
753 TestCase strokeAndFillCase1(geo, strokeAndFill, reporter, kS1);
754 TestCase strokeAndFillCase2(geo, strokeAndFill, reporter, kS2);
bsalomona0587862016-06-09 06:03:38 -0700755 SkPaint strokeAndFillDash = strokeDash;
756 strokeAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
757 // Dash is ignored for stroke and fill
758 TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1);
759 TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2);
bsalomon487f8d32016-07-20 07:15:44 -0700760 // Scale affects the stroke, but check to make sure this didn't become a simpler shape (e.g.
761 // stroke-and-filled rect can become a rect), in which case the scale shouldn't matter and the
762 // geometries should agree.
bsalomona395f7c2016-08-24 17:47:40 -0700763 if (geo.strokeAndFillIsConvertedToFill(strokeAndFillDash)) {
bsalomon487f8d32016-07-20 07:15:44 -0700764 REPORTER_ASSERT(reporter, !strokeAndFillCase1.baseShape().style().applies());
bsalomon97fd2d42016-05-09 13:02:01 -0700765 strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
766 TestCase::kAllSame_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700767 strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2,
768 TestCase::kAllSame_ComparisonExpecation);
bsalomon97fd2d42016-05-09 13:02:01 -0700769 } else {
770 strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
771 TestCase::kSameUpToStroke_ComparisonExpecation);
772 }
bsalomona0587862016-06-09 06:03:38 -0700773 strokeAndFillDashCase1.compare(reporter, strokeAndFillCase1,
774 TestCase::kAllSame_ComparisonExpecation);
775 strokeAndFillDashCase2.compare(reporter, strokeAndFillCase2,
776 TestCase::kAllSame_ComparisonExpecation);
bsalomon97fd2d42016-05-09 13:02:01 -0700777}
778
bsalomona395f7c2016-08-24 17:47:40 -0700779template <typename T>
780static void test_stroke_param_impl(skiatest::Reporter* reporter, const Geo& geo,
bsalomon06077562016-05-04 13:50:29 -0700781 std::function<void(SkPaint*, T)> setter, T a, T b,
782 bool paramAffectsStroke,
783 bool paramAffectsDashAndStroke) {
784 // Set the stroke width so that we don't get hairline. However, call the setter afterward so
785 // that it can override the stroke width.
bsalomon47cc7692016-04-26 12:56:00 -0700786 SkPaint strokeA;
787 strokeA.setStyle(SkPaint::kStroke_Style);
788 strokeA.setStrokeWidth(2.f);
789 setter(&strokeA, a);
790 SkPaint strokeB;
791 strokeB.setStyle(SkPaint::kStroke_Style);
792 strokeB.setStrokeWidth(2.f);
793 setter(&strokeB, b);
794
bsalomonfb083272016-05-04 08:27:41 -0700795 TestCase strokeACase(geo, strokeA, reporter);
796 TestCase strokeBCase(geo, strokeB, reporter);
bsalomon06077562016-05-04 13:50:29 -0700797 if (paramAffectsStroke) {
bsalomon0ae36a22016-07-18 07:31:13 -0700798 // If stroking is immediately incorporated into a geometric transformation then the base
799 // shapes will differ.
bsalomona395f7c2016-08-24 17:47:40 -0700800 if (geo.strokeIsConvertedToFill()) {
bsalomon0ae36a22016-07-18 07:31:13 -0700801 strokeACase.compare(reporter, strokeBCase,
802 TestCase::kAllDifferent_ComparisonExpecation);
bsalomon487f8d32016-07-20 07:15:44 -0700803 } else {
804 strokeACase.compare(reporter, strokeBCase,
805 TestCase::kSameUpToStroke_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700806 }
bsalomon06077562016-05-04 13:50:29 -0700807 } else {
808 strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation);
809 }
bsalomon47cc7692016-04-26 12:56:00 -0700810
bsalomonf0cf3552016-05-05 08:28:30 -0700811 SkPaint strokeAndFillA = strokeA;
812 SkPaint strokeAndFillB = strokeB;
813 strokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style);
814 strokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style);
815 TestCase strokeAndFillACase(geo, strokeAndFillA, reporter);
816 TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter);
817 if (paramAffectsStroke) {
bsalomon0ae36a22016-07-18 07:31:13 -0700818 // If stroking is immediately incorporated into a geometric transformation then the base
819 // shapes will differ.
bsalomona395f7c2016-08-24 17:47:40 -0700820 if (geo.strokeAndFillIsConvertedToFill(strokeAndFillA) ||
821 geo.strokeAndFillIsConvertedToFill(strokeAndFillB)) {
bsalomon0ae36a22016-07-18 07:31:13 -0700822 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
bsalomon487f8d32016-07-20 07:15:44 -0700823 TestCase::kAllDifferent_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700824 } else {
825 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
bsalomon487f8d32016-07-20 07:15:44 -0700826 TestCase::kSameUpToStroke_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700827 }
bsalomonf0cf3552016-05-05 08:28:30 -0700828 } else {
829 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
830 TestCase::kAllSame_ComparisonExpecation);
831 }
832
bsalomon47cc7692016-04-26 12:56:00 -0700833 // Make sure stroking params don't affect fill style.
834 SkPaint fillA = strokeA, fillB = strokeB;
835 fillA.setStyle(SkPaint::kFill_Style);
836 fillB.setStyle(SkPaint::kFill_Style);
bsalomonfb083272016-05-04 08:27:41 -0700837 TestCase fillACase(geo, fillA, reporter);
838 TestCase fillBCase(geo, fillB, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700839 fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
840
841 // Make sure just applying the dash but not stroke gives the same key for both stroking
842 // variations.
843 SkPaint dashA = strokeA, dashB = strokeB;
844 dashA.setPathEffect(make_dash());
845 dashB.setPathEffect(make_dash());
bsalomonfb083272016-05-04 08:27:41 -0700846 TestCase dashACase(geo, dashA, reporter);
847 TestCase dashBCase(geo, dashB, reporter);
bsalomon06077562016-05-04 13:50:29 -0700848 if (paramAffectsDashAndStroke) {
bsalomon487f8d32016-07-20 07:15:44 -0700849 dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
bsalomon06077562016-05-04 13:50:29 -0700850 } else {
851 dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation);
852 }
bsalomon47cc7692016-04-26 12:56:00 -0700853}
854
bsalomona395f7c2016-08-24 17:47:40 -0700855template <typename T>
856static void test_stroke_param(skiatest::Reporter* reporter, const Geo& geo,
bsalomon06077562016-05-04 13:50:29 -0700857 std::function<void(SkPaint*, T)> setter, T a, T b) {
858 test_stroke_param_impl(reporter, geo, setter, a, b, true, true);
859};
860
bsalomona395f7c2016-08-24 17:47:40 -0700861static void test_stroke_cap(skiatest::Reporter* reporter, const Geo& geo) {
862 SkPaint hairline;
863 hairline.setStrokeWidth(0);
864 hairline.setStyle(SkPaint::kStroke_Style);
865 GrShape shape = geo.makeShape(hairline);
bsalomon06077562016-05-04 13:50:29 -0700866 // The cap should only affect shapes that may be open.
867 bool affectsStroke = !shape.knownToBeClosed();
868 // Dashing adds ends that need caps.
869 bool affectsDashAndStroke = true;
bsalomona395f7c2016-08-24 17:47:40 -0700870 test_stroke_param_impl<SkPaint::Cap>(
bsalomon06077562016-05-04 13:50:29 -0700871 reporter,
872 geo,
873 [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
874 SkPaint::kButt_Cap, SkPaint::kRound_Cap,
875 affectsStroke,
876 affectsDashAndStroke);
877};
878
bsalomon0ae36a22016-07-18 07:31:13 -0700879static bool shape_known_not_to_have_joins(const GrShape& shape) {
880 return shape.asLine(nullptr, nullptr) || shape.isEmpty();
881}
882
bsalomona395f7c2016-08-24 17:47:40 -0700883static void test_stroke_join(skiatest::Reporter* reporter, const Geo& geo) {
884 SkPaint hairline;
885 hairline.setStrokeWidth(0);
886 hairline.setStyle(SkPaint::kStroke_Style);
887 GrShape shape = geo.makeShape(hairline);
bsalomon0ae36a22016-07-18 07:31:13 -0700888 // GrShape recognizes certain types don't have joins and will prevent the join type from
889 // affecting the style key.
890 // Dashing doesn't add additional joins. However, GrShape currently loses track of this
891 // after applying the dash.
892 bool affectsStroke = !shape_known_not_to_have_joins(shape);
bsalomona395f7c2016-08-24 17:47:40 -0700893 test_stroke_param_impl<SkPaint::Join>(
bsalomon0ae36a22016-07-18 07:31:13 -0700894 reporter,
895 geo,
896 [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
897 SkPaint::kRound_Join, SkPaint::kBevel_Join,
898 affectsStroke, true);
899};
900
bsalomona395f7c2016-08-24 17:47:40 -0700901static void test_miter_limit(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon06077562016-05-04 13:50:29 -0700902 auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) {
903 p->setStrokeJoin(SkPaint::kMiter_Join);
904 p->setStrokeMiter(miter);
905 };
bsalomon47cc7692016-04-26 12:56:00 -0700906
bsalomon06077562016-05-04 13:50:29 -0700907 auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) {
908 p->setStrokeJoin(SkPaint::kRound_Join);
909 p->setStrokeMiter(miter);
910 };
bsalomon47cc7692016-04-26 12:56:00 -0700911
bsalomona395f7c2016-08-24 17:47:40 -0700912 SkPaint hairline;
913 hairline.setStrokeWidth(0);
914 hairline.setStyle(SkPaint::kStroke_Style);
915 GrShape shape = geo.makeShape(hairline);
bsalomon0ae36a22016-07-18 07:31:13 -0700916 bool mayHaveJoins = !shape_known_not_to_have_joins(shape);
917
bsalomon06077562016-05-04 13:50:29 -0700918 // The miter limit should affect stroked and dashed-stroked cases when the join type is
919 // miter.
bsalomona395f7c2016-08-24 17:47:40 -0700920 test_stroke_param_impl<SkScalar>(
bsalomon06077562016-05-04 13:50:29 -0700921 reporter,
922 geo,
923 setMiterJoinAndLimit,
924 0.5f, 0.75f,
bsalomon0ae36a22016-07-18 07:31:13 -0700925 mayHaveJoins,
bsalomon06077562016-05-04 13:50:29 -0700926 true);
bsalomon47cc7692016-04-26 12:56:00 -0700927
bsalomon06077562016-05-04 13:50:29 -0700928 // The miter limit should not affect stroked and dashed-stroked cases when the join type is
929 // not miter.
bsalomona395f7c2016-08-24 17:47:40 -0700930 test_stroke_param_impl<SkScalar>(
bsalomon06077562016-05-04 13:50:29 -0700931 reporter,
932 geo,
933 setOtherJoinAndLimit,
934 0.5f, 0.75f,
935 false,
936 false);
bsalomon47cc7692016-04-26 12:56:00 -0700937}
938
bsalomona395f7c2016-08-24 17:47:40 -0700939static void test_dash_fill(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700940 // A dash with no stroke should have no effect
941 using DashFactoryFn = sk_sp<SkPathEffect>(*)();
942 for (DashFactoryFn md : {&make_dash, &make_null_dash}) {
943 SkPaint dashFill;
944 dashFill.setPathEffect((*md)());
bsalomonfb083272016-05-04 08:27:41 -0700945 TestCase dashFillCase(geo, dashFill, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700946
bsalomonfb083272016-05-04 08:27:41 -0700947 TestCase fillCase(geo, SkPaint(), reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700948 dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
949 }
950}
951
bsalomona395f7c2016-08-24 17:47:40 -0700952void test_null_dash(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700953 SkPaint fill;
954 SkPaint stroke;
955 stroke.setStyle(SkPaint::kStroke_Style);
956 stroke.setStrokeWidth(1.f);
957 SkPaint dash;
958 dash.setStyle(SkPaint::kStroke_Style);
959 dash.setStrokeWidth(1.f);
960 dash.setPathEffect(make_dash());
961 SkPaint nullDash;
962 nullDash.setStyle(SkPaint::kStroke_Style);
963 nullDash.setStrokeWidth(1.f);
964 nullDash.setPathEffect(make_null_dash());
965
bsalomonfb083272016-05-04 08:27:41 -0700966 TestCase fillCase(geo, fill, reporter);
967 TestCase strokeCase(geo, stroke, reporter);
968 TestCase dashCase(geo, dash, reporter);
969 TestCase nullDashCase(geo, nullDash, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700970
bsalomon487f8d32016-07-20 07:15:44 -0700971 // We expect the null dash to be ignored so nullDashCase should match strokeCase, always.
bsalomon47cc7692016-04-26 12:56:00 -0700972 nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation);
bsalomon487f8d32016-07-20 07:15:44 -0700973 // Check whether the fillCase or strokeCase/nullDashCase would undergo a geometric tranformation
974 // on construction in order to determine how to compare the fill and stroke.
bsalomona395f7c2016-08-24 17:47:40 -0700975 if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700976 nullDashCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation);
977 } else {
978 nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation);
979 }
980 // In the null dash case we may immediately convert to a fill, but not for the normal dash case.
bsalomona395f7c2016-08-24 17:47:40 -0700981 if (geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700982 nullDashCase.compare(reporter, dashCase, TestCase::kAllDifferent_ComparisonExpecation);
983 } else {
984 nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation);
985 }
bsalomon47cc7692016-04-26 12:56:00 -0700986}
987
bsalomona395f7c2016-08-24 17:47:40 -0700988void test_path_effect_makes_rrect(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon72dc51c2016-04-27 06:46:23 -0700989 /**
990 * This path effect takes any input path and turns it into a rrect. It passes through stroke
991 * info.
992 */
993 class RRectPathEffect : SkPathEffect {
994 public:
995 static const SkRRect& RRect() {
996 static const SkRRect kRRect = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 3, 5);
997 return kRRect;
998 }
999
1000 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1001 const SkRect* cullR) const override {
1002 dst->reset();
1003 dst->addRRect(RRect());
1004 return true;
1005 }
1006 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1007 *dst = RRect().getBounds();
1008 }
1009 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new RRectPathEffect); }
1010 Factory getFactory() const override { return nullptr; }
1011 void toString(SkString*) const override {}
1012 private:
1013 RRectPathEffect() {}
1014 };
1015
1016 SkPaint fill;
bsalomonfb083272016-05-04 08:27:41 -07001017 TestCase fillGeoCase(geo, fill, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001018
1019 SkPaint pe;
1020 pe.setPathEffect(RRectPathEffect::Make());
bsalomonfb083272016-05-04 08:27:41 -07001021 TestCase geoPECase(geo, pe, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001022
1023 SkPaint peStroke;
1024 peStroke.setPathEffect(RRectPathEffect::Make());
1025 peStroke.setStrokeWidth(2.f);
1026 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001027 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001028
bsalomon487f8d32016-07-20 07:15:44 -07001029 // Check whether constructing the filled case would cause the base shape to have a different
1030 // geometry (because of a geometric transformation upon initial GrShape construction).
bsalomona395f7c2016-08-24 17:47:40 -07001031 if (geo.fillChangesGeom()) {
bsalomon487f8d32016-07-20 07:15:44 -07001032 fillGeoCase.compare(reporter, geoPECase, TestCase::kAllDifferent_ComparisonExpecation);
1033 fillGeoCase.compare(reporter, geoPEStrokeCase,
1034 TestCase::kAllDifferent_ComparisonExpecation);
1035 } else {
1036 fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation);
1037 fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation);
1038 }
bsalomon72dc51c2016-04-27 06:46:23 -07001039 geoPECase.compare(reporter, geoPEStrokeCase,
1040 TestCase::kSameUpToStroke_ComparisonExpecation);
1041
bsalomona395f7c2016-08-24 17:47:40 -07001042 TestCase rrectFillCase(reporter, RRectPathEffect::RRect(), fill);
bsalomon72dc51c2016-04-27 06:46:23 -07001043 SkPaint stroke = peStroke;
1044 stroke.setPathEffect(nullptr);
bsalomona395f7c2016-08-24 17:47:40 -07001045 TestCase rrectStrokeCase(reporter, RRectPathEffect::RRect(), stroke);
bsalomon72dc51c2016-04-27 06:46:23 -07001046
1047 SkRRect rrect;
1048 // Applying the path effect should make a SkRRect shape. There is no further stroking in the
1049 // geoPECase, so the full style should be the same as just the PE.
bsalomon70493962016-06-10 08:05:14 -07001050 REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().asRRect(&rrect, nullptr, nullptr,
1051 nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001052 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
1053 REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == rrectFillCase.baseKey());
1054
bsalomon70493962016-06-10 08:05:14 -07001055 REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr,
1056 nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001057 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
1058 REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == rrectFillCase.baseKey());
1059
1060 // In the PE+stroke case applying the full style should be the same as just stroking the rrect.
bsalomon70493962016-06-10 08:05:14 -07001061 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().asRRect(&rrect, nullptr,
1062 nullptr, nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001063 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
1064 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == rrectFillCase.baseKey());
1065
bsalomon70493962016-06-10 08:05:14 -07001066 REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().asRRect(&rrect, nullptr,
1067 nullptr, nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001068 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() ==
1069 rrectStrokeCase.appliedFullStyleKey());
1070}
1071
bsalomona395f7c2016-08-24 17:47:40 -07001072void test_unknown_path_effect(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon72dc51c2016-04-27 06:46:23 -07001073 /**
1074 * This path effect just adds two lineTos to the input path.
1075 */
1076 class AddLineTosPathEffect : SkPathEffect {
1077 public:
1078 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1079 const SkRect* cullR) const override {
1080 *dst = src;
1081 dst->lineTo(0, 0);
1082 dst->lineTo(10, 10);
1083 return true;
1084 }
1085 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1086 *dst = src;
1087 dst->growToInclude(0, 0);
1088 dst->growToInclude(10, 10);
1089 }
1090 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new AddLineTosPathEffect); }
1091 Factory getFactory() const override { return nullptr; }
1092 void toString(SkString*) const override {}
1093 private:
1094 AddLineTosPathEffect() {}
1095 };
1096
bsalomon9ad5d7c2016-05-04 08:44:15 -07001097 // This path effect should make the keys invalid when it is applied. We only produce a path
bsalomon72dc51c2016-04-27 06:46:23 -07001098 // effect key for dash path effects. So the only way another arbitrary path effect can produce
1099 // a styled result with a key is to produce a non-path shape that has a purely geometric key.
1100 SkPaint peStroke;
1101 peStroke.setPathEffect(AddLineTosPathEffect::Make());
1102 peStroke.setStrokeWidth(2.f);
1103 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001104 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001105 TestCase::SelfExpectations expectations;
1106 expectations.fPEHasEffect = true;
1107 expectations.fPEHasValidKey = false;
1108 expectations.fStrokeApplies = true;
1109 geoPEStrokeCase.testExpectations(reporter, expectations);
1110}
1111
bsalomona395f7c2016-08-24 17:47:40 -07001112void test_make_hairline_path_effect(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon9ad5d7c2016-05-04 08:44:15 -07001113 /**
1114 * This path effect just changes the stroke rec to hairline.
1115 */
1116 class MakeHairlinePathEffect : SkPathEffect {
1117 public:
1118 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* strokeRec,
1119 const SkRect* cullR) const override {
1120 *dst = src;
1121 strokeRec->setHairlineStyle();
1122 return true;
1123 }
bsalomon70493962016-06-10 08:05:14 -07001124 void computeFastBounds(SkRect* dst, const SkRect& src) const override { *dst = src; }
bsalomon9ad5d7c2016-05-04 08:44:15 -07001125 static sk_sp<SkPathEffect> Make() {
1126 return sk_sp<SkPathEffect>(new MakeHairlinePathEffect);
1127 }
1128 Factory getFactory() const override { return nullptr; }
1129 void toString(SkString*) const override {}
1130 private:
1131 MakeHairlinePathEffect() {}
1132 };
1133
1134 SkPaint fill;
1135 SkPaint pe;
1136 pe.setPathEffect(MakeHairlinePathEffect::Make());
1137
1138 TestCase peCase(geo, pe, reporter);
1139
bsalomonee295642016-06-06 14:01:25 -07001140 SkPath a, b, c;
bsalomon9ad5d7c2016-05-04 08:44:15 -07001141 peCase.baseShape().asPath(&a);
1142 peCase.appliedPathEffectShape().asPath(&b);
bsalomonee295642016-06-06 14:01:25 -07001143 peCase.appliedFullStyleShape().asPath(&c);
bsalomona395f7c2016-08-24 17:47:40 -07001144 if (geo.isNonPath(pe)) {
bsalomonee295642016-06-06 14:01:25 -07001145 // RRect types can have a change in start index or direction after the PE is applied. This
1146 // is because once the PE is applied, GrShape may canonicalize the dir and index since it
1147 // is not germane to the styling any longer.
1148 // Instead we just check that the paths would fill the same both before and after styling.
1149 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1150 REPORTER_ASSERT(reporter, paths_fill_same(a, c));
bsalomon9ad5d7c2016-05-04 08:44:15 -07001151 } else {
bsalomona4817af2016-06-23 11:48:26 -07001152 // The base shape cannot perform canonicalization on the path's fill type because of an
1153 // unknown path effect. However, after the path effect is applied the resulting hairline
1154 // shape will canonicalize the path fill type since hairlines (and stroking in general)
1155 // don't distinguish between even/odd and non-zero winding.
1156 a.setFillType(b.getFillType());
bsalomonee295642016-06-06 14:01:25 -07001157 REPORTER_ASSERT(reporter, a == b);
1158 REPORTER_ASSERT(reporter, a == c);
bsalomon9ad5d7c2016-05-04 08:44:15 -07001159 REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey().empty());
1160 REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey().empty());
1161 }
bsalomonee295642016-06-06 14:01:25 -07001162 REPORTER_ASSERT(reporter, peCase.appliedPathEffectShape().style().isSimpleHairline());
1163 REPORTER_ASSERT(reporter, peCase.appliedFullStyleShape().style().isSimpleHairline());
bsalomon9ad5d7c2016-05-04 08:44:15 -07001164}
1165
bsalomona395f7c2016-08-24 17:47:40 -07001166void test_volatile_path(skiatest::Reporter* reporter, const Geo& geo) {
1167 SkPath vPath = geo.path();
bsalomon4eeccc92016-04-27 13:30:25 -07001168 vPath.setIsVolatile(true);
1169
1170 SkPaint dashAndStroke;
1171 dashAndStroke.setPathEffect(make_dash());
1172 dashAndStroke.setStrokeWidth(2.f);
1173 dashAndStroke.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001174 TestCase volatileCase(reporter, vPath, dashAndStroke);
bsalomon4eeccc92016-04-27 13:30:25 -07001175 // We expect a shape made from a volatile path to have a key iff the shape is recognized
1176 // as a specialized geometry.
bsalomona395f7c2016-08-24 17:47:40 -07001177 if (geo.isNonPath(dashAndStroke)) {
bsalomon4eeccc92016-04-27 13:30:25 -07001178 REPORTER_ASSERT(reporter, SkToBool(volatileCase.baseKey().count()));
1179 // In this case all the keys should be identical to the non-volatile case.
bsalomona395f7c2016-08-24 17:47:40 -07001180 TestCase nonVolatileCase(reporter, geo.path(), dashAndStroke);
bsalomon4eeccc92016-04-27 13:30:25 -07001181 volatileCase.compare(reporter, nonVolatileCase, TestCase::kAllSame_ComparisonExpecation);
1182 } else {
1183 // None of the keys should be valid.
1184 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.baseKey().count()));
1185 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectKey().count()));
1186 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedFullStyleKey().count()));
1187 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectThenStrokeKey().count()));
1188 }
1189}
1190
bsalomona395f7c2016-08-24 17:47:40 -07001191void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon409ed732016-04-27 12:36:02 -07001192 /**
1193 * This path effect returns an empty path.
1194 */
1195 class EmptyPathEffect : SkPathEffect {
1196 public:
1197 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1198 const SkRect* cullR) const override {
1199 dst->reset();
1200 return true;
1201 }
1202 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1203 dst->setEmpty();
1204 }
1205 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new EmptyPathEffect); }
1206 Factory getFactory() const override { return nullptr; }
1207 void toString(SkString*) const override {}
1208 private:
1209 EmptyPathEffect() {}
1210 };
1211
1212 SkPath emptyPath;
1213 GrShape emptyShape(emptyPath);
1214 Key emptyKey;
1215 make_key(&emptyKey, emptyShape);
bsalomon7c73a532016-05-11 15:15:56 -07001216 REPORTER_ASSERT(reporter, emptyShape.isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001217
1218 SkPaint pe;
1219 pe.setPathEffect(EmptyPathEffect::Make());
bsalomonfb083272016-05-04 08:27:41 -07001220 TestCase geoCase(geo, pe, reporter);
bsalomon409ed732016-04-27 12:36:02 -07001221 REPORTER_ASSERT(reporter, geoCase.appliedFullStyleKey() == emptyKey);
1222 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectKey() == emptyKey);
1223 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectThenStrokeKey() == emptyKey);
bsalomon7c73a532016-05-11 15:15:56 -07001224 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectShape().isEmpty());
1225 REPORTER_ASSERT(reporter, geoCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001226
1227 SkPaint peStroke;
1228 peStroke.setPathEffect(EmptyPathEffect::Make());
1229 peStroke.setStrokeWidth(2.f);
1230 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001231 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon409ed732016-04-27 12:36:02 -07001232 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == emptyKey);
1233 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == emptyKey);
1234 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey);
bsalomon7c73a532016-05-11 15:15:56 -07001235 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty());
1236 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001237}
1238
bsalomona395f7c2016-08-24 17:47:40 -07001239void test_path_effect_fails(skiatest::Reporter* reporter, const Geo& geo) {
bsalomond6723842016-06-07 12:20:15 -07001240 /**
bsalomon0ae36a22016-07-18 07:31:13 -07001241 * This path effect always fails to apply.
bsalomond6723842016-06-07 12:20:15 -07001242 */
1243 class FailurePathEffect : SkPathEffect {
1244 public:
1245 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1246 const SkRect* cullR) const override {
1247 return false;
1248 }
1249 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1250 *dst = src;
1251 }
1252 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new FailurePathEffect); }
1253 Factory getFactory() const override { return nullptr; }
1254 void toString(SkString*) const override {}
1255 private:
1256 FailurePathEffect() {}
1257 };
1258
1259 SkPaint fill;
1260 TestCase fillCase(geo, fill, reporter);
1261
1262 SkPaint pe;
1263 pe.setPathEffect(FailurePathEffect::Make());
1264 TestCase peCase(geo, pe, reporter);
1265
1266 SkPaint stroke;
1267 stroke.setStrokeWidth(2.f);
1268 stroke.setStyle(SkPaint::kStroke_Style);
1269 TestCase strokeCase(geo, stroke, reporter);
1270
1271 SkPaint peStroke = stroke;
1272 peStroke.setPathEffect(FailurePathEffect::Make());
1273 TestCase peStrokeCase(geo, peStroke, reporter);
1274
1275 // In general the path effect failure can cause some of the TestCase::compare() tests to fail
1276 // for at least two reasons: 1) We will initially treat the shape as unkeyable because of the
1277 // path effect, but then when the path effect fails we can key it. 2) GrShape will change its
1278 // mind about whether a unclosed rect is actually rect. The path effect initially bars us from
1279 // closing it but after the effect fails we can (for the fill+pe case). This causes different
1280 // routes through GrShape to have equivalent but different representations of the path (closed
1281 // or not) but that fill the same.
1282 SkPath a;
1283 SkPath b;
1284 fillCase.appliedPathEffectShape().asPath(&a);
1285 peCase.appliedPathEffectShape().asPath(&b);
1286 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1287
1288 fillCase.appliedFullStyleShape().asPath(&a);
1289 peCase.appliedFullStyleShape().asPath(&b);
1290 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1291
1292 strokeCase.appliedPathEffectShape().asPath(&a);
1293 peStrokeCase.appliedPathEffectShape().asPath(&b);
1294 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1295
1296 strokeCase.appliedFullStyleShape().asPath(&a);
1297 peStrokeCase.appliedFullStyleShape().asPath(&b);
1298 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1299}
1300
bsalomon409ed732016-04-27 12:36:02 -07001301void test_empty_shape(skiatest::Reporter* reporter) {
1302 SkPath emptyPath;
1303 SkPaint fill;
bsalomona395f7c2016-08-24 17:47:40 -07001304 TestCase fillEmptyCase(reporter, emptyPath, fill);
bsalomon7c73a532016-05-11 15:15:56 -07001305 REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty());
1306 REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty());
1307 REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001308
1309 Key emptyKey(fillEmptyCase.baseKey());
1310 REPORTER_ASSERT(reporter, emptyKey.count());
1311 TestCase::SelfExpectations expectations;
1312 expectations.fStrokeApplies = false;
1313 expectations.fPEHasEffect = false;
1314 // This will test whether applying style preserves emptiness
1315 fillEmptyCase.testExpectations(reporter, expectations);
1316
1317 // Stroking an empty path should have no effect
1318 SkPath emptyPath2;
1319 SkPaint stroke;
1320 stroke.setStrokeWidth(2.f);
1321 stroke.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001322 TestCase strokeEmptyCase(reporter, emptyPath2, stroke);
bsalomon409ed732016-04-27 12:36:02 -07001323 strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
1324
1325 // Dashing and stroking an empty path should have no effect
1326 SkPath emptyPath3;
1327 SkPaint dashAndStroke;
1328 dashAndStroke.setPathEffect(make_dash());
1329 dashAndStroke.setStrokeWidth(2.f);
1330 dashAndStroke.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001331 TestCase dashAndStrokeEmptyCase(reporter, emptyPath3, dashAndStroke);
bsalomon409ed732016-04-27 12:36:02 -07001332 dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase,
1333 TestCase::kAllSame_ComparisonExpecation);
bsalomon5e410b42016-04-28 09:30:46 -07001334
1335 // A shape made from an empty rrect should behave the same as an empty path.
1336 SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty());
1337 REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type);
bsalomona395f7c2016-08-24 17:47:40 -07001338 TestCase dashAndStrokeEmptyRRectCase(reporter, emptyRRect, dashAndStroke);
bsalomon5e410b42016-04-28 09:30:46 -07001339 dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
1340 TestCase::kAllSame_ComparisonExpecation);
1341
1342 // Same for a rect.
1343 SkRect emptyRect = SkRect::MakeEmpty();
bsalomona395f7c2016-08-24 17:47:40 -07001344 TestCase dashAndStrokeEmptyRectCase(reporter, emptyRect, dashAndStroke);
bsalomon5e410b42016-04-28 09:30:46 -07001345 dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase,
1346 TestCase::kAllSame_ComparisonExpecation);
bsalomon409ed732016-04-27 12:36:02 -07001347}
1348
bsalomon70493962016-06-10 08:05:14 -07001349// rect and oval types have rrect start indices that collapse to the same point. Here we select the
1350// canonical point in these cases.
1351unsigned canonicalize_rrect_start(int s, const SkRRect& rrect) {
1352 switch (rrect.getType()) {
1353 case SkRRect::kRect_Type:
1354 return (s + 1) & 0b110;
1355 case SkRRect::kOval_Type:
1356 return s & 0b110;
1357 default:
1358 return s;
1359 }
1360}
1361
1362void test_rrect(skiatest::Reporter* r, const SkRRect& rrect) {
bsalomoncadb5a22016-06-10 18:28:06 -07001363 enum Style {
bsalomon70493962016-06-10 08:05:14 -07001364 kFill,
1365 kStroke,
1366 kHairline,
1367 kStrokeAndFill
1368 };
1369
1370 // SkStrokeRec has no default cons., so init with kFill before calling the setters below.
1371 SkStrokeRec strokeRecs[4] { SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle,
1372 SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle};
1373 strokeRecs[kFill].setFillStyle();
1374 strokeRecs[kStroke].setStrokeStyle(2.f);
1375 strokeRecs[kHairline].setHairlineStyle();
1376 strokeRecs[kStrokeAndFill].setStrokeStyle(3.f, true);
bsalomon487f8d32016-07-20 07:15:44 -07001377 // Use a bevel join to avoid complications of stroke+filled rects becoming filled rects before
1378 // applyStyle() is called.
1379 strokeRecs[kStrokeAndFill].setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 1.f);
bsalomon70493962016-06-10 08:05:14 -07001380 sk_sp<SkPathEffect> dashEffect = make_dash();
1381
bsalomoncadb5a22016-06-10 18:28:06 -07001382 static constexpr Style kStyleCnt = static_cast<Style>(SK_ARRAY_COUNT(strokeRecs));
1383
1384 auto index = [](bool inverted,
1385 SkPath::Direction dir,
1386 unsigned start,
1387 Style style,
1388 bool dash) -> int {
1389 return inverted * (2 * 8 * kStyleCnt * 2) +
1390 dir * ( 8 * kStyleCnt * 2) +
1391 start * ( kStyleCnt * 2) +
1392 style * ( 2) +
1393 dash;
1394 };
1395 static const SkPath::Direction kSecondDirection = static_cast<SkPath::Direction>(1);
1396 const int cnt = index(true, kSecondDirection, 7, static_cast<Style>(kStyleCnt - 1), true) + 1;
1397 SkAutoTArray<GrShape> shapes(cnt);
1398 for (bool inverted : {false, true}) {
1399 for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
1400 for (unsigned start = 0; start < 8; ++start) {
1401 for (Style style : {kFill, kStroke, kHairline, kStrokeAndFill}) {
1402 for (bool dash : {false, true}) {
bsalomon70493962016-06-10 08:05:14 -07001403 SkPathEffect* pe = dash ? dashEffect.get() : nullptr;
bsalomoncadb5a22016-06-10 18:28:06 -07001404 shapes[index(inverted, dir, start, style, dash)] =
1405 GrShape(rrect, dir, start, SkToBool(inverted),
bsalomon70493962016-06-10 08:05:14 -07001406 GrStyle(strokeRecs[style], pe));
1407 }
1408 }
1409 }
1410 }
1411 }
1412
bsalomonfd32df72016-06-14 14:37:21 -07001413 // Get the keys for some example shape instances that we'll use for comparision against the
1414 // rest.
1415 static constexpr SkPath::Direction kExamplesDir = SkPath::kCW_Direction;
1416 static constexpr unsigned kExamplesStart = 0;
1417 const GrShape& exampleFillCase = shapes[index(false, kExamplesDir, kExamplesStart, kFill,
1418 false)];
bsalomon70493962016-06-10 08:05:14 -07001419 Key exampleFillCaseKey;
1420 make_key(&exampleFillCaseKey, exampleFillCase);
1421
bsalomonfd32df72016-06-14 14:37:21 -07001422 const GrShape& exampleStrokeAndFillCase = shapes[index(false, kExamplesDir, kExamplesStart,
1423 kStrokeAndFill, false)];
bsalomon70493962016-06-10 08:05:14 -07001424 Key exampleStrokeAndFillCaseKey;
1425 make_key(&exampleStrokeAndFillCaseKey, exampleStrokeAndFillCase);
1426
bsalomonfd32df72016-06-14 14:37:21 -07001427 const GrShape& exampleInvFillCase = shapes[index(true, kExamplesDir, kExamplesStart, kFill,
1428 false)];
bsalomon70493962016-06-10 08:05:14 -07001429 Key exampleInvFillCaseKey;
1430 make_key(&exampleInvFillCaseKey, exampleInvFillCase);
1431
bsalomonfd32df72016-06-14 14:37:21 -07001432 const GrShape& exampleInvStrokeAndFillCase = shapes[index(true, kExamplesDir, kExamplesStart,
1433 kStrokeAndFill, false)];
bsalomon70493962016-06-10 08:05:14 -07001434 Key exampleInvStrokeAndFillCaseKey;
1435 make_key(&exampleInvStrokeAndFillCaseKey, exampleInvStrokeAndFillCase);
1436
bsalomonfd32df72016-06-14 14:37:21 -07001437 const GrShape& exampleStrokeCase = shapes[index(false, kExamplesDir, kExamplesStart, kStroke,
1438 false)];
bsalomon70493962016-06-10 08:05:14 -07001439 Key exampleStrokeCaseKey;
1440 make_key(&exampleStrokeCaseKey, exampleStrokeCase);
1441
bsalomonfd32df72016-06-14 14:37:21 -07001442 const GrShape& exampleInvStrokeCase = shapes[index(true, kExamplesDir, kExamplesStart, kStroke,
1443 false)];
1444 Key exampleInvStrokeCaseKey;
1445 make_key(&exampleInvStrokeCaseKey, exampleInvStrokeCase);
1446
1447 const GrShape& exampleHairlineCase = shapes[index(false, kExamplesDir, kExamplesStart,
1448 kHairline, false)];
bsalomon70493962016-06-10 08:05:14 -07001449 Key exampleHairlineCaseKey;
1450 make_key(&exampleHairlineCaseKey, exampleHairlineCase);
1451
bsalomonfd32df72016-06-14 14:37:21 -07001452 const GrShape& exampleInvHairlineCase = shapes[index(true, kExamplesDir, kExamplesStart,
1453 kHairline, false)];
1454 Key exampleInvHairlineCaseKey;
1455 make_key(&exampleInvHairlineCaseKey, exampleInvHairlineCase);
1456
bsalomon70493962016-06-10 08:05:14 -07001457 // These are dummy initializations to suppress warnings.
bsalomoncadb5a22016-06-10 18:28:06 -07001458 SkRRect queryRR = SkRRect::MakeEmpty();
1459 SkPath::Direction queryDir = SkPath::kCW_Direction;
1460 unsigned queryStart = ~0U;
1461 bool queryInverted = true;
bsalomon70493962016-06-10 08:05:14 -07001462
bsalomoncadb5a22016-06-10 18:28:06 -07001463 REPORTER_ASSERT(r, exampleFillCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
1464 REPORTER_ASSERT(r, queryRR == rrect);
1465 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1466 REPORTER_ASSERT(r, 0 == queryStart);
1467 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001468
bsalomoncadb5a22016-06-10 18:28:06 -07001469 REPORTER_ASSERT(r, exampleInvFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1470 &queryInverted));
1471 REPORTER_ASSERT(r, queryRR == rrect);
1472 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1473 REPORTER_ASSERT(r, 0 == queryStart);
1474 REPORTER_ASSERT(r, queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001475
bsalomoncadb5a22016-06-10 18:28:06 -07001476 REPORTER_ASSERT(r, exampleStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1477 &queryInverted));
1478 REPORTER_ASSERT(r, queryRR == rrect);
1479 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1480 REPORTER_ASSERT(r, 0 == queryStart);
1481 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001482
bsalomoncadb5a22016-06-10 18:28:06 -07001483 REPORTER_ASSERT(r, exampleInvStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1484 &queryInverted));
1485 REPORTER_ASSERT(r, queryRR == rrect);
1486 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1487 REPORTER_ASSERT(r, 0 == queryStart);
1488 REPORTER_ASSERT(r, queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001489
bsalomoncadb5a22016-06-10 18:28:06 -07001490 REPORTER_ASSERT(r, exampleHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1491 &queryInverted));
1492 REPORTER_ASSERT(r, queryRR == rrect);
1493 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1494 REPORTER_ASSERT(r, 0 == queryStart);
1495 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001496
bsalomonfd32df72016-06-14 14:37:21 -07001497 REPORTER_ASSERT(r, exampleInvHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1498 &queryInverted));
1499 REPORTER_ASSERT(r, queryRR == rrect);
1500 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1501 REPORTER_ASSERT(r, 0 == queryStart);
1502 REPORTER_ASSERT(r, queryInverted);
1503
bsalomoncadb5a22016-06-10 18:28:06 -07001504 REPORTER_ASSERT(r, exampleStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
1505 REPORTER_ASSERT(r, queryRR == rrect);
1506 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1507 REPORTER_ASSERT(r, 0 == queryStart);
1508 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001509
bsalomonfd32df72016-06-14 14:37:21 -07001510 REPORTER_ASSERT(r, exampleInvStrokeCase.asRRect(&queryRR, &queryDir, &queryStart,
1511 &queryInverted));
1512 REPORTER_ASSERT(r, queryRR == rrect);
1513 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1514 REPORTER_ASSERT(r, 0 == queryStart);
1515 REPORTER_ASSERT(r, queryInverted);
1516
bsalomon70493962016-06-10 08:05:14 -07001517 // Remember that the key reflects the geometry before styling is applied.
1518 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvFillCaseKey);
1519 REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeAndFillCaseKey);
1520 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeAndFillCaseKey);
1521 REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001522 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001523 REPORTER_ASSERT(r, exampleFillCaseKey == exampleHairlineCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001524 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvHairlineCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001525 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvFillCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001526 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvStrokeCaseKey);
1527 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvHairlineCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001528
bsalomoncadb5a22016-06-10 18:28:06 -07001529 for (bool inverted : {false, true}) {
1530 for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
1531 for (unsigned start = 0; start < 8; ++start) {
1532 for (bool dash : {false, true}) {
1533 const GrShape& fillCase = shapes[index(inverted, dir, start, kFill, dash)];
bsalomon70493962016-06-10 08:05:14 -07001534 Key fillCaseKey;
1535 make_key(&fillCaseKey, fillCase);
1536
bsalomoncadb5a22016-06-10 18:28:06 -07001537 const GrShape& strokeAndFillCase = shapes[index(inverted, dir, start,
1538 kStrokeAndFill, dash)];
bsalomon70493962016-06-10 08:05:14 -07001539 Key strokeAndFillCaseKey;
1540 make_key(&strokeAndFillCaseKey, strokeAndFillCase);
1541
1542 // Both fill and stroke-and-fill shapes must respect the inverseness and both
1543 // ignore dashing.
1544 REPORTER_ASSERT(r, !fillCase.style().pathEffect());
1545 REPORTER_ASSERT(r, !strokeAndFillCase.style().pathEffect());
1546 TestCase a(fillCase, r);
1547 TestCase b(inverted ? exampleInvFillCase : exampleFillCase, r);
1548 TestCase c(strokeAndFillCase, r);
1549 TestCase d(inverted ? exampleInvStrokeAndFillCase
1550 : exampleStrokeAndFillCase, r);
1551 a.compare(r, b, TestCase::kAllSame_ComparisonExpecation);
1552 c.compare(r, d, TestCase::kAllSame_ComparisonExpecation);
1553
bsalomoncadb5a22016-06-10 18:28:06 -07001554 const GrShape& strokeCase = shapes[index(inverted, dir, start, kStroke, dash)];
1555 const GrShape& hairlineCase = shapes[index(inverted, dir, start, kHairline,
1556 dash)];
bsalomon70493962016-06-10 08:05:14 -07001557
1558 TestCase e(strokeCase, r);
bsalomon70493962016-06-10 08:05:14 -07001559 TestCase g(hairlineCase, r);
bsalomon70493962016-06-10 08:05:14 -07001560
bsalomonfd32df72016-06-14 14:37:21 -07001561 // Both hairline and stroke shapes must respect the dashing.
bsalomon70493962016-06-10 08:05:14 -07001562 if (dash) {
bsalomonfd32df72016-06-14 14:37:21 -07001563 // Dashing always ignores the inverseness. skbug.com/5421
1564 TestCase f(exampleStrokeCase, r);
1565 TestCase h(exampleHairlineCase, r);
bsalomoncadb5a22016-06-10 18:28:06 -07001566 unsigned expectedStart = canonicalize_rrect_start(start, rrect);
bsalomon70493962016-06-10 08:05:14 -07001567 REPORTER_ASSERT(r, strokeCase.style().pathEffect());
1568 REPORTER_ASSERT(r, hairlineCase.style().pathEffect());
1569
bsalomoncadb5a22016-06-10 18:28:06 -07001570 REPORTER_ASSERT(r, strokeCase.asRRect(&queryRR, &queryDir, &queryStart,
1571 &queryInverted));
1572 REPORTER_ASSERT(r, queryRR == rrect);
1573 REPORTER_ASSERT(r, queryDir == dir);
1574 REPORTER_ASSERT(r, queryStart == expectedStart);
1575 REPORTER_ASSERT(r, !queryInverted);
1576 REPORTER_ASSERT(r, hairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1577 &queryInverted));
1578 REPORTER_ASSERT(r, queryRR == rrect);
1579 REPORTER_ASSERT(r, queryDir == dir);
1580 REPORTER_ASSERT(r, queryStart == expectedStart);
1581 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001582
1583 // The pre-style case for the dash will match the non-dash example iff the
1584 // dir and start match (dir=cw, start=0).
bsalomoncadb5a22016-06-10 18:28:06 -07001585 if (0 == expectedStart && SkPath::kCW_Direction == dir) {
bsalomon70493962016-06-10 08:05:14 -07001586 e.compare(r, f, TestCase::kSameUpToPE_ComparisonExpecation);
1587 g.compare(r, h, TestCase::kSameUpToPE_ComparisonExpecation);
1588 } else {
1589 e.compare(r, f, TestCase::kAllDifferent_ComparisonExpecation);
1590 g.compare(r, h, TestCase::kAllDifferent_ComparisonExpecation);
1591 }
1592 } else {
bsalomonfd32df72016-06-14 14:37:21 -07001593 TestCase f(inverted ? exampleInvStrokeCase : exampleStrokeCase, r);
1594 TestCase h(inverted ? exampleInvHairlineCase : exampleHairlineCase, r);
bsalomon70493962016-06-10 08:05:14 -07001595 REPORTER_ASSERT(r, !strokeCase.style().pathEffect());
1596 REPORTER_ASSERT(r, !hairlineCase.style().pathEffect());
1597 e.compare(r, f, TestCase::kAllSame_ComparisonExpecation);
1598 g.compare(r, h, TestCase::kAllSame_ComparisonExpecation);
1599 }
1600 }
1601 }
1602 }
1603 }
1604}
1605
bsalomon0a0f67e2016-06-28 11:56:42 -07001606void test_lines(skiatest::Reporter* r) {
1607 static constexpr SkPoint kA { 1, 1};
1608 static constexpr SkPoint kB { 5, -9};
1609 static constexpr SkPoint kC {-3, 17};
1610
1611 SkPath lineAB;
1612 lineAB.moveTo(kA);
1613 lineAB.lineTo(kB);
1614
1615 SkPath lineBA;
1616 lineBA.moveTo(kB);
1617 lineBA.lineTo(kA);
1618
1619 SkPath lineAC;
1620 lineAC.moveTo(kB);
1621 lineAC.lineTo(kC);
1622
1623 SkPath invLineAB = lineAB;
1624 invLineAB.setFillType(SkPath::kInverseEvenOdd_FillType);
1625
1626 SkPaint fill;
1627 SkPaint stroke;
1628 stroke.setStyle(SkPaint::kStroke_Style);
1629 stroke.setStrokeWidth(2.f);
1630 SkPaint hairline;
1631 hairline.setStyle(SkPaint::kStroke_Style);
1632 hairline.setStrokeWidth(0.f);
1633 SkPaint dash = stroke;
1634 dash.setPathEffect(make_dash());
1635
bsalomona395f7c2016-08-24 17:47:40 -07001636 TestCase fillAB(r, lineAB, fill);
1637 TestCase fillEmpty(r, SkPath(), fill);
bsalomon0a0f67e2016-06-28 11:56:42 -07001638 fillAB.compare(r, fillEmpty, TestCase::kAllSame_ComparisonExpecation);
1639 REPORTER_ASSERT(r, !fillAB.baseShape().asLine(nullptr, nullptr));
1640
bsalomona395f7c2016-08-24 17:47:40 -07001641 TestCase strokeAB(r, lineAB, stroke);
1642 TestCase strokeBA(r, lineBA, stroke);
1643 TestCase strokeAC(r, lineAC, stroke);
bsalomon0a0f67e2016-06-28 11:56:42 -07001644
bsalomona395f7c2016-08-24 17:47:40 -07001645 TestCase hairlineAB(r, lineAB, hairline);
1646 TestCase hairlineBA(r, lineBA, hairline);
1647 TestCase hairlineAC(r, lineAC, hairline);
bsalomon0a0f67e2016-06-28 11:56:42 -07001648
bsalomona395f7c2016-08-24 17:47:40 -07001649 TestCase dashAB(r, lineAB, dash);
1650 TestCase dashBA(r, lineBA, dash);
1651 TestCase dashAC(r, lineAC, dash);
bsalomon0a0f67e2016-06-28 11:56:42 -07001652
1653 strokeAB.compare(r, fillAB, TestCase::kAllDifferent_ComparisonExpecation);
1654
1655 strokeAB.compare(r, strokeBA, TestCase::kAllSame_ComparisonExpecation);
1656 strokeAB.compare(r, strokeAC, TestCase::kAllDifferent_ComparisonExpecation);
1657
1658 hairlineAB.compare(r, hairlineBA, TestCase::kAllSame_ComparisonExpecation);
1659 hairlineAB.compare(r, hairlineAC, TestCase::kAllDifferent_ComparisonExpecation);
1660
1661 dashAB.compare(r, dashBA, TestCase::kAllDifferent_ComparisonExpecation);
1662 dashAB.compare(r, dashAC, TestCase::kAllDifferent_ComparisonExpecation);
1663
1664 strokeAB.compare(r, hairlineAB, TestCase::kSameUpToStroke_ComparisonExpecation);
1665
1666 // One of dashAB or dashBA should have the same line as strokeAB. It depends upon how
1667 // GrShape canonicalizes line endpoints (when it can, i.e. when not dashed).
1668 bool canonicalizeAsAB;
1669 SkPoint canonicalPts[2] {kA, kB};
1670 // Init these to suppress warnings.
1671 bool inverted = true;
1672 SkPoint pts[2] {{0, 0}, {0, 0}};
1673 REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted);
1674 if (pts[0] == kA && pts[1] == kB) {
1675 canonicalizeAsAB = true;
1676 } else if (pts[1] == kA && pts[0] == kB) {
1677 canonicalizeAsAB = false;
1678 SkTSwap(canonicalPts[0], canonicalPts[1]);
1679 } else {
1680 ERRORF(r, "Should return pts (a,b) or (b, a)");
1681 return;
1682 };
1683
1684 strokeAB.compare(r, canonicalizeAsAB ? dashAB : dashBA,
1685 TestCase::kSameUpToPE_ComparisonExpecation);
1686 REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted &&
1687 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1688 REPORTER_ASSERT(r, hairlineAB.baseShape().asLine(pts, &inverted) && !inverted &&
1689 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1690 REPORTER_ASSERT(r, dashAB.baseShape().asLine(pts, &inverted) && !inverted &&
1691 pts[0] == kA && pts[1] == kB);
1692 REPORTER_ASSERT(r, dashBA.baseShape().asLine(pts, &inverted) && !inverted &&
1693 pts[0] == kB && pts[1] == kA);
1694
1695
bsalomona395f7c2016-08-24 17:47:40 -07001696 TestCase strokeInvAB(r, invLineAB, stroke);
1697 TestCase hairlineInvAB(r, invLineAB, hairline);
1698 TestCase dashInvAB(r, invLineAB, dash);
bsalomon0a0f67e2016-06-28 11:56:42 -07001699 strokeInvAB.compare(r, strokeAB, TestCase::kAllDifferent_ComparisonExpecation);
1700 hairlineInvAB.compare(r, hairlineAB, TestCase::kAllDifferent_ComparisonExpecation);
1701 // Dashing ignores inverse.
1702 dashInvAB.compare(r, dashAB, TestCase::kAllSame_ComparisonExpecation);
1703
1704 REPORTER_ASSERT(r, strokeInvAB.baseShape().asLine(pts, &inverted) && inverted &&
1705 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1706 REPORTER_ASSERT(r, hairlineInvAB.baseShape().asLine(pts, &inverted) && inverted &&
1707 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1708 // Dashing ignores inverse.
1709 REPORTER_ASSERT(r, dashInvAB.baseShape().asLine(pts, &inverted) && !inverted &&
1710 pts[0] == kA && pts[1] == kB);
1711
1712}
1713
bsalomon0ae36a22016-07-18 07:31:13 -07001714static void test_stroked_lines(skiatest::Reporter* r) {
1715 // Paints to try
1716 SkPaint buttCap;
1717 buttCap.setStyle(SkPaint::kStroke_Style);
1718 buttCap.setStrokeWidth(4);
1719 buttCap.setStrokeCap(SkPaint::kButt_Cap);
1720
1721 SkPaint squareCap = buttCap;
1722 squareCap.setStrokeCap(SkPaint::kSquare_Cap);
1723
1724 SkPaint roundCap = buttCap;
1725 roundCap.setStrokeCap(SkPaint::kRound_Cap);
1726
1727 // vertical
1728 SkPath linePath;
1729 linePath.moveTo(4, 4);
1730 linePath.lineTo(4, 5);
1731
1732 SkPaint fill;
1733
bsalomona395f7c2016-08-24 17:47:40 -07001734 TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 4, 6, 5), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001735 TestCase::kAllSame_ComparisonExpecation);
1736
bsalomona395f7c2016-08-24 17:47:40 -07001737 TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 7), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001738 TestCase::kAllSame_ComparisonExpecation);
1739
bsalomona395f7c2016-08-24 17:47:40 -07001740 TestCase(r, linePath, roundCap).compare(r,
1741 TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 7), 2, 2), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001742 TestCase::kAllSame_ComparisonExpecation);
1743
1744 // horizontal
1745 linePath.reset();
1746 linePath.moveTo(4, 4);
1747 linePath.lineTo(5, 4);
1748
bsalomona395f7c2016-08-24 17:47:40 -07001749 TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(4, 2, 5, 6), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001750 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001751 TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 7, 6), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001752 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001753 TestCase(r, linePath, roundCap).compare(r,
1754 TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 7, 6), 2, 2), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001755 TestCase::kAllSame_ComparisonExpecation);
1756
1757 // point
1758 linePath.reset();
1759 linePath.moveTo(4, 4);
1760 linePath.lineTo(4, 4);
1761
bsalomona395f7c2016-08-24 17:47:40 -07001762 TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeEmpty(), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001763 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001764 TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 6), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001765 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001766 TestCase(r, linePath, roundCap).compare(r,
1767 TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 6), 2, 2), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001768 TestCase::kAllSame_ComparisonExpecation);
1769}
1770
bsalomon47cc7692016-04-26 12:56:00 -07001771DEF_TEST(GrShape, reporter) {
bsalomona395f7c2016-08-24 17:47:40 -07001772 SkTArray<std::unique_ptr<Geo>> geos;
1773 SkTArray<std::unique_ptr<RRectPathGeo>> rrectPathGeos;
1774
bsalomonee295642016-06-06 14:01:25 -07001775 for (auto r : { SkRect::MakeWH(10, 20),
1776 SkRect::MakeWH(-10, -20),
1777 SkRect::MakeWH(-10, 20),
1778 SkRect::MakeWH(10, -20)}) {
bsalomona395f7c2016-08-24 17:47:40 -07001779 geos.emplace_back(new RectGeo(r));
1780 SkPath rectPath;
1781 rectPath.addRect(r);
1782 geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
1783 PathGeo::Invert::kNo));
1784 geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
1785 PathGeo::Invert::kYes));
1786 rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
1787 PathGeo::Invert::kNo));
bsalomonee295642016-06-06 14:01:25 -07001788 }
bsalomon47cc7692016-04-26 12:56:00 -07001789 for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)),
bsalomonee295642016-06-06 14:01:25 -07001790 SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4),
1791 SkRRect::MakeOval(SkRect::MakeWH(20, 20))}) {
bsalomona395f7c2016-08-24 17:47:40 -07001792 geos.emplace_back(new RRectGeo(rr));
bsalomon70493962016-06-10 08:05:14 -07001793 test_rrect(reporter, rr);
bsalomona395f7c2016-08-24 17:47:40 -07001794 SkPath rectPath;
1795 rectPath.addRRect(rr);
1796 geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
1797 PathGeo::Invert::kNo));
1798 geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
1799 PathGeo::Invert::kYes));
1800 rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, rr,
1801 RRectPathGeo::RRectForStroke::kYes,
1802 PathGeo::Invert::kNo));
bsalomon72dc51c2016-04-27 06:46:23 -07001803 }
1804
bsalomon72dc51c2016-04-27 06:46:23 -07001805 SkPath openRectPath;
1806 openRectPath.moveTo(0, 0);
1807 openRectPath.lineTo(10, 0);
1808 openRectPath.lineTo(10, 10);
1809 openRectPath.lineTo(0, 10);
bsalomona395f7c2016-08-24 17:47:40 -07001810 geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
1811 RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kNo));
1812 geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
1813 RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kYes));
1814 rrectPathGeos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
1815 RRectPathGeo::RRectForStroke::kNo,
1816 PathGeo::Invert::kNo));
bsalomon72dc51c2016-04-27 06:46:23 -07001817
1818 SkPath quadPath;
1819 quadPath.quadTo(10, 10, 5, 8);
bsalomona395f7c2016-08-24 17:47:40 -07001820 geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kNo));
1821 geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kYes));
bsalomon398e3f42016-06-13 10:22:48 -07001822
1823 SkPath linePath;
1824 linePath.lineTo(10, 10);
bsalomona395f7c2016-08-24 17:47:40 -07001825 geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kNo));
1826 geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kYes));
bsalomon72dc51c2016-04-27 06:46:23 -07001827
bsalomon0ae36a22016-07-18 07:31:13 -07001828 // Horizontal and vertical paths become rrects when stroked.
1829 SkPath vLinePath;
1830 vLinePath.lineTo(0, 10);
bsalomona395f7c2016-08-24 17:47:40 -07001831 geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kNo));
1832 geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kYes));
bsalomon0ae36a22016-07-18 07:31:13 -07001833
1834 SkPath hLinePath;
1835 hLinePath.lineTo(10, 0);
bsalomona395f7c2016-08-24 17:47:40 -07001836 geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kNo));
1837 geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kYes));
bsalomon0ae36a22016-07-18 07:31:13 -07001838
bsalomona395f7c2016-08-24 17:47:40 -07001839 for (int i = 0; i < geos.count(); ++i) {
1840 test_basic(reporter, *geos[i]);
1841 test_scale(reporter, *geos[i]);
1842 test_dash_fill(reporter, *geos[i]);
1843 test_null_dash(reporter, *geos[i]);
1844 // Test modifying various stroke params.
1845 test_stroke_param<SkScalar>(
1846 reporter, *geos[i],
bsalomon70493962016-06-10 08:05:14 -07001847 [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
1848 SkIntToScalar(2), SkIntToScalar(4));
bsalomona395f7c2016-08-24 17:47:40 -07001849 test_stroke_join(reporter, *geos[i]);
1850 test_stroke_cap(reporter, *geos[i]);
1851 test_miter_limit(reporter, *geos[i]);
1852 test_path_effect_makes_rrect(reporter, *geos[i]);
1853 test_unknown_path_effect(reporter, *geos[i]);
1854 test_path_effect_makes_empty_shape(reporter, *geos[i]);
1855 test_path_effect_fails(reporter, *geos[i]);
1856 test_make_hairline_path_effect(reporter, *geos[i]);
1857 test_volatile_path(reporter, *geos[i]);
bsalomon70493962016-06-10 08:05:14 -07001858 }
bsalomonfd32df72016-06-14 14:37:21 -07001859
bsalomona395f7c2016-08-24 17:47:40 -07001860 for (int i = 0; i < rrectPathGeos.count(); ++i) {
1861 const RRectPathGeo& rrgeo = *rrectPathGeos[i];
bsalomon72dc51c2016-04-27 06:46:23 -07001862 SkPaint fillPaint;
bsalomona395f7c2016-08-24 17:47:40 -07001863 TestCase fillPathCase(reporter, rrgeo.path(), fillPaint);
bsalomon72dc51c2016-04-27 06:46:23 -07001864 SkRRect rrect;
bsalomona395f7c2016-08-24 17:47:40 -07001865 REPORTER_ASSERT(reporter, rrgeo.isNonPath(fillPaint) ==
bsalomon70493962016-06-10 08:05:14 -07001866 fillPathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
1867 nullptr));
bsalomona395f7c2016-08-24 17:47:40 -07001868 if (rrgeo.isNonPath(fillPaint)) {
1869 TestCase fillPathCase2(reporter, rrgeo.path(), fillPaint);
1870 REPORTER_ASSERT(reporter, rrect == rrgeo.rrect());
1871 TestCase fillRRectCase(reporter, rrect, fillPaint);
bsalomon70493962016-06-10 08:05:14 -07001872 fillPathCase2.compare(reporter, fillRRectCase,
1873 TestCase::kAllSame_ComparisonExpecation);
bsalomon72dc51c2016-04-27 06:46:23 -07001874 }
bsalomon72dc51c2016-04-27 06:46:23 -07001875 SkPaint strokePaint;
1876 strokePaint.setStrokeWidth(3.f);
1877 strokePaint.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001878 TestCase strokePathCase(reporter, rrgeo.path(), strokePaint);
1879 if (rrgeo.isNonPath(strokePaint)) {
bsalomon0ae36a22016-07-18 07:31:13 -07001880 REPORTER_ASSERT(reporter, strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
1881 nullptr));
bsalomona395f7c2016-08-24 17:47:40 -07001882 REPORTER_ASSERT(reporter, rrect == rrgeo.rrect());
1883 TestCase strokeRRectCase(reporter, rrect, strokePaint);
bsalomon72dc51c2016-04-27 06:46:23 -07001884 strokePathCase.compare(reporter, strokeRRectCase,
bsalomonee295642016-06-06 14:01:25 -07001885 TestCase::kAllSame_ComparisonExpecation);
bsalomon72dc51c2016-04-27 06:46:23 -07001886 }
bsalomon47cc7692016-04-26 12:56:00 -07001887 }
bsalomon409ed732016-04-27 12:36:02 -07001888
bsalomon4eeccc92016-04-27 13:30:25 -07001889 // Test a volatile empty path.
bsalomona395f7c2016-08-24 17:47:40 -07001890 test_volatile_path(reporter, PathGeo(SkPath(), PathGeo::Invert::kNo));
bsalomon4eeccc92016-04-27 13:30:25 -07001891
bsalomon409ed732016-04-27 12:36:02 -07001892 test_empty_shape(reporter);
bsalomon0a0f67e2016-06-28 11:56:42 -07001893
1894 test_lines(reporter);
bsalomon0ae36a22016-07-18 07:31:13 -07001895
1896 test_stroked_lines(reporter);
bsalomon47cc7692016-04-26 12:56:00 -07001897}
1898
1899#endif