blob: dae71c14ce6c2130e9333eafed7f62d371d14fe3 [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"
Mike Reedebfce6d2016-12-12 10:02:12 -050018#include "SkClipOpPriv.h"
bsalomon47cc7692016-04-26 12:56:00 -070019
bsalomon72dc51c2016-04-27 06:46:23 -070020using Key = SkTArray<uint32_t>;
21
22static bool make_key(Key* key, const GrShape& shape) {
23 int size = shape.unstyledKeySize();
24 if (size <= 0) {
25 key->reset(0);
26 return false;
27 }
28 SkASSERT(size);
29 key->reset(size);
30 shape.writeUnstyledKey(key->begin());
31 return true;
32}
33
bsalomonee295642016-06-06 14:01:25 -070034static bool paths_fill_same(const SkPath& a, const SkPath& b) {
35 SkPath pathXor;
36 Op(a, b, SkPathOp::kXOR_SkPathOp, &pathXor);
37 return pathXor.isEmpty();
38}
39
bsalomon9fb42032016-05-13 09:23:38 -070040static bool test_bounds_by_rasterizing(const SkPath& path, const SkRect& bounds) {
bsalomon164fd9f2016-08-26 06:45:06 -070041 // We test the bounds by rasterizing the path into a kRes by kRes grid. The bounds is
42 // mapped to the range kRes/4 to 3*kRes/4 in x and y. A difference clip is used to avoid
43 // rendering within the bounds (with a tolerance). Then we render the path and check that
44 // everything got clipped out.
bsalomon9fb42032016-05-13 09:23:38 -070045 static constexpr int kRes = 2000;
46 // This tolerance is in units of 1/kRes fractions of the bounds width/height.
47 static constexpr int kTol = 0;
48 GR_STATIC_ASSERT(kRes % 4 == 0);
49 SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
50 sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
51 surface->getCanvas()->clear(0x0);
52 SkRect clip = SkRect::MakeXYWH(kRes/4, kRes/4, kRes/2, kRes/2);
53 SkMatrix matrix;
54 matrix.setRectToRect(bounds, clip, SkMatrix::kFill_ScaleToFit);
55 clip.outset(SkIntToScalar(kTol), SkIntToScalar(kTol));
Mike Reedc1f77742016-12-09 09:00:50 -050056 surface->getCanvas()->clipRect(clip, kDifference_SkClipOp);
bsalomon9fb42032016-05-13 09:23:38 -070057 surface->getCanvas()->concat(matrix);
58 SkPaint whitePaint;
59 whitePaint.setColor(SK_ColorWHITE);
60 surface->getCanvas()->drawPath(path, whitePaint);
61 SkPixmap pixmap;
62 surface->getCanvas()->peekPixels(&pixmap);
63#if defined(SK_BUILD_FOR_WIN)
64 // The static constexpr version in #else causes cl.exe to crash.
65 const uint8_t* kZeros = reinterpret_cast<uint8_t*>(calloc(kRes, 1));
66#else
67 static constexpr uint8_t kZeros[kRes] = {0};
68#endif
bsalomon164fd9f2016-08-26 06:45:06 -070069 for (int y = 0; y < kRes; ++y) {
bsalomon9fb42032016-05-13 09:23:38 -070070 const uint8_t* row = pixmap.addr8(0, y);
71 if (0 != memcmp(kZeros, row, kRes)) {
72 return false;
73 }
74 }
75#ifdef SK_BUILD_FOR_WIN
76 free(const_cast<uint8_t*>(kZeros));
77#endif
78 return true;
79}
bsalomon72dc51c2016-04-27 06:46:23 -070080
bsalomon9fb42032016-05-13 09:23:38 -070081namespace {
bsalomona395f7c2016-08-24 17:47:40 -070082/**
83 * Geo is a factory for creating a GrShape from another representation. It also answers some
84 * questions about expected behavior for GrShape given the inputs.
85 */
86class Geo {
87public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040088 virtual ~Geo() {}
bsalomona395f7c2016-08-24 17:47:40 -070089 virtual GrShape makeShape(const SkPaint&) const = 0;
90 virtual SkPath path() const = 0;
91 // These functions allow tests to check for special cases where style gets
92 // applied by GrShape in its constructor (without calling GrShape::applyStyle).
93 // These unfortunately rely on knowing details of GrShape's implementation.
94 // These predicates are factored out here to avoid littering the rest of the
95 // test code with GrShape implementation details.
96 virtual bool fillChangesGeom() const { return false; }
97 virtual bool strokeIsConvertedToFill() const { return false; }
98 virtual bool strokeAndFillIsConvertedToFill(const SkPaint&) const { return false; }
99 // Is this something we expect GrShape to recognize as something simpler than a path.
100 virtual bool isNonPath(const SkPaint& paint) const { return true; }
101};
102
103class RectGeo : public Geo {
104public:
105 RectGeo(const SkRect& rect) : fRect(rect) {}
106
107 SkPath path() const override {
108 SkPath path;
109 path.addRect(fRect);
110 return path;
111 }
112
113 GrShape makeShape(const SkPaint& paint) const override {
114 return GrShape(fRect, paint);
115 }
116
117 bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
118 SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
119 // Converted to an outset rectangle.
120 return paint.getStrokeJoin() == SkPaint::kMiter_Join &&
121 paint.getStrokeMiter() >= SK_ScalarSqrt2;
122 }
123
124private:
125 SkRect fRect;
126};
127
128class RRectGeo : public Geo {
129public:
130 RRectGeo(const SkRRect& rrect) : fRRect(rrect) {}
131
132 GrShape makeShape(const SkPaint& paint) const override {
133 return GrShape(fRRect, paint);
134 }
135
136 SkPath path() const override {
137 SkPath path;
138 path.addRRect(fRRect);
139 return path;
140 }
141
142 bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
143 SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
144 if (fRRect.isRect()) {
145 return RectGeo(fRRect.rect()).strokeAndFillIsConvertedToFill(paint);
146 }
147 return false;
148 }
149
150private:
151 SkRRect fRRect;
152};
153
154class PathGeo : public Geo {
155public:
156 enum class Invert { kNo, kYes };
157
158 PathGeo(const SkPath& path, Invert invert) : fPath(path) {
159 SkASSERT(!path.isInverseFillType());
160 if (Invert::kYes == invert) {
161 if (fPath.getFillType() == SkPath::kEvenOdd_FillType) {
162 fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
163 } else {
164 SkASSERT(fPath.getFillType() == SkPath::kWinding_FillType);
165 fPath.setFillType(SkPath::kInverseWinding_FillType);
166 }
167 }
168 }
169
170 GrShape makeShape(const SkPaint& paint) const override {
171 return GrShape(fPath, paint);
172 }
173
174 SkPath path() const override { return fPath; }
175
176 bool fillChangesGeom() const override {
177 // unclosed rects get closed. Lines get turned into empty geometry
178 return this->isUnclosedRect() || (fPath.isLine(nullptr) && !fPath.isInverseFillType());
179 }
180
181 bool strokeIsConvertedToFill() const override {
182 return this->isAxisAlignedLine();
183 }
184
185 bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
186 SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
187 if (this->isAxisAlignedLine()) {
188 // The fill is ignored (zero area) and the stroke is converted to a rrect.
189 return true;
190 }
191 SkRect rect;
192 unsigned start;
193 SkPath::Direction dir;
194 if (SkPathPriv::IsSimpleClosedRect(fPath, &rect, &dir, &start)) {
195 return RectGeo(rect).strokeAndFillIsConvertedToFill(paint);
196 }
197 return false;
198 }
199
200 bool isNonPath(const SkPaint& paint) const override {
201 return fPath.isLine(nullptr) || fPath.isEmpty();
202 }
203
204private:
205 bool isAxisAlignedLine() const {
206 SkPoint pts[2];
207 if (!fPath.isLine(pts)) {
208 return false;
209 }
210 return pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY;
211 }
212
213 bool isUnclosedRect() const {
214 bool closed;
215 return fPath.isRect(nullptr, &closed, nullptr) && !closed;
216 }
217
218 SkPath fPath;
219};
220
221class RRectPathGeo : public PathGeo {
222public:
223 enum class RRectForStroke { kNo, kYes };
224
225 RRectPathGeo(const SkPath& path, const SkRRect& equivalentRRect, RRectForStroke rrectForStroke,
226 Invert invert)
227 : PathGeo(path, invert)
228 , fRRect(equivalentRRect)
229 , fRRectForStroke(rrectForStroke) {}
230
231 RRectPathGeo(const SkPath& path, const SkRect& equivalentRect, RRectForStroke rrectForStroke,
232 Invert invert)
233 : RRectPathGeo(path, SkRRect::MakeRect(equivalentRect), rrectForStroke, invert) {}
234
235 bool isNonPath(const SkPaint& paint) const override {
236 if (SkPaint::kFill_Style == paint.getStyle() || RRectForStroke::kYes == fRRectForStroke) {
237 return true;
238 }
239 return false;
240 }
241
242 const SkRRect& rrect() const { return fRRect; }
243
244private:
245 SkRRect fRRect;
246 RRectForStroke fRRectForStroke;
247};
248
bsalomon47cc7692016-04-26 12:56:00 -0700249class TestCase {
250public:
bsalomona395f7c2016-08-24 17:47:40 -0700251 TestCase(const Geo& geo, const SkPaint& paint, skiatest::Reporter* r,
252 SkScalar scale = SK_Scalar1) : fBase(geo.makeShape(paint)) {
bsalomon97fd2d42016-05-09 13:02:01 -0700253 this->init(r, scale);
bsalomon47cc7692016-04-26 12:56:00 -0700254 }
255
bsalomona395f7c2016-08-24 17:47:40 -0700256 template<typename... ShapeArgs>
257 TestCase(skiatest::Reporter* r, ShapeArgs... shapeArgs)
258 : fBase(shapeArgs...) {
259 this->init(r, SK_Scalar1);
260 }
261
bsalomon70493962016-06-10 08:05:14 -0700262 TestCase(const GrShape& shape, skiatest::Reporter* r, SkScalar scale = SK_Scalar1)
263 : fBase(shape) {
264 this->init(r, scale);
265 }
266
bsalomon47cc7692016-04-26 12:56:00 -0700267 struct SelfExpectations {
268 bool fPEHasEffect;
269 bool fPEHasValidKey;
270 bool fStrokeApplies;
271 };
272
273 void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const;
274
275 enum ComparisonExpecation {
276 kAllDifferent_ComparisonExpecation,
277 kSameUpToPE_ComparisonExpecation,
278 kSameUpToStroke_ComparisonExpecation,
279 kAllSame_ComparisonExpecation,
280 };
281
282 void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const;
283
bsalomon72dc51c2016-04-27 06:46:23 -0700284 const GrShape& baseShape() const { return fBase; }
285 const GrShape& appliedPathEffectShape() const { return fAppliedPE; }
286 const GrShape& appliedFullStyleShape() const { return fAppliedFull; }
287
288 // The returned array's count will be 0 if the key shape has no key.
289 const Key& baseKey() const { return fBaseKey; }
290 const Key& appliedPathEffectKey() const { return fAppliedPEKey; }
291 const Key& appliedFullStyleKey() const { return fAppliedFullKey; }
bsalomon409ed732016-04-27 12:36:02 -0700292 const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; }
bsalomon72dc51c2016-04-27 06:46:23 -0700293
bsalomon47cc7692016-04-26 12:56:00 -0700294private:
bsalomon9fb42032016-05-13 09:23:38 -0700295 static void CheckBounds(skiatest::Reporter* r, const GrShape& shape, const SkRect& bounds) {
296 SkPath path;
297 shape.asPath(&path);
298 // If the bounds are empty, the path ought to be as well.
bsalomon0ae36a22016-07-18 07:31:13 -0700299 if (bounds.fLeft > bounds.fRight || bounds.fTop > bounds.fBottom) {
bsalomon9fb42032016-05-13 09:23:38 -0700300 REPORTER_ASSERT(r, path.isEmpty());
301 return;
302 }
303 if (path.isEmpty()) {
304 return;
305 }
bsalomon70493962016-06-10 08:05:14 -0700306 // The bounds API explicitly calls out that it does not consider inverseness.
307 SkPath p = path;
308 p.setFillType(SkPath::ConvertToNonInverseFillType(path.getFillType()));
309 REPORTER_ASSERT(r, test_bounds_by_rasterizing(p, bounds));
bsalomon9fb42032016-05-13 09:23:38 -0700310 }
311
bsalomon97fd2d42016-05-09 13:02:01 -0700312 void init(skiatest::Reporter* r, SkScalar scale) {
313 fAppliedPE = fBase.applyStyle(GrStyle::Apply::kPathEffectOnly, scale);
314 fAppliedPEThenStroke = fAppliedPE.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec,
315 scale);
316 fAppliedFull = fBase.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale);
bsalomon47cc7692016-04-26 12:56:00 -0700317
bsalomon72dc51c2016-04-27 06:46:23 -0700318 make_key(&fBaseKey, fBase);
319 make_key(&fAppliedPEKey, fAppliedPE);
320 make_key(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
321 make_key(&fAppliedFullKey, fAppliedFull);
bsalomonfb083272016-05-04 08:27:41 -0700322
323 // Applying the path effect and then the stroke should always be the same as applying
324 // both in one go.
325 REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey);
326 SkPath a, b;
327 fAppliedPEThenStroke.asPath(&a);
328 fAppliedFull.asPath(&b);
bsalomonee295642016-06-06 14:01:25 -0700329 // If the output of the path effect is a rrect then it is possible for a and b to be
330 // different paths that fill identically. The reason is that fAppliedFull will do this:
331 // base -> apply path effect -> rrect_as_path -> stroke -> stroked_rrect_as_path
332 // fAppliedPEThenStroke will have converted the rrect_as_path back to a rrect. However,
333 // now that there is no longer a path effect, the direction and starting index get
334 // canonicalized before the stroke.
bsalomon70493962016-06-10 08:05:14 -0700335 if (fAppliedPE.asRRect(nullptr, nullptr, nullptr, nullptr)) {
bsalomonee295642016-06-06 14:01:25 -0700336 REPORTER_ASSERT(r, paths_fill_same(a, b));
337 } else {
338 REPORTER_ASSERT(r, a == b);
339 }
bsalomon7c73a532016-05-11 15:15:56 -0700340 REPORTER_ASSERT(r, fAppliedFull.isEmpty() == fAppliedPEThenStroke.isEmpty());
341
342 SkPath path;
343 fBase.asPath(&path);
344 REPORTER_ASSERT(r, path.isEmpty() == fBase.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700345 REPORTER_ASSERT(r, path.getSegmentMasks() == fBase.segmentMask());
bsalomon7c73a532016-05-11 15:15:56 -0700346 fAppliedPE.asPath(&path);
347 REPORTER_ASSERT(r, path.isEmpty() == fAppliedPE.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700348 REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedPE.segmentMask());
bsalomon7c73a532016-05-11 15:15:56 -0700349 fAppliedFull.asPath(&path);
350 REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700351 REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedFull.segmentMask());
bsalomonfb083272016-05-04 08:27:41 -0700352
bsalomon9fb42032016-05-13 09:23:38 -0700353 CheckBounds(r, fBase, fBase.bounds());
354 CheckBounds(r, fAppliedPE, fAppliedPE.bounds());
355 CheckBounds(r, fAppliedPEThenStroke, fAppliedPEThenStroke.bounds());
356 CheckBounds(r, fAppliedFull, fAppliedFull.bounds());
bsalomon0a0f67e2016-06-28 11:56:42 -0700357 SkRect styledBounds = fBase.styledBounds();
bsalomon9fb42032016-05-13 09:23:38 -0700358 CheckBounds(r, fAppliedFull, styledBounds);
bsalomon0a0f67e2016-06-28 11:56:42 -0700359 styledBounds = fAppliedPE.styledBounds();
bsalomon9fb42032016-05-13 09:23:38 -0700360 CheckBounds(r, fAppliedFull, styledBounds);
361
bsalomonfb083272016-05-04 08:27:41 -0700362 // Check that the same path is produced when style is applied by GrShape and GrStyle.
363 SkPath preStyle;
364 SkPath postPathEffect;
365 SkPath postAllStyle;
366
367 fBase.asPath(&preStyle);
bsalomon1a0b9ed2016-05-06 11:07:03 -0700368 SkStrokeRec postPEStrokeRec(SkStrokeRec::kFill_InitStyle);
bsalomon97fd2d42016-05-09 13:02:01 -0700369 if (fBase.style().applyPathEffectToPath(&postPathEffect, &postPEStrokeRec, preStyle,
370 scale)) {
bsalomon1a0b9ed2016-05-06 11:07:03 -0700371 // run postPathEffect through GrShape to get any geometry reductions that would have
372 // occurred to fAppliedPE.
373 GrShape(postPathEffect, GrStyle(postPEStrokeRec, nullptr)).asPath(&postPathEffect);
374
bsalomonfb083272016-05-04 08:27:41 -0700375 SkPath testPath;
376 fAppliedPE.asPath(&testPath);
377 REPORTER_ASSERT(r, testPath == postPathEffect);
bsalomon1a0b9ed2016-05-06 11:07:03 -0700378 REPORTER_ASSERT(r, postPEStrokeRec.hasEqualEffect(fAppliedPE.style().strokeRec()));
bsalomonfb083272016-05-04 08:27:41 -0700379 }
380 SkStrokeRec::InitStyle fillOrHairline;
bsalomon97fd2d42016-05-09 13:02:01 -0700381 if (fBase.style().applyToPath(&postAllStyle, &fillOrHairline, preStyle, scale)) {
bsalomonfb083272016-05-04 08:27:41 -0700382 SkPath testPath;
383 fAppliedFull.asPath(&testPath);
bsalomon1b28c1a2016-06-20 12:28:17 -0700384 if (fBase.style().hasPathEffect()) {
385 // Because GrShape always does two-stage application when there is a path effect
386 // there may be a reduction/canonicalization step between the path effect and
387 // strokerec not reflected in postAllStyle since it applied both the path effect
388 // and strokerec without analyzing the intermediate path.
389 REPORTER_ASSERT(r, paths_fill_same(postAllStyle, testPath));
390 } else {
391 // Make sure that postAllStyle sees any reductions/canonicalizations that GrShape
392 // would apply.
393 GrShape(postAllStyle, GrStyle(fillOrHairline)).asPath(&postAllStyle);
394 REPORTER_ASSERT(r, testPath == postAllStyle);
395 }
396
bsalomonfb083272016-05-04 08:27:41 -0700397 if (fillOrHairline == SkStrokeRec::kFill_InitStyle) {
398 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleFill());
399 } else {
400 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleHairline());
401 }
402 }
bsalomon47cc7692016-04-26 12:56:00 -0700403 }
404
405 GrShape fBase;
406 GrShape fAppliedPE;
407 GrShape fAppliedPEThenStroke;
408 GrShape fAppliedFull;
409
410 Key fBaseKey;
411 Key fAppliedPEKey;
412 Key fAppliedPEThenStrokeKey;
413 Key fAppliedFullKey;
bsalomon47cc7692016-04-26 12:56:00 -0700414};
415
416void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const {
bsalomon47cc7692016-04-26 12:56:00 -0700417 // The base's key should always be valid (unless the path is volatile)
bsalomon72dc51c2016-04-27 06:46:23 -0700418 REPORTER_ASSERT(reporter, fBaseKey.count());
bsalomon47cc7692016-04-26 12:56:00 -0700419 if (expectations.fPEHasEffect) {
420 REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700421 REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedPEKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700422 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700423 REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedFullKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700424 if (expectations.fStrokeApplies && expectations.fPEHasValidKey) {
425 REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700426 REPORTER_ASSERT(reporter, SkToBool(fAppliedFullKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700427 }
428 } else {
429 REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey);
bsalomonfb083272016-05-04 08:27:41 -0700430 SkPath a, b;
bsalomon72dc51c2016-04-27 06:46:23 -0700431 fBase.asPath(&a);
432 fAppliedPE.asPath(&b);
433 REPORTER_ASSERT(reporter, a == b);
bsalomon47cc7692016-04-26 12:56:00 -0700434 if (expectations.fStrokeApplies) {
435 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
436 } else {
437 REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey);
438 }
439 }
440}
441
bsalomonac5fcea2016-06-23 12:23:07 -0700442static bool can_interchange_winding_and_even_odd_fill(const GrShape& shape) {
443 SkPath path;
444 shape.asPath(&path);
445 if (shape.style().hasNonDashPathEffect()) {
446 return false;
447 }
448 const SkStrokeRec::Style strokeRecStyle = shape.style().strokeRec().getStyle();
449 return strokeRecStyle == SkStrokeRec::kStroke_Style ||
450 strokeRecStyle == SkStrokeRec::kHairline_Style ||
451 (shape.style().isSimpleFill() && path.isConvex());
452}
453
454static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrShape& b,
455 const Key& keyA, const Key& keyB) {
bsalomonee295642016-06-06 14:01:25 -0700456 // GrShape only respects the input winding direction and start point for rrect shapes
457 // when there is a path effect. Thus, if there are two GrShapes representing the same rrect
458 // but one has a path effect in its style and the other doesn't then asPath() and the unstyled
459 // key will differ. GrShape will have canonicalized the direction and start point for the shape
460 // without the path effect. If *both* have path effects then they should have both preserved
461 // the direction and starting point.
462
463 // The asRRect() output params are all initialized just to silence compiler warnings about
464 // uninitialized variables.
465 SkRRect rrectA = SkRRect::MakeEmpty(), rrectB = SkRRect::MakeEmpty();
466 SkPath::Direction dirA = SkPath::kCW_Direction, dirB = SkPath::kCW_Direction;
467 unsigned startA = ~0U, startB = ~0U;
bsalomon70493962016-06-10 08:05:14 -0700468 bool invertedA = true, invertedB = true;
bsalomonee295642016-06-06 14:01:25 -0700469
bsalomon70493962016-06-10 08:05:14 -0700470 bool aIsRRect = a.asRRect(&rrectA, &dirA, &startA, &invertedA);
471 bool bIsRRect = b.asRRect(&rrectB, &dirB, &startB, &invertedB);
bsalomonee295642016-06-06 14:01:25 -0700472 bool aHasPE = a.style().hasPathEffect();
473 bool bHasPE = b.style().hasPathEffect();
474 bool allowSameRRectButDiffStartAndDir = (aIsRRect && bIsRRect) && (aHasPE != bHasPE);
bsalomon425c27f2016-06-23 13:18:45 -0700475 // GrShape will close paths with simple fill style.
476 bool allowedClosednessDiff = (a.style().isSimpleFill() != b.style().isSimpleFill());
bsalomonee295642016-06-06 14:01:25 -0700477 SkPath pathA, pathB;
478 a.asPath(&pathA);
479 b.asPath(&pathB);
bsalomon70493962016-06-10 08:05:14 -0700480
bsalomonfd32df72016-06-14 14:37:21 -0700481 // Having a dash path effect can allow 'a' but not 'b' to turn a inverse fill type into a
482 // non-inverse fill type (or vice versa).
bsalomon70493962016-06-10 08:05:14 -0700483 bool ignoreInversenessDifference = false;
484 if (pathA.isInverseFillType() != pathB.isInverseFillType()) {
485 const GrShape* s1 = pathA.isInverseFillType() ? &a : &b;
486 const GrShape* s2 = pathA.isInverseFillType() ? &b : &a;
bsalomonfd32df72016-06-14 14:37:21 -0700487 bool canDropInverse1 = s1->style().isDashed();
488 bool canDropInverse2 = s2->style().isDashed();
489 ignoreInversenessDifference = (canDropInverse1 != canDropInverse2);
bsalomon70493962016-06-10 08:05:14 -0700490 }
bsalomona4817af2016-06-23 11:48:26 -0700491 bool ignoreWindingVsEvenOdd = false;
492 if (SkPath::ConvertToNonInverseFillType(pathA.getFillType()) !=
493 SkPath::ConvertToNonInverseFillType(pathB.getFillType())) {
bsalomonac5fcea2016-06-23 12:23:07 -0700494 bool aCanChange = can_interchange_winding_and_even_odd_fill(a);
495 bool bCanChange = can_interchange_winding_and_even_odd_fill(b);
bsalomona4817af2016-06-23 11:48:26 -0700496 if (aCanChange != bCanChange) {
497 ignoreWindingVsEvenOdd = true;
498 }
499 }
bsalomonee295642016-06-06 14:01:25 -0700500 if (allowSameRRectButDiffStartAndDir) {
501 REPORTER_ASSERT(r, rrectA == rrectB);
502 REPORTER_ASSERT(r, paths_fill_same(pathA, pathB));
bsalomon70493962016-06-10 08:05:14 -0700503 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
bsalomonee295642016-06-06 14:01:25 -0700504 } else {
bsalomon70493962016-06-10 08:05:14 -0700505 SkPath pA = pathA;
506 SkPath pB = pathB;
bsalomon425c27f2016-06-23 13:18:45 -0700507 REPORTER_ASSERT(r, a.inverseFilled() == pA.isInverseFillType());
508 REPORTER_ASSERT(r, b.inverseFilled() == pB.isInverseFillType());
bsalomon70493962016-06-10 08:05:14 -0700509 if (ignoreInversenessDifference) {
510 pA.setFillType(SkPath::ConvertToNonInverseFillType(pathA.getFillType()));
511 pB.setFillType(SkPath::ConvertToNonInverseFillType(pathB.getFillType()));
bsalomona4817af2016-06-23 11:48:26 -0700512 }
513 if (ignoreWindingVsEvenOdd) {
514 pA.setFillType(pA.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
515 : SkPath::kEvenOdd_FillType);
516 pB.setFillType(pB.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
517 : SkPath::kEvenOdd_FillType);
518 }
519 if (!ignoreInversenessDifference && !ignoreWindingVsEvenOdd) {
bsalomon70493962016-06-10 08:05:14 -0700520 REPORTER_ASSERT(r, keyA == keyB);
bsalomona4817af2016-06-23 11:48:26 -0700521 } else {
522 REPORTER_ASSERT(r, keyA != keyB);
bsalomon70493962016-06-10 08:05:14 -0700523 }
bsalomon425c27f2016-06-23 13:18:45 -0700524 if (allowedClosednessDiff) {
bsalomon93f66bc2016-06-21 08:35:49 -0700525 // GrShape will close paths with simple fill style. Make the non-filled path closed
526 // so that the comparision will succeed. Make sure both are closed before comparing.
527 pA.close();
528 pB.close();
529 }
bsalomon70493962016-06-10 08:05:14 -0700530 REPORTER_ASSERT(r, pA == pB);
bsalomonee295642016-06-06 14:01:25 -0700531 REPORTER_ASSERT(r, aIsRRect == bIsRRect);
532 if (aIsRRect) {
533 REPORTER_ASSERT(r, rrectA == rrectB);
534 REPORTER_ASSERT(r, dirA == dirB);
535 REPORTER_ASSERT(r, startA == startB);
bsalomon70493962016-06-10 08:05:14 -0700536 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
bsalomonee295642016-06-06 14:01:25 -0700537 }
538 }
539 REPORTER_ASSERT(r, a.isEmpty() == b.isEmpty());
bsalomon425c27f2016-06-23 13:18:45 -0700540 REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeClosed() == b.knownToBeClosed());
541 // closedness can affect convexity.
542 REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeConvex() == b.knownToBeConvex());
543 if (a.knownToBeConvex()) {
544 REPORTER_ASSERT(r, pathA.isConvex());
545 }
546 if (b.knownToBeConvex()) {
547 REPORTER_ASSERT(r, pathB.isConvex());
548 }
bsalomonee295642016-06-06 14:01:25 -0700549 REPORTER_ASSERT(r, a.bounds() == b.bounds());
bsalomon06115ee2016-06-07 06:28:51 -0700550 REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask());
bsalomon0a0f67e2016-06-28 11:56:42 -0700551 // Init these to suppress warnings.
552 SkPoint pts[4] {{0, 0,}, {0, 0}, {0, 0}, {0, 0}} ;
553 bool invertedLine[2] {true, true};
554 REPORTER_ASSERT(r, a.asLine(pts, &invertedLine[0]) == b.asLine(pts + 2, &invertedLine[1]));
bsalomon425c27f2016-06-23 13:18:45 -0700555 // mayBeInverseFilledAfterStyling() is allowed to differ if one has a arbitrary PE and the other
556 // doesn't (since the PE can set any fill type on its output path).
557 // Moreover, dash style explicitly ignores inverseness. So if one is dashed but not the other
558 // then they may disagree about inverseness.
559 if (a.style().hasNonDashPathEffect() == b.style().hasNonDashPathEffect() &&
560 a.style().isDashed() == b.style().isDashed()) {
561 REPORTER_ASSERT(r, a.mayBeInverseFilledAfterStyling() ==
562 b.mayBeInverseFilledAfterStyling());
563 }
bsalomon0a0f67e2016-06-28 11:56:42 -0700564 if (a.asLine(nullptr, nullptr)) {
bsalomon398e3f42016-06-13 10:22:48 -0700565 REPORTER_ASSERT(r, pts[2] == pts[0] && pts[3] == pts[1]);
bsalomon0a0f67e2016-06-28 11:56:42 -0700566 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedLine[0] == invertedLine[1]);
567 REPORTER_ASSERT(r, invertedLine[0] == a.inverseFilled());
568 REPORTER_ASSERT(r, invertedLine[1] == b.inverseFilled());
bsalomon398e3f42016-06-13 10:22:48 -0700569 }
bsalomon425c27f2016-06-23 13:18:45 -0700570 REPORTER_ASSERT(r, ignoreInversenessDifference || a.inverseFilled() == b.inverseFilled());
bsalomonee295642016-06-06 14:01:25 -0700571}
572
573void TestCase::compare(skiatest::Reporter* r, const TestCase& that,
bsalomon47cc7692016-04-26 12:56:00 -0700574 ComparisonExpecation expectation) const {
bsalomon72dc51c2016-04-27 06:46:23 -0700575 SkPath a, b;
bsalomon47cc7692016-04-26 12:56:00 -0700576 switch (expectation) {
577 case kAllDifferent_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700578 REPORTER_ASSERT(r, fBaseKey != that.fBaseKey);
579 REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
580 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700581 break;
582 case kSameUpToPE_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700583 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
584 REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
585 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700586 break;
587 case kSameUpToStroke_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700588 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
589 check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
590 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700591 break;
592 case kAllSame_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700593 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
594 check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
595 check_equivalence(r, fAppliedFull, that.fAppliedFull, fAppliedFullKey,
596 that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700597 break;
598 }
599}
600} // namespace
601
602static sk_sp<SkPathEffect> make_dash() {
603 static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f };
604 static const SkScalar kPhase = 0.75;
605 return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase);
606}
607
608static sk_sp<SkPathEffect> make_null_dash() {
609 static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0};
610 return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f);
611}
612
bsalomona395f7c2016-08-24 17:47:40 -0700613static void test_basic(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700614 sk_sp<SkPathEffect> dashPE = make_dash();
615
616 TestCase::SelfExpectations expectations;
617 SkPaint fill;
618
bsalomonfb083272016-05-04 08:27:41 -0700619 TestCase fillCase(geo, fill, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700620 expectations.fPEHasEffect = false;
621 expectations.fPEHasValidKey = false;
622 expectations.fStrokeApplies = false;
623 fillCase.testExpectations(reporter, expectations);
624 // Test that another GrShape instance built from the same primitive is the same.
bsalomonfb083272016-05-04 08:27:41 -0700625 TestCase(geo, fill, reporter).compare(reporter, fillCase,
626 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700627
628 SkPaint stroke2RoundBevel;
629 stroke2RoundBevel.setStyle(SkPaint::kStroke_Style);
630 stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap);
631 stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join);
632 stroke2RoundBevel.setStrokeWidth(2.f);
bsalomonfb083272016-05-04 08:27:41 -0700633 TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700634 expectations.fPEHasValidKey = true;
635 expectations.fPEHasEffect = false;
bsalomona395f7c2016-08-24 17:47:40 -0700636 expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
bsalomon47cc7692016-04-26 12:56:00 -0700637 stroke2RoundBevelCase.testExpectations(reporter, expectations);
bsalomonfb083272016-05-04 08:27:41 -0700638 TestCase(geo, stroke2RoundBevel, reporter).compare(reporter, stroke2RoundBevelCase,
639 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700640
641 SkPaint stroke2RoundBevelDash = stroke2RoundBevel;
642 stroke2RoundBevelDash.setPathEffect(make_dash());
bsalomonfb083272016-05-04 08:27:41 -0700643 TestCase stroke2RoundBevelDashCase(geo, stroke2RoundBevelDash, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700644 expectations.fPEHasValidKey = true;
645 expectations.fPEHasEffect = true;
646 expectations.fStrokeApplies = true;
647 stroke2RoundBevelDashCase.testExpectations(reporter, expectations);
bsalomonfb083272016-05-04 08:27:41 -0700648 TestCase(geo, stroke2RoundBevelDash, reporter).compare(reporter, stroke2RoundBevelDashCase,
649 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700650
bsalomona395f7c2016-08-24 17:47:40 -0700651 if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700652 fillCase.compare(reporter, stroke2RoundBevelCase,
653 TestCase::kAllDifferent_ComparisonExpecation);
654 fillCase.compare(reporter, stroke2RoundBevelDashCase,
655 TestCase::kAllDifferent_ComparisonExpecation);
656 } else {
657 fillCase.compare(reporter, stroke2RoundBevelCase,
658 TestCase::kSameUpToStroke_ComparisonExpecation);
659 fillCase.compare(reporter, stroke2RoundBevelDashCase,
660 TestCase::kSameUpToPE_ComparisonExpecation);
661 }
bsalomona395f7c2016-08-24 17:47:40 -0700662 if (geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700663 stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
664 TestCase::kAllDifferent_ComparisonExpecation);
665 } else {
666 stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
667 TestCase::kSameUpToPE_ComparisonExpecation);
668 }
bsalomon72dc51c2016-04-27 06:46:23 -0700669
bsalomonf0cf3552016-05-05 08:28:30 -0700670 // Stroke and fill cases
671 SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel;
672 stroke2RoundBevelAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
673 TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter);
674 expectations.fPEHasValidKey = true;
675 expectations.fPEHasEffect = false;
bsalomona395f7c2016-08-24 17:47:40 -0700676 expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
bsalomonf0cf3552016-05-05 08:28:30 -0700677 stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations);
678 TestCase(geo, stroke2RoundBevelAndFill, reporter).compare(reporter,
679 stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation);
680
681 SkPaint stroke2RoundBevelAndFillDash = stroke2RoundBevelDash;
682 stroke2RoundBevelAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
683 TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter);
684 expectations.fPEHasValidKey = true;
bsalomona0587862016-06-09 06:03:38 -0700685 expectations.fPEHasEffect = false;
bsalomona395f7c2016-08-24 17:47:40 -0700686 expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
bsalomonf0cf3552016-05-05 08:28:30 -0700687 stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations);
688 TestCase(geo, stroke2RoundBevelAndFillDash, reporter).compare(
689 reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation);
bsalomona0587862016-06-09 06:03:38 -0700690 stroke2RoundBevelAndFillDashCase.compare(reporter, stroke2RoundBevelAndFillCase,
691 TestCase::kAllSame_ComparisonExpecation);
bsalomonf0cf3552016-05-05 08:28:30 -0700692
bsalomon72dc51c2016-04-27 06:46:23 -0700693 SkPaint hairline;
694 hairline.setStyle(SkPaint::kStroke_Style);
695 hairline.setStrokeWidth(0.f);
bsalomonfb083272016-05-04 08:27:41 -0700696 TestCase hairlineCase(geo, hairline, reporter);
bsalomon487f8d32016-07-20 07:15:44 -0700697 // Since hairline style doesn't change the SkPath data, it is keyed identically to fill (except
698 // in the line and unclosed rect cases).
bsalomona395f7c2016-08-24 17:47:40 -0700699 if (geo.fillChangesGeom()) {
bsalomon487f8d32016-07-20 07:15:44 -0700700 hairlineCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation);
701 } else {
702 hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
703 }
bsalomon9ad5d7c2016-05-04 08:44:15 -0700704 REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline());
705 REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline());
706 REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline());
bsalomon47cc7692016-04-26 12:56:00 -0700707
bsalomon0ae36a22016-07-18 07:31:13 -0700708}
709
bsalomona395f7c2016-08-24 17:47:40 -0700710static void test_scale(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon97fd2d42016-05-09 13:02:01 -0700711 sk_sp<SkPathEffect> dashPE = make_dash();
712
713 static const SkScalar kS1 = 1.f;
714 static const SkScalar kS2 = 2.f;
715
716 SkPaint fill;
717 TestCase fillCase1(geo, fill, reporter, kS1);
718 TestCase fillCase2(geo, fill, reporter, kS2);
719 // Scale doesn't affect fills.
720 fillCase1.compare(reporter, fillCase2, TestCase::kAllSame_ComparisonExpecation);
721
722 SkPaint hairline;
723 hairline.setStyle(SkPaint::kStroke_Style);
724 hairline.setStrokeWidth(0.f);
725 TestCase hairlineCase1(geo, hairline, reporter, kS1);
726 TestCase hairlineCase2(geo, hairline, reporter, kS2);
727 // Scale doesn't affect hairlines.
728 hairlineCase1.compare(reporter, hairlineCase2, TestCase::kAllSame_ComparisonExpecation);
729
730 SkPaint stroke;
731 stroke.setStyle(SkPaint::kStroke_Style);
732 stroke.setStrokeWidth(2.f);
733 TestCase strokeCase1(geo, stroke, reporter, kS1);
734 TestCase strokeCase2(geo, stroke, reporter, kS2);
bsalomon0ae36a22016-07-18 07:31:13 -0700735 // Scale affects the stroke
bsalomona395f7c2016-08-24 17:47:40 -0700736 if (geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700737 REPORTER_ASSERT(reporter, !strokeCase1.baseShape().style().applies());
bsalomon0ae36a22016-07-18 07:31:13 -0700738 strokeCase1.compare(reporter, strokeCase2, TestCase::kAllSame_ComparisonExpecation);
739 } else {
740 strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation);
741 }
bsalomon97fd2d42016-05-09 13:02:01 -0700742
743 SkPaint strokeDash = stroke;
744 strokeDash.setPathEffect(make_dash());
745 TestCase strokeDashCase1(geo, strokeDash, reporter, kS1);
746 TestCase strokeDashCase2(geo, strokeDash, reporter, kS2);
747 // Scale affects the dash and the stroke.
bsalomon487f8d32016-07-20 07:15:44 -0700748 strokeDashCase1.compare(reporter, strokeDashCase2,
749 TestCase::kSameUpToPE_ComparisonExpecation);
bsalomon97fd2d42016-05-09 13:02:01 -0700750
751 // Stroke and fill cases
752 SkPaint strokeAndFill = stroke;
753 strokeAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
754 TestCase strokeAndFillCase1(geo, strokeAndFill, reporter, kS1);
755 TestCase strokeAndFillCase2(geo, strokeAndFill, reporter, kS2);
bsalomona0587862016-06-09 06:03:38 -0700756 SkPaint strokeAndFillDash = strokeDash;
757 strokeAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
758 // Dash is ignored for stroke and fill
759 TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1);
760 TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2);
bsalomon487f8d32016-07-20 07:15:44 -0700761 // Scale affects the stroke, but check to make sure this didn't become a simpler shape (e.g.
762 // stroke-and-filled rect can become a rect), in which case the scale shouldn't matter and the
763 // geometries should agree.
bsalomona395f7c2016-08-24 17:47:40 -0700764 if (geo.strokeAndFillIsConvertedToFill(strokeAndFillDash)) {
bsalomon487f8d32016-07-20 07:15:44 -0700765 REPORTER_ASSERT(reporter, !strokeAndFillCase1.baseShape().style().applies());
bsalomon97fd2d42016-05-09 13:02:01 -0700766 strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
767 TestCase::kAllSame_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700768 strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2,
769 TestCase::kAllSame_ComparisonExpecation);
bsalomon97fd2d42016-05-09 13:02:01 -0700770 } else {
771 strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
772 TestCase::kSameUpToStroke_ComparisonExpecation);
773 }
bsalomona0587862016-06-09 06:03:38 -0700774 strokeAndFillDashCase1.compare(reporter, strokeAndFillCase1,
775 TestCase::kAllSame_ComparisonExpecation);
776 strokeAndFillDashCase2.compare(reporter, strokeAndFillCase2,
777 TestCase::kAllSame_ComparisonExpecation);
bsalomon97fd2d42016-05-09 13:02:01 -0700778}
779
bsalomona395f7c2016-08-24 17:47:40 -0700780template <typename T>
781static void test_stroke_param_impl(skiatest::Reporter* reporter, const Geo& geo,
bsalomon06077562016-05-04 13:50:29 -0700782 std::function<void(SkPaint*, T)> setter, T a, T b,
783 bool paramAffectsStroke,
784 bool paramAffectsDashAndStroke) {
785 // Set the stroke width so that we don't get hairline. However, call the setter afterward so
786 // that it can override the stroke width.
bsalomon47cc7692016-04-26 12:56:00 -0700787 SkPaint strokeA;
788 strokeA.setStyle(SkPaint::kStroke_Style);
789 strokeA.setStrokeWidth(2.f);
790 setter(&strokeA, a);
791 SkPaint strokeB;
792 strokeB.setStyle(SkPaint::kStroke_Style);
793 strokeB.setStrokeWidth(2.f);
794 setter(&strokeB, b);
795
bsalomonfb083272016-05-04 08:27:41 -0700796 TestCase strokeACase(geo, strokeA, reporter);
797 TestCase strokeBCase(geo, strokeB, reporter);
bsalomon06077562016-05-04 13:50:29 -0700798 if (paramAffectsStroke) {
bsalomon0ae36a22016-07-18 07:31:13 -0700799 // If stroking is immediately incorporated into a geometric transformation then the base
800 // shapes will differ.
bsalomona395f7c2016-08-24 17:47:40 -0700801 if (geo.strokeIsConvertedToFill()) {
bsalomon0ae36a22016-07-18 07:31:13 -0700802 strokeACase.compare(reporter, strokeBCase,
803 TestCase::kAllDifferent_ComparisonExpecation);
bsalomon487f8d32016-07-20 07:15:44 -0700804 } else {
805 strokeACase.compare(reporter, strokeBCase,
806 TestCase::kSameUpToStroke_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700807 }
bsalomon06077562016-05-04 13:50:29 -0700808 } else {
809 strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation);
810 }
bsalomon47cc7692016-04-26 12:56:00 -0700811
bsalomonf0cf3552016-05-05 08:28:30 -0700812 SkPaint strokeAndFillA = strokeA;
813 SkPaint strokeAndFillB = strokeB;
814 strokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style);
815 strokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style);
816 TestCase strokeAndFillACase(geo, strokeAndFillA, reporter);
817 TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter);
818 if (paramAffectsStroke) {
bsalomon0ae36a22016-07-18 07:31:13 -0700819 // If stroking is immediately incorporated into a geometric transformation then the base
820 // shapes will differ.
bsalomona395f7c2016-08-24 17:47:40 -0700821 if (geo.strokeAndFillIsConvertedToFill(strokeAndFillA) ||
822 geo.strokeAndFillIsConvertedToFill(strokeAndFillB)) {
bsalomon0ae36a22016-07-18 07:31:13 -0700823 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
bsalomon487f8d32016-07-20 07:15:44 -0700824 TestCase::kAllDifferent_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700825 } else {
826 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
bsalomon487f8d32016-07-20 07:15:44 -0700827 TestCase::kSameUpToStroke_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700828 }
bsalomonf0cf3552016-05-05 08:28:30 -0700829 } else {
830 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
831 TestCase::kAllSame_ComparisonExpecation);
832 }
833
bsalomon47cc7692016-04-26 12:56:00 -0700834 // Make sure stroking params don't affect fill style.
835 SkPaint fillA = strokeA, fillB = strokeB;
836 fillA.setStyle(SkPaint::kFill_Style);
837 fillB.setStyle(SkPaint::kFill_Style);
bsalomonfb083272016-05-04 08:27:41 -0700838 TestCase fillACase(geo, fillA, reporter);
839 TestCase fillBCase(geo, fillB, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700840 fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
841
842 // Make sure just applying the dash but not stroke gives the same key for both stroking
843 // variations.
844 SkPaint dashA = strokeA, dashB = strokeB;
845 dashA.setPathEffect(make_dash());
846 dashB.setPathEffect(make_dash());
bsalomonfb083272016-05-04 08:27:41 -0700847 TestCase dashACase(geo, dashA, reporter);
848 TestCase dashBCase(geo, dashB, reporter);
bsalomon06077562016-05-04 13:50:29 -0700849 if (paramAffectsDashAndStroke) {
bsalomon487f8d32016-07-20 07:15:44 -0700850 dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
bsalomon06077562016-05-04 13:50:29 -0700851 } else {
852 dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation);
853 }
bsalomon47cc7692016-04-26 12:56:00 -0700854}
855
bsalomona395f7c2016-08-24 17:47:40 -0700856template <typename T>
857static void test_stroke_param(skiatest::Reporter* reporter, const Geo& geo,
bsalomon06077562016-05-04 13:50:29 -0700858 std::function<void(SkPaint*, T)> setter, T a, T b) {
859 test_stroke_param_impl(reporter, geo, setter, a, b, true, true);
860};
861
bsalomona395f7c2016-08-24 17:47:40 -0700862static void test_stroke_cap(skiatest::Reporter* reporter, const Geo& geo) {
863 SkPaint hairline;
864 hairline.setStrokeWidth(0);
865 hairline.setStyle(SkPaint::kStroke_Style);
866 GrShape shape = geo.makeShape(hairline);
bsalomon06077562016-05-04 13:50:29 -0700867 // The cap should only affect shapes that may be open.
868 bool affectsStroke = !shape.knownToBeClosed();
869 // Dashing adds ends that need caps.
870 bool affectsDashAndStroke = true;
bsalomona395f7c2016-08-24 17:47:40 -0700871 test_stroke_param_impl<SkPaint::Cap>(
bsalomon06077562016-05-04 13:50:29 -0700872 reporter,
873 geo,
874 [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
875 SkPaint::kButt_Cap, SkPaint::kRound_Cap,
876 affectsStroke,
877 affectsDashAndStroke);
878};
879
bsalomon0ae36a22016-07-18 07:31:13 -0700880static bool shape_known_not_to_have_joins(const GrShape& shape) {
881 return shape.asLine(nullptr, nullptr) || shape.isEmpty();
882}
883
bsalomona395f7c2016-08-24 17:47:40 -0700884static void test_stroke_join(skiatest::Reporter* reporter, const Geo& geo) {
885 SkPaint hairline;
886 hairline.setStrokeWidth(0);
887 hairline.setStyle(SkPaint::kStroke_Style);
888 GrShape shape = geo.makeShape(hairline);
bsalomon0ae36a22016-07-18 07:31:13 -0700889 // GrShape recognizes certain types don't have joins and will prevent the join type from
890 // affecting the style key.
891 // Dashing doesn't add additional joins. However, GrShape currently loses track of this
892 // after applying the dash.
893 bool affectsStroke = !shape_known_not_to_have_joins(shape);
bsalomona395f7c2016-08-24 17:47:40 -0700894 test_stroke_param_impl<SkPaint::Join>(
bsalomon0ae36a22016-07-18 07:31:13 -0700895 reporter,
896 geo,
897 [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
898 SkPaint::kRound_Join, SkPaint::kBevel_Join,
899 affectsStroke, true);
900};
901
bsalomona395f7c2016-08-24 17:47:40 -0700902static void test_miter_limit(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon06077562016-05-04 13:50:29 -0700903 auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) {
904 p->setStrokeJoin(SkPaint::kMiter_Join);
905 p->setStrokeMiter(miter);
906 };
bsalomon47cc7692016-04-26 12:56:00 -0700907
bsalomon06077562016-05-04 13:50:29 -0700908 auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) {
909 p->setStrokeJoin(SkPaint::kRound_Join);
910 p->setStrokeMiter(miter);
911 };
bsalomon47cc7692016-04-26 12:56:00 -0700912
bsalomona395f7c2016-08-24 17:47:40 -0700913 SkPaint hairline;
914 hairline.setStrokeWidth(0);
915 hairline.setStyle(SkPaint::kStroke_Style);
916 GrShape shape = geo.makeShape(hairline);
bsalomon0ae36a22016-07-18 07:31:13 -0700917 bool mayHaveJoins = !shape_known_not_to_have_joins(shape);
918
bsalomon06077562016-05-04 13:50:29 -0700919 // The miter limit should affect stroked and dashed-stroked cases when the join type is
920 // miter.
bsalomona395f7c2016-08-24 17:47:40 -0700921 test_stroke_param_impl<SkScalar>(
bsalomon06077562016-05-04 13:50:29 -0700922 reporter,
923 geo,
924 setMiterJoinAndLimit,
925 0.5f, 0.75f,
bsalomon0ae36a22016-07-18 07:31:13 -0700926 mayHaveJoins,
bsalomon06077562016-05-04 13:50:29 -0700927 true);
bsalomon47cc7692016-04-26 12:56:00 -0700928
bsalomon06077562016-05-04 13:50:29 -0700929 // The miter limit should not affect stroked and dashed-stroked cases when the join type is
930 // not miter.
bsalomona395f7c2016-08-24 17:47:40 -0700931 test_stroke_param_impl<SkScalar>(
bsalomon06077562016-05-04 13:50:29 -0700932 reporter,
933 geo,
934 setOtherJoinAndLimit,
935 0.5f, 0.75f,
936 false,
937 false);
bsalomon47cc7692016-04-26 12:56:00 -0700938}
939
bsalomona395f7c2016-08-24 17:47:40 -0700940static void test_dash_fill(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700941 // A dash with no stroke should have no effect
942 using DashFactoryFn = sk_sp<SkPathEffect>(*)();
943 for (DashFactoryFn md : {&make_dash, &make_null_dash}) {
944 SkPaint dashFill;
945 dashFill.setPathEffect((*md)());
bsalomonfb083272016-05-04 08:27:41 -0700946 TestCase dashFillCase(geo, dashFill, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700947
bsalomonfb083272016-05-04 08:27:41 -0700948 TestCase fillCase(geo, SkPaint(), reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700949 dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
950 }
951}
952
bsalomona395f7c2016-08-24 17:47:40 -0700953void test_null_dash(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700954 SkPaint fill;
955 SkPaint stroke;
956 stroke.setStyle(SkPaint::kStroke_Style);
957 stroke.setStrokeWidth(1.f);
958 SkPaint dash;
959 dash.setStyle(SkPaint::kStroke_Style);
960 dash.setStrokeWidth(1.f);
961 dash.setPathEffect(make_dash());
962 SkPaint nullDash;
963 nullDash.setStyle(SkPaint::kStroke_Style);
964 nullDash.setStrokeWidth(1.f);
965 nullDash.setPathEffect(make_null_dash());
966
bsalomonfb083272016-05-04 08:27:41 -0700967 TestCase fillCase(geo, fill, reporter);
968 TestCase strokeCase(geo, stroke, reporter);
969 TestCase dashCase(geo, dash, reporter);
970 TestCase nullDashCase(geo, nullDash, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700971
bsalomon487f8d32016-07-20 07:15:44 -0700972 // We expect the null dash to be ignored so nullDashCase should match strokeCase, always.
bsalomon47cc7692016-04-26 12:56:00 -0700973 nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation);
bsalomon487f8d32016-07-20 07:15:44 -0700974 // Check whether the fillCase or strokeCase/nullDashCase would undergo a geometric tranformation
975 // on construction in order to determine how to compare the fill and stroke.
bsalomona395f7c2016-08-24 17:47:40 -0700976 if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700977 nullDashCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation);
978 } else {
979 nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation);
980 }
981 // 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 -0700982 if (geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700983 nullDashCase.compare(reporter, dashCase, TestCase::kAllDifferent_ComparisonExpecation);
984 } else {
985 nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation);
986 }
bsalomon47cc7692016-04-26 12:56:00 -0700987}
988
bsalomona395f7c2016-08-24 17:47:40 -0700989void test_path_effect_makes_rrect(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon72dc51c2016-04-27 06:46:23 -0700990 /**
991 * This path effect takes any input path and turns it into a rrect. It passes through stroke
992 * info.
993 */
994 class RRectPathEffect : SkPathEffect {
995 public:
996 static const SkRRect& RRect() {
997 static const SkRRect kRRect = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 3, 5);
998 return kRRect;
999 }
1000
1001 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1002 const SkRect* cullR) const override {
1003 dst->reset();
1004 dst->addRRect(RRect());
1005 return true;
1006 }
1007 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1008 *dst = RRect().getBounds();
1009 }
1010 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new RRectPathEffect); }
1011 Factory getFactory() const override { return nullptr; }
1012 void toString(SkString*) const override {}
1013 private:
1014 RRectPathEffect() {}
1015 };
1016
1017 SkPaint fill;
bsalomonfb083272016-05-04 08:27:41 -07001018 TestCase fillGeoCase(geo, fill, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001019
1020 SkPaint pe;
1021 pe.setPathEffect(RRectPathEffect::Make());
bsalomonfb083272016-05-04 08:27:41 -07001022 TestCase geoPECase(geo, pe, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001023
1024 SkPaint peStroke;
1025 peStroke.setPathEffect(RRectPathEffect::Make());
1026 peStroke.setStrokeWidth(2.f);
1027 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001028 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001029
bsalomon487f8d32016-07-20 07:15:44 -07001030 // Check whether constructing the filled case would cause the base shape to have a different
1031 // geometry (because of a geometric transformation upon initial GrShape construction).
bsalomona395f7c2016-08-24 17:47:40 -07001032 if (geo.fillChangesGeom()) {
bsalomon487f8d32016-07-20 07:15:44 -07001033 fillGeoCase.compare(reporter, geoPECase, TestCase::kAllDifferent_ComparisonExpecation);
1034 fillGeoCase.compare(reporter, geoPEStrokeCase,
1035 TestCase::kAllDifferent_ComparisonExpecation);
1036 } else {
1037 fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation);
1038 fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation);
1039 }
bsalomon72dc51c2016-04-27 06:46:23 -07001040 geoPECase.compare(reporter, geoPEStrokeCase,
1041 TestCase::kSameUpToStroke_ComparisonExpecation);
1042
bsalomona395f7c2016-08-24 17:47:40 -07001043 TestCase rrectFillCase(reporter, RRectPathEffect::RRect(), fill);
bsalomon72dc51c2016-04-27 06:46:23 -07001044 SkPaint stroke = peStroke;
1045 stroke.setPathEffect(nullptr);
bsalomona395f7c2016-08-24 17:47:40 -07001046 TestCase rrectStrokeCase(reporter, RRectPathEffect::RRect(), stroke);
bsalomon72dc51c2016-04-27 06:46:23 -07001047
1048 SkRRect rrect;
1049 // Applying the path effect should make a SkRRect shape. There is no further stroking in the
1050 // geoPECase, so the full style should be the same as just the PE.
bsalomon70493962016-06-10 08:05:14 -07001051 REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().asRRect(&rrect, nullptr, nullptr,
1052 nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001053 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
1054 REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == rrectFillCase.baseKey());
1055
bsalomon70493962016-06-10 08:05:14 -07001056 REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr,
1057 nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001058 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
1059 REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == rrectFillCase.baseKey());
1060
1061 // In the PE+stroke case applying the full style should be the same as just stroking the rrect.
bsalomon70493962016-06-10 08:05:14 -07001062 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().asRRect(&rrect, nullptr,
1063 nullptr, nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001064 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
1065 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == rrectFillCase.baseKey());
1066
bsalomon70493962016-06-10 08:05:14 -07001067 REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().asRRect(&rrect, nullptr,
1068 nullptr, nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001069 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() ==
1070 rrectStrokeCase.appliedFullStyleKey());
1071}
1072
bsalomona395f7c2016-08-24 17:47:40 -07001073void test_unknown_path_effect(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon72dc51c2016-04-27 06:46:23 -07001074 /**
1075 * This path effect just adds two lineTos to the input path.
1076 */
1077 class AddLineTosPathEffect : SkPathEffect {
1078 public:
1079 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1080 const SkRect* cullR) const override {
1081 *dst = src;
bsalomon67fa4e32016-09-21 08:26:57 -07001082 // To avoid triggering data-based keying of paths with few verbs we add many segments.
1083 for (int i = 0; i < 100; ++i) {
1084 dst->lineTo(SkIntToScalar(i), SkIntToScalar(i));
1085 }
bsalomon72dc51c2016-04-27 06:46:23 -07001086 return true;
1087 }
1088 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1089 *dst = src;
1090 dst->growToInclude(0, 0);
bsalomon67fa4e32016-09-21 08:26:57 -07001091 dst->growToInclude(100, 100);
bsalomon72dc51c2016-04-27 06:46:23 -07001092 }
1093 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new AddLineTosPathEffect); }
1094 Factory getFactory() const override { return nullptr; }
1095 void toString(SkString*) const override {}
1096 private:
1097 AddLineTosPathEffect() {}
1098 };
1099
bsalomon9ad5d7c2016-05-04 08:44:15 -07001100 // This path effect should make the keys invalid when it is applied. We only produce a path
bsalomon72dc51c2016-04-27 06:46:23 -07001101 // effect key for dash path effects. So the only way another arbitrary path effect can produce
1102 // a styled result with a key is to produce a non-path shape that has a purely geometric key.
1103 SkPaint peStroke;
1104 peStroke.setPathEffect(AddLineTosPathEffect::Make());
1105 peStroke.setStrokeWidth(2.f);
1106 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001107 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001108 TestCase::SelfExpectations expectations;
1109 expectations.fPEHasEffect = true;
1110 expectations.fPEHasValidKey = false;
1111 expectations.fStrokeApplies = true;
1112 geoPEStrokeCase.testExpectations(reporter, expectations);
1113}
1114
bsalomona395f7c2016-08-24 17:47:40 -07001115void test_make_hairline_path_effect(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon9ad5d7c2016-05-04 08:44:15 -07001116 /**
1117 * This path effect just changes the stroke rec to hairline.
1118 */
1119 class MakeHairlinePathEffect : SkPathEffect {
1120 public:
1121 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* strokeRec,
1122 const SkRect* cullR) const override {
1123 *dst = src;
1124 strokeRec->setHairlineStyle();
1125 return true;
1126 }
bsalomon70493962016-06-10 08:05:14 -07001127 void computeFastBounds(SkRect* dst, const SkRect& src) const override { *dst = src; }
bsalomon9ad5d7c2016-05-04 08:44:15 -07001128 static sk_sp<SkPathEffect> Make() {
1129 return sk_sp<SkPathEffect>(new MakeHairlinePathEffect);
1130 }
1131 Factory getFactory() const override { return nullptr; }
1132 void toString(SkString*) const override {}
1133 private:
1134 MakeHairlinePathEffect() {}
1135 };
1136
1137 SkPaint fill;
1138 SkPaint pe;
1139 pe.setPathEffect(MakeHairlinePathEffect::Make());
1140
1141 TestCase peCase(geo, pe, reporter);
1142
bsalomonee295642016-06-06 14:01:25 -07001143 SkPath a, b, c;
bsalomon9ad5d7c2016-05-04 08:44:15 -07001144 peCase.baseShape().asPath(&a);
1145 peCase.appliedPathEffectShape().asPath(&b);
bsalomonee295642016-06-06 14:01:25 -07001146 peCase.appliedFullStyleShape().asPath(&c);
bsalomona395f7c2016-08-24 17:47:40 -07001147 if (geo.isNonPath(pe)) {
bsalomonee295642016-06-06 14:01:25 -07001148 // RRect types can have a change in start index or direction after the PE is applied. This
1149 // is because once the PE is applied, GrShape may canonicalize the dir and index since it
1150 // is not germane to the styling any longer.
1151 // Instead we just check that the paths would fill the same both before and after styling.
1152 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1153 REPORTER_ASSERT(reporter, paths_fill_same(a, c));
bsalomon9ad5d7c2016-05-04 08:44:15 -07001154 } else {
bsalomona4817af2016-06-23 11:48:26 -07001155 // The base shape cannot perform canonicalization on the path's fill type because of an
1156 // unknown path effect. However, after the path effect is applied the resulting hairline
1157 // shape will canonicalize the path fill type since hairlines (and stroking in general)
1158 // don't distinguish between even/odd and non-zero winding.
1159 a.setFillType(b.getFillType());
bsalomonee295642016-06-06 14:01:25 -07001160 REPORTER_ASSERT(reporter, a == b);
1161 REPORTER_ASSERT(reporter, a == c);
bsalomon67fa4e32016-09-21 08:26:57 -07001162 // If the resulting path is small enough then it will have a key.
1163 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1164 REPORTER_ASSERT(reporter, paths_fill_same(a, c));
bsalomonaa840642016-09-23 12:09:16 -07001165 REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey().empty());
1166 REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey().empty());
bsalomon9ad5d7c2016-05-04 08:44:15 -07001167 }
bsalomonee295642016-06-06 14:01:25 -07001168 REPORTER_ASSERT(reporter, peCase.appliedPathEffectShape().style().isSimpleHairline());
1169 REPORTER_ASSERT(reporter, peCase.appliedFullStyleShape().style().isSimpleHairline());
bsalomon9ad5d7c2016-05-04 08:44:15 -07001170}
1171
bsalomona395f7c2016-08-24 17:47:40 -07001172void test_volatile_path(skiatest::Reporter* reporter, const Geo& geo) {
1173 SkPath vPath = geo.path();
bsalomon4eeccc92016-04-27 13:30:25 -07001174 vPath.setIsVolatile(true);
1175
1176 SkPaint dashAndStroke;
1177 dashAndStroke.setPathEffect(make_dash());
1178 dashAndStroke.setStrokeWidth(2.f);
1179 dashAndStroke.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001180 TestCase volatileCase(reporter, vPath, dashAndStroke);
bsalomon4eeccc92016-04-27 13:30:25 -07001181 // We expect a shape made from a volatile path to have a key iff the shape is recognized
bsalomonaa840642016-09-23 12:09:16 -07001182 // as a specialized geometry.
1183 if (geo.isNonPath(dashAndStroke)) {
bsalomon4eeccc92016-04-27 13:30:25 -07001184 REPORTER_ASSERT(reporter, SkToBool(volatileCase.baseKey().count()));
1185 // In this case all the keys should be identical to the non-volatile case.
bsalomona395f7c2016-08-24 17:47:40 -07001186 TestCase nonVolatileCase(reporter, geo.path(), dashAndStroke);
bsalomon4eeccc92016-04-27 13:30:25 -07001187 volatileCase.compare(reporter, nonVolatileCase, TestCase::kAllSame_ComparisonExpecation);
1188 } else {
1189 // None of the keys should be valid.
1190 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.baseKey().count()));
1191 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectKey().count()));
1192 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedFullStyleKey().count()));
1193 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectThenStrokeKey().count()));
1194 }
1195}
1196
bsalomona395f7c2016-08-24 17:47:40 -07001197void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon409ed732016-04-27 12:36:02 -07001198 /**
1199 * This path effect returns an empty path.
1200 */
1201 class EmptyPathEffect : SkPathEffect {
1202 public:
1203 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1204 const SkRect* cullR) const override {
1205 dst->reset();
1206 return true;
1207 }
1208 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1209 dst->setEmpty();
1210 }
1211 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new EmptyPathEffect); }
1212 Factory getFactory() const override { return nullptr; }
1213 void toString(SkString*) const override {}
1214 private:
1215 EmptyPathEffect() {}
1216 };
1217
1218 SkPath emptyPath;
1219 GrShape emptyShape(emptyPath);
1220 Key emptyKey;
1221 make_key(&emptyKey, emptyShape);
bsalomon7c73a532016-05-11 15:15:56 -07001222 REPORTER_ASSERT(reporter, emptyShape.isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001223
1224 SkPaint pe;
1225 pe.setPathEffect(EmptyPathEffect::Make());
bsalomonfb083272016-05-04 08:27:41 -07001226 TestCase geoCase(geo, pe, reporter);
bsalomon409ed732016-04-27 12:36:02 -07001227 REPORTER_ASSERT(reporter, geoCase.appliedFullStyleKey() == emptyKey);
1228 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectKey() == emptyKey);
1229 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectThenStrokeKey() == emptyKey);
bsalomon7c73a532016-05-11 15:15:56 -07001230 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectShape().isEmpty());
1231 REPORTER_ASSERT(reporter, geoCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001232
1233 SkPaint peStroke;
1234 peStroke.setPathEffect(EmptyPathEffect::Make());
1235 peStroke.setStrokeWidth(2.f);
1236 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001237 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon409ed732016-04-27 12:36:02 -07001238 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == emptyKey);
1239 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == emptyKey);
1240 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey);
bsalomon7c73a532016-05-11 15:15:56 -07001241 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty());
1242 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001243}
1244
bsalomona395f7c2016-08-24 17:47:40 -07001245void test_path_effect_fails(skiatest::Reporter* reporter, const Geo& geo) {
bsalomond6723842016-06-07 12:20:15 -07001246 /**
bsalomon0ae36a22016-07-18 07:31:13 -07001247 * This path effect always fails to apply.
bsalomond6723842016-06-07 12:20:15 -07001248 */
1249 class FailurePathEffect : SkPathEffect {
1250 public:
1251 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1252 const SkRect* cullR) const override {
1253 return false;
1254 }
1255 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1256 *dst = src;
1257 }
1258 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new FailurePathEffect); }
1259 Factory getFactory() const override { return nullptr; }
1260 void toString(SkString*) const override {}
1261 private:
1262 FailurePathEffect() {}
1263 };
1264
1265 SkPaint fill;
1266 TestCase fillCase(geo, fill, reporter);
1267
1268 SkPaint pe;
1269 pe.setPathEffect(FailurePathEffect::Make());
1270 TestCase peCase(geo, pe, reporter);
1271
1272 SkPaint stroke;
1273 stroke.setStrokeWidth(2.f);
1274 stroke.setStyle(SkPaint::kStroke_Style);
1275 TestCase strokeCase(geo, stroke, reporter);
1276
1277 SkPaint peStroke = stroke;
1278 peStroke.setPathEffect(FailurePathEffect::Make());
1279 TestCase peStrokeCase(geo, peStroke, reporter);
1280
1281 // In general the path effect failure can cause some of the TestCase::compare() tests to fail
1282 // for at least two reasons: 1) We will initially treat the shape as unkeyable because of the
1283 // path effect, but then when the path effect fails we can key it. 2) GrShape will change its
1284 // mind about whether a unclosed rect is actually rect. The path effect initially bars us from
1285 // closing it but after the effect fails we can (for the fill+pe case). This causes different
1286 // routes through GrShape to have equivalent but different representations of the path (closed
1287 // or not) but that fill the same.
1288 SkPath a;
1289 SkPath b;
1290 fillCase.appliedPathEffectShape().asPath(&a);
1291 peCase.appliedPathEffectShape().asPath(&b);
1292 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1293
1294 fillCase.appliedFullStyleShape().asPath(&a);
1295 peCase.appliedFullStyleShape().asPath(&b);
1296 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1297
1298 strokeCase.appliedPathEffectShape().asPath(&a);
1299 peStrokeCase.appliedPathEffectShape().asPath(&b);
1300 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1301
1302 strokeCase.appliedFullStyleShape().asPath(&a);
1303 peStrokeCase.appliedFullStyleShape().asPath(&b);
1304 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1305}
1306
bsalomon409ed732016-04-27 12:36:02 -07001307void test_empty_shape(skiatest::Reporter* reporter) {
1308 SkPath emptyPath;
1309 SkPaint fill;
bsalomona395f7c2016-08-24 17:47:40 -07001310 TestCase fillEmptyCase(reporter, emptyPath, fill);
bsalomon7c73a532016-05-11 15:15:56 -07001311 REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty());
1312 REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty());
1313 REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001314
1315 Key emptyKey(fillEmptyCase.baseKey());
1316 REPORTER_ASSERT(reporter, emptyKey.count());
1317 TestCase::SelfExpectations expectations;
1318 expectations.fStrokeApplies = false;
1319 expectations.fPEHasEffect = false;
1320 // This will test whether applying style preserves emptiness
1321 fillEmptyCase.testExpectations(reporter, expectations);
1322
1323 // Stroking an empty path should have no effect
1324 SkPath emptyPath2;
1325 SkPaint stroke;
1326 stroke.setStrokeWidth(2.f);
1327 stroke.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001328 TestCase strokeEmptyCase(reporter, emptyPath2, stroke);
bsalomon409ed732016-04-27 12:36:02 -07001329 strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
1330
1331 // Dashing and stroking an empty path should have no effect
1332 SkPath emptyPath3;
1333 SkPaint dashAndStroke;
1334 dashAndStroke.setPathEffect(make_dash());
1335 dashAndStroke.setStrokeWidth(2.f);
1336 dashAndStroke.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001337 TestCase dashAndStrokeEmptyCase(reporter, emptyPath3, dashAndStroke);
bsalomon409ed732016-04-27 12:36:02 -07001338 dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase,
1339 TestCase::kAllSame_ComparisonExpecation);
bsalomon5e410b42016-04-28 09:30:46 -07001340
1341 // A shape made from an empty rrect should behave the same as an empty path.
1342 SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty());
1343 REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type);
bsalomona395f7c2016-08-24 17:47:40 -07001344 TestCase dashAndStrokeEmptyRRectCase(reporter, emptyRRect, dashAndStroke);
bsalomon5e410b42016-04-28 09:30:46 -07001345 dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
1346 TestCase::kAllSame_ComparisonExpecation);
1347
1348 // Same for a rect.
1349 SkRect emptyRect = SkRect::MakeEmpty();
bsalomona395f7c2016-08-24 17:47:40 -07001350 TestCase dashAndStrokeEmptyRectCase(reporter, emptyRect, dashAndStroke);
bsalomon5e410b42016-04-28 09:30:46 -07001351 dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase,
1352 TestCase::kAllSame_ComparisonExpecation);
bsalomon409ed732016-04-27 12:36:02 -07001353}
1354
bsalomon70493962016-06-10 08:05:14 -07001355// rect and oval types have rrect start indices that collapse to the same point. Here we select the
1356// canonical point in these cases.
1357unsigned canonicalize_rrect_start(int s, const SkRRect& rrect) {
1358 switch (rrect.getType()) {
1359 case SkRRect::kRect_Type:
1360 return (s + 1) & 0b110;
1361 case SkRRect::kOval_Type:
1362 return s & 0b110;
1363 default:
1364 return s;
1365 }
1366}
1367
1368void test_rrect(skiatest::Reporter* r, const SkRRect& rrect) {
bsalomoncadb5a22016-06-10 18:28:06 -07001369 enum Style {
bsalomon70493962016-06-10 08:05:14 -07001370 kFill,
1371 kStroke,
1372 kHairline,
1373 kStrokeAndFill
1374 };
1375
1376 // SkStrokeRec has no default cons., so init with kFill before calling the setters below.
1377 SkStrokeRec strokeRecs[4] { SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle,
1378 SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle};
1379 strokeRecs[kFill].setFillStyle();
1380 strokeRecs[kStroke].setStrokeStyle(2.f);
1381 strokeRecs[kHairline].setHairlineStyle();
1382 strokeRecs[kStrokeAndFill].setStrokeStyle(3.f, true);
bsalomon487f8d32016-07-20 07:15:44 -07001383 // Use a bevel join to avoid complications of stroke+filled rects becoming filled rects before
1384 // applyStyle() is called.
1385 strokeRecs[kStrokeAndFill].setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 1.f);
bsalomon70493962016-06-10 08:05:14 -07001386 sk_sp<SkPathEffect> dashEffect = make_dash();
1387
bsalomoncadb5a22016-06-10 18:28:06 -07001388 static constexpr Style kStyleCnt = static_cast<Style>(SK_ARRAY_COUNT(strokeRecs));
1389
1390 auto index = [](bool inverted,
1391 SkPath::Direction dir,
1392 unsigned start,
1393 Style style,
1394 bool dash) -> int {
1395 return inverted * (2 * 8 * kStyleCnt * 2) +
1396 dir * ( 8 * kStyleCnt * 2) +
1397 start * ( kStyleCnt * 2) +
1398 style * ( 2) +
1399 dash;
1400 };
1401 static const SkPath::Direction kSecondDirection = static_cast<SkPath::Direction>(1);
1402 const int cnt = index(true, kSecondDirection, 7, static_cast<Style>(kStyleCnt - 1), true) + 1;
1403 SkAutoTArray<GrShape> shapes(cnt);
1404 for (bool inverted : {false, true}) {
1405 for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
1406 for (unsigned start = 0; start < 8; ++start) {
1407 for (Style style : {kFill, kStroke, kHairline, kStrokeAndFill}) {
1408 for (bool dash : {false, true}) {
Robert Phillipsf809c1e2017-01-13 11:02:42 -05001409 sk_sp<SkPathEffect> pe = dash ? dashEffect : nullptr;
bsalomoncadb5a22016-06-10 18:28:06 -07001410 shapes[index(inverted, dir, start, style, dash)] =
1411 GrShape(rrect, dir, start, SkToBool(inverted),
Robert Phillipsf809c1e2017-01-13 11:02:42 -05001412 GrStyle(strokeRecs[style], std::move(pe)));
bsalomon70493962016-06-10 08:05:14 -07001413 }
1414 }
1415 }
1416 }
1417 }
1418
bsalomonfd32df72016-06-14 14:37:21 -07001419 // Get the keys for some example shape instances that we'll use for comparision against the
1420 // rest.
1421 static constexpr SkPath::Direction kExamplesDir = SkPath::kCW_Direction;
1422 static constexpr unsigned kExamplesStart = 0;
1423 const GrShape& exampleFillCase = shapes[index(false, kExamplesDir, kExamplesStart, kFill,
1424 false)];
bsalomon70493962016-06-10 08:05:14 -07001425 Key exampleFillCaseKey;
1426 make_key(&exampleFillCaseKey, exampleFillCase);
1427
bsalomonfd32df72016-06-14 14:37:21 -07001428 const GrShape& exampleStrokeAndFillCase = shapes[index(false, kExamplesDir, kExamplesStart,
1429 kStrokeAndFill, false)];
bsalomon70493962016-06-10 08:05:14 -07001430 Key exampleStrokeAndFillCaseKey;
1431 make_key(&exampleStrokeAndFillCaseKey, exampleStrokeAndFillCase);
1432
bsalomonfd32df72016-06-14 14:37:21 -07001433 const GrShape& exampleInvFillCase = shapes[index(true, kExamplesDir, kExamplesStart, kFill,
1434 false)];
bsalomon70493962016-06-10 08:05:14 -07001435 Key exampleInvFillCaseKey;
1436 make_key(&exampleInvFillCaseKey, exampleInvFillCase);
1437
bsalomonfd32df72016-06-14 14:37:21 -07001438 const GrShape& exampleInvStrokeAndFillCase = shapes[index(true, kExamplesDir, kExamplesStart,
1439 kStrokeAndFill, false)];
bsalomon70493962016-06-10 08:05:14 -07001440 Key exampleInvStrokeAndFillCaseKey;
1441 make_key(&exampleInvStrokeAndFillCaseKey, exampleInvStrokeAndFillCase);
1442
bsalomonfd32df72016-06-14 14:37:21 -07001443 const GrShape& exampleStrokeCase = shapes[index(false, kExamplesDir, kExamplesStart, kStroke,
1444 false)];
bsalomon70493962016-06-10 08:05:14 -07001445 Key exampleStrokeCaseKey;
1446 make_key(&exampleStrokeCaseKey, exampleStrokeCase);
1447
bsalomonfd32df72016-06-14 14:37:21 -07001448 const GrShape& exampleInvStrokeCase = shapes[index(true, kExamplesDir, kExamplesStart, kStroke,
1449 false)];
1450 Key exampleInvStrokeCaseKey;
1451 make_key(&exampleInvStrokeCaseKey, exampleInvStrokeCase);
1452
1453 const GrShape& exampleHairlineCase = shapes[index(false, kExamplesDir, kExamplesStart,
1454 kHairline, false)];
bsalomon70493962016-06-10 08:05:14 -07001455 Key exampleHairlineCaseKey;
1456 make_key(&exampleHairlineCaseKey, exampleHairlineCase);
1457
bsalomonfd32df72016-06-14 14:37:21 -07001458 const GrShape& exampleInvHairlineCase = shapes[index(true, kExamplesDir, kExamplesStart,
1459 kHairline, false)];
1460 Key exampleInvHairlineCaseKey;
1461 make_key(&exampleInvHairlineCaseKey, exampleInvHairlineCase);
1462
bsalomon70493962016-06-10 08:05:14 -07001463 // These are dummy initializations to suppress warnings.
bsalomoncadb5a22016-06-10 18:28:06 -07001464 SkRRect queryRR = SkRRect::MakeEmpty();
1465 SkPath::Direction queryDir = SkPath::kCW_Direction;
1466 unsigned queryStart = ~0U;
1467 bool queryInverted = true;
bsalomon70493962016-06-10 08:05:14 -07001468
bsalomoncadb5a22016-06-10 18:28:06 -07001469 REPORTER_ASSERT(r, exampleFillCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
1470 REPORTER_ASSERT(r, queryRR == rrect);
1471 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1472 REPORTER_ASSERT(r, 0 == queryStart);
1473 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001474
bsalomoncadb5a22016-06-10 18:28:06 -07001475 REPORTER_ASSERT(r, exampleInvFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1476 &queryInverted));
1477 REPORTER_ASSERT(r, queryRR == rrect);
1478 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1479 REPORTER_ASSERT(r, 0 == queryStart);
1480 REPORTER_ASSERT(r, queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001481
bsalomoncadb5a22016-06-10 18:28:06 -07001482 REPORTER_ASSERT(r, exampleStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1483 &queryInverted));
1484 REPORTER_ASSERT(r, queryRR == rrect);
1485 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1486 REPORTER_ASSERT(r, 0 == queryStart);
1487 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001488
bsalomoncadb5a22016-06-10 18:28:06 -07001489 REPORTER_ASSERT(r, exampleInvStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1490 &queryInverted));
1491 REPORTER_ASSERT(r, queryRR == rrect);
1492 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1493 REPORTER_ASSERT(r, 0 == queryStart);
1494 REPORTER_ASSERT(r, queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001495
bsalomoncadb5a22016-06-10 18:28:06 -07001496 REPORTER_ASSERT(r, exampleHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1497 &queryInverted));
1498 REPORTER_ASSERT(r, queryRR == rrect);
1499 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1500 REPORTER_ASSERT(r, 0 == queryStart);
1501 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001502
bsalomonfd32df72016-06-14 14:37:21 -07001503 REPORTER_ASSERT(r, exampleInvHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1504 &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);
1509
bsalomoncadb5a22016-06-10 18:28:06 -07001510 REPORTER_ASSERT(r, exampleStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
1511 REPORTER_ASSERT(r, queryRR == rrect);
1512 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1513 REPORTER_ASSERT(r, 0 == queryStart);
1514 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001515
bsalomonfd32df72016-06-14 14:37:21 -07001516 REPORTER_ASSERT(r, exampleInvStrokeCase.asRRect(&queryRR, &queryDir, &queryStart,
1517 &queryInverted));
1518 REPORTER_ASSERT(r, queryRR == rrect);
1519 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1520 REPORTER_ASSERT(r, 0 == queryStart);
1521 REPORTER_ASSERT(r, queryInverted);
1522
bsalomon70493962016-06-10 08:05:14 -07001523 // Remember that the key reflects the geometry before styling is applied.
1524 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvFillCaseKey);
1525 REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeAndFillCaseKey);
1526 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeAndFillCaseKey);
1527 REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001528 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001529 REPORTER_ASSERT(r, exampleFillCaseKey == exampleHairlineCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001530 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvHairlineCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001531 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvFillCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001532 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvStrokeCaseKey);
1533 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvHairlineCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001534
bsalomoncadb5a22016-06-10 18:28:06 -07001535 for (bool inverted : {false, true}) {
1536 for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
1537 for (unsigned start = 0; start < 8; ++start) {
1538 for (bool dash : {false, true}) {
1539 const GrShape& fillCase = shapes[index(inverted, dir, start, kFill, dash)];
bsalomon70493962016-06-10 08:05:14 -07001540 Key fillCaseKey;
1541 make_key(&fillCaseKey, fillCase);
1542
bsalomoncadb5a22016-06-10 18:28:06 -07001543 const GrShape& strokeAndFillCase = shapes[index(inverted, dir, start,
1544 kStrokeAndFill, dash)];
bsalomon70493962016-06-10 08:05:14 -07001545 Key strokeAndFillCaseKey;
1546 make_key(&strokeAndFillCaseKey, strokeAndFillCase);
1547
1548 // Both fill and stroke-and-fill shapes must respect the inverseness and both
1549 // ignore dashing.
1550 REPORTER_ASSERT(r, !fillCase.style().pathEffect());
1551 REPORTER_ASSERT(r, !strokeAndFillCase.style().pathEffect());
1552 TestCase a(fillCase, r);
1553 TestCase b(inverted ? exampleInvFillCase : exampleFillCase, r);
1554 TestCase c(strokeAndFillCase, r);
1555 TestCase d(inverted ? exampleInvStrokeAndFillCase
1556 : exampleStrokeAndFillCase, r);
1557 a.compare(r, b, TestCase::kAllSame_ComparisonExpecation);
1558 c.compare(r, d, TestCase::kAllSame_ComparisonExpecation);
1559
bsalomoncadb5a22016-06-10 18:28:06 -07001560 const GrShape& strokeCase = shapes[index(inverted, dir, start, kStroke, dash)];
1561 const GrShape& hairlineCase = shapes[index(inverted, dir, start, kHairline,
1562 dash)];
bsalomon70493962016-06-10 08:05:14 -07001563
1564 TestCase e(strokeCase, r);
bsalomon70493962016-06-10 08:05:14 -07001565 TestCase g(hairlineCase, r);
bsalomon70493962016-06-10 08:05:14 -07001566
bsalomonfd32df72016-06-14 14:37:21 -07001567 // Both hairline and stroke shapes must respect the dashing.
bsalomon70493962016-06-10 08:05:14 -07001568 if (dash) {
bsalomonfd32df72016-06-14 14:37:21 -07001569 // Dashing always ignores the inverseness. skbug.com/5421
1570 TestCase f(exampleStrokeCase, r);
1571 TestCase h(exampleHairlineCase, r);
bsalomoncadb5a22016-06-10 18:28:06 -07001572 unsigned expectedStart = canonicalize_rrect_start(start, rrect);
bsalomon70493962016-06-10 08:05:14 -07001573 REPORTER_ASSERT(r, strokeCase.style().pathEffect());
1574 REPORTER_ASSERT(r, hairlineCase.style().pathEffect());
1575
bsalomoncadb5a22016-06-10 18:28:06 -07001576 REPORTER_ASSERT(r, strokeCase.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);
1582 REPORTER_ASSERT(r, hairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1583 &queryInverted));
1584 REPORTER_ASSERT(r, queryRR == rrect);
1585 REPORTER_ASSERT(r, queryDir == dir);
1586 REPORTER_ASSERT(r, queryStart == expectedStart);
1587 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001588
1589 // The pre-style case for the dash will match the non-dash example iff the
1590 // dir and start match (dir=cw, start=0).
bsalomoncadb5a22016-06-10 18:28:06 -07001591 if (0 == expectedStart && SkPath::kCW_Direction == dir) {
bsalomon70493962016-06-10 08:05:14 -07001592 e.compare(r, f, TestCase::kSameUpToPE_ComparisonExpecation);
1593 g.compare(r, h, TestCase::kSameUpToPE_ComparisonExpecation);
1594 } else {
1595 e.compare(r, f, TestCase::kAllDifferent_ComparisonExpecation);
1596 g.compare(r, h, TestCase::kAllDifferent_ComparisonExpecation);
1597 }
1598 } else {
bsalomonfd32df72016-06-14 14:37:21 -07001599 TestCase f(inverted ? exampleInvStrokeCase : exampleStrokeCase, r);
1600 TestCase h(inverted ? exampleInvHairlineCase : exampleHairlineCase, r);
bsalomon70493962016-06-10 08:05:14 -07001601 REPORTER_ASSERT(r, !strokeCase.style().pathEffect());
1602 REPORTER_ASSERT(r, !hairlineCase.style().pathEffect());
1603 e.compare(r, f, TestCase::kAllSame_ComparisonExpecation);
1604 g.compare(r, h, TestCase::kAllSame_ComparisonExpecation);
1605 }
1606 }
1607 }
1608 }
1609 }
1610}
1611
bsalomon0a0f67e2016-06-28 11:56:42 -07001612void test_lines(skiatest::Reporter* r) {
1613 static constexpr SkPoint kA { 1, 1};
1614 static constexpr SkPoint kB { 5, -9};
1615 static constexpr SkPoint kC {-3, 17};
1616
1617 SkPath lineAB;
1618 lineAB.moveTo(kA);
1619 lineAB.lineTo(kB);
1620
1621 SkPath lineBA;
1622 lineBA.moveTo(kB);
1623 lineBA.lineTo(kA);
1624
1625 SkPath lineAC;
1626 lineAC.moveTo(kB);
1627 lineAC.lineTo(kC);
1628
1629 SkPath invLineAB = lineAB;
1630 invLineAB.setFillType(SkPath::kInverseEvenOdd_FillType);
1631
1632 SkPaint fill;
1633 SkPaint stroke;
1634 stroke.setStyle(SkPaint::kStroke_Style);
1635 stroke.setStrokeWidth(2.f);
1636 SkPaint hairline;
1637 hairline.setStyle(SkPaint::kStroke_Style);
1638 hairline.setStrokeWidth(0.f);
1639 SkPaint dash = stroke;
1640 dash.setPathEffect(make_dash());
1641
bsalomona395f7c2016-08-24 17:47:40 -07001642 TestCase fillAB(r, lineAB, fill);
1643 TestCase fillEmpty(r, SkPath(), fill);
bsalomon0a0f67e2016-06-28 11:56:42 -07001644 fillAB.compare(r, fillEmpty, TestCase::kAllSame_ComparisonExpecation);
1645 REPORTER_ASSERT(r, !fillAB.baseShape().asLine(nullptr, nullptr));
1646
bsalomona395f7c2016-08-24 17:47:40 -07001647 TestCase strokeAB(r, lineAB, stroke);
1648 TestCase strokeBA(r, lineBA, stroke);
1649 TestCase strokeAC(r, lineAC, stroke);
bsalomon0a0f67e2016-06-28 11:56:42 -07001650
bsalomona395f7c2016-08-24 17:47:40 -07001651 TestCase hairlineAB(r, lineAB, hairline);
1652 TestCase hairlineBA(r, lineBA, hairline);
1653 TestCase hairlineAC(r, lineAC, hairline);
bsalomon0a0f67e2016-06-28 11:56:42 -07001654
bsalomona395f7c2016-08-24 17:47:40 -07001655 TestCase dashAB(r, lineAB, dash);
1656 TestCase dashBA(r, lineBA, dash);
1657 TestCase dashAC(r, lineAC, dash);
bsalomon0a0f67e2016-06-28 11:56:42 -07001658
1659 strokeAB.compare(r, fillAB, TestCase::kAllDifferent_ComparisonExpecation);
1660
1661 strokeAB.compare(r, strokeBA, TestCase::kAllSame_ComparisonExpecation);
1662 strokeAB.compare(r, strokeAC, TestCase::kAllDifferent_ComparisonExpecation);
1663
1664 hairlineAB.compare(r, hairlineBA, TestCase::kAllSame_ComparisonExpecation);
1665 hairlineAB.compare(r, hairlineAC, TestCase::kAllDifferent_ComparisonExpecation);
1666
1667 dashAB.compare(r, dashBA, TestCase::kAllDifferent_ComparisonExpecation);
1668 dashAB.compare(r, dashAC, TestCase::kAllDifferent_ComparisonExpecation);
1669
1670 strokeAB.compare(r, hairlineAB, TestCase::kSameUpToStroke_ComparisonExpecation);
1671
1672 // One of dashAB or dashBA should have the same line as strokeAB. It depends upon how
1673 // GrShape canonicalizes line endpoints (when it can, i.e. when not dashed).
1674 bool canonicalizeAsAB;
1675 SkPoint canonicalPts[2] {kA, kB};
1676 // Init these to suppress warnings.
1677 bool inverted = true;
1678 SkPoint pts[2] {{0, 0}, {0, 0}};
1679 REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted);
1680 if (pts[0] == kA && pts[1] == kB) {
1681 canonicalizeAsAB = true;
1682 } else if (pts[1] == kA && pts[0] == kB) {
1683 canonicalizeAsAB = false;
1684 SkTSwap(canonicalPts[0], canonicalPts[1]);
1685 } else {
1686 ERRORF(r, "Should return pts (a,b) or (b, a)");
1687 return;
1688 };
1689
1690 strokeAB.compare(r, canonicalizeAsAB ? dashAB : dashBA,
1691 TestCase::kSameUpToPE_ComparisonExpecation);
1692 REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted &&
1693 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1694 REPORTER_ASSERT(r, hairlineAB.baseShape().asLine(pts, &inverted) && !inverted &&
1695 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1696 REPORTER_ASSERT(r, dashAB.baseShape().asLine(pts, &inverted) && !inverted &&
1697 pts[0] == kA && pts[1] == kB);
1698 REPORTER_ASSERT(r, dashBA.baseShape().asLine(pts, &inverted) && !inverted &&
1699 pts[0] == kB && pts[1] == kA);
1700
1701
bsalomona395f7c2016-08-24 17:47:40 -07001702 TestCase strokeInvAB(r, invLineAB, stroke);
1703 TestCase hairlineInvAB(r, invLineAB, hairline);
1704 TestCase dashInvAB(r, invLineAB, dash);
bsalomon0a0f67e2016-06-28 11:56:42 -07001705 strokeInvAB.compare(r, strokeAB, TestCase::kAllDifferent_ComparisonExpecation);
1706 hairlineInvAB.compare(r, hairlineAB, TestCase::kAllDifferent_ComparisonExpecation);
1707 // Dashing ignores inverse.
1708 dashInvAB.compare(r, dashAB, TestCase::kAllSame_ComparisonExpecation);
1709
1710 REPORTER_ASSERT(r, strokeInvAB.baseShape().asLine(pts, &inverted) && inverted &&
1711 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1712 REPORTER_ASSERT(r, hairlineInvAB.baseShape().asLine(pts, &inverted) && inverted &&
1713 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1714 // Dashing ignores inverse.
1715 REPORTER_ASSERT(r, dashInvAB.baseShape().asLine(pts, &inverted) && !inverted &&
1716 pts[0] == kA && pts[1] == kB);
1717
1718}
1719
bsalomon0ae36a22016-07-18 07:31:13 -07001720static void test_stroked_lines(skiatest::Reporter* r) {
1721 // Paints to try
1722 SkPaint buttCap;
1723 buttCap.setStyle(SkPaint::kStroke_Style);
1724 buttCap.setStrokeWidth(4);
1725 buttCap.setStrokeCap(SkPaint::kButt_Cap);
1726
1727 SkPaint squareCap = buttCap;
1728 squareCap.setStrokeCap(SkPaint::kSquare_Cap);
1729
1730 SkPaint roundCap = buttCap;
1731 roundCap.setStrokeCap(SkPaint::kRound_Cap);
1732
1733 // vertical
1734 SkPath linePath;
1735 linePath.moveTo(4, 4);
1736 linePath.lineTo(4, 5);
1737
1738 SkPaint fill;
1739
bsalomona395f7c2016-08-24 17:47:40 -07001740 TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 4, 6, 5), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001741 TestCase::kAllSame_ComparisonExpecation);
1742
bsalomona395f7c2016-08-24 17:47:40 -07001743 TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 7), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001744 TestCase::kAllSame_ComparisonExpecation);
1745
bsalomona395f7c2016-08-24 17:47:40 -07001746 TestCase(r, linePath, roundCap).compare(r,
1747 TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 7), 2, 2), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001748 TestCase::kAllSame_ComparisonExpecation);
1749
1750 // horizontal
1751 linePath.reset();
1752 linePath.moveTo(4, 4);
1753 linePath.lineTo(5, 4);
1754
bsalomona395f7c2016-08-24 17:47:40 -07001755 TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(4, 2, 5, 6), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001756 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001757 TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 7, 6), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001758 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001759 TestCase(r, linePath, roundCap).compare(r,
1760 TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 7, 6), 2, 2), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001761 TestCase::kAllSame_ComparisonExpecation);
1762
1763 // point
1764 linePath.reset();
1765 linePath.moveTo(4, 4);
1766 linePath.lineTo(4, 4);
1767
bsalomona395f7c2016-08-24 17:47:40 -07001768 TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeEmpty(), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001769 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001770 TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 6), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001771 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001772 TestCase(r, linePath, roundCap).compare(r,
1773 TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 6), 2, 2), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001774 TestCase::kAllSame_ComparisonExpecation);
1775}
1776
bsalomon67fa4e32016-09-21 08:26:57 -07001777static void test_short_path_keys(skiatest::Reporter* r) {
1778 SkPaint paints[4];
1779 paints[1].setStyle(SkPaint::kStroke_Style);
1780 paints[1].setStrokeWidth(5.f);
1781 paints[2].setStyle(SkPaint::kStroke_Style);
1782 paints[2].setStrokeWidth(0.f);
1783 paints[3].setStyle(SkPaint::kStrokeAndFill_Style);
1784 paints[3].setStrokeWidth(5.f);
1785
bsalomonaa840642016-09-23 12:09:16 -07001786 auto compare = [r, &paints] (const SkPath& pathA, const SkPath& pathB,
bsalomon67fa4e32016-09-21 08:26:57 -07001787 TestCase::ComparisonExpecation expectation) {
bsalomonaa840642016-09-23 12:09:16 -07001788 SkPath volatileA = pathA;
1789 SkPath volatileB = pathB;
1790 volatileA.setIsVolatile(true);
1791 volatileB.setIsVolatile(true);
bsalomon67fa4e32016-09-21 08:26:57 -07001792 for (const SkPaint& paint : paints) {
bsalomonaa840642016-09-23 12:09:16 -07001793 REPORTER_ASSERT(r, !GrShape(volatileA, paint).hasUnstyledKey());
1794 REPORTER_ASSERT(r, !GrShape(volatileB, paint).hasUnstyledKey());
bsalomon67fa4e32016-09-21 08:26:57 -07001795 for (PathGeo::Invert invert : {PathGeo::Invert::kNo, PathGeo::Invert::kYes}) {
bsalomonaa840642016-09-23 12:09:16 -07001796 TestCase caseA(PathGeo(pathA, invert), paint, r);
1797 TestCase caseB(PathGeo(pathB, invert), paint, r);
1798 caseA.compare(r, caseB, expectation);
bsalomon67fa4e32016-09-21 08:26:57 -07001799 }
1800 }
1801 };
1802
1803 SkPath pathA;
1804 SkPath pathB;
1805
1806 // Two identical paths
1807 pathA.lineTo(10.f, 10.f);
1808 pathA.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
1809
1810 pathB.lineTo(10.f, 10.f);
1811 pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
bsalomonaa840642016-09-23 12:09:16 -07001812 compare(pathA, pathB, TestCase::kAllSame_ComparisonExpecation);
bsalomon67fa4e32016-09-21 08:26:57 -07001813
1814 // Give path b a different point
1815 pathB.reset();
1816 pathB.lineTo(10.f, 10.f);
1817 pathB.conicTo(21.f, 20.f, 20.f, 30.f, 0.7f);
bsalomonaa840642016-09-23 12:09:16 -07001818 compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
bsalomon67fa4e32016-09-21 08:26:57 -07001819
1820 // Give path b a different conic weight
1821 pathB.reset();
1822 pathB.lineTo(10.f, 10.f);
1823 pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f);
bsalomonaa840642016-09-23 12:09:16 -07001824 compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
bsalomon67fa4e32016-09-21 08:26:57 -07001825
1826 // Give path b an extra lineTo verb
1827 pathB.reset();
1828 pathB.lineTo(10.f, 10.f);
1829 pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f);
1830 pathB.lineTo(50.f, 50.f);
bsalomonaa840642016-09-23 12:09:16 -07001831 compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
bsalomon67fa4e32016-09-21 08:26:57 -07001832
1833 // Give path b a close
1834 pathB.reset();
1835 pathB.lineTo(10.f, 10.f);
1836 pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
1837 pathB.close();
bsalomonaa840642016-09-23 12:09:16 -07001838 compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
bsalomon67fa4e32016-09-21 08:26:57 -07001839}
1840
bsalomon47cc7692016-04-26 12:56:00 -07001841DEF_TEST(GrShape, reporter) {
bsalomona395f7c2016-08-24 17:47:40 -07001842 SkTArray<std::unique_ptr<Geo>> geos;
1843 SkTArray<std::unique_ptr<RRectPathGeo>> rrectPathGeos;
1844
bsalomonee295642016-06-06 14:01:25 -07001845 for (auto r : { SkRect::MakeWH(10, 20),
1846 SkRect::MakeWH(-10, -20),
1847 SkRect::MakeWH(-10, 20),
1848 SkRect::MakeWH(10, -20)}) {
bsalomona395f7c2016-08-24 17:47:40 -07001849 geos.emplace_back(new RectGeo(r));
1850 SkPath rectPath;
1851 rectPath.addRect(r);
1852 geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
1853 PathGeo::Invert::kNo));
1854 geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
1855 PathGeo::Invert::kYes));
1856 rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
1857 PathGeo::Invert::kNo));
bsalomonee295642016-06-06 14:01:25 -07001858 }
bsalomon47cc7692016-04-26 12:56:00 -07001859 for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)),
bsalomonee295642016-06-06 14:01:25 -07001860 SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4),
1861 SkRRect::MakeOval(SkRect::MakeWH(20, 20))}) {
bsalomona395f7c2016-08-24 17:47:40 -07001862 geos.emplace_back(new RRectGeo(rr));
bsalomon70493962016-06-10 08:05:14 -07001863 test_rrect(reporter, rr);
bsalomona395f7c2016-08-24 17:47:40 -07001864 SkPath rectPath;
1865 rectPath.addRRect(rr);
1866 geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
1867 PathGeo::Invert::kNo));
1868 geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
1869 PathGeo::Invert::kYes));
1870 rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, rr,
1871 RRectPathGeo::RRectForStroke::kYes,
1872 PathGeo::Invert::kNo));
bsalomon72dc51c2016-04-27 06:46:23 -07001873 }
1874
bsalomon72dc51c2016-04-27 06:46:23 -07001875 SkPath openRectPath;
1876 openRectPath.moveTo(0, 0);
1877 openRectPath.lineTo(10, 0);
1878 openRectPath.lineTo(10, 10);
1879 openRectPath.lineTo(0, 10);
bsalomona395f7c2016-08-24 17:47:40 -07001880 geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
1881 RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kNo));
1882 geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
1883 RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kYes));
1884 rrectPathGeos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
1885 RRectPathGeo::RRectForStroke::kNo,
1886 PathGeo::Invert::kNo));
bsalomon72dc51c2016-04-27 06:46:23 -07001887
1888 SkPath quadPath;
1889 quadPath.quadTo(10, 10, 5, 8);
bsalomona395f7c2016-08-24 17:47:40 -07001890 geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kNo));
1891 geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kYes));
bsalomon398e3f42016-06-13 10:22:48 -07001892
1893 SkPath linePath;
1894 linePath.lineTo(10, 10);
bsalomona395f7c2016-08-24 17:47:40 -07001895 geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kNo));
1896 geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kYes));
bsalomon72dc51c2016-04-27 06:46:23 -07001897
bsalomon0ae36a22016-07-18 07:31:13 -07001898 // Horizontal and vertical paths become rrects when stroked.
1899 SkPath vLinePath;
1900 vLinePath.lineTo(0, 10);
bsalomona395f7c2016-08-24 17:47:40 -07001901 geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kNo));
1902 geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kYes));
bsalomon0ae36a22016-07-18 07:31:13 -07001903
1904 SkPath hLinePath;
1905 hLinePath.lineTo(10, 0);
bsalomona395f7c2016-08-24 17:47:40 -07001906 geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kNo));
1907 geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kYes));
bsalomon0ae36a22016-07-18 07:31:13 -07001908
bsalomona395f7c2016-08-24 17:47:40 -07001909 for (int i = 0; i < geos.count(); ++i) {
1910 test_basic(reporter, *geos[i]);
1911 test_scale(reporter, *geos[i]);
1912 test_dash_fill(reporter, *geos[i]);
1913 test_null_dash(reporter, *geos[i]);
1914 // Test modifying various stroke params.
1915 test_stroke_param<SkScalar>(
1916 reporter, *geos[i],
bsalomon70493962016-06-10 08:05:14 -07001917 [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
1918 SkIntToScalar(2), SkIntToScalar(4));
bsalomona395f7c2016-08-24 17:47:40 -07001919 test_stroke_join(reporter, *geos[i]);
1920 test_stroke_cap(reporter, *geos[i]);
1921 test_miter_limit(reporter, *geos[i]);
1922 test_path_effect_makes_rrect(reporter, *geos[i]);
1923 test_unknown_path_effect(reporter, *geos[i]);
1924 test_path_effect_makes_empty_shape(reporter, *geos[i]);
1925 test_path_effect_fails(reporter, *geos[i]);
1926 test_make_hairline_path_effect(reporter, *geos[i]);
1927 test_volatile_path(reporter, *geos[i]);
bsalomon70493962016-06-10 08:05:14 -07001928 }
bsalomonfd32df72016-06-14 14:37:21 -07001929
bsalomona395f7c2016-08-24 17:47:40 -07001930 for (int i = 0; i < rrectPathGeos.count(); ++i) {
1931 const RRectPathGeo& rrgeo = *rrectPathGeos[i];
bsalomon72dc51c2016-04-27 06:46:23 -07001932 SkPaint fillPaint;
bsalomona395f7c2016-08-24 17:47:40 -07001933 TestCase fillPathCase(reporter, rrgeo.path(), fillPaint);
bsalomon72dc51c2016-04-27 06:46:23 -07001934 SkRRect rrect;
bsalomona395f7c2016-08-24 17:47:40 -07001935 REPORTER_ASSERT(reporter, rrgeo.isNonPath(fillPaint) ==
bsalomon70493962016-06-10 08:05:14 -07001936 fillPathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
1937 nullptr));
bsalomona395f7c2016-08-24 17:47:40 -07001938 if (rrgeo.isNonPath(fillPaint)) {
1939 TestCase fillPathCase2(reporter, rrgeo.path(), fillPaint);
1940 REPORTER_ASSERT(reporter, rrect == rrgeo.rrect());
1941 TestCase fillRRectCase(reporter, rrect, fillPaint);
bsalomon70493962016-06-10 08:05:14 -07001942 fillPathCase2.compare(reporter, fillRRectCase,
1943 TestCase::kAllSame_ComparisonExpecation);
bsalomon72dc51c2016-04-27 06:46:23 -07001944 }
bsalomon72dc51c2016-04-27 06:46:23 -07001945 SkPaint strokePaint;
1946 strokePaint.setStrokeWidth(3.f);
1947 strokePaint.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001948 TestCase strokePathCase(reporter, rrgeo.path(), strokePaint);
1949 if (rrgeo.isNonPath(strokePaint)) {
bsalomon0ae36a22016-07-18 07:31:13 -07001950 REPORTER_ASSERT(reporter, strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
1951 nullptr));
bsalomona395f7c2016-08-24 17:47:40 -07001952 REPORTER_ASSERT(reporter, rrect == rrgeo.rrect());
1953 TestCase strokeRRectCase(reporter, rrect, strokePaint);
bsalomon72dc51c2016-04-27 06:46:23 -07001954 strokePathCase.compare(reporter, strokeRRectCase,
bsalomonee295642016-06-06 14:01:25 -07001955 TestCase::kAllSame_ComparisonExpecation);
bsalomon72dc51c2016-04-27 06:46:23 -07001956 }
bsalomon47cc7692016-04-26 12:56:00 -07001957 }
bsalomon409ed732016-04-27 12:36:02 -07001958
bsalomon4eeccc92016-04-27 13:30:25 -07001959 // Test a volatile empty path.
bsalomona395f7c2016-08-24 17:47:40 -07001960 test_volatile_path(reporter, PathGeo(SkPath(), PathGeo::Invert::kNo));
bsalomon4eeccc92016-04-27 13:30:25 -07001961
bsalomon409ed732016-04-27 12:36:02 -07001962 test_empty_shape(reporter);
bsalomon0a0f67e2016-06-28 11:56:42 -07001963
1964 test_lines(reporter);
bsalomon0ae36a22016-07-18 07:31:13 -07001965
1966 test_stroked_lines(reporter);
bsalomon67fa4e32016-09-21 08:26:57 -07001967
1968 test_short_path_keys(reporter);
bsalomon47cc7692016-04-26 12:56:00 -07001969}
1970
1971#endif