blob: fc426857e1db21171408d94c54b323c3ff15d5ea [file] [log] [blame]
bsalomon47cc7692016-04-26 12:56:00 -07001/*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include <initializer_list>
9#include <functional>
10#include "Test.h"
11#if SK_SUPPORT_GPU
12#include "GrShape.h"
bsalomon9fb42032016-05-13 09:23:38 -070013#include "SkCanvas.h"
bsalomon47cc7692016-04-26 12:56:00 -070014#include "SkDashPathEffect.h"
bsalomon9fb42032016-05-13 09:23:38 -070015#include "SkPath.h"
bsalomonee295642016-06-06 14:01:25 -070016#include "SkPathOps.h"
bsalomon9fb42032016-05-13 09:23:38 -070017#include "SkSurface.h"
bsalomon47cc7692016-04-26 12:56:00 -070018
bsalomon72dc51c2016-04-27 06:46:23 -070019using Key = SkTArray<uint32_t>;
20
21static bool make_key(Key* key, const GrShape& shape) {
22 int size = shape.unstyledKeySize();
23 if (size <= 0) {
24 key->reset(0);
25 return false;
26 }
27 SkASSERT(size);
28 key->reset(size);
29 shape.writeUnstyledKey(key->begin());
30 return true;
31}
32
bsalomonee295642016-06-06 14:01:25 -070033static bool paths_fill_same(const SkPath& a, const SkPath& b) {
34 SkPath pathXor;
35 Op(a, b, SkPathOp::kXOR_SkPathOp, &pathXor);
36 return pathXor.isEmpty();
37}
38
bsalomon9fb42032016-05-13 09:23:38 -070039static bool test_bounds_by_rasterizing(const SkPath& path, const SkRect& bounds) {
bsalomon164fd9f2016-08-26 06:45:06 -070040 // We test the bounds by rasterizing the path into a kRes by kRes grid. The bounds is
41 // mapped to the range kRes/4 to 3*kRes/4 in x and y. A difference clip is used to avoid
42 // rendering within the bounds (with a tolerance). Then we render the path and check that
43 // everything got clipped out.
bsalomon9fb42032016-05-13 09:23:38 -070044 static constexpr int kRes = 2000;
45 // This tolerance is in units of 1/kRes fractions of the bounds width/height.
46 static constexpr int kTol = 0;
47 GR_STATIC_ASSERT(kRes % 4 == 0);
48 SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
49 sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
50 surface->getCanvas()->clear(0x0);
51 SkRect clip = SkRect::MakeXYWH(kRes/4, kRes/4, kRes/2, kRes/2);
52 SkMatrix matrix;
53 matrix.setRectToRect(bounds, clip, SkMatrix::kFill_ScaleToFit);
54 clip.outset(SkIntToScalar(kTol), SkIntToScalar(kTol));
reed73603f32016-09-20 08:42:38 -070055 surface->getCanvas()->clipRect(clip, SkCanvas::kDifference_Op);
bsalomon9fb42032016-05-13 09:23:38 -070056 surface->getCanvas()->concat(matrix);
57 SkPaint whitePaint;
58 whitePaint.setColor(SK_ColorWHITE);
59 surface->getCanvas()->drawPath(path, whitePaint);
60 SkPixmap pixmap;
61 surface->getCanvas()->peekPixels(&pixmap);
62#if defined(SK_BUILD_FOR_WIN)
63 // The static constexpr version in #else causes cl.exe to crash.
64 const uint8_t* kZeros = reinterpret_cast<uint8_t*>(calloc(kRes, 1));
65#else
66 static constexpr uint8_t kZeros[kRes] = {0};
67#endif
bsalomon164fd9f2016-08-26 06:45:06 -070068 for (int y = 0; y < kRes; ++y) {
bsalomon9fb42032016-05-13 09:23:38 -070069 const uint8_t* row = pixmap.addr8(0, y);
70 if (0 != memcmp(kZeros, row, kRes)) {
71 return false;
72 }
73 }
74#ifdef SK_BUILD_FOR_WIN
75 free(const_cast<uint8_t*>(kZeros));
76#endif
77 return true;
78}
bsalomon72dc51c2016-04-27 06:46:23 -070079
bsalomon9fb42032016-05-13 09:23:38 -070080namespace {
bsalomona395f7c2016-08-24 17:47:40 -070081/**
82 * Geo is a factory for creating a GrShape from another representation. It also answers some
83 * questions about expected behavior for GrShape given the inputs.
84 */
85class Geo {
86public:
Mike Kleinfc6c37b2016-09-27 09:34:10 -040087 virtual ~Geo() {}
bsalomona395f7c2016-08-24 17:47:40 -070088 virtual GrShape makeShape(const SkPaint&) const = 0;
89 virtual SkPath path() const = 0;
90 // These functions allow tests to check for special cases where style gets
91 // applied by GrShape in its constructor (without calling GrShape::applyStyle).
92 // These unfortunately rely on knowing details of GrShape's implementation.
93 // These predicates are factored out here to avoid littering the rest of the
94 // test code with GrShape implementation details.
95 virtual bool fillChangesGeom() const { return false; }
96 virtual bool strokeIsConvertedToFill() const { return false; }
97 virtual bool strokeAndFillIsConvertedToFill(const SkPaint&) const { return false; }
98 // Is this something we expect GrShape to recognize as something simpler than a path.
99 virtual bool isNonPath(const SkPaint& paint) const { return true; }
100};
101
102class RectGeo : public Geo {
103public:
104 RectGeo(const SkRect& rect) : fRect(rect) {}
105
106 SkPath path() const override {
107 SkPath path;
108 path.addRect(fRect);
109 return path;
110 }
111
112 GrShape makeShape(const SkPaint& paint) const override {
113 return GrShape(fRect, paint);
114 }
115
116 bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
117 SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
118 // Converted to an outset rectangle.
119 return paint.getStrokeJoin() == SkPaint::kMiter_Join &&
120 paint.getStrokeMiter() >= SK_ScalarSqrt2;
121 }
122
123private:
124 SkRect fRect;
125};
126
127class RRectGeo : public Geo {
128public:
129 RRectGeo(const SkRRect& rrect) : fRRect(rrect) {}
130
131 GrShape makeShape(const SkPaint& paint) const override {
132 return GrShape(fRRect, paint);
133 }
134
135 SkPath path() const override {
136 SkPath path;
137 path.addRRect(fRRect);
138 return path;
139 }
140
141 bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
142 SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
143 if (fRRect.isRect()) {
144 return RectGeo(fRRect.rect()).strokeAndFillIsConvertedToFill(paint);
145 }
146 return false;
147 }
148
149private:
150 SkRRect fRRect;
151};
152
153class PathGeo : public Geo {
154public:
155 enum class Invert { kNo, kYes };
156
157 PathGeo(const SkPath& path, Invert invert) : fPath(path) {
158 SkASSERT(!path.isInverseFillType());
159 if (Invert::kYes == invert) {
160 if (fPath.getFillType() == SkPath::kEvenOdd_FillType) {
161 fPath.setFillType(SkPath::kInverseEvenOdd_FillType);
162 } else {
163 SkASSERT(fPath.getFillType() == SkPath::kWinding_FillType);
164 fPath.setFillType(SkPath::kInverseWinding_FillType);
165 }
166 }
167 }
168
169 GrShape makeShape(const SkPaint& paint) const override {
170 return GrShape(fPath, paint);
171 }
172
173 SkPath path() const override { return fPath; }
174
175 bool fillChangesGeom() const override {
176 // unclosed rects get closed. Lines get turned into empty geometry
177 return this->isUnclosedRect() || (fPath.isLine(nullptr) && !fPath.isInverseFillType());
178 }
179
180 bool strokeIsConvertedToFill() const override {
181 return this->isAxisAlignedLine();
182 }
183
184 bool strokeAndFillIsConvertedToFill(const SkPaint& paint) const override {
185 SkASSERT(paint.getStyle() == SkPaint::kStrokeAndFill_Style);
186 if (this->isAxisAlignedLine()) {
187 // The fill is ignored (zero area) and the stroke is converted to a rrect.
188 return true;
189 }
190 SkRect rect;
191 unsigned start;
192 SkPath::Direction dir;
193 if (SkPathPriv::IsSimpleClosedRect(fPath, &rect, &dir, &start)) {
194 return RectGeo(rect).strokeAndFillIsConvertedToFill(paint);
195 }
196 return false;
197 }
198
199 bool isNonPath(const SkPaint& paint) const override {
200 return fPath.isLine(nullptr) || fPath.isEmpty();
201 }
202
203private:
204 bool isAxisAlignedLine() const {
205 SkPoint pts[2];
206 if (!fPath.isLine(pts)) {
207 return false;
208 }
209 return pts[0].fX == pts[1].fX || pts[0].fY == pts[1].fY;
210 }
211
212 bool isUnclosedRect() const {
213 bool closed;
214 return fPath.isRect(nullptr, &closed, nullptr) && !closed;
215 }
216
217 SkPath fPath;
218};
219
220class RRectPathGeo : public PathGeo {
221public:
222 enum class RRectForStroke { kNo, kYes };
223
224 RRectPathGeo(const SkPath& path, const SkRRect& equivalentRRect, RRectForStroke rrectForStroke,
225 Invert invert)
226 : PathGeo(path, invert)
227 , fRRect(equivalentRRect)
228 , fRRectForStroke(rrectForStroke) {}
229
230 RRectPathGeo(const SkPath& path, const SkRect& equivalentRect, RRectForStroke rrectForStroke,
231 Invert invert)
232 : RRectPathGeo(path, SkRRect::MakeRect(equivalentRect), rrectForStroke, invert) {}
233
234 bool isNonPath(const SkPaint& paint) const override {
235 if (SkPaint::kFill_Style == paint.getStyle() || RRectForStroke::kYes == fRRectForStroke) {
236 return true;
237 }
238 return false;
239 }
240
241 const SkRRect& rrect() const { return fRRect; }
242
243private:
244 SkRRect fRRect;
245 RRectForStroke fRRectForStroke;
246};
247
bsalomon47cc7692016-04-26 12:56:00 -0700248class TestCase {
249public:
bsalomona395f7c2016-08-24 17:47:40 -0700250 TestCase(const Geo& geo, const SkPaint& paint, skiatest::Reporter* r,
251 SkScalar scale = SK_Scalar1) : fBase(geo.makeShape(paint)) {
bsalomon97fd2d42016-05-09 13:02:01 -0700252 this->init(r, scale);
bsalomon47cc7692016-04-26 12:56:00 -0700253 }
254
bsalomona395f7c2016-08-24 17:47:40 -0700255 template<typename... ShapeArgs>
256 TestCase(skiatest::Reporter* r, ShapeArgs... shapeArgs)
257 : fBase(shapeArgs...) {
258 this->init(r, SK_Scalar1);
259 }
260
bsalomon70493962016-06-10 08:05:14 -0700261 TestCase(const GrShape& shape, skiatest::Reporter* r, SkScalar scale = SK_Scalar1)
262 : fBase(shape) {
263 this->init(r, scale);
264 }
265
bsalomon47cc7692016-04-26 12:56:00 -0700266 struct SelfExpectations {
267 bool fPEHasEffect;
268 bool fPEHasValidKey;
269 bool fStrokeApplies;
270 };
271
272 void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const;
273
274 enum ComparisonExpecation {
275 kAllDifferent_ComparisonExpecation,
276 kSameUpToPE_ComparisonExpecation,
277 kSameUpToStroke_ComparisonExpecation,
278 kAllSame_ComparisonExpecation,
279 };
280
281 void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const;
282
bsalomon72dc51c2016-04-27 06:46:23 -0700283 const GrShape& baseShape() const { return fBase; }
284 const GrShape& appliedPathEffectShape() const { return fAppliedPE; }
285 const GrShape& appliedFullStyleShape() const { return fAppliedFull; }
286
287 // The returned array's count will be 0 if the key shape has no key.
288 const Key& baseKey() const { return fBaseKey; }
289 const Key& appliedPathEffectKey() const { return fAppliedPEKey; }
290 const Key& appliedFullStyleKey() const { return fAppliedFullKey; }
bsalomon409ed732016-04-27 12:36:02 -0700291 const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; }
bsalomon72dc51c2016-04-27 06:46:23 -0700292
bsalomon47cc7692016-04-26 12:56:00 -0700293private:
bsalomon9fb42032016-05-13 09:23:38 -0700294 static void CheckBounds(skiatest::Reporter* r, const GrShape& shape, const SkRect& bounds) {
295 SkPath path;
296 shape.asPath(&path);
297 // If the bounds are empty, the path ought to be as well.
bsalomon0ae36a22016-07-18 07:31:13 -0700298 if (bounds.fLeft > bounds.fRight || bounds.fTop > bounds.fBottom) {
bsalomon9fb42032016-05-13 09:23:38 -0700299 REPORTER_ASSERT(r, path.isEmpty());
300 return;
301 }
302 if (path.isEmpty()) {
303 return;
304 }
bsalomon70493962016-06-10 08:05:14 -0700305 // The bounds API explicitly calls out that it does not consider inverseness.
306 SkPath p = path;
307 p.setFillType(SkPath::ConvertToNonInverseFillType(path.getFillType()));
308 REPORTER_ASSERT(r, test_bounds_by_rasterizing(p, bounds));
bsalomon9fb42032016-05-13 09:23:38 -0700309 }
310
bsalomon97fd2d42016-05-09 13:02:01 -0700311 void init(skiatest::Reporter* r, SkScalar scale) {
312 fAppliedPE = fBase.applyStyle(GrStyle::Apply::kPathEffectOnly, scale);
313 fAppliedPEThenStroke = fAppliedPE.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec,
314 scale);
315 fAppliedFull = fBase.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale);
bsalomon47cc7692016-04-26 12:56:00 -0700316
bsalomon72dc51c2016-04-27 06:46:23 -0700317 make_key(&fBaseKey, fBase);
318 make_key(&fAppliedPEKey, fAppliedPE);
319 make_key(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
320 make_key(&fAppliedFullKey, fAppliedFull);
bsalomonfb083272016-05-04 08:27:41 -0700321
322 // Applying the path effect and then the stroke should always be the same as applying
323 // both in one go.
324 REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey);
325 SkPath a, b;
326 fAppliedPEThenStroke.asPath(&a);
327 fAppliedFull.asPath(&b);
bsalomonee295642016-06-06 14:01:25 -0700328 // If the output of the path effect is a rrect then it is possible for a and b to be
329 // different paths that fill identically. The reason is that fAppliedFull will do this:
330 // base -> apply path effect -> rrect_as_path -> stroke -> stroked_rrect_as_path
331 // fAppliedPEThenStroke will have converted the rrect_as_path back to a rrect. However,
332 // now that there is no longer a path effect, the direction and starting index get
333 // canonicalized before the stroke.
bsalomon70493962016-06-10 08:05:14 -0700334 if (fAppliedPE.asRRect(nullptr, nullptr, nullptr, nullptr)) {
bsalomonee295642016-06-06 14:01:25 -0700335 REPORTER_ASSERT(r, paths_fill_same(a, b));
336 } else {
337 REPORTER_ASSERT(r, a == b);
338 }
bsalomon7c73a532016-05-11 15:15:56 -0700339 REPORTER_ASSERT(r, fAppliedFull.isEmpty() == fAppliedPEThenStroke.isEmpty());
340
341 SkPath path;
342 fBase.asPath(&path);
343 REPORTER_ASSERT(r, path.isEmpty() == fBase.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700344 REPORTER_ASSERT(r, path.getSegmentMasks() == fBase.segmentMask());
bsalomon7c73a532016-05-11 15:15:56 -0700345 fAppliedPE.asPath(&path);
346 REPORTER_ASSERT(r, path.isEmpty() == fAppliedPE.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700347 REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedPE.segmentMask());
bsalomon7c73a532016-05-11 15:15:56 -0700348 fAppliedFull.asPath(&path);
349 REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull.isEmpty());
bsalomon06115ee2016-06-07 06:28:51 -0700350 REPORTER_ASSERT(r, path.getSegmentMasks() == fAppliedFull.segmentMask());
bsalomonfb083272016-05-04 08:27:41 -0700351
bsalomon9fb42032016-05-13 09:23:38 -0700352 CheckBounds(r, fBase, fBase.bounds());
353 CheckBounds(r, fAppliedPE, fAppliedPE.bounds());
354 CheckBounds(r, fAppliedPEThenStroke, fAppliedPEThenStroke.bounds());
355 CheckBounds(r, fAppliedFull, fAppliedFull.bounds());
bsalomon0a0f67e2016-06-28 11:56:42 -0700356 SkRect styledBounds = fBase.styledBounds();
bsalomon9fb42032016-05-13 09:23:38 -0700357 CheckBounds(r, fAppliedFull, styledBounds);
bsalomon0a0f67e2016-06-28 11:56:42 -0700358 styledBounds = fAppliedPE.styledBounds();
bsalomon9fb42032016-05-13 09:23:38 -0700359 CheckBounds(r, fAppliedFull, styledBounds);
360
bsalomonfb083272016-05-04 08:27:41 -0700361 // Check that the same path is produced when style is applied by GrShape and GrStyle.
362 SkPath preStyle;
363 SkPath postPathEffect;
364 SkPath postAllStyle;
365
366 fBase.asPath(&preStyle);
bsalomon1a0b9ed2016-05-06 11:07:03 -0700367 SkStrokeRec postPEStrokeRec(SkStrokeRec::kFill_InitStyle);
bsalomon97fd2d42016-05-09 13:02:01 -0700368 if (fBase.style().applyPathEffectToPath(&postPathEffect, &postPEStrokeRec, preStyle,
369 scale)) {
bsalomon1a0b9ed2016-05-06 11:07:03 -0700370 // run postPathEffect through GrShape to get any geometry reductions that would have
371 // occurred to fAppliedPE.
372 GrShape(postPathEffect, GrStyle(postPEStrokeRec, nullptr)).asPath(&postPathEffect);
373
bsalomonfb083272016-05-04 08:27:41 -0700374 SkPath testPath;
375 fAppliedPE.asPath(&testPath);
376 REPORTER_ASSERT(r, testPath == postPathEffect);
bsalomon1a0b9ed2016-05-06 11:07:03 -0700377 REPORTER_ASSERT(r, postPEStrokeRec.hasEqualEffect(fAppliedPE.style().strokeRec()));
bsalomonfb083272016-05-04 08:27:41 -0700378 }
379 SkStrokeRec::InitStyle fillOrHairline;
bsalomon97fd2d42016-05-09 13:02:01 -0700380 if (fBase.style().applyToPath(&postAllStyle, &fillOrHairline, preStyle, scale)) {
bsalomonfb083272016-05-04 08:27:41 -0700381 SkPath testPath;
382 fAppliedFull.asPath(&testPath);
bsalomon1b28c1a2016-06-20 12:28:17 -0700383 if (fBase.style().hasPathEffect()) {
384 // Because GrShape always does two-stage application when there is a path effect
385 // there may be a reduction/canonicalization step between the path effect and
386 // strokerec not reflected in postAllStyle since it applied both the path effect
387 // and strokerec without analyzing the intermediate path.
388 REPORTER_ASSERT(r, paths_fill_same(postAllStyle, testPath));
389 } else {
390 // Make sure that postAllStyle sees any reductions/canonicalizations that GrShape
391 // would apply.
392 GrShape(postAllStyle, GrStyle(fillOrHairline)).asPath(&postAllStyle);
393 REPORTER_ASSERT(r, testPath == postAllStyle);
394 }
395
bsalomonfb083272016-05-04 08:27:41 -0700396 if (fillOrHairline == SkStrokeRec::kFill_InitStyle) {
397 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleFill());
398 } else {
399 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleHairline());
400 }
401 }
bsalomon47cc7692016-04-26 12:56:00 -0700402 }
403
404 GrShape fBase;
405 GrShape fAppliedPE;
406 GrShape fAppliedPEThenStroke;
407 GrShape fAppliedFull;
408
409 Key fBaseKey;
410 Key fAppliedPEKey;
411 Key fAppliedPEThenStrokeKey;
412 Key fAppliedFullKey;
bsalomon47cc7692016-04-26 12:56:00 -0700413};
414
415void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const {
bsalomon47cc7692016-04-26 12:56:00 -0700416 // The base's key should always be valid (unless the path is volatile)
bsalomon72dc51c2016-04-27 06:46:23 -0700417 REPORTER_ASSERT(reporter, fBaseKey.count());
bsalomon47cc7692016-04-26 12:56:00 -0700418 if (expectations.fPEHasEffect) {
419 REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700420 REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedPEKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700421 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700422 REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedFullKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700423 if (expectations.fStrokeApplies && expectations.fPEHasValidKey) {
424 REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700425 REPORTER_ASSERT(reporter, SkToBool(fAppliedFullKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700426 }
427 } else {
428 REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey);
bsalomonfb083272016-05-04 08:27:41 -0700429 SkPath a, b;
bsalomon72dc51c2016-04-27 06:46:23 -0700430 fBase.asPath(&a);
431 fAppliedPE.asPath(&b);
432 REPORTER_ASSERT(reporter, a == b);
bsalomon47cc7692016-04-26 12:56:00 -0700433 if (expectations.fStrokeApplies) {
434 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
435 } else {
436 REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey);
437 }
438 }
439}
440
bsalomonac5fcea2016-06-23 12:23:07 -0700441static bool can_interchange_winding_and_even_odd_fill(const GrShape& shape) {
442 SkPath path;
443 shape.asPath(&path);
444 if (shape.style().hasNonDashPathEffect()) {
445 return false;
446 }
447 const SkStrokeRec::Style strokeRecStyle = shape.style().strokeRec().getStyle();
448 return strokeRecStyle == SkStrokeRec::kStroke_Style ||
449 strokeRecStyle == SkStrokeRec::kHairline_Style ||
450 (shape.style().isSimpleFill() && path.isConvex());
451}
452
453static void check_equivalence(skiatest::Reporter* r, const GrShape& a, const GrShape& b,
454 const Key& keyA, const Key& keyB) {
bsalomonee295642016-06-06 14:01:25 -0700455 // GrShape only respects the input winding direction and start point for rrect shapes
456 // when there is a path effect. Thus, if there are two GrShapes representing the same rrect
457 // but one has a path effect in its style and the other doesn't then asPath() and the unstyled
458 // key will differ. GrShape will have canonicalized the direction and start point for the shape
459 // without the path effect. If *both* have path effects then they should have both preserved
460 // the direction and starting point.
461
462 // The asRRect() output params are all initialized just to silence compiler warnings about
463 // uninitialized variables.
464 SkRRect rrectA = SkRRect::MakeEmpty(), rrectB = SkRRect::MakeEmpty();
465 SkPath::Direction dirA = SkPath::kCW_Direction, dirB = SkPath::kCW_Direction;
466 unsigned startA = ~0U, startB = ~0U;
bsalomon70493962016-06-10 08:05:14 -0700467 bool invertedA = true, invertedB = true;
bsalomonee295642016-06-06 14:01:25 -0700468
bsalomon70493962016-06-10 08:05:14 -0700469 bool aIsRRect = a.asRRect(&rrectA, &dirA, &startA, &invertedA);
470 bool bIsRRect = b.asRRect(&rrectB, &dirB, &startB, &invertedB);
bsalomonee295642016-06-06 14:01:25 -0700471 bool aHasPE = a.style().hasPathEffect();
472 bool bHasPE = b.style().hasPathEffect();
473 bool allowSameRRectButDiffStartAndDir = (aIsRRect && bIsRRect) && (aHasPE != bHasPE);
bsalomon425c27f2016-06-23 13:18:45 -0700474 // GrShape will close paths with simple fill style.
475 bool allowedClosednessDiff = (a.style().isSimpleFill() != b.style().isSimpleFill());
bsalomonee295642016-06-06 14:01:25 -0700476 SkPath pathA, pathB;
477 a.asPath(&pathA);
478 b.asPath(&pathB);
bsalomon70493962016-06-10 08:05:14 -0700479
bsalomonfd32df72016-06-14 14:37:21 -0700480 // Having a dash path effect can allow 'a' but not 'b' to turn a inverse fill type into a
481 // non-inverse fill type (or vice versa).
bsalomon70493962016-06-10 08:05:14 -0700482 bool ignoreInversenessDifference = false;
483 if (pathA.isInverseFillType() != pathB.isInverseFillType()) {
484 const GrShape* s1 = pathA.isInverseFillType() ? &a : &b;
485 const GrShape* s2 = pathA.isInverseFillType() ? &b : &a;
bsalomonfd32df72016-06-14 14:37:21 -0700486 bool canDropInverse1 = s1->style().isDashed();
487 bool canDropInverse2 = s2->style().isDashed();
488 ignoreInversenessDifference = (canDropInverse1 != canDropInverse2);
bsalomon70493962016-06-10 08:05:14 -0700489 }
bsalomona4817af2016-06-23 11:48:26 -0700490 bool ignoreWindingVsEvenOdd = false;
491 if (SkPath::ConvertToNonInverseFillType(pathA.getFillType()) !=
492 SkPath::ConvertToNonInverseFillType(pathB.getFillType())) {
bsalomonac5fcea2016-06-23 12:23:07 -0700493 bool aCanChange = can_interchange_winding_and_even_odd_fill(a);
494 bool bCanChange = can_interchange_winding_and_even_odd_fill(b);
bsalomona4817af2016-06-23 11:48:26 -0700495 if (aCanChange != bCanChange) {
496 ignoreWindingVsEvenOdd = true;
497 }
498 }
bsalomonee295642016-06-06 14:01:25 -0700499 if (allowSameRRectButDiffStartAndDir) {
500 REPORTER_ASSERT(r, rrectA == rrectB);
501 REPORTER_ASSERT(r, paths_fill_same(pathA, pathB));
bsalomon70493962016-06-10 08:05:14 -0700502 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
bsalomonee295642016-06-06 14:01:25 -0700503 } else {
bsalomon70493962016-06-10 08:05:14 -0700504 SkPath pA = pathA;
505 SkPath pB = pathB;
bsalomon425c27f2016-06-23 13:18:45 -0700506 REPORTER_ASSERT(r, a.inverseFilled() == pA.isInverseFillType());
507 REPORTER_ASSERT(r, b.inverseFilled() == pB.isInverseFillType());
bsalomon70493962016-06-10 08:05:14 -0700508 if (ignoreInversenessDifference) {
509 pA.setFillType(SkPath::ConvertToNonInverseFillType(pathA.getFillType()));
510 pB.setFillType(SkPath::ConvertToNonInverseFillType(pathB.getFillType()));
bsalomona4817af2016-06-23 11:48:26 -0700511 }
512 if (ignoreWindingVsEvenOdd) {
513 pA.setFillType(pA.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
514 : SkPath::kEvenOdd_FillType);
515 pB.setFillType(pB.isInverseFillType() ? SkPath::kInverseEvenOdd_FillType
516 : SkPath::kEvenOdd_FillType);
517 }
518 if (!ignoreInversenessDifference && !ignoreWindingVsEvenOdd) {
bsalomon70493962016-06-10 08:05:14 -0700519 REPORTER_ASSERT(r, keyA == keyB);
bsalomona4817af2016-06-23 11:48:26 -0700520 } else {
521 REPORTER_ASSERT(r, keyA != keyB);
bsalomon70493962016-06-10 08:05:14 -0700522 }
bsalomon425c27f2016-06-23 13:18:45 -0700523 if (allowedClosednessDiff) {
bsalomon93f66bc2016-06-21 08:35:49 -0700524 // GrShape will close paths with simple fill style. Make the non-filled path closed
525 // so that the comparision will succeed. Make sure both are closed before comparing.
526 pA.close();
527 pB.close();
528 }
bsalomon70493962016-06-10 08:05:14 -0700529 REPORTER_ASSERT(r, pA == pB);
bsalomonee295642016-06-06 14:01:25 -0700530 REPORTER_ASSERT(r, aIsRRect == bIsRRect);
531 if (aIsRRect) {
532 REPORTER_ASSERT(r, rrectA == rrectB);
533 REPORTER_ASSERT(r, dirA == dirB);
534 REPORTER_ASSERT(r, startA == startB);
bsalomon70493962016-06-10 08:05:14 -0700535 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedA == invertedB);
bsalomonee295642016-06-06 14:01:25 -0700536 }
537 }
538 REPORTER_ASSERT(r, a.isEmpty() == b.isEmpty());
bsalomon425c27f2016-06-23 13:18:45 -0700539 REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeClosed() == b.knownToBeClosed());
540 // closedness can affect convexity.
541 REPORTER_ASSERT(r, allowedClosednessDiff || a.knownToBeConvex() == b.knownToBeConvex());
542 if (a.knownToBeConvex()) {
543 REPORTER_ASSERT(r, pathA.isConvex());
544 }
545 if (b.knownToBeConvex()) {
546 REPORTER_ASSERT(r, pathB.isConvex());
547 }
bsalomonee295642016-06-06 14:01:25 -0700548 REPORTER_ASSERT(r, a.bounds() == b.bounds());
bsalomon06115ee2016-06-07 06:28:51 -0700549 REPORTER_ASSERT(r, a.segmentMask() == b.segmentMask());
bsalomon0a0f67e2016-06-28 11:56:42 -0700550 // Init these to suppress warnings.
551 SkPoint pts[4] {{0, 0,}, {0, 0}, {0, 0}, {0, 0}} ;
552 bool invertedLine[2] {true, true};
553 REPORTER_ASSERT(r, a.asLine(pts, &invertedLine[0]) == b.asLine(pts + 2, &invertedLine[1]));
bsalomon425c27f2016-06-23 13:18:45 -0700554 // mayBeInverseFilledAfterStyling() is allowed to differ if one has a arbitrary PE and the other
555 // doesn't (since the PE can set any fill type on its output path).
556 // Moreover, dash style explicitly ignores inverseness. So if one is dashed but not the other
557 // then they may disagree about inverseness.
558 if (a.style().hasNonDashPathEffect() == b.style().hasNonDashPathEffect() &&
559 a.style().isDashed() == b.style().isDashed()) {
560 REPORTER_ASSERT(r, a.mayBeInverseFilledAfterStyling() ==
561 b.mayBeInverseFilledAfterStyling());
562 }
bsalomon0a0f67e2016-06-28 11:56:42 -0700563 if (a.asLine(nullptr, nullptr)) {
bsalomon398e3f42016-06-13 10:22:48 -0700564 REPORTER_ASSERT(r, pts[2] == pts[0] && pts[3] == pts[1]);
bsalomon0a0f67e2016-06-28 11:56:42 -0700565 REPORTER_ASSERT(r, ignoreInversenessDifference || invertedLine[0] == invertedLine[1]);
566 REPORTER_ASSERT(r, invertedLine[0] == a.inverseFilled());
567 REPORTER_ASSERT(r, invertedLine[1] == b.inverseFilled());
bsalomon398e3f42016-06-13 10:22:48 -0700568 }
bsalomon425c27f2016-06-23 13:18:45 -0700569 REPORTER_ASSERT(r, ignoreInversenessDifference || a.inverseFilled() == b.inverseFilled());
bsalomonee295642016-06-06 14:01:25 -0700570}
571
572void TestCase::compare(skiatest::Reporter* r, const TestCase& that,
bsalomon47cc7692016-04-26 12:56:00 -0700573 ComparisonExpecation expectation) const {
bsalomon72dc51c2016-04-27 06:46:23 -0700574 SkPath a, b;
bsalomon47cc7692016-04-26 12:56:00 -0700575 switch (expectation) {
576 case kAllDifferent_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700577 REPORTER_ASSERT(r, fBaseKey != that.fBaseKey);
578 REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
579 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700580 break;
581 case kSameUpToPE_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700582 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
583 REPORTER_ASSERT(r, fAppliedPEKey != that.fAppliedPEKey);
584 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700585 break;
586 case kSameUpToStroke_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700587 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
588 check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
589 REPORTER_ASSERT(r, fAppliedFullKey != that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700590 break;
591 case kAllSame_ComparisonExpecation:
bsalomonee295642016-06-06 14:01:25 -0700592 check_equivalence(r, fBase, that.fBase, fBaseKey, that.fBaseKey);
593 check_equivalence(r, fAppliedPE, that.fAppliedPE, fAppliedPEKey, that.fAppliedPEKey);
594 check_equivalence(r, fAppliedFull, that.fAppliedFull, fAppliedFullKey,
595 that.fAppliedFullKey);
bsalomon47cc7692016-04-26 12:56:00 -0700596 break;
597 }
598}
599} // namespace
600
601static sk_sp<SkPathEffect> make_dash() {
602 static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f };
603 static const SkScalar kPhase = 0.75;
604 return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase);
605}
606
607static sk_sp<SkPathEffect> make_null_dash() {
608 static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0};
609 return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f);
610}
611
bsalomona395f7c2016-08-24 17:47:40 -0700612static void test_basic(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700613 sk_sp<SkPathEffect> dashPE = make_dash();
614
615 TestCase::SelfExpectations expectations;
616 SkPaint fill;
617
bsalomonfb083272016-05-04 08:27:41 -0700618 TestCase fillCase(geo, fill, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700619 expectations.fPEHasEffect = false;
620 expectations.fPEHasValidKey = false;
621 expectations.fStrokeApplies = false;
622 fillCase.testExpectations(reporter, expectations);
623 // Test that another GrShape instance built from the same primitive is the same.
bsalomonfb083272016-05-04 08:27:41 -0700624 TestCase(geo, fill, reporter).compare(reporter, fillCase,
625 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700626
627 SkPaint stroke2RoundBevel;
628 stroke2RoundBevel.setStyle(SkPaint::kStroke_Style);
629 stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap);
630 stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join);
631 stroke2RoundBevel.setStrokeWidth(2.f);
bsalomonfb083272016-05-04 08:27:41 -0700632 TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700633 expectations.fPEHasValidKey = true;
634 expectations.fPEHasEffect = false;
bsalomona395f7c2016-08-24 17:47:40 -0700635 expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
bsalomon47cc7692016-04-26 12:56:00 -0700636 stroke2RoundBevelCase.testExpectations(reporter, expectations);
bsalomonfb083272016-05-04 08:27:41 -0700637 TestCase(geo, stroke2RoundBevel, reporter).compare(reporter, stroke2RoundBevelCase,
638 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700639
640 SkPaint stroke2RoundBevelDash = stroke2RoundBevel;
641 stroke2RoundBevelDash.setPathEffect(make_dash());
bsalomonfb083272016-05-04 08:27:41 -0700642 TestCase stroke2RoundBevelDashCase(geo, stroke2RoundBevelDash, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700643 expectations.fPEHasValidKey = true;
644 expectations.fPEHasEffect = true;
645 expectations.fStrokeApplies = true;
646 stroke2RoundBevelDashCase.testExpectations(reporter, expectations);
bsalomonfb083272016-05-04 08:27:41 -0700647 TestCase(geo, stroke2RoundBevelDash, reporter).compare(reporter, stroke2RoundBevelDashCase,
648 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700649
bsalomona395f7c2016-08-24 17:47:40 -0700650 if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700651 fillCase.compare(reporter, stroke2RoundBevelCase,
652 TestCase::kAllDifferent_ComparisonExpecation);
653 fillCase.compare(reporter, stroke2RoundBevelDashCase,
654 TestCase::kAllDifferent_ComparisonExpecation);
655 } else {
656 fillCase.compare(reporter, stroke2RoundBevelCase,
657 TestCase::kSameUpToStroke_ComparisonExpecation);
658 fillCase.compare(reporter, stroke2RoundBevelDashCase,
659 TestCase::kSameUpToPE_ComparisonExpecation);
660 }
bsalomona395f7c2016-08-24 17:47:40 -0700661 if (geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700662 stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
663 TestCase::kAllDifferent_ComparisonExpecation);
664 } else {
665 stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
666 TestCase::kSameUpToPE_ComparisonExpecation);
667 }
bsalomon72dc51c2016-04-27 06:46:23 -0700668
bsalomonf0cf3552016-05-05 08:28:30 -0700669 // Stroke and fill cases
670 SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel;
671 stroke2RoundBevelAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
672 TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter);
673 expectations.fPEHasValidKey = true;
674 expectations.fPEHasEffect = false;
bsalomona395f7c2016-08-24 17:47:40 -0700675 expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
bsalomonf0cf3552016-05-05 08:28:30 -0700676 stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations);
677 TestCase(geo, stroke2RoundBevelAndFill, reporter).compare(reporter,
678 stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation);
679
680 SkPaint stroke2RoundBevelAndFillDash = stroke2RoundBevelDash;
681 stroke2RoundBevelAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
682 TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter);
683 expectations.fPEHasValidKey = true;
bsalomona0587862016-06-09 06:03:38 -0700684 expectations.fPEHasEffect = false;
bsalomona395f7c2016-08-24 17:47:40 -0700685 expectations.fStrokeApplies = !geo.strokeIsConvertedToFill();
bsalomonf0cf3552016-05-05 08:28:30 -0700686 stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations);
687 TestCase(geo, stroke2RoundBevelAndFillDash, reporter).compare(
688 reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation);
bsalomona0587862016-06-09 06:03:38 -0700689 stroke2RoundBevelAndFillDashCase.compare(reporter, stroke2RoundBevelAndFillCase,
690 TestCase::kAllSame_ComparisonExpecation);
bsalomonf0cf3552016-05-05 08:28:30 -0700691
bsalomon72dc51c2016-04-27 06:46:23 -0700692 SkPaint hairline;
693 hairline.setStyle(SkPaint::kStroke_Style);
694 hairline.setStrokeWidth(0.f);
bsalomonfb083272016-05-04 08:27:41 -0700695 TestCase hairlineCase(geo, hairline, reporter);
bsalomon487f8d32016-07-20 07:15:44 -0700696 // Since hairline style doesn't change the SkPath data, it is keyed identically to fill (except
697 // in the line and unclosed rect cases).
bsalomona395f7c2016-08-24 17:47:40 -0700698 if (geo.fillChangesGeom()) {
bsalomon487f8d32016-07-20 07:15:44 -0700699 hairlineCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation);
700 } else {
701 hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
702 }
bsalomon9ad5d7c2016-05-04 08:44:15 -0700703 REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline());
704 REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline());
705 REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline());
bsalomon47cc7692016-04-26 12:56:00 -0700706
bsalomon0ae36a22016-07-18 07:31:13 -0700707}
708
bsalomona395f7c2016-08-24 17:47:40 -0700709static void test_scale(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon97fd2d42016-05-09 13:02:01 -0700710 sk_sp<SkPathEffect> dashPE = make_dash();
711
712 static const SkScalar kS1 = 1.f;
713 static const SkScalar kS2 = 2.f;
714
715 SkPaint fill;
716 TestCase fillCase1(geo, fill, reporter, kS1);
717 TestCase fillCase2(geo, fill, reporter, kS2);
718 // Scale doesn't affect fills.
719 fillCase1.compare(reporter, fillCase2, TestCase::kAllSame_ComparisonExpecation);
720
721 SkPaint hairline;
722 hairline.setStyle(SkPaint::kStroke_Style);
723 hairline.setStrokeWidth(0.f);
724 TestCase hairlineCase1(geo, hairline, reporter, kS1);
725 TestCase hairlineCase2(geo, hairline, reporter, kS2);
726 // Scale doesn't affect hairlines.
727 hairlineCase1.compare(reporter, hairlineCase2, TestCase::kAllSame_ComparisonExpecation);
728
729 SkPaint stroke;
730 stroke.setStyle(SkPaint::kStroke_Style);
731 stroke.setStrokeWidth(2.f);
732 TestCase strokeCase1(geo, stroke, reporter, kS1);
733 TestCase strokeCase2(geo, stroke, reporter, kS2);
bsalomon0ae36a22016-07-18 07:31:13 -0700734 // Scale affects the stroke
bsalomona395f7c2016-08-24 17:47:40 -0700735 if (geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700736 REPORTER_ASSERT(reporter, !strokeCase1.baseShape().style().applies());
bsalomon0ae36a22016-07-18 07:31:13 -0700737 strokeCase1.compare(reporter, strokeCase2, TestCase::kAllSame_ComparisonExpecation);
738 } else {
739 strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation);
740 }
bsalomon97fd2d42016-05-09 13:02:01 -0700741
742 SkPaint strokeDash = stroke;
743 strokeDash.setPathEffect(make_dash());
744 TestCase strokeDashCase1(geo, strokeDash, reporter, kS1);
745 TestCase strokeDashCase2(geo, strokeDash, reporter, kS2);
746 // Scale affects the dash and the stroke.
bsalomon487f8d32016-07-20 07:15:44 -0700747 strokeDashCase1.compare(reporter, strokeDashCase2,
748 TestCase::kSameUpToPE_ComparisonExpecation);
bsalomon97fd2d42016-05-09 13:02:01 -0700749
750 // Stroke and fill cases
751 SkPaint strokeAndFill = stroke;
752 strokeAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
753 TestCase strokeAndFillCase1(geo, strokeAndFill, reporter, kS1);
754 TestCase strokeAndFillCase2(geo, strokeAndFill, reporter, kS2);
bsalomona0587862016-06-09 06:03:38 -0700755 SkPaint strokeAndFillDash = strokeDash;
756 strokeAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
757 // Dash is ignored for stroke and fill
758 TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1);
759 TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2);
bsalomon487f8d32016-07-20 07:15:44 -0700760 // Scale affects the stroke, but check to make sure this didn't become a simpler shape (e.g.
761 // stroke-and-filled rect can become a rect), in which case the scale shouldn't matter and the
762 // geometries should agree.
bsalomona395f7c2016-08-24 17:47:40 -0700763 if (geo.strokeAndFillIsConvertedToFill(strokeAndFillDash)) {
bsalomon487f8d32016-07-20 07:15:44 -0700764 REPORTER_ASSERT(reporter, !strokeAndFillCase1.baseShape().style().applies());
bsalomon97fd2d42016-05-09 13:02:01 -0700765 strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
766 TestCase::kAllSame_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700767 strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2,
768 TestCase::kAllSame_ComparisonExpecation);
bsalomon97fd2d42016-05-09 13:02:01 -0700769 } else {
770 strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
771 TestCase::kSameUpToStroke_ComparisonExpecation);
772 }
bsalomona0587862016-06-09 06:03:38 -0700773 strokeAndFillDashCase1.compare(reporter, strokeAndFillCase1,
774 TestCase::kAllSame_ComparisonExpecation);
775 strokeAndFillDashCase2.compare(reporter, strokeAndFillCase2,
776 TestCase::kAllSame_ComparisonExpecation);
bsalomon97fd2d42016-05-09 13:02:01 -0700777}
778
bsalomona395f7c2016-08-24 17:47:40 -0700779template <typename T>
780static void test_stroke_param_impl(skiatest::Reporter* reporter, const Geo& geo,
bsalomon06077562016-05-04 13:50:29 -0700781 std::function<void(SkPaint*, T)> setter, T a, T b,
782 bool paramAffectsStroke,
783 bool paramAffectsDashAndStroke) {
784 // Set the stroke width so that we don't get hairline. However, call the setter afterward so
785 // that it can override the stroke width.
bsalomon47cc7692016-04-26 12:56:00 -0700786 SkPaint strokeA;
787 strokeA.setStyle(SkPaint::kStroke_Style);
788 strokeA.setStrokeWidth(2.f);
789 setter(&strokeA, a);
790 SkPaint strokeB;
791 strokeB.setStyle(SkPaint::kStroke_Style);
792 strokeB.setStrokeWidth(2.f);
793 setter(&strokeB, b);
794
bsalomonfb083272016-05-04 08:27:41 -0700795 TestCase strokeACase(geo, strokeA, reporter);
796 TestCase strokeBCase(geo, strokeB, reporter);
bsalomon06077562016-05-04 13:50:29 -0700797 if (paramAffectsStroke) {
bsalomon0ae36a22016-07-18 07:31:13 -0700798 // If stroking is immediately incorporated into a geometric transformation then the base
799 // shapes will differ.
bsalomona395f7c2016-08-24 17:47:40 -0700800 if (geo.strokeIsConvertedToFill()) {
bsalomon0ae36a22016-07-18 07:31:13 -0700801 strokeACase.compare(reporter, strokeBCase,
802 TestCase::kAllDifferent_ComparisonExpecation);
bsalomon487f8d32016-07-20 07:15:44 -0700803 } else {
804 strokeACase.compare(reporter, strokeBCase,
805 TestCase::kSameUpToStroke_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700806 }
bsalomon06077562016-05-04 13:50:29 -0700807 } else {
808 strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation);
809 }
bsalomon47cc7692016-04-26 12:56:00 -0700810
bsalomonf0cf3552016-05-05 08:28:30 -0700811 SkPaint strokeAndFillA = strokeA;
812 SkPaint strokeAndFillB = strokeB;
813 strokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style);
814 strokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style);
815 TestCase strokeAndFillACase(geo, strokeAndFillA, reporter);
816 TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter);
817 if (paramAffectsStroke) {
bsalomon0ae36a22016-07-18 07:31:13 -0700818 // If stroking is immediately incorporated into a geometric transformation then the base
819 // shapes will differ.
bsalomona395f7c2016-08-24 17:47:40 -0700820 if (geo.strokeAndFillIsConvertedToFill(strokeAndFillA) ||
821 geo.strokeAndFillIsConvertedToFill(strokeAndFillB)) {
bsalomon0ae36a22016-07-18 07:31:13 -0700822 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
bsalomon487f8d32016-07-20 07:15:44 -0700823 TestCase::kAllDifferent_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700824 } else {
825 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
bsalomon487f8d32016-07-20 07:15:44 -0700826 TestCase::kSameUpToStroke_ComparisonExpecation);
bsalomon0ae36a22016-07-18 07:31:13 -0700827 }
bsalomonf0cf3552016-05-05 08:28:30 -0700828 } else {
829 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
830 TestCase::kAllSame_ComparisonExpecation);
831 }
832
bsalomon47cc7692016-04-26 12:56:00 -0700833 // Make sure stroking params don't affect fill style.
834 SkPaint fillA = strokeA, fillB = strokeB;
835 fillA.setStyle(SkPaint::kFill_Style);
836 fillB.setStyle(SkPaint::kFill_Style);
bsalomonfb083272016-05-04 08:27:41 -0700837 TestCase fillACase(geo, fillA, reporter);
838 TestCase fillBCase(geo, fillB, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700839 fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
840
841 // Make sure just applying the dash but not stroke gives the same key for both stroking
842 // variations.
843 SkPaint dashA = strokeA, dashB = strokeB;
844 dashA.setPathEffect(make_dash());
845 dashB.setPathEffect(make_dash());
bsalomonfb083272016-05-04 08:27:41 -0700846 TestCase dashACase(geo, dashA, reporter);
847 TestCase dashBCase(geo, dashB, reporter);
bsalomon06077562016-05-04 13:50:29 -0700848 if (paramAffectsDashAndStroke) {
bsalomon487f8d32016-07-20 07:15:44 -0700849 dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
bsalomon06077562016-05-04 13:50:29 -0700850 } else {
851 dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation);
852 }
bsalomon47cc7692016-04-26 12:56:00 -0700853}
854
bsalomona395f7c2016-08-24 17:47:40 -0700855template <typename T>
856static void test_stroke_param(skiatest::Reporter* reporter, const Geo& geo,
bsalomon06077562016-05-04 13:50:29 -0700857 std::function<void(SkPaint*, T)> setter, T a, T b) {
858 test_stroke_param_impl(reporter, geo, setter, a, b, true, true);
859};
860
bsalomona395f7c2016-08-24 17:47:40 -0700861static void test_stroke_cap(skiatest::Reporter* reporter, const Geo& geo) {
862 SkPaint hairline;
863 hairline.setStrokeWidth(0);
864 hairline.setStyle(SkPaint::kStroke_Style);
865 GrShape shape = geo.makeShape(hairline);
bsalomon06077562016-05-04 13:50:29 -0700866 // The cap should only affect shapes that may be open.
867 bool affectsStroke = !shape.knownToBeClosed();
868 // Dashing adds ends that need caps.
869 bool affectsDashAndStroke = true;
bsalomona395f7c2016-08-24 17:47:40 -0700870 test_stroke_param_impl<SkPaint::Cap>(
bsalomon06077562016-05-04 13:50:29 -0700871 reporter,
872 geo,
873 [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
874 SkPaint::kButt_Cap, SkPaint::kRound_Cap,
875 affectsStroke,
876 affectsDashAndStroke);
877};
878
bsalomon0ae36a22016-07-18 07:31:13 -0700879static bool shape_known_not_to_have_joins(const GrShape& shape) {
880 return shape.asLine(nullptr, nullptr) || shape.isEmpty();
881}
882
bsalomona395f7c2016-08-24 17:47:40 -0700883static void test_stroke_join(skiatest::Reporter* reporter, const Geo& geo) {
884 SkPaint hairline;
885 hairline.setStrokeWidth(0);
886 hairline.setStyle(SkPaint::kStroke_Style);
887 GrShape shape = geo.makeShape(hairline);
bsalomon0ae36a22016-07-18 07:31:13 -0700888 // GrShape recognizes certain types don't have joins and will prevent the join type from
889 // affecting the style key.
890 // Dashing doesn't add additional joins. However, GrShape currently loses track of this
891 // after applying the dash.
892 bool affectsStroke = !shape_known_not_to_have_joins(shape);
bsalomona395f7c2016-08-24 17:47:40 -0700893 test_stroke_param_impl<SkPaint::Join>(
bsalomon0ae36a22016-07-18 07:31:13 -0700894 reporter,
895 geo,
896 [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
897 SkPaint::kRound_Join, SkPaint::kBevel_Join,
898 affectsStroke, true);
899};
900
bsalomona395f7c2016-08-24 17:47:40 -0700901static void test_miter_limit(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon06077562016-05-04 13:50:29 -0700902 auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) {
903 p->setStrokeJoin(SkPaint::kMiter_Join);
904 p->setStrokeMiter(miter);
905 };
bsalomon47cc7692016-04-26 12:56:00 -0700906
bsalomon06077562016-05-04 13:50:29 -0700907 auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) {
908 p->setStrokeJoin(SkPaint::kRound_Join);
909 p->setStrokeMiter(miter);
910 };
bsalomon47cc7692016-04-26 12:56:00 -0700911
bsalomona395f7c2016-08-24 17:47:40 -0700912 SkPaint hairline;
913 hairline.setStrokeWidth(0);
914 hairline.setStyle(SkPaint::kStroke_Style);
915 GrShape shape = geo.makeShape(hairline);
bsalomon0ae36a22016-07-18 07:31:13 -0700916 bool mayHaveJoins = !shape_known_not_to_have_joins(shape);
917
bsalomon06077562016-05-04 13:50:29 -0700918 // The miter limit should affect stroked and dashed-stroked cases when the join type is
919 // miter.
bsalomona395f7c2016-08-24 17:47:40 -0700920 test_stroke_param_impl<SkScalar>(
bsalomon06077562016-05-04 13:50:29 -0700921 reporter,
922 geo,
923 setMiterJoinAndLimit,
924 0.5f, 0.75f,
bsalomon0ae36a22016-07-18 07:31:13 -0700925 mayHaveJoins,
bsalomon06077562016-05-04 13:50:29 -0700926 true);
bsalomon47cc7692016-04-26 12:56:00 -0700927
bsalomon06077562016-05-04 13:50:29 -0700928 // The miter limit should not affect stroked and dashed-stroked cases when the join type is
929 // not miter.
bsalomona395f7c2016-08-24 17:47:40 -0700930 test_stroke_param_impl<SkScalar>(
bsalomon06077562016-05-04 13:50:29 -0700931 reporter,
932 geo,
933 setOtherJoinAndLimit,
934 0.5f, 0.75f,
935 false,
936 false);
bsalomon47cc7692016-04-26 12:56:00 -0700937}
938
bsalomona395f7c2016-08-24 17:47:40 -0700939static void test_dash_fill(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700940 // A dash with no stroke should have no effect
941 using DashFactoryFn = sk_sp<SkPathEffect>(*)();
942 for (DashFactoryFn md : {&make_dash, &make_null_dash}) {
943 SkPaint dashFill;
944 dashFill.setPathEffect((*md)());
bsalomonfb083272016-05-04 08:27:41 -0700945 TestCase dashFillCase(geo, dashFill, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700946
bsalomonfb083272016-05-04 08:27:41 -0700947 TestCase fillCase(geo, SkPaint(), reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700948 dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
949 }
950}
951
bsalomona395f7c2016-08-24 17:47:40 -0700952void test_null_dash(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700953 SkPaint fill;
954 SkPaint stroke;
955 stroke.setStyle(SkPaint::kStroke_Style);
956 stroke.setStrokeWidth(1.f);
957 SkPaint dash;
958 dash.setStyle(SkPaint::kStroke_Style);
959 dash.setStrokeWidth(1.f);
960 dash.setPathEffect(make_dash());
961 SkPaint nullDash;
962 nullDash.setStyle(SkPaint::kStroke_Style);
963 nullDash.setStrokeWidth(1.f);
964 nullDash.setPathEffect(make_null_dash());
965
bsalomonfb083272016-05-04 08:27:41 -0700966 TestCase fillCase(geo, fill, reporter);
967 TestCase strokeCase(geo, stroke, reporter);
968 TestCase dashCase(geo, dash, reporter);
969 TestCase nullDashCase(geo, nullDash, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700970
bsalomon487f8d32016-07-20 07:15:44 -0700971 // We expect the null dash to be ignored so nullDashCase should match strokeCase, always.
bsalomon47cc7692016-04-26 12:56:00 -0700972 nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation);
bsalomon487f8d32016-07-20 07:15:44 -0700973 // Check whether the fillCase or strokeCase/nullDashCase would undergo a geometric tranformation
974 // on construction in order to determine how to compare the fill and stroke.
bsalomona395f7c2016-08-24 17:47:40 -0700975 if (geo.fillChangesGeom() || geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700976 nullDashCase.compare(reporter, fillCase, TestCase::kAllDifferent_ComparisonExpecation);
977 } else {
978 nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation);
979 }
980 // In the null dash case we may immediately convert to a fill, but not for the normal dash case.
bsalomona395f7c2016-08-24 17:47:40 -0700981 if (geo.strokeIsConvertedToFill()) {
bsalomon487f8d32016-07-20 07:15:44 -0700982 nullDashCase.compare(reporter, dashCase, TestCase::kAllDifferent_ComparisonExpecation);
983 } else {
984 nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation);
985 }
bsalomon47cc7692016-04-26 12:56:00 -0700986}
987
bsalomona395f7c2016-08-24 17:47:40 -0700988void test_path_effect_makes_rrect(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon72dc51c2016-04-27 06:46:23 -0700989 /**
990 * This path effect takes any input path and turns it into a rrect. It passes through stroke
991 * info.
992 */
993 class RRectPathEffect : SkPathEffect {
994 public:
995 static const SkRRect& RRect() {
996 static const SkRRect kRRect = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 3, 5);
997 return kRRect;
998 }
999
1000 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1001 const SkRect* cullR) const override {
1002 dst->reset();
1003 dst->addRRect(RRect());
1004 return true;
1005 }
1006 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1007 *dst = RRect().getBounds();
1008 }
1009 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new RRectPathEffect); }
1010 Factory getFactory() const override { return nullptr; }
1011 void toString(SkString*) const override {}
1012 private:
1013 RRectPathEffect() {}
1014 };
1015
1016 SkPaint fill;
bsalomonfb083272016-05-04 08:27:41 -07001017 TestCase fillGeoCase(geo, fill, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001018
1019 SkPaint pe;
1020 pe.setPathEffect(RRectPathEffect::Make());
bsalomonfb083272016-05-04 08:27:41 -07001021 TestCase geoPECase(geo, pe, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001022
1023 SkPaint peStroke;
1024 peStroke.setPathEffect(RRectPathEffect::Make());
1025 peStroke.setStrokeWidth(2.f);
1026 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001027 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001028
bsalomon487f8d32016-07-20 07:15:44 -07001029 // Check whether constructing the filled case would cause the base shape to have a different
1030 // geometry (because of a geometric transformation upon initial GrShape construction).
bsalomona395f7c2016-08-24 17:47:40 -07001031 if (geo.fillChangesGeom()) {
bsalomon487f8d32016-07-20 07:15:44 -07001032 fillGeoCase.compare(reporter, geoPECase, TestCase::kAllDifferent_ComparisonExpecation);
1033 fillGeoCase.compare(reporter, geoPEStrokeCase,
1034 TestCase::kAllDifferent_ComparisonExpecation);
1035 } else {
1036 fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation);
1037 fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation);
1038 }
bsalomon72dc51c2016-04-27 06:46:23 -07001039 geoPECase.compare(reporter, geoPEStrokeCase,
1040 TestCase::kSameUpToStroke_ComparisonExpecation);
1041
bsalomona395f7c2016-08-24 17:47:40 -07001042 TestCase rrectFillCase(reporter, RRectPathEffect::RRect(), fill);
bsalomon72dc51c2016-04-27 06:46:23 -07001043 SkPaint stroke = peStroke;
1044 stroke.setPathEffect(nullptr);
bsalomona395f7c2016-08-24 17:47:40 -07001045 TestCase rrectStrokeCase(reporter, RRectPathEffect::RRect(), stroke);
bsalomon72dc51c2016-04-27 06:46:23 -07001046
1047 SkRRect rrect;
1048 // Applying the path effect should make a SkRRect shape. There is no further stroking in the
1049 // geoPECase, so the full style should be the same as just the PE.
bsalomon70493962016-06-10 08:05:14 -07001050 REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().asRRect(&rrect, nullptr, nullptr,
1051 nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001052 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
1053 REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == rrectFillCase.baseKey());
1054
bsalomon70493962016-06-10 08:05:14 -07001055 REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().asRRect(&rrect, nullptr, nullptr,
1056 nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001057 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
1058 REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == rrectFillCase.baseKey());
1059
1060 // In the PE+stroke case applying the full style should be the same as just stroking the rrect.
bsalomon70493962016-06-10 08:05:14 -07001061 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().asRRect(&rrect, nullptr,
1062 nullptr, nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001063 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
1064 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == rrectFillCase.baseKey());
1065
bsalomon70493962016-06-10 08:05:14 -07001066 REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().asRRect(&rrect, nullptr,
1067 nullptr, nullptr));
bsalomon72dc51c2016-04-27 06:46:23 -07001068 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() ==
1069 rrectStrokeCase.appliedFullStyleKey());
1070}
1071
bsalomona395f7c2016-08-24 17:47:40 -07001072void test_unknown_path_effect(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon72dc51c2016-04-27 06:46:23 -07001073 /**
1074 * This path effect just adds two lineTos to the input path.
1075 */
1076 class AddLineTosPathEffect : SkPathEffect {
1077 public:
1078 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1079 const SkRect* cullR) const override {
1080 *dst = src;
bsalomon67fa4e32016-09-21 08:26:57 -07001081 // To avoid triggering data-based keying of paths with few verbs we add many segments.
1082 for (int i = 0; i < 100; ++i) {
1083 dst->lineTo(SkIntToScalar(i), SkIntToScalar(i));
1084 }
bsalomon72dc51c2016-04-27 06:46:23 -07001085 return true;
1086 }
1087 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1088 *dst = src;
1089 dst->growToInclude(0, 0);
bsalomon67fa4e32016-09-21 08:26:57 -07001090 dst->growToInclude(100, 100);
bsalomon72dc51c2016-04-27 06:46:23 -07001091 }
1092 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new AddLineTosPathEffect); }
1093 Factory getFactory() const override { return nullptr; }
1094 void toString(SkString*) const override {}
1095 private:
1096 AddLineTosPathEffect() {}
1097 };
1098
bsalomon9ad5d7c2016-05-04 08:44:15 -07001099 // This path effect should make the keys invalid when it is applied. We only produce a path
bsalomon72dc51c2016-04-27 06:46:23 -07001100 // effect key for dash path effects. So the only way another arbitrary path effect can produce
1101 // a styled result with a key is to produce a non-path shape that has a purely geometric key.
1102 SkPaint peStroke;
1103 peStroke.setPathEffect(AddLineTosPathEffect::Make());
1104 peStroke.setStrokeWidth(2.f);
1105 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001106 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001107 TestCase::SelfExpectations expectations;
1108 expectations.fPEHasEffect = true;
1109 expectations.fPEHasValidKey = false;
1110 expectations.fStrokeApplies = true;
1111 geoPEStrokeCase.testExpectations(reporter, expectations);
1112}
1113
bsalomona395f7c2016-08-24 17:47:40 -07001114void test_make_hairline_path_effect(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon9ad5d7c2016-05-04 08:44:15 -07001115 /**
1116 * This path effect just changes the stroke rec to hairline.
1117 */
1118 class MakeHairlinePathEffect : SkPathEffect {
1119 public:
1120 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* strokeRec,
1121 const SkRect* cullR) const override {
1122 *dst = src;
1123 strokeRec->setHairlineStyle();
1124 return true;
1125 }
bsalomon70493962016-06-10 08:05:14 -07001126 void computeFastBounds(SkRect* dst, const SkRect& src) const override { *dst = src; }
bsalomon9ad5d7c2016-05-04 08:44:15 -07001127 static sk_sp<SkPathEffect> Make() {
1128 return sk_sp<SkPathEffect>(new MakeHairlinePathEffect);
1129 }
1130 Factory getFactory() const override { return nullptr; }
1131 void toString(SkString*) const override {}
1132 private:
1133 MakeHairlinePathEffect() {}
1134 };
1135
1136 SkPaint fill;
1137 SkPaint pe;
1138 pe.setPathEffect(MakeHairlinePathEffect::Make());
1139
1140 TestCase peCase(geo, pe, reporter);
1141
bsalomonee295642016-06-06 14:01:25 -07001142 SkPath a, b, c;
bsalomon9ad5d7c2016-05-04 08:44:15 -07001143 peCase.baseShape().asPath(&a);
1144 peCase.appliedPathEffectShape().asPath(&b);
bsalomonee295642016-06-06 14:01:25 -07001145 peCase.appliedFullStyleShape().asPath(&c);
bsalomona395f7c2016-08-24 17:47:40 -07001146 if (geo.isNonPath(pe)) {
bsalomonee295642016-06-06 14:01:25 -07001147 // RRect types can have a change in start index or direction after the PE is applied. This
1148 // is because once the PE is applied, GrShape may canonicalize the dir and index since it
1149 // is not germane to the styling any longer.
1150 // Instead we just check that the paths would fill the same both before and after styling.
1151 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1152 REPORTER_ASSERT(reporter, paths_fill_same(a, c));
bsalomon9ad5d7c2016-05-04 08:44:15 -07001153 } else {
bsalomona4817af2016-06-23 11:48:26 -07001154 // The base shape cannot perform canonicalization on the path's fill type because of an
1155 // unknown path effect. However, after the path effect is applied the resulting hairline
1156 // shape will canonicalize the path fill type since hairlines (and stroking in general)
1157 // don't distinguish between even/odd and non-zero winding.
1158 a.setFillType(b.getFillType());
bsalomonee295642016-06-06 14:01:25 -07001159 REPORTER_ASSERT(reporter, a == b);
1160 REPORTER_ASSERT(reporter, a == c);
bsalomon67fa4e32016-09-21 08:26:57 -07001161 // If the resulting path is small enough then it will have a key.
1162 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1163 REPORTER_ASSERT(reporter, paths_fill_same(a, c));
bsalomonaa840642016-09-23 12:09:16 -07001164 REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey().empty());
1165 REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey().empty());
bsalomon9ad5d7c2016-05-04 08:44:15 -07001166 }
bsalomonee295642016-06-06 14:01:25 -07001167 REPORTER_ASSERT(reporter, peCase.appliedPathEffectShape().style().isSimpleHairline());
1168 REPORTER_ASSERT(reporter, peCase.appliedFullStyleShape().style().isSimpleHairline());
bsalomon9ad5d7c2016-05-04 08:44:15 -07001169}
1170
bsalomona395f7c2016-08-24 17:47:40 -07001171void test_volatile_path(skiatest::Reporter* reporter, const Geo& geo) {
1172 SkPath vPath = geo.path();
bsalomon4eeccc92016-04-27 13:30:25 -07001173 vPath.setIsVolatile(true);
1174
1175 SkPaint dashAndStroke;
1176 dashAndStroke.setPathEffect(make_dash());
1177 dashAndStroke.setStrokeWidth(2.f);
1178 dashAndStroke.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001179 TestCase volatileCase(reporter, vPath, dashAndStroke);
bsalomon4eeccc92016-04-27 13:30:25 -07001180 // We expect a shape made from a volatile path to have a key iff the shape is recognized
bsalomonaa840642016-09-23 12:09:16 -07001181 // as a specialized geometry.
1182 if (geo.isNonPath(dashAndStroke)) {
bsalomon4eeccc92016-04-27 13:30:25 -07001183 REPORTER_ASSERT(reporter, SkToBool(volatileCase.baseKey().count()));
1184 // In this case all the keys should be identical to the non-volatile case.
bsalomona395f7c2016-08-24 17:47:40 -07001185 TestCase nonVolatileCase(reporter, geo.path(), dashAndStroke);
bsalomon4eeccc92016-04-27 13:30:25 -07001186 volatileCase.compare(reporter, nonVolatileCase, TestCase::kAllSame_ComparisonExpecation);
1187 } else {
1188 // None of the keys should be valid.
1189 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.baseKey().count()));
1190 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectKey().count()));
1191 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedFullStyleKey().count()));
1192 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectThenStrokeKey().count()));
1193 }
1194}
1195
bsalomona395f7c2016-08-24 17:47:40 -07001196void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const Geo& geo) {
bsalomon409ed732016-04-27 12:36:02 -07001197 /**
1198 * This path effect returns an empty path.
1199 */
1200 class EmptyPathEffect : SkPathEffect {
1201 public:
1202 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1203 const SkRect* cullR) const override {
1204 dst->reset();
1205 return true;
1206 }
1207 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1208 dst->setEmpty();
1209 }
1210 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new EmptyPathEffect); }
1211 Factory getFactory() const override { return nullptr; }
1212 void toString(SkString*) const override {}
1213 private:
1214 EmptyPathEffect() {}
1215 };
1216
1217 SkPath emptyPath;
1218 GrShape emptyShape(emptyPath);
1219 Key emptyKey;
1220 make_key(&emptyKey, emptyShape);
bsalomon7c73a532016-05-11 15:15:56 -07001221 REPORTER_ASSERT(reporter, emptyShape.isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001222
1223 SkPaint pe;
1224 pe.setPathEffect(EmptyPathEffect::Make());
bsalomonfb083272016-05-04 08:27:41 -07001225 TestCase geoCase(geo, pe, reporter);
bsalomon409ed732016-04-27 12:36:02 -07001226 REPORTER_ASSERT(reporter, geoCase.appliedFullStyleKey() == emptyKey);
1227 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectKey() == emptyKey);
1228 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectThenStrokeKey() == emptyKey);
bsalomon7c73a532016-05-11 15:15:56 -07001229 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectShape().isEmpty());
1230 REPORTER_ASSERT(reporter, geoCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001231
1232 SkPaint peStroke;
1233 peStroke.setPathEffect(EmptyPathEffect::Make());
1234 peStroke.setStrokeWidth(2.f);
1235 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -07001236 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon409ed732016-04-27 12:36:02 -07001237 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == emptyKey);
1238 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == emptyKey);
1239 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey);
bsalomon7c73a532016-05-11 15:15:56 -07001240 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty());
1241 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001242}
1243
bsalomona395f7c2016-08-24 17:47:40 -07001244void test_path_effect_fails(skiatest::Reporter* reporter, const Geo& geo) {
bsalomond6723842016-06-07 12:20:15 -07001245 /**
bsalomon0ae36a22016-07-18 07:31:13 -07001246 * This path effect always fails to apply.
bsalomond6723842016-06-07 12:20:15 -07001247 */
1248 class FailurePathEffect : SkPathEffect {
1249 public:
1250 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
1251 const SkRect* cullR) const override {
1252 return false;
1253 }
1254 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
1255 *dst = src;
1256 }
1257 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new FailurePathEffect); }
1258 Factory getFactory() const override { return nullptr; }
1259 void toString(SkString*) const override {}
1260 private:
1261 FailurePathEffect() {}
1262 };
1263
1264 SkPaint fill;
1265 TestCase fillCase(geo, fill, reporter);
1266
1267 SkPaint pe;
1268 pe.setPathEffect(FailurePathEffect::Make());
1269 TestCase peCase(geo, pe, reporter);
1270
1271 SkPaint stroke;
1272 stroke.setStrokeWidth(2.f);
1273 stroke.setStyle(SkPaint::kStroke_Style);
1274 TestCase strokeCase(geo, stroke, reporter);
1275
1276 SkPaint peStroke = stroke;
1277 peStroke.setPathEffect(FailurePathEffect::Make());
1278 TestCase peStrokeCase(geo, peStroke, reporter);
1279
1280 // In general the path effect failure can cause some of the TestCase::compare() tests to fail
1281 // for at least two reasons: 1) We will initially treat the shape as unkeyable because of the
1282 // path effect, but then when the path effect fails we can key it. 2) GrShape will change its
1283 // mind about whether a unclosed rect is actually rect. The path effect initially bars us from
1284 // closing it but after the effect fails we can (for the fill+pe case). This causes different
1285 // routes through GrShape to have equivalent but different representations of the path (closed
1286 // or not) but that fill the same.
1287 SkPath a;
1288 SkPath b;
1289 fillCase.appliedPathEffectShape().asPath(&a);
1290 peCase.appliedPathEffectShape().asPath(&b);
1291 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1292
1293 fillCase.appliedFullStyleShape().asPath(&a);
1294 peCase.appliedFullStyleShape().asPath(&b);
1295 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1296
1297 strokeCase.appliedPathEffectShape().asPath(&a);
1298 peStrokeCase.appliedPathEffectShape().asPath(&b);
1299 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1300
1301 strokeCase.appliedFullStyleShape().asPath(&a);
1302 peStrokeCase.appliedFullStyleShape().asPath(&b);
1303 REPORTER_ASSERT(reporter, paths_fill_same(a, b));
1304}
1305
bsalomon409ed732016-04-27 12:36:02 -07001306void test_empty_shape(skiatest::Reporter* reporter) {
1307 SkPath emptyPath;
1308 SkPaint fill;
bsalomona395f7c2016-08-24 17:47:40 -07001309 TestCase fillEmptyCase(reporter, emptyPath, fill);
bsalomon7c73a532016-05-11 15:15:56 -07001310 REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty());
1311 REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty());
1312 REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -07001313
1314 Key emptyKey(fillEmptyCase.baseKey());
1315 REPORTER_ASSERT(reporter, emptyKey.count());
1316 TestCase::SelfExpectations expectations;
1317 expectations.fStrokeApplies = false;
1318 expectations.fPEHasEffect = false;
1319 // This will test whether applying style preserves emptiness
1320 fillEmptyCase.testExpectations(reporter, expectations);
1321
1322 // Stroking an empty path should have no effect
1323 SkPath emptyPath2;
1324 SkPaint stroke;
1325 stroke.setStrokeWidth(2.f);
1326 stroke.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001327 TestCase strokeEmptyCase(reporter, emptyPath2, stroke);
bsalomon409ed732016-04-27 12:36:02 -07001328 strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
1329
1330 // Dashing and stroking an empty path should have no effect
1331 SkPath emptyPath3;
1332 SkPaint dashAndStroke;
1333 dashAndStroke.setPathEffect(make_dash());
1334 dashAndStroke.setStrokeWidth(2.f);
1335 dashAndStroke.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001336 TestCase dashAndStrokeEmptyCase(reporter, emptyPath3, dashAndStroke);
bsalomon409ed732016-04-27 12:36:02 -07001337 dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase,
1338 TestCase::kAllSame_ComparisonExpecation);
bsalomon5e410b42016-04-28 09:30:46 -07001339
1340 // A shape made from an empty rrect should behave the same as an empty path.
1341 SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty());
1342 REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type);
bsalomona395f7c2016-08-24 17:47:40 -07001343 TestCase dashAndStrokeEmptyRRectCase(reporter, emptyRRect, dashAndStroke);
bsalomon5e410b42016-04-28 09:30:46 -07001344 dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
1345 TestCase::kAllSame_ComparisonExpecation);
1346
1347 // Same for a rect.
1348 SkRect emptyRect = SkRect::MakeEmpty();
bsalomona395f7c2016-08-24 17:47:40 -07001349 TestCase dashAndStrokeEmptyRectCase(reporter, emptyRect, dashAndStroke);
bsalomon5e410b42016-04-28 09:30:46 -07001350 dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase,
1351 TestCase::kAllSame_ComparisonExpecation);
bsalomon409ed732016-04-27 12:36:02 -07001352}
1353
bsalomon70493962016-06-10 08:05:14 -07001354// rect and oval types have rrect start indices that collapse to the same point. Here we select the
1355// canonical point in these cases.
1356unsigned canonicalize_rrect_start(int s, const SkRRect& rrect) {
1357 switch (rrect.getType()) {
1358 case SkRRect::kRect_Type:
1359 return (s + 1) & 0b110;
1360 case SkRRect::kOval_Type:
1361 return s & 0b110;
1362 default:
1363 return s;
1364 }
1365}
1366
1367void test_rrect(skiatest::Reporter* r, const SkRRect& rrect) {
bsalomoncadb5a22016-06-10 18:28:06 -07001368 enum Style {
bsalomon70493962016-06-10 08:05:14 -07001369 kFill,
1370 kStroke,
1371 kHairline,
1372 kStrokeAndFill
1373 };
1374
1375 // SkStrokeRec has no default cons., so init with kFill before calling the setters below.
1376 SkStrokeRec strokeRecs[4] { SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle,
1377 SkStrokeRec::kFill_InitStyle, SkStrokeRec::kFill_InitStyle};
1378 strokeRecs[kFill].setFillStyle();
1379 strokeRecs[kStroke].setStrokeStyle(2.f);
1380 strokeRecs[kHairline].setHairlineStyle();
1381 strokeRecs[kStrokeAndFill].setStrokeStyle(3.f, true);
bsalomon487f8d32016-07-20 07:15:44 -07001382 // Use a bevel join to avoid complications of stroke+filled rects becoming filled rects before
1383 // applyStyle() is called.
1384 strokeRecs[kStrokeAndFill].setStrokeParams(SkPaint::kButt_Cap, SkPaint::kBevel_Join, 1.f);
bsalomon70493962016-06-10 08:05:14 -07001385 sk_sp<SkPathEffect> dashEffect = make_dash();
1386
bsalomoncadb5a22016-06-10 18:28:06 -07001387 static constexpr Style kStyleCnt = static_cast<Style>(SK_ARRAY_COUNT(strokeRecs));
1388
1389 auto index = [](bool inverted,
1390 SkPath::Direction dir,
1391 unsigned start,
1392 Style style,
1393 bool dash) -> int {
1394 return inverted * (2 * 8 * kStyleCnt * 2) +
1395 dir * ( 8 * kStyleCnt * 2) +
1396 start * ( kStyleCnt * 2) +
1397 style * ( 2) +
1398 dash;
1399 };
1400 static const SkPath::Direction kSecondDirection = static_cast<SkPath::Direction>(1);
1401 const int cnt = index(true, kSecondDirection, 7, static_cast<Style>(kStyleCnt - 1), true) + 1;
1402 SkAutoTArray<GrShape> shapes(cnt);
1403 for (bool inverted : {false, true}) {
1404 for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
1405 for (unsigned start = 0; start < 8; ++start) {
1406 for (Style style : {kFill, kStroke, kHairline, kStrokeAndFill}) {
1407 for (bool dash : {false, true}) {
bsalomon70493962016-06-10 08:05:14 -07001408 SkPathEffect* pe = dash ? dashEffect.get() : nullptr;
bsalomoncadb5a22016-06-10 18:28:06 -07001409 shapes[index(inverted, dir, start, style, dash)] =
1410 GrShape(rrect, dir, start, SkToBool(inverted),
bsalomon70493962016-06-10 08:05:14 -07001411 GrStyle(strokeRecs[style], pe));
1412 }
1413 }
1414 }
1415 }
1416 }
1417
bsalomonfd32df72016-06-14 14:37:21 -07001418 // Get the keys for some example shape instances that we'll use for comparision against the
1419 // rest.
1420 static constexpr SkPath::Direction kExamplesDir = SkPath::kCW_Direction;
1421 static constexpr unsigned kExamplesStart = 0;
1422 const GrShape& exampleFillCase = shapes[index(false, kExamplesDir, kExamplesStart, kFill,
1423 false)];
bsalomon70493962016-06-10 08:05:14 -07001424 Key exampleFillCaseKey;
1425 make_key(&exampleFillCaseKey, exampleFillCase);
1426
bsalomonfd32df72016-06-14 14:37:21 -07001427 const GrShape& exampleStrokeAndFillCase = shapes[index(false, kExamplesDir, kExamplesStart,
1428 kStrokeAndFill, false)];
bsalomon70493962016-06-10 08:05:14 -07001429 Key exampleStrokeAndFillCaseKey;
1430 make_key(&exampleStrokeAndFillCaseKey, exampleStrokeAndFillCase);
1431
bsalomonfd32df72016-06-14 14:37:21 -07001432 const GrShape& exampleInvFillCase = shapes[index(true, kExamplesDir, kExamplesStart, kFill,
1433 false)];
bsalomon70493962016-06-10 08:05:14 -07001434 Key exampleInvFillCaseKey;
1435 make_key(&exampleInvFillCaseKey, exampleInvFillCase);
1436
bsalomonfd32df72016-06-14 14:37:21 -07001437 const GrShape& exampleInvStrokeAndFillCase = shapes[index(true, kExamplesDir, kExamplesStart,
1438 kStrokeAndFill, false)];
bsalomon70493962016-06-10 08:05:14 -07001439 Key exampleInvStrokeAndFillCaseKey;
1440 make_key(&exampleInvStrokeAndFillCaseKey, exampleInvStrokeAndFillCase);
1441
bsalomonfd32df72016-06-14 14:37:21 -07001442 const GrShape& exampleStrokeCase = shapes[index(false, kExamplesDir, kExamplesStart, kStroke,
1443 false)];
bsalomon70493962016-06-10 08:05:14 -07001444 Key exampleStrokeCaseKey;
1445 make_key(&exampleStrokeCaseKey, exampleStrokeCase);
1446
bsalomonfd32df72016-06-14 14:37:21 -07001447 const GrShape& exampleInvStrokeCase = shapes[index(true, kExamplesDir, kExamplesStart, kStroke,
1448 false)];
1449 Key exampleInvStrokeCaseKey;
1450 make_key(&exampleInvStrokeCaseKey, exampleInvStrokeCase);
1451
1452 const GrShape& exampleHairlineCase = shapes[index(false, kExamplesDir, kExamplesStart,
1453 kHairline, false)];
bsalomon70493962016-06-10 08:05:14 -07001454 Key exampleHairlineCaseKey;
1455 make_key(&exampleHairlineCaseKey, exampleHairlineCase);
1456
bsalomonfd32df72016-06-14 14:37:21 -07001457 const GrShape& exampleInvHairlineCase = shapes[index(true, kExamplesDir, kExamplesStart,
1458 kHairline, false)];
1459 Key exampleInvHairlineCaseKey;
1460 make_key(&exampleInvHairlineCaseKey, exampleInvHairlineCase);
1461
bsalomon70493962016-06-10 08:05:14 -07001462 // These are dummy initializations to suppress warnings.
bsalomoncadb5a22016-06-10 18:28:06 -07001463 SkRRect queryRR = SkRRect::MakeEmpty();
1464 SkPath::Direction queryDir = SkPath::kCW_Direction;
1465 unsigned queryStart = ~0U;
1466 bool queryInverted = true;
bsalomon70493962016-06-10 08:05:14 -07001467
bsalomoncadb5a22016-06-10 18:28:06 -07001468 REPORTER_ASSERT(r, exampleFillCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
1469 REPORTER_ASSERT(r, queryRR == rrect);
1470 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1471 REPORTER_ASSERT(r, 0 == queryStart);
1472 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001473
bsalomoncadb5a22016-06-10 18:28:06 -07001474 REPORTER_ASSERT(r, exampleInvFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1475 &queryInverted));
1476 REPORTER_ASSERT(r, queryRR == rrect);
1477 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1478 REPORTER_ASSERT(r, 0 == queryStart);
1479 REPORTER_ASSERT(r, queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001480
bsalomoncadb5a22016-06-10 18:28:06 -07001481 REPORTER_ASSERT(r, exampleStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1482 &queryInverted));
1483 REPORTER_ASSERT(r, queryRR == rrect);
1484 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1485 REPORTER_ASSERT(r, 0 == queryStart);
1486 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001487
bsalomoncadb5a22016-06-10 18:28:06 -07001488 REPORTER_ASSERT(r, exampleInvStrokeAndFillCase.asRRect(&queryRR, &queryDir, &queryStart,
1489 &queryInverted));
1490 REPORTER_ASSERT(r, queryRR == rrect);
1491 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1492 REPORTER_ASSERT(r, 0 == queryStart);
1493 REPORTER_ASSERT(r, queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001494
bsalomoncadb5a22016-06-10 18:28:06 -07001495 REPORTER_ASSERT(r, exampleHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1496 &queryInverted));
1497 REPORTER_ASSERT(r, queryRR == rrect);
1498 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1499 REPORTER_ASSERT(r, 0 == queryStart);
1500 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001501
bsalomonfd32df72016-06-14 14:37:21 -07001502 REPORTER_ASSERT(r, exampleInvHairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1503 &queryInverted));
1504 REPORTER_ASSERT(r, queryRR == rrect);
1505 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1506 REPORTER_ASSERT(r, 0 == queryStart);
1507 REPORTER_ASSERT(r, queryInverted);
1508
bsalomoncadb5a22016-06-10 18:28:06 -07001509 REPORTER_ASSERT(r, exampleStrokeCase.asRRect(&queryRR, &queryDir, &queryStart, &queryInverted));
1510 REPORTER_ASSERT(r, queryRR == rrect);
1511 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1512 REPORTER_ASSERT(r, 0 == queryStart);
1513 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001514
bsalomonfd32df72016-06-14 14:37:21 -07001515 REPORTER_ASSERT(r, exampleInvStrokeCase.asRRect(&queryRR, &queryDir, &queryStart,
1516 &queryInverted));
1517 REPORTER_ASSERT(r, queryRR == rrect);
1518 REPORTER_ASSERT(r, SkPath::kCW_Direction == queryDir);
1519 REPORTER_ASSERT(r, 0 == queryStart);
1520 REPORTER_ASSERT(r, queryInverted);
1521
bsalomon70493962016-06-10 08:05:14 -07001522 // Remember that the key reflects the geometry before styling is applied.
1523 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvFillCaseKey);
1524 REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeAndFillCaseKey);
1525 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeAndFillCaseKey);
1526 REPORTER_ASSERT(r, exampleFillCaseKey == exampleStrokeCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001527 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvStrokeCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001528 REPORTER_ASSERT(r, exampleFillCaseKey == exampleHairlineCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001529 REPORTER_ASSERT(r, exampleFillCaseKey != exampleInvHairlineCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001530 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvFillCaseKey);
bsalomonfd32df72016-06-14 14:37:21 -07001531 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvStrokeCaseKey);
1532 REPORTER_ASSERT(r, exampleInvStrokeAndFillCaseKey == exampleInvHairlineCaseKey);
bsalomon70493962016-06-10 08:05:14 -07001533
bsalomoncadb5a22016-06-10 18:28:06 -07001534 for (bool inverted : {false, true}) {
1535 for (SkPath::Direction dir : {SkPath::kCW_Direction, SkPath::kCCW_Direction}) {
1536 for (unsigned start = 0; start < 8; ++start) {
1537 for (bool dash : {false, true}) {
1538 const GrShape& fillCase = shapes[index(inverted, dir, start, kFill, dash)];
bsalomon70493962016-06-10 08:05:14 -07001539 Key fillCaseKey;
1540 make_key(&fillCaseKey, fillCase);
1541
bsalomoncadb5a22016-06-10 18:28:06 -07001542 const GrShape& strokeAndFillCase = shapes[index(inverted, dir, start,
1543 kStrokeAndFill, dash)];
bsalomon70493962016-06-10 08:05:14 -07001544 Key strokeAndFillCaseKey;
1545 make_key(&strokeAndFillCaseKey, strokeAndFillCase);
1546
1547 // Both fill and stroke-and-fill shapes must respect the inverseness and both
1548 // ignore dashing.
1549 REPORTER_ASSERT(r, !fillCase.style().pathEffect());
1550 REPORTER_ASSERT(r, !strokeAndFillCase.style().pathEffect());
1551 TestCase a(fillCase, r);
1552 TestCase b(inverted ? exampleInvFillCase : exampleFillCase, r);
1553 TestCase c(strokeAndFillCase, r);
1554 TestCase d(inverted ? exampleInvStrokeAndFillCase
1555 : exampleStrokeAndFillCase, r);
1556 a.compare(r, b, TestCase::kAllSame_ComparisonExpecation);
1557 c.compare(r, d, TestCase::kAllSame_ComparisonExpecation);
1558
bsalomoncadb5a22016-06-10 18:28:06 -07001559 const GrShape& strokeCase = shapes[index(inverted, dir, start, kStroke, dash)];
1560 const GrShape& hairlineCase = shapes[index(inverted, dir, start, kHairline,
1561 dash)];
bsalomon70493962016-06-10 08:05:14 -07001562
1563 TestCase e(strokeCase, r);
bsalomon70493962016-06-10 08:05:14 -07001564 TestCase g(hairlineCase, r);
bsalomon70493962016-06-10 08:05:14 -07001565
bsalomonfd32df72016-06-14 14:37:21 -07001566 // Both hairline and stroke shapes must respect the dashing.
bsalomon70493962016-06-10 08:05:14 -07001567 if (dash) {
bsalomonfd32df72016-06-14 14:37:21 -07001568 // Dashing always ignores the inverseness. skbug.com/5421
1569 TestCase f(exampleStrokeCase, r);
1570 TestCase h(exampleHairlineCase, r);
bsalomoncadb5a22016-06-10 18:28:06 -07001571 unsigned expectedStart = canonicalize_rrect_start(start, rrect);
bsalomon70493962016-06-10 08:05:14 -07001572 REPORTER_ASSERT(r, strokeCase.style().pathEffect());
1573 REPORTER_ASSERT(r, hairlineCase.style().pathEffect());
1574
bsalomoncadb5a22016-06-10 18:28:06 -07001575 REPORTER_ASSERT(r, strokeCase.asRRect(&queryRR, &queryDir, &queryStart,
1576 &queryInverted));
1577 REPORTER_ASSERT(r, queryRR == rrect);
1578 REPORTER_ASSERT(r, queryDir == dir);
1579 REPORTER_ASSERT(r, queryStart == expectedStart);
1580 REPORTER_ASSERT(r, !queryInverted);
1581 REPORTER_ASSERT(r, hairlineCase.asRRect(&queryRR, &queryDir, &queryStart,
1582 &queryInverted));
1583 REPORTER_ASSERT(r, queryRR == rrect);
1584 REPORTER_ASSERT(r, queryDir == dir);
1585 REPORTER_ASSERT(r, queryStart == expectedStart);
1586 REPORTER_ASSERT(r, !queryInverted);
bsalomon70493962016-06-10 08:05:14 -07001587
1588 // The pre-style case for the dash will match the non-dash example iff the
1589 // dir and start match (dir=cw, start=0).
bsalomoncadb5a22016-06-10 18:28:06 -07001590 if (0 == expectedStart && SkPath::kCW_Direction == dir) {
bsalomon70493962016-06-10 08:05:14 -07001591 e.compare(r, f, TestCase::kSameUpToPE_ComparisonExpecation);
1592 g.compare(r, h, TestCase::kSameUpToPE_ComparisonExpecation);
1593 } else {
1594 e.compare(r, f, TestCase::kAllDifferent_ComparisonExpecation);
1595 g.compare(r, h, TestCase::kAllDifferent_ComparisonExpecation);
1596 }
1597 } else {
bsalomonfd32df72016-06-14 14:37:21 -07001598 TestCase f(inverted ? exampleInvStrokeCase : exampleStrokeCase, r);
1599 TestCase h(inverted ? exampleInvHairlineCase : exampleHairlineCase, r);
bsalomon70493962016-06-10 08:05:14 -07001600 REPORTER_ASSERT(r, !strokeCase.style().pathEffect());
1601 REPORTER_ASSERT(r, !hairlineCase.style().pathEffect());
1602 e.compare(r, f, TestCase::kAllSame_ComparisonExpecation);
1603 g.compare(r, h, TestCase::kAllSame_ComparisonExpecation);
1604 }
1605 }
1606 }
1607 }
1608 }
1609}
1610
bsalomon0a0f67e2016-06-28 11:56:42 -07001611void test_lines(skiatest::Reporter* r) {
1612 static constexpr SkPoint kA { 1, 1};
1613 static constexpr SkPoint kB { 5, -9};
1614 static constexpr SkPoint kC {-3, 17};
1615
1616 SkPath lineAB;
1617 lineAB.moveTo(kA);
1618 lineAB.lineTo(kB);
1619
1620 SkPath lineBA;
1621 lineBA.moveTo(kB);
1622 lineBA.lineTo(kA);
1623
1624 SkPath lineAC;
1625 lineAC.moveTo(kB);
1626 lineAC.lineTo(kC);
1627
1628 SkPath invLineAB = lineAB;
1629 invLineAB.setFillType(SkPath::kInverseEvenOdd_FillType);
1630
1631 SkPaint fill;
1632 SkPaint stroke;
1633 stroke.setStyle(SkPaint::kStroke_Style);
1634 stroke.setStrokeWidth(2.f);
1635 SkPaint hairline;
1636 hairline.setStyle(SkPaint::kStroke_Style);
1637 hairline.setStrokeWidth(0.f);
1638 SkPaint dash = stroke;
1639 dash.setPathEffect(make_dash());
1640
bsalomona395f7c2016-08-24 17:47:40 -07001641 TestCase fillAB(r, lineAB, fill);
1642 TestCase fillEmpty(r, SkPath(), fill);
bsalomon0a0f67e2016-06-28 11:56:42 -07001643 fillAB.compare(r, fillEmpty, TestCase::kAllSame_ComparisonExpecation);
1644 REPORTER_ASSERT(r, !fillAB.baseShape().asLine(nullptr, nullptr));
1645
bsalomona395f7c2016-08-24 17:47:40 -07001646 TestCase strokeAB(r, lineAB, stroke);
1647 TestCase strokeBA(r, lineBA, stroke);
1648 TestCase strokeAC(r, lineAC, stroke);
bsalomon0a0f67e2016-06-28 11:56:42 -07001649
bsalomona395f7c2016-08-24 17:47:40 -07001650 TestCase hairlineAB(r, lineAB, hairline);
1651 TestCase hairlineBA(r, lineBA, hairline);
1652 TestCase hairlineAC(r, lineAC, hairline);
bsalomon0a0f67e2016-06-28 11:56:42 -07001653
bsalomona395f7c2016-08-24 17:47:40 -07001654 TestCase dashAB(r, lineAB, dash);
1655 TestCase dashBA(r, lineBA, dash);
1656 TestCase dashAC(r, lineAC, dash);
bsalomon0a0f67e2016-06-28 11:56:42 -07001657
1658 strokeAB.compare(r, fillAB, TestCase::kAllDifferent_ComparisonExpecation);
1659
1660 strokeAB.compare(r, strokeBA, TestCase::kAllSame_ComparisonExpecation);
1661 strokeAB.compare(r, strokeAC, TestCase::kAllDifferent_ComparisonExpecation);
1662
1663 hairlineAB.compare(r, hairlineBA, TestCase::kAllSame_ComparisonExpecation);
1664 hairlineAB.compare(r, hairlineAC, TestCase::kAllDifferent_ComparisonExpecation);
1665
1666 dashAB.compare(r, dashBA, TestCase::kAllDifferent_ComparisonExpecation);
1667 dashAB.compare(r, dashAC, TestCase::kAllDifferent_ComparisonExpecation);
1668
1669 strokeAB.compare(r, hairlineAB, TestCase::kSameUpToStroke_ComparisonExpecation);
1670
1671 // One of dashAB or dashBA should have the same line as strokeAB. It depends upon how
1672 // GrShape canonicalizes line endpoints (when it can, i.e. when not dashed).
1673 bool canonicalizeAsAB;
1674 SkPoint canonicalPts[2] {kA, kB};
1675 // Init these to suppress warnings.
1676 bool inverted = true;
1677 SkPoint pts[2] {{0, 0}, {0, 0}};
1678 REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted);
1679 if (pts[0] == kA && pts[1] == kB) {
1680 canonicalizeAsAB = true;
1681 } else if (pts[1] == kA && pts[0] == kB) {
1682 canonicalizeAsAB = false;
1683 SkTSwap(canonicalPts[0], canonicalPts[1]);
1684 } else {
1685 ERRORF(r, "Should return pts (a,b) or (b, a)");
1686 return;
1687 };
1688
1689 strokeAB.compare(r, canonicalizeAsAB ? dashAB : dashBA,
1690 TestCase::kSameUpToPE_ComparisonExpecation);
1691 REPORTER_ASSERT(r, strokeAB.baseShape().asLine(pts, &inverted) && !inverted &&
1692 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1693 REPORTER_ASSERT(r, hairlineAB.baseShape().asLine(pts, &inverted) && !inverted &&
1694 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1695 REPORTER_ASSERT(r, dashAB.baseShape().asLine(pts, &inverted) && !inverted &&
1696 pts[0] == kA && pts[1] == kB);
1697 REPORTER_ASSERT(r, dashBA.baseShape().asLine(pts, &inverted) && !inverted &&
1698 pts[0] == kB && pts[1] == kA);
1699
1700
bsalomona395f7c2016-08-24 17:47:40 -07001701 TestCase strokeInvAB(r, invLineAB, stroke);
1702 TestCase hairlineInvAB(r, invLineAB, hairline);
1703 TestCase dashInvAB(r, invLineAB, dash);
bsalomon0a0f67e2016-06-28 11:56:42 -07001704 strokeInvAB.compare(r, strokeAB, TestCase::kAllDifferent_ComparisonExpecation);
1705 hairlineInvAB.compare(r, hairlineAB, TestCase::kAllDifferent_ComparisonExpecation);
1706 // Dashing ignores inverse.
1707 dashInvAB.compare(r, dashAB, TestCase::kAllSame_ComparisonExpecation);
1708
1709 REPORTER_ASSERT(r, strokeInvAB.baseShape().asLine(pts, &inverted) && inverted &&
1710 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1711 REPORTER_ASSERT(r, hairlineInvAB.baseShape().asLine(pts, &inverted) && inverted &&
1712 pts[0] == canonicalPts[0] && pts[1] == canonicalPts[1]);
1713 // Dashing ignores inverse.
1714 REPORTER_ASSERT(r, dashInvAB.baseShape().asLine(pts, &inverted) && !inverted &&
1715 pts[0] == kA && pts[1] == kB);
1716
1717}
1718
bsalomon0ae36a22016-07-18 07:31:13 -07001719static void test_stroked_lines(skiatest::Reporter* r) {
1720 // Paints to try
1721 SkPaint buttCap;
1722 buttCap.setStyle(SkPaint::kStroke_Style);
1723 buttCap.setStrokeWidth(4);
1724 buttCap.setStrokeCap(SkPaint::kButt_Cap);
1725
1726 SkPaint squareCap = buttCap;
1727 squareCap.setStrokeCap(SkPaint::kSquare_Cap);
1728
1729 SkPaint roundCap = buttCap;
1730 roundCap.setStrokeCap(SkPaint::kRound_Cap);
1731
1732 // vertical
1733 SkPath linePath;
1734 linePath.moveTo(4, 4);
1735 linePath.lineTo(4, 5);
1736
1737 SkPaint fill;
1738
bsalomona395f7c2016-08-24 17:47:40 -07001739 TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 4, 6, 5), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001740 TestCase::kAllSame_ComparisonExpecation);
1741
bsalomona395f7c2016-08-24 17:47:40 -07001742 TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 7), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001743 TestCase::kAllSame_ComparisonExpecation);
1744
bsalomona395f7c2016-08-24 17:47:40 -07001745 TestCase(r, linePath, roundCap).compare(r,
1746 TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 7), 2, 2), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001747 TestCase::kAllSame_ComparisonExpecation);
1748
1749 // horizontal
1750 linePath.reset();
1751 linePath.moveTo(4, 4);
1752 linePath.lineTo(5, 4);
1753
bsalomona395f7c2016-08-24 17:47:40 -07001754 TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeLTRB(4, 2, 5, 6), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001755 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001756 TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 7, 6), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001757 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001758 TestCase(r, linePath, roundCap).compare(r,
1759 TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 7, 6), 2, 2), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001760 TestCase::kAllSame_ComparisonExpecation);
1761
1762 // point
1763 linePath.reset();
1764 linePath.moveTo(4, 4);
1765 linePath.lineTo(4, 4);
1766
bsalomona395f7c2016-08-24 17:47:40 -07001767 TestCase(r, linePath, buttCap).compare(r, TestCase(r, SkRect::MakeEmpty(), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001768 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001769 TestCase(r, linePath, squareCap).compare(r, TestCase(r, SkRect::MakeLTRB(2, 2, 6, 6), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001770 TestCase::kAllSame_ComparisonExpecation);
bsalomona395f7c2016-08-24 17:47:40 -07001771 TestCase(r, linePath, roundCap).compare(r,
1772 TestCase(r, SkRRect::MakeRectXY(SkRect::MakeLTRB(2, 2, 6, 6), 2, 2), fill),
bsalomon0ae36a22016-07-18 07:31:13 -07001773 TestCase::kAllSame_ComparisonExpecation);
1774}
1775
bsalomon67fa4e32016-09-21 08:26:57 -07001776static void test_short_path_keys(skiatest::Reporter* r) {
1777 SkPaint paints[4];
1778 paints[1].setStyle(SkPaint::kStroke_Style);
1779 paints[1].setStrokeWidth(5.f);
1780 paints[2].setStyle(SkPaint::kStroke_Style);
1781 paints[2].setStrokeWidth(0.f);
1782 paints[3].setStyle(SkPaint::kStrokeAndFill_Style);
1783 paints[3].setStrokeWidth(5.f);
1784
bsalomonaa840642016-09-23 12:09:16 -07001785 auto compare = [r, &paints] (const SkPath& pathA, const SkPath& pathB,
bsalomon67fa4e32016-09-21 08:26:57 -07001786 TestCase::ComparisonExpecation expectation) {
bsalomonaa840642016-09-23 12:09:16 -07001787 SkPath volatileA = pathA;
1788 SkPath volatileB = pathB;
1789 volatileA.setIsVolatile(true);
1790 volatileB.setIsVolatile(true);
bsalomon67fa4e32016-09-21 08:26:57 -07001791 for (const SkPaint& paint : paints) {
bsalomonaa840642016-09-23 12:09:16 -07001792 REPORTER_ASSERT(r, !GrShape(volatileA, paint).hasUnstyledKey());
1793 REPORTER_ASSERT(r, !GrShape(volatileB, paint).hasUnstyledKey());
bsalomon67fa4e32016-09-21 08:26:57 -07001794 for (PathGeo::Invert invert : {PathGeo::Invert::kNo, PathGeo::Invert::kYes}) {
bsalomonaa840642016-09-23 12:09:16 -07001795 TestCase caseA(PathGeo(pathA, invert), paint, r);
1796 TestCase caseB(PathGeo(pathB, invert), paint, r);
1797 caseA.compare(r, caseB, expectation);
bsalomon67fa4e32016-09-21 08:26:57 -07001798 }
1799 }
1800 };
1801
1802 SkPath pathA;
1803 SkPath pathB;
1804
1805 // Two identical paths
1806 pathA.lineTo(10.f, 10.f);
1807 pathA.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
1808
1809 pathB.lineTo(10.f, 10.f);
1810 pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
bsalomonaa840642016-09-23 12:09:16 -07001811 compare(pathA, pathB, TestCase::kAllSame_ComparisonExpecation);
bsalomon67fa4e32016-09-21 08:26:57 -07001812
1813 // Give path b a different point
1814 pathB.reset();
1815 pathB.lineTo(10.f, 10.f);
1816 pathB.conicTo(21.f, 20.f, 20.f, 30.f, 0.7f);
bsalomonaa840642016-09-23 12:09:16 -07001817 compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
bsalomon67fa4e32016-09-21 08:26:57 -07001818
1819 // Give path b a different conic weight
1820 pathB.reset();
1821 pathB.lineTo(10.f, 10.f);
1822 pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f);
bsalomonaa840642016-09-23 12:09:16 -07001823 compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
bsalomon67fa4e32016-09-21 08:26:57 -07001824
1825 // Give path b an extra lineTo verb
1826 pathB.reset();
1827 pathB.lineTo(10.f, 10.f);
1828 pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.6f);
1829 pathB.lineTo(50.f, 50.f);
bsalomonaa840642016-09-23 12:09:16 -07001830 compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
bsalomon67fa4e32016-09-21 08:26:57 -07001831
1832 // Give path b a close
1833 pathB.reset();
1834 pathB.lineTo(10.f, 10.f);
1835 pathB.conicTo(20.f, 20.f, 20.f, 30.f, 0.7f);
1836 pathB.close();
bsalomonaa840642016-09-23 12:09:16 -07001837 compare(pathA, pathB, TestCase::kAllDifferent_ComparisonExpecation);
bsalomon67fa4e32016-09-21 08:26:57 -07001838}
1839
bsalomon47cc7692016-04-26 12:56:00 -07001840DEF_TEST(GrShape, reporter) {
bsalomona395f7c2016-08-24 17:47:40 -07001841 SkTArray<std::unique_ptr<Geo>> geos;
1842 SkTArray<std::unique_ptr<RRectPathGeo>> rrectPathGeos;
1843
bsalomonee295642016-06-06 14:01:25 -07001844 for (auto r : { SkRect::MakeWH(10, 20),
1845 SkRect::MakeWH(-10, -20),
1846 SkRect::MakeWH(-10, 20),
1847 SkRect::MakeWH(10, -20)}) {
bsalomona395f7c2016-08-24 17:47:40 -07001848 geos.emplace_back(new RectGeo(r));
1849 SkPath rectPath;
1850 rectPath.addRect(r);
1851 geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
1852 PathGeo::Invert::kNo));
1853 geos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
1854 PathGeo::Invert::kYes));
1855 rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, r, RRectPathGeo::RRectForStroke::kYes,
1856 PathGeo::Invert::kNo));
bsalomonee295642016-06-06 14:01:25 -07001857 }
bsalomon47cc7692016-04-26 12:56:00 -07001858 for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)),
bsalomonee295642016-06-06 14:01:25 -07001859 SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4),
1860 SkRRect::MakeOval(SkRect::MakeWH(20, 20))}) {
bsalomona395f7c2016-08-24 17:47:40 -07001861 geos.emplace_back(new RRectGeo(rr));
bsalomon70493962016-06-10 08:05:14 -07001862 test_rrect(reporter, rr);
bsalomona395f7c2016-08-24 17:47:40 -07001863 SkPath rectPath;
1864 rectPath.addRRect(rr);
1865 geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
1866 PathGeo::Invert::kNo));
1867 geos.emplace_back(new RRectPathGeo(rectPath, rr, RRectPathGeo::RRectForStroke::kYes,
1868 PathGeo::Invert::kYes));
1869 rrectPathGeos.emplace_back(new RRectPathGeo(rectPath, rr,
1870 RRectPathGeo::RRectForStroke::kYes,
1871 PathGeo::Invert::kNo));
bsalomon72dc51c2016-04-27 06:46:23 -07001872 }
1873
bsalomon72dc51c2016-04-27 06:46:23 -07001874 SkPath openRectPath;
1875 openRectPath.moveTo(0, 0);
1876 openRectPath.lineTo(10, 0);
1877 openRectPath.lineTo(10, 10);
1878 openRectPath.lineTo(0, 10);
bsalomona395f7c2016-08-24 17:47:40 -07001879 geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
1880 RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kNo));
1881 geos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
1882 RRectPathGeo::RRectForStroke::kNo, PathGeo::Invert::kYes));
1883 rrectPathGeos.emplace_back(new RRectPathGeo(openRectPath, SkRect::MakeWH(10, 10),
1884 RRectPathGeo::RRectForStroke::kNo,
1885 PathGeo::Invert::kNo));
bsalomon72dc51c2016-04-27 06:46:23 -07001886
1887 SkPath quadPath;
1888 quadPath.quadTo(10, 10, 5, 8);
bsalomona395f7c2016-08-24 17:47:40 -07001889 geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kNo));
1890 geos.emplace_back(new PathGeo(quadPath, PathGeo::Invert::kYes));
bsalomon398e3f42016-06-13 10:22:48 -07001891
1892 SkPath linePath;
1893 linePath.lineTo(10, 10);
bsalomona395f7c2016-08-24 17:47:40 -07001894 geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kNo));
1895 geos.emplace_back(new PathGeo(linePath, PathGeo::Invert::kYes));
bsalomon72dc51c2016-04-27 06:46:23 -07001896
bsalomon0ae36a22016-07-18 07:31:13 -07001897 // Horizontal and vertical paths become rrects when stroked.
1898 SkPath vLinePath;
1899 vLinePath.lineTo(0, 10);
bsalomona395f7c2016-08-24 17:47:40 -07001900 geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kNo));
1901 geos.emplace_back(new PathGeo(vLinePath, PathGeo::Invert::kYes));
bsalomon0ae36a22016-07-18 07:31:13 -07001902
1903 SkPath hLinePath;
1904 hLinePath.lineTo(10, 0);
bsalomona395f7c2016-08-24 17:47:40 -07001905 geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kNo));
1906 geos.emplace_back(new PathGeo(hLinePath, PathGeo::Invert::kYes));
bsalomon0ae36a22016-07-18 07:31:13 -07001907
bsalomona395f7c2016-08-24 17:47:40 -07001908 for (int i = 0; i < geos.count(); ++i) {
1909 test_basic(reporter, *geos[i]);
1910 test_scale(reporter, *geos[i]);
1911 test_dash_fill(reporter, *geos[i]);
1912 test_null_dash(reporter, *geos[i]);
1913 // Test modifying various stroke params.
1914 test_stroke_param<SkScalar>(
1915 reporter, *geos[i],
bsalomon70493962016-06-10 08:05:14 -07001916 [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
1917 SkIntToScalar(2), SkIntToScalar(4));
bsalomona395f7c2016-08-24 17:47:40 -07001918 test_stroke_join(reporter, *geos[i]);
1919 test_stroke_cap(reporter, *geos[i]);
1920 test_miter_limit(reporter, *geos[i]);
1921 test_path_effect_makes_rrect(reporter, *geos[i]);
1922 test_unknown_path_effect(reporter, *geos[i]);
1923 test_path_effect_makes_empty_shape(reporter, *geos[i]);
1924 test_path_effect_fails(reporter, *geos[i]);
1925 test_make_hairline_path_effect(reporter, *geos[i]);
1926 test_volatile_path(reporter, *geos[i]);
bsalomon70493962016-06-10 08:05:14 -07001927 }
bsalomonfd32df72016-06-14 14:37:21 -07001928
bsalomona395f7c2016-08-24 17:47:40 -07001929 for (int i = 0; i < rrectPathGeos.count(); ++i) {
1930 const RRectPathGeo& rrgeo = *rrectPathGeos[i];
bsalomon72dc51c2016-04-27 06:46:23 -07001931 SkPaint fillPaint;
bsalomona395f7c2016-08-24 17:47:40 -07001932 TestCase fillPathCase(reporter, rrgeo.path(), fillPaint);
bsalomon72dc51c2016-04-27 06:46:23 -07001933 SkRRect rrect;
bsalomona395f7c2016-08-24 17:47:40 -07001934 REPORTER_ASSERT(reporter, rrgeo.isNonPath(fillPaint) ==
bsalomon70493962016-06-10 08:05:14 -07001935 fillPathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
1936 nullptr));
bsalomona395f7c2016-08-24 17:47:40 -07001937 if (rrgeo.isNonPath(fillPaint)) {
1938 TestCase fillPathCase2(reporter, rrgeo.path(), fillPaint);
1939 REPORTER_ASSERT(reporter, rrect == rrgeo.rrect());
1940 TestCase fillRRectCase(reporter, rrect, fillPaint);
bsalomon70493962016-06-10 08:05:14 -07001941 fillPathCase2.compare(reporter, fillRRectCase,
1942 TestCase::kAllSame_ComparisonExpecation);
bsalomon72dc51c2016-04-27 06:46:23 -07001943 }
bsalomon72dc51c2016-04-27 06:46:23 -07001944 SkPaint strokePaint;
1945 strokePaint.setStrokeWidth(3.f);
1946 strokePaint.setStyle(SkPaint::kStroke_Style);
bsalomona395f7c2016-08-24 17:47:40 -07001947 TestCase strokePathCase(reporter, rrgeo.path(), strokePaint);
1948 if (rrgeo.isNonPath(strokePaint)) {
bsalomon0ae36a22016-07-18 07:31:13 -07001949 REPORTER_ASSERT(reporter, strokePathCase.baseShape().asRRect(&rrect, nullptr, nullptr,
1950 nullptr));
bsalomona395f7c2016-08-24 17:47:40 -07001951 REPORTER_ASSERT(reporter, rrect == rrgeo.rrect());
1952 TestCase strokeRRectCase(reporter, rrect, strokePaint);
bsalomon72dc51c2016-04-27 06:46:23 -07001953 strokePathCase.compare(reporter, strokeRRectCase,
bsalomonee295642016-06-06 14:01:25 -07001954 TestCase::kAllSame_ComparisonExpecation);
bsalomon72dc51c2016-04-27 06:46:23 -07001955 }
bsalomon47cc7692016-04-26 12:56:00 -07001956 }
bsalomon409ed732016-04-27 12:36:02 -07001957
bsalomon4eeccc92016-04-27 13:30:25 -07001958 // Test a volatile empty path.
bsalomona395f7c2016-08-24 17:47:40 -07001959 test_volatile_path(reporter, PathGeo(SkPath(), PathGeo::Invert::kNo));
bsalomon4eeccc92016-04-27 13:30:25 -07001960
bsalomon409ed732016-04-27 12:36:02 -07001961 test_empty_shape(reporter);
bsalomon0a0f67e2016-06-28 11:56:42 -07001962
1963 test_lines(reporter);
bsalomon0ae36a22016-07-18 07:31:13 -07001964
1965 test_stroked_lines(reporter);
bsalomon67fa4e32016-09-21 08:26:57 -07001966
1967 test_short_path_keys(reporter);
bsalomon47cc7692016-04-26 12:56:00 -07001968}
1969
1970#endif