blob: 0e83b697d86819af1d9d5af30374fee537fb4876 [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"
16#include "SkSurface.h"
bsalomon47cc7692016-04-26 12:56:00 -070017
bsalomon72dc51c2016-04-27 06:46:23 -070018using Key = SkTArray<uint32_t>;
19
20static bool make_key(Key* key, const GrShape& shape) {
21 int size = shape.unstyledKeySize();
22 if (size <= 0) {
23 key->reset(0);
24 return false;
25 }
26 SkASSERT(size);
27 key->reset(size);
28 shape.writeUnstyledKey(key->begin());
29 return true;
30}
31
bsalomon9fb42032016-05-13 09:23:38 -070032static bool test_bounds_by_rasterizing(const SkPath& path, const SkRect& bounds) {
33 static constexpr int kRes = 2000;
34 // This tolerance is in units of 1/kRes fractions of the bounds width/height.
35 static constexpr int kTol = 0;
36 GR_STATIC_ASSERT(kRes % 4 == 0);
37 SkImageInfo info = SkImageInfo::MakeA8(kRes, kRes);
38 sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
39 surface->getCanvas()->clear(0x0);
40 SkRect clip = SkRect::MakeXYWH(kRes/4, kRes/4, kRes/2, kRes/2);
41 SkMatrix matrix;
42 matrix.setRectToRect(bounds, clip, SkMatrix::kFill_ScaleToFit);
43 clip.outset(SkIntToScalar(kTol), SkIntToScalar(kTol));
44 surface->getCanvas()->clipRect(clip, SkRegion::kDifference_Op);
45 surface->getCanvas()->concat(matrix);
46 SkPaint whitePaint;
47 whitePaint.setColor(SK_ColorWHITE);
48 surface->getCanvas()->drawPath(path, whitePaint);
49 SkPixmap pixmap;
50 surface->getCanvas()->peekPixels(&pixmap);
51#if defined(SK_BUILD_FOR_WIN)
52 // The static constexpr version in #else causes cl.exe to crash.
53 const uint8_t* kZeros = reinterpret_cast<uint8_t*>(calloc(kRes, 1));
54#else
55 static constexpr uint8_t kZeros[kRes] = {0};
56#endif
57 for (int y = 0; y < kRes/4; ++y) {
58 const uint8_t* row = pixmap.addr8(0, y);
59 if (0 != memcmp(kZeros, row, kRes)) {
60 return false;
61 }
62 }
63#ifdef SK_BUILD_FOR_WIN
64 free(const_cast<uint8_t*>(kZeros));
65#endif
66 return true;
67}
bsalomon72dc51c2016-04-27 06:46:23 -070068
bsalomon9fb42032016-05-13 09:23:38 -070069namespace {
bsalomon47cc7692016-04-26 12:56:00 -070070class TestCase {
71public:
bsalomon72dc51c2016-04-27 06:46:23 -070072 template <typename GEO>
bsalomon97fd2d42016-05-09 13:02:01 -070073 TestCase(const GEO& geo, const SkPaint& paint, skiatest::Reporter* r,
74 SkScalar scale = SK_Scalar1) : fBase(geo, paint) {
75 this->init(r, scale);
bsalomon47cc7692016-04-26 12:56:00 -070076 }
77
78 struct SelfExpectations {
79 bool fPEHasEffect;
80 bool fPEHasValidKey;
81 bool fStrokeApplies;
82 };
83
84 void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const;
85
86 enum ComparisonExpecation {
87 kAllDifferent_ComparisonExpecation,
88 kSameUpToPE_ComparisonExpecation,
89 kSameUpToStroke_ComparisonExpecation,
90 kAllSame_ComparisonExpecation,
91 };
92
93 void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const;
94
bsalomon72dc51c2016-04-27 06:46:23 -070095 const GrShape& baseShape() const { return fBase; }
96 const GrShape& appliedPathEffectShape() const { return fAppliedPE; }
97 const GrShape& appliedFullStyleShape() const { return fAppliedFull; }
98
99 // The returned array's count will be 0 if the key shape has no key.
100 const Key& baseKey() const { return fBaseKey; }
101 const Key& appliedPathEffectKey() const { return fAppliedPEKey; }
102 const Key& appliedFullStyleKey() const { return fAppliedFullKey; }
bsalomon409ed732016-04-27 12:36:02 -0700103 const Key& appliedPathEffectThenStrokeKey() const { return fAppliedPEThenStrokeKey; }
bsalomon72dc51c2016-04-27 06:46:23 -0700104
bsalomon47cc7692016-04-26 12:56:00 -0700105private:
bsalomon9fb42032016-05-13 09:23:38 -0700106 static void CheckBounds(skiatest::Reporter* r, const GrShape& shape, const SkRect& bounds) {
107 SkPath path;
108 shape.asPath(&path);
109 // If the bounds are empty, the path ought to be as well.
110 if (bounds.isEmpty()) {
111 REPORTER_ASSERT(r, path.isEmpty());
112 return;
113 }
114 if (path.isEmpty()) {
115 return;
116 }
117 REPORTER_ASSERT(r, test_bounds_by_rasterizing(path, bounds));
118 }
119
bsalomon97fd2d42016-05-09 13:02:01 -0700120 void init(skiatest::Reporter* r, SkScalar scale) {
121 fAppliedPE = fBase.applyStyle(GrStyle::Apply::kPathEffectOnly, scale);
122 fAppliedPEThenStroke = fAppliedPE.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec,
123 scale);
124 fAppliedFull = fBase.applyStyle(GrStyle::Apply::kPathEffectAndStrokeRec, scale);
bsalomon47cc7692016-04-26 12:56:00 -0700125
bsalomon72dc51c2016-04-27 06:46:23 -0700126 make_key(&fBaseKey, fBase);
127 make_key(&fAppliedPEKey, fAppliedPE);
128 make_key(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
129 make_key(&fAppliedFullKey, fAppliedFull);
bsalomonfb083272016-05-04 08:27:41 -0700130
131 // Applying the path effect and then the stroke should always be the same as applying
132 // both in one go.
133 REPORTER_ASSERT(r, fAppliedPEThenStrokeKey == fAppliedFullKey);
134 SkPath a, b;
135 fAppliedPEThenStroke.asPath(&a);
136 fAppliedFull.asPath(&b);
137 REPORTER_ASSERT(r, a == b);
bsalomon7c73a532016-05-11 15:15:56 -0700138 REPORTER_ASSERT(r, fAppliedFull.isEmpty() == fAppliedPEThenStroke.isEmpty());
139
140 SkPath path;
141 fBase.asPath(&path);
142 REPORTER_ASSERT(r, path.isEmpty() == fBase.isEmpty());
143 fAppliedPE.asPath(&path);
144 REPORTER_ASSERT(r, path.isEmpty() == fAppliedPE.isEmpty());
145 fAppliedFull.asPath(&path);
146 REPORTER_ASSERT(r, path.isEmpty() == fAppliedFull.isEmpty());
bsalomonfb083272016-05-04 08:27:41 -0700147
bsalomon9fb42032016-05-13 09:23:38 -0700148 CheckBounds(r, fBase, fBase.bounds());
149 CheckBounds(r, fAppliedPE, fAppliedPE.bounds());
150 CheckBounds(r, fAppliedPEThenStroke, fAppliedPEThenStroke.bounds());
151 CheckBounds(r, fAppliedFull, fAppliedFull.bounds());
152 SkRect styledBounds;
153 fBase.styledBounds(&styledBounds);
154 CheckBounds(r, fAppliedFull, styledBounds);
155 fAppliedPE.styledBounds(&styledBounds);
156 CheckBounds(r, fAppliedFull, styledBounds);
157
bsalomonfb083272016-05-04 08:27:41 -0700158 // Check that the same path is produced when style is applied by GrShape and GrStyle.
159 SkPath preStyle;
160 SkPath postPathEffect;
161 SkPath postAllStyle;
162
163 fBase.asPath(&preStyle);
bsalomon1a0b9ed2016-05-06 11:07:03 -0700164 SkStrokeRec postPEStrokeRec(SkStrokeRec::kFill_InitStyle);
bsalomon97fd2d42016-05-09 13:02:01 -0700165 if (fBase.style().applyPathEffectToPath(&postPathEffect, &postPEStrokeRec, preStyle,
166 scale)) {
bsalomon1a0b9ed2016-05-06 11:07:03 -0700167 // run postPathEffect through GrShape to get any geometry reductions that would have
168 // occurred to fAppliedPE.
169 GrShape(postPathEffect, GrStyle(postPEStrokeRec, nullptr)).asPath(&postPathEffect);
170
bsalomonfb083272016-05-04 08:27:41 -0700171 SkPath testPath;
172 fAppliedPE.asPath(&testPath);
173 REPORTER_ASSERT(r, testPath == postPathEffect);
bsalomon1a0b9ed2016-05-06 11:07:03 -0700174 REPORTER_ASSERT(r, postPEStrokeRec.hasEqualEffect(fAppliedPE.style().strokeRec()));
bsalomonfb083272016-05-04 08:27:41 -0700175 }
176 SkStrokeRec::InitStyle fillOrHairline;
bsalomon97fd2d42016-05-09 13:02:01 -0700177 if (fBase.style().applyToPath(&postAllStyle, &fillOrHairline, preStyle, scale)) {
bsalomon1a0b9ed2016-05-06 11:07:03 -0700178 // run postPathEffect through GrShape to get any reductions that would have occurred
179 // to fAppliedFull.
180 GrShape(postAllStyle, GrStyle(fillOrHairline)).asPath(&postAllStyle);
181
bsalomonfb083272016-05-04 08:27:41 -0700182 SkPath testPath;
183 fAppliedFull.asPath(&testPath);
184 REPORTER_ASSERT(r, testPath == postAllStyle);
185 if (fillOrHairline == SkStrokeRec::kFill_InitStyle) {
186 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleFill());
187 } else {
188 REPORTER_ASSERT(r, fAppliedFull.style().isSimpleHairline());
189 }
190 }
bsalomon47cc7692016-04-26 12:56:00 -0700191 }
192
193 GrShape fBase;
194 GrShape fAppliedPE;
195 GrShape fAppliedPEThenStroke;
196 GrShape fAppliedFull;
197
198 Key fBaseKey;
199 Key fAppliedPEKey;
200 Key fAppliedPEThenStrokeKey;
201 Key fAppliedFullKey;
bsalomon47cc7692016-04-26 12:56:00 -0700202};
203
204void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const {
bsalomon47cc7692016-04-26 12:56:00 -0700205 // The base's key should always be valid (unless the path is volatile)
bsalomon72dc51c2016-04-27 06:46:23 -0700206 REPORTER_ASSERT(reporter, fBaseKey.count());
bsalomon47cc7692016-04-26 12:56:00 -0700207 if (expectations.fPEHasEffect) {
208 REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700209 REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedPEKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700210 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700211 REPORTER_ASSERT(reporter, expectations.fPEHasValidKey == SkToBool(fAppliedFullKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700212 if (expectations.fStrokeApplies && expectations.fPEHasValidKey) {
213 REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700214 REPORTER_ASSERT(reporter, SkToBool(fAppliedFullKey.count()));
bsalomon47cc7692016-04-26 12:56:00 -0700215 }
216 } else {
217 REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey);
bsalomonfb083272016-05-04 08:27:41 -0700218 SkPath a, b;
bsalomon72dc51c2016-04-27 06:46:23 -0700219 fBase.asPath(&a);
220 fAppliedPE.asPath(&b);
221 REPORTER_ASSERT(reporter, a == b);
bsalomon47cc7692016-04-26 12:56:00 -0700222 if (expectations.fStrokeApplies) {
223 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
224 } else {
225 REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey);
226 }
227 }
228}
229
230void TestCase::compare(skiatest::Reporter* reporter, const TestCase& that,
231 ComparisonExpecation expectation) const {
bsalomon72dc51c2016-04-27 06:46:23 -0700232 SkPath a, b;
bsalomon47cc7692016-04-26 12:56:00 -0700233 switch (expectation) {
234 case kAllDifferent_ComparisonExpecation:
235 REPORTER_ASSERT(reporter, fBaseKey != that.fBaseKey);
236 REPORTER_ASSERT(reporter, fAppliedPEKey != that.fAppliedPEKey);
237 REPORTER_ASSERT(reporter, fAppliedFullKey != that.fAppliedFullKey);
238 break;
239 case kSameUpToPE_ComparisonExpecation:
240 REPORTER_ASSERT(reporter, fBaseKey == that.fBaseKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700241 fBase.asPath(&a);
242 that.fBase.asPath(&b);
243 REPORTER_ASSERT(reporter, a == b);
bsalomon7c73a532016-05-11 15:15:56 -0700244 REPORTER_ASSERT(reporter, fBase.isEmpty() == that.fBase.isEmpty());
bsalomon47cc7692016-04-26 12:56:00 -0700245 REPORTER_ASSERT(reporter, fAppliedPEKey != that.fAppliedPEKey);
246 REPORTER_ASSERT(reporter, fAppliedFullKey != that.fAppliedFullKey);
247 break;
248 case kSameUpToStroke_ComparisonExpecation:
249 REPORTER_ASSERT(reporter, fBaseKey == that.fBaseKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700250 fBase.asPath(&a);
251 that.fBase.asPath(&b);
252 REPORTER_ASSERT(reporter, a == b);
bsalomon7c73a532016-05-11 15:15:56 -0700253 REPORTER_ASSERT(reporter, fBase.isEmpty() == that.fBase.isEmpty());
bsalomon47cc7692016-04-26 12:56:00 -0700254 REPORTER_ASSERT(reporter, fAppliedPEKey == that.fAppliedPEKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700255 fAppliedPE.asPath(&a);
256 that.fAppliedPE.asPath(&b);
257 REPORTER_ASSERT(reporter, a == b);
bsalomon7c73a532016-05-11 15:15:56 -0700258 REPORTER_ASSERT(reporter, fAppliedPE.isEmpty() == that.fAppliedPE.isEmpty());
bsalomon47cc7692016-04-26 12:56:00 -0700259 REPORTER_ASSERT(reporter, fAppliedFullKey != that.fAppliedFullKey);
260 break;
261 case kAllSame_ComparisonExpecation:
262 REPORTER_ASSERT(reporter, fBaseKey == that.fBaseKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700263 fBase.asPath(&a);
264 that.fBase.asPath(&b);
265 REPORTER_ASSERT(reporter, a == b);
bsalomon7c73a532016-05-11 15:15:56 -0700266 REPORTER_ASSERT(reporter, fBase.isEmpty() == that.fBase.isEmpty());
bsalomon47cc7692016-04-26 12:56:00 -0700267 REPORTER_ASSERT(reporter, fAppliedPEKey == that.fAppliedPEKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700268 fAppliedPE.asPath(&a);
269 that.fAppliedPE.asPath(&b);
270 REPORTER_ASSERT(reporter, a == b);
bsalomon7c73a532016-05-11 15:15:56 -0700271 REPORTER_ASSERT(reporter, fAppliedPE.isEmpty() == that.fAppliedPE.isEmpty());
bsalomon47cc7692016-04-26 12:56:00 -0700272 REPORTER_ASSERT(reporter, fAppliedFullKey == that.fAppliedFullKey);
bsalomon72dc51c2016-04-27 06:46:23 -0700273 fAppliedFull.asPath(&a);
274 that.fAppliedFull.asPath(&b);
275 REPORTER_ASSERT(reporter, a == b);
bsalomon7c73a532016-05-11 15:15:56 -0700276 REPORTER_ASSERT(reporter, fAppliedFull.isEmpty() == that.fAppliedFull.isEmpty());
bsalomon47cc7692016-04-26 12:56:00 -0700277 break;
278 }
279}
280} // namespace
281
282static sk_sp<SkPathEffect> make_dash() {
283 static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f };
284 static const SkScalar kPhase = 0.75;
285 return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase);
286}
287
288static sk_sp<SkPathEffect> make_null_dash() {
289 static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0};
290 return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f);
291}
292
bsalomon72dc51c2016-04-27 06:46:23 -0700293template<typename GEO>
294static void test_basic(skiatest::Reporter* reporter, const GEO& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700295 sk_sp<SkPathEffect> dashPE = make_dash();
296
297 TestCase::SelfExpectations expectations;
298 SkPaint fill;
299
bsalomonfb083272016-05-04 08:27:41 -0700300 TestCase fillCase(geo, fill, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700301 expectations.fPEHasEffect = false;
302 expectations.fPEHasValidKey = false;
303 expectations.fStrokeApplies = false;
304 fillCase.testExpectations(reporter, expectations);
305 // Test that another GrShape instance built from the same primitive is the same.
bsalomonfb083272016-05-04 08:27:41 -0700306 TestCase(geo, fill, reporter).compare(reporter, fillCase,
307 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700308
309 SkPaint stroke2RoundBevel;
310 stroke2RoundBevel.setStyle(SkPaint::kStroke_Style);
311 stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap);
312 stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join);
313 stroke2RoundBevel.setStrokeWidth(2.f);
bsalomonfb083272016-05-04 08:27:41 -0700314 TestCase stroke2RoundBevelCase(geo, stroke2RoundBevel, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700315 expectations.fPEHasValidKey = true;
316 expectations.fPEHasEffect = false;
317 expectations.fStrokeApplies = true;
318 stroke2RoundBevelCase.testExpectations(reporter, expectations);
bsalomonfb083272016-05-04 08:27:41 -0700319 TestCase(geo, stroke2RoundBevel, reporter).compare(reporter, stroke2RoundBevelCase,
320 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700321
322 SkPaint stroke2RoundBevelDash = stroke2RoundBevel;
323 stroke2RoundBevelDash.setPathEffect(make_dash());
bsalomonfb083272016-05-04 08:27:41 -0700324 TestCase stroke2RoundBevelDashCase(geo, stroke2RoundBevelDash, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700325 expectations.fPEHasValidKey = true;
326 expectations.fPEHasEffect = true;
327 expectations.fStrokeApplies = true;
328 stroke2RoundBevelDashCase.testExpectations(reporter, expectations);
bsalomonfb083272016-05-04 08:27:41 -0700329 TestCase(geo, stroke2RoundBevelDash, reporter).compare(reporter, stroke2RoundBevelDashCase,
330 TestCase::kAllSame_ComparisonExpecation);
bsalomon47cc7692016-04-26 12:56:00 -0700331
332 fillCase.compare(reporter, stroke2RoundBevelCase,
333 TestCase::kSameUpToStroke_ComparisonExpecation);
334 fillCase.compare(reporter, stroke2RoundBevelDashCase,
335 TestCase::kSameUpToPE_ComparisonExpecation);
336 stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
337 TestCase::kSameUpToPE_ComparisonExpecation);
bsalomon72dc51c2016-04-27 06:46:23 -0700338
bsalomonf0cf3552016-05-05 08:28:30 -0700339 // Stroke and fill cases
340 SkPaint stroke2RoundBevelAndFill = stroke2RoundBevel;
341 stroke2RoundBevelAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
342 TestCase stroke2RoundBevelAndFillCase(geo, stroke2RoundBevelAndFill, reporter);
343 expectations.fPEHasValidKey = true;
344 expectations.fPEHasEffect = false;
345 expectations.fStrokeApplies = true;
346 stroke2RoundBevelAndFillCase.testExpectations(reporter, expectations);
347 TestCase(geo, stroke2RoundBevelAndFill, reporter).compare(reporter,
348 stroke2RoundBevelAndFillCase, TestCase::kAllSame_ComparisonExpecation);
349
350 SkPaint stroke2RoundBevelAndFillDash = stroke2RoundBevelDash;
351 stroke2RoundBevelAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
352 TestCase stroke2RoundBevelAndFillDashCase(geo, stroke2RoundBevelAndFillDash, reporter);
353 expectations.fPEHasValidKey = true;
354 expectations.fPEHasEffect = true;
355 expectations.fStrokeApplies = true;
356 stroke2RoundBevelAndFillDashCase.testExpectations(reporter, expectations);
357 TestCase(geo, stroke2RoundBevelAndFillDash, reporter).compare(
358 reporter, stroke2RoundBevelAndFillDashCase, TestCase::kAllSame_ComparisonExpecation);
359
360 stroke2RoundBevelAndFillCase.compare(reporter, stroke2RoundBevelCase,
361 TestCase::kSameUpToStroke_ComparisonExpecation);
362 stroke2RoundBevelAndFillDashCase.compare(reporter, stroke2RoundBevelDashCase,
363 TestCase::kSameUpToStroke_ComparisonExpecation);
364 stroke2RoundBevelAndFillCase.compare(reporter, stroke2RoundBevelAndFillDashCase,
365 TestCase::kSameUpToPE_ComparisonExpecation);
366
bsalomon72dc51c2016-04-27 06:46:23 -0700367 SkPaint hairline;
368 hairline.setStyle(SkPaint::kStroke_Style);
369 hairline.setStrokeWidth(0.f);
bsalomonfb083272016-05-04 08:27:41 -0700370 TestCase hairlineCase(geo, hairline, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700371 // Since hairline style doesn't change the SkPath data, it is keyed identically to fill.
372 hairlineCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
bsalomon9ad5d7c2016-05-04 08:44:15 -0700373 REPORTER_ASSERT(reporter, hairlineCase.baseShape().style().isSimpleHairline());
374 REPORTER_ASSERT(reporter, hairlineCase.appliedFullStyleShape().style().isSimpleHairline());
375 REPORTER_ASSERT(reporter, hairlineCase.appliedPathEffectShape().style().isSimpleHairline());
bsalomon47cc7692016-04-26 12:56:00 -0700376}
377
bsalomon97fd2d42016-05-09 13:02:01 -0700378template<typename GEO>
379static void test_scale(skiatest::Reporter* reporter, const GEO& geo) {
380 sk_sp<SkPathEffect> dashPE = make_dash();
381
382 static const SkScalar kS1 = 1.f;
383 static const SkScalar kS2 = 2.f;
384
385 SkPaint fill;
386 TestCase fillCase1(geo, fill, reporter, kS1);
387 TestCase fillCase2(geo, fill, reporter, kS2);
388 // Scale doesn't affect fills.
389 fillCase1.compare(reporter, fillCase2, TestCase::kAllSame_ComparisonExpecation);
390
391 SkPaint hairline;
392 hairline.setStyle(SkPaint::kStroke_Style);
393 hairline.setStrokeWidth(0.f);
394 TestCase hairlineCase1(geo, hairline, reporter, kS1);
395 TestCase hairlineCase2(geo, hairline, reporter, kS2);
396 // Scale doesn't affect hairlines.
397 hairlineCase1.compare(reporter, hairlineCase2, TestCase::kAllSame_ComparisonExpecation);
398
399 SkPaint stroke;
400 stroke.setStyle(SkPaint::kStroke_Style);
401 stroke.setStrokeWidth(2.f);
402 TestCase strokeCase1(geo, stroke, reporter, kS1);
403 TestCase strokeCase2(geo, stroke, reporter, kS2);
404 // Scale affects the stroke.
405 strokeCase1.compare(reporter, strokeCase2, TestCase::kSameUpToStroke_ComparisonExpecation);
406
407 SkPaint strokeDash = stroke;
408 strokeDash.setPathEffect(make_dash());
409 TestCase strokeDashCase1(geo, strokeDash, reporter, kS1);
410 TestCase strokeDashCase2(geo, strokeDash, reporter, kS2);
411 // Scale affects the dash and the stroke.
412 strokeDashCase1.compare(reporter, strokeDashCase2, TestCase::kSameUpToPE_ComparisonExpecation);
413
414 // Stroke and fill cases
415 SkPaint strokeAndFill = stroke;
416 strokeAndFill.setStyle(SkPaint::kStrokeAndFill_Style);
417 TestCase strokeAndFillCase1(geo, strokeAndFill, reporter, kS1);
418 TestCase strokeAndFillCase2(geo, strokeAndFill, reporter, kS2);
419 // Scale affects the stroke. Though, this can wind up creating a rect when the input is a rect.
420 // In that case we wind up with a pure geometry key and the geometries are the same.
421 SkRRect rrect;
422 if (strokeAndFillCase1.appliedFullStyleShape().asRRect(&rrect)) {
423 // We currently only expect to get here in the rect->rect case.
424 REPORTER_ASSERT(reporter, rrect.isRect());
425 REPORTER_ASSERT(reporter, strokeAndFillCase1.baseShape().asRRect(&rrect) && rrect.isRect());
426 strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
427 TestCase::kAllSame_ComparisonExpecation);
428 } else {
429 strokeAndFillCase1.compare(reporter, strokeAndFillCase2,
430 TestCase::kSameUpToStroke_ComparisonExpecation);
431 }
432
433 SkPaint strokeAndFillDash = strokeDash;
434 strokeAndFillDash.setStyle(SkPaint::kStrokeAndFill_Style);
435 TestCase strokeAndFillDashCase1(geo, strokeAndFillDash, reporter, kS1);
436 TestCase strokeAndFillDashCase2(geo, strokeAndFillDash, reporter, kS2);
437 // Scale affects the path effect and stroke.
438 strokeAndFillDashCase1.compare(reporter, strokeAndFillDashCase2,
439 TestCase::kSameUpToPE_ComparisonExpecation);
440}
441
bsalomon72dc51c2016-04-27 06:46:23 -0700442template <typename GEO, typename T>
bsalomon06077562016-05-04 13:50:29 -0700443static void test_stroke_param_impl(skiatest::Reporter* reporter, const GEO& geo,
444 std::function<void(SkPaint*, T)> setter, T a, T b,
445 bool paramAffectsStroke,
446 bool paramAffectsDashAndStroke) {
447 // Set the stroke width so that we don't get hairline. However, call the setter afterward so
448 // that it can override the stroke width.
bsalomon47cc7692016-04-26 12:56:00 -0700449 SkPaint strokeA;
450 strokeA.setStyle(SkPaint::kStroke_Style);
451 strokeA.setStrokeWidth(2.f);
452 setter(&strokeA, a);
453 SkPaint strokeB;
454 strokeB.setStyle(SkPaint::kStroke_Style);
455 strokeB.setStrokeWidth(2.f);
456 setter(&strokeB, b);
457
bsalomonfb083272016-05-04 08:27:41 -0700458 TestCase strokeACase(geo, strokeA, reporter);
459 TestCase strokeBCase(geo, strokeB, reporter);
bsalomon06077562016-05-04 13:50:29 -0700460 if (paramAffectsStroke) {
461 strokeACase.compare(reporter, strokeBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
462 } else {
463 strokeACase.compare(reporter, strokeBCase, TestCase::kAllSame_ComparisonExpecation);
464 }
bsalomon47cc7692016-04-26 12:56:00 -0700465
bsalomonf0cf3552016-05-05 08:28:30 -0700466 SkPaint strokeAndFillA = strokeA;
467 SkPaint strokeAndFillB = strokeB;
468 strokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style);
469 strokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style);
470 TestCase strokeAndFillACase(geo, strokeAndFillA, reporter);
471 TestCase strokeAndFillBCase(geo, strokeAndFillB, reporter);
472 if (paramAffectsStroke) {
473 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
474 TestCase::kSameUpToStroke_ComparisonExpecation);
475 } else {
476 strokeAndFillACase.compare(reporter, strokeAndFillBCase,
477 TestCase::kAllSame_ComparisonExpecation);
478 }
479
bsalomon47cc7692016-04-26 12:56:00 -0700480 // Make sure stroking params don't affect fill style.
481 SkPaint fillA = strokeA, fillB = strokeB;
482 fillA.setStyle(SkPaint::kFill_Style);
483 fillB.setStyle(SkPaint::kFill_Style);
bsalomonfb083272016-05-04 08:27:41 -0700484 TestCase fillACase(geo, fillA, reporter);
485 TestCase fillBCase(geo, fillB, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700486 fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
487
488 // Make sure just applying the dash but not stroke gives the same key for both stroking
489 // variations.
490 SkPaint dashA = strokeA, dashB = strokeB;
491 dashA.setPathEffect(make_dash());
492 dashB.setPathEffect(make_dash());
bsalomonfb083272016-05-04 08:27:41 -0700493 TestCase dashACase(geo, dashA, reporter);
494 TestCase dashBCase(geo, dashB, reporter);
bsalomon06077562016-05-04 13:50:29 -0700495 if (paramAffectsDashAndStroke) {
496 dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
497 } else {
498 dashACase.compare(reporter, dashBCase, TestCase::kAllSame_ComparisonExpecation);
499 }
bsalomonf0cf3552016-05-05 08:28:30 -0700500
501 SkPaint dashStrokeAndFillA = dashA, dashStrokeAndFillB = dashB;
502 dashStrokeAndFillA.setStyle(SkPaint::kStrokeAndFill_Style);
503 dashStrokeAndFillB.setStyle(SkPaint::kStrokeAndFill_Style);
504 TestCase dashStrokeAndFillACase(geo, dashStrokeAndFillA, reporter);
505 TestCase dashStrokeAndFillBCase(geo, dashStrokeAndFillB, reporter);
506 if (paramAffectsDashAndStroke) {
507 dashStrokeAndFillACase.compare(reporter, dashStrokeAndFillBCase,
508 TestCase::kSameUpToStroke_ComparisonExpecation);
509 } else {
510 dashStrokeAndFillACase.compare(reporter, dashStrokeAndFillBCase,
511 TestCase::kAllSame_ComparisonExpecation);
512 }
bsalomon47cc7692016-04-26 12:56:00 -0700513}
514
bsalomon06077562016-05-04 13:50:29 -0700515template <typename GEO, typename T>
516static void test_stroke_param(skiatest::Reporter* reporter, const GEO& geo,
517 std::function<void(SkPaint*, T)> setter, T a, T b) {
518 test_stroke_param_impl(reporter, geo, setter, a, b, true, true);
519};
520
521template <typename GEO>
522static void test_stroke_cap(skiatest::Reporter* reporter, const GEO& geo) {
523 GrShape shape(geo, GrStyle(SkStrokeRec::kHairline_InitStyle));
524 // The cap should only affect shapes that may be open.
525 bool affectsStroke = !shape.knownToBeClosed();
526 // Dashing adds ends that need caps.
527 bool affectsDashAndStroke = true;
528 test_stroke_param_impl<GEO, SkPaint::Cap>(
529 reporter,
530 geo,
531 [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
532 SkPaint::kButt_Cap, SkPaint::kRound_Cap,
533 affectsStroke,
534 affectsDashAndStroke);
535};
536
bsalomon72dc51c2016-04-27 06:46:23 -0700537template <typename GEO>
538static void test_miter_limit(skiatest::Reporter* reporter, const GEO& geo) {
bsalomon06077562016-05-04 13:50:29 -0700539 auto setMiterJoinAndLimit = [](SkPaint* p, SkScalar miter) {
540 p->setStrokeJoin(SkPaint::kMiter_Join);
541 p->setStrokeMiter(miter);
542 };
bsalomon47cc7692016-04-26 12:56:00 -0700543
bsalomon06077562016-05-04 13:50:29 -0700544 auto setOtherJoinAndLimit = [](SkPaint* p, SkScalar miter) {
545 p->setStrokeJoin(SkPaint::kRound_Join);
546 p->setStrokeMiter(miter);
547 };
bsalomon47cc7692016-04-26 12:56:00 -0700548
bsalomon06077562016-05-04 13:50:29 -0700549 // The miter limit should affect stroked and dashed-stroked cases when the join type is
550 // miter.
551 test_stroke_param_impl<GEO, SkScalar>(
552 reporter,
553 geo,
554 setMiterJoinAndLimit,
555 0.5f, 0.75f,
556 true,
557 true);
bsalomon47cc7692016-04-26 12:56:00 -0700558
bsalomon06077562016-05-04 13:50:29 -0700559 // The miter limit should not affect stroked and dashed-stroked cases when the join type is
560 // not miter.
561 test_stroke_param_impl<GEO, SkScalar>(
562 reporter,
563 geo,
564 setOtherJoinAndLimit,
565 0.5f, 0.75f,
566 false,
567 false);
bsalomon47cc7692016-04-26 12:56:00 -0700568}
569
bsalomon72dc51c2016-04-27 06:46:23 -0700570template<typename GEO>
571static void test_dash_fill(skiatest::Reporter* reporter, const GEO& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700572 // A dash with no stroke should have no effect
573 using DashFactoryFn = sk_sp<SkPathEffect>(*)();
574 for (DashFactoryFn md : {&make_dash, &make_null_dash}) {
575 SkPaint dashFill;
576 dashFill.setPathEffect((*md)());
bsalomonfb083272016-05-04 08:27:41 -0700577 TestCase dashFillCase(geo, dashFill, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700578
bsalomonfb083272016-05-04 08:27:41 -0700579 TestCase fillCase(geo, SkPaint(), reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700580 dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
581 }
582}
583
bsalomon72dc51c2016-04-27 06:46:23 -0700584template<typename GEO>
585void test_null_dash(skiatest::Reporter* reporter, const GEO& geo) {
bsalomon47cc7692016-04-26 12:56:00 -0700586 SkPaint fill;
587 SkPaint stroke;
588 stroke.setStyle(SkPaint::kStroke_Style);
589 stroke.setStrokeWidth(1.f);
590 SkPaint dash;
591 dash.setStyle(SkPaint::kStroke_Style);
592 dash.setStrokeWidth(1.f);
593 dash.setPathEffect(make_dash());
594 SkPaint nullDash;
595 nullDash.setStyle(SkPaint::kStroke_Style);
596 nullDash.setStrokeWidth(1.f);
597 nullDash.setPathEffect(make_null_dash());
598
bsalomonfb083272016-05-04 08:27:41 -0700599 TestCase fillCase(geo, fill, reporter);
600 TestCase strokeCase(geo, stroke, reporter);
601 TestCase dashCase(geo, dash, reporter);
602 TestCase nullDashCase(geo, nullDash, reporter);
bsalomon47cc7692016-04-26 12:56:00 -0700603
604 nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation);
605 nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation);
606 nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation);
607}
608
bsalomon72dc51c2016-04-27 06:46:23 -0700609template <typename GEO>
610void test_path_effect_makes_rrect(skiatest::Reporter* reporter, const GEO& geo) {
611 /**
612 * This path effect takes any input path and turns it into a rrect. It passes through stroke
613 * info.
614 */
615 class RRectPathEffect : SkPathEffect {
616 public:
617 static const SkRRect& RRect() {
618 static const SkRRect kRRect = SkRRect::MakeRectXY(SkRect::MakeWH(12, 12), 3, 5);
619 return kRRect;
620 }
621
622 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
623 const SkRect* cullR) const override {
624 dst->reset();
625 dst->addRRect(RRect());
626 return true;
627 }
628 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
629 *dst = RRect().getBounds();
630 }
631 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new RRectPathEffect); }
632 Factory getFactory() const override { return nullptr; }
633 void toString(SkString*) const override {}
634 private:
635 RRectPathEffect() {}
636 };
637
638 SkPaint fill;
bsalomonfb083272016-05-04 08:27:41 -0700639 TestCase fillGeoCase(geo, fill, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700640
641 SkPaint pe;
642 pe.setPathEffect(RRectPathEffect::Make());
bsalomonfb083272016-05-04 08:27:41 -0700643 TestCase geoPECase(geo, pe, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700644
645 SkPaint peStroke;
646 peStroke.setPathEffect(RRectPathEffect::Make());
647 peStroke.setStrokeWidth(2.f);
648 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -0700649 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700650
651 fillGeoCase.compare(reporter, geoPECase, TestCase::kSameUpToPE_ComparisonExpecation);
652 fillGeoCase.compare(reporter, geoPEStrokeCase, TestCase::kSameUpToPE_ComparisonExpecation);
653 geoPECase.compare(reporter, geoPEStrokeCase,
654 TestCase::kSameUpToStroke_ComparisonExpecation);
655
bsalomonfb083272016-05-04 08:27:41 -0700656 TestCase rrectFillCase(RRectPathEffect::RRect(), fill, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700657 SkPaint stroke = peStroke;
658 stroke.setPathEffect(nullptr);
bsalomonfb083272016-05-04 08:27:41 -0700659 TestCase rrectStrokeCase(RRectPathEffect::RRect(), stroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700660
661 SkRRect rrect;
662 // Applying the path effect should make a SkRRect shape. There is no further stroking in the
663 // geoPECase, so the full style should be the same as just the PE.
664 REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectShape().asRRect(&rrect));
665 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
666 REPORTER_ASSERT(reporter, geoPECase.appliedPathEffectKey() == rrectFillCase.baseKey());
667
668 REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleShape().asRRect(&rrect));
669 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
670 REPORTER_ASSERT(reporter, geoPECase.appliedFullStyleKey() == rrectFillCase.baseKey());
671
672 // In the PE+stroke case applying the full style should be the same as just stroking the rrect.
673 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().asRRect(&rrect));
674 REPORTER_ASSERT(reporter, rrect == RRectPathEffect::RRect());
675 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == rrectFillCase.baseKey());
676
677 REPORTER_ASSERT(reporter, !geoPEStrokeCase.appliedFullStyleShape().asRRect(&rrect));
678 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() ==
679 rrectStrokeCase.appliedFullStyleKey());
680}
681
682template <typename GEO>
683void test_unknown_path_effect(skiatest::Reporter* reporter, const GEO& geo) {
684 /**
685 * This path effect just adds two lineTos to the input path.
686 */
687 class AddLineTosPathEffect : SkPathEffect {
688 public:
689 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
690 const SkRect* cullR) const override {
691 *dst = src;
692 dst->lineTo(0, 0);
693 dst->lineTo(10, 10);
694 return true;
695 }
696 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
697 *dst = src;
698 dst->growToInclude(0, 0);
699 dst->growToInclude(10, 10);
700 }
701 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new AddLineTosPathEffect); }
702 Factory getFactory() const override { return nullptr; }
703 void toString(SkString*) const override {}
704 private:
705 AddLineTosPathEffect() {}
706 };
707
bsalomon9ad5d7c2016-05-04 08:44:15 -0700708 // This path effect should make the keys invalid when it is applied. We only produce a path
bsalomon72dc51c2016-04-27 06:46:23 -0700709 // effect key for dash path effects. So the only way another arbitrary path effect can produce
710 // a styled result with a key is to produce a non-path shape that has a purely geometric key.
711 SkPaint peStroke;
712 peStroke.setPathEffect(AddLineTosPathEffect::Make());
713 peStroke.setStrokeWidth(2.f);
714 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -0700715 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700716 TestCase::SelfExpectations expectations;
717 expectations.fPEHasEffect = true;
718 expectations.fPEHasValidKey = false;
719 expectations.fStrokeApplies = true;
720 geoPEStrokeCase.testExpectations(reporter, expectations);
721}
722
bsalomon9ad5d7c2016-05-04 08:44:15 -0700723template <typename GEO>
724void test_make_hairline_path_effect(skiatest::Reporter* reporter, const GEO& geo, bool isNonPath) {
725 /**
726 * This path effect just changes the stroke rec to hairline.
727 */
728 class MakeHairlinePathEffect : SkPathEffect {
729 public:
730 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec* strokeRec,
731 const SkRect* cullR) const override {
732 *dst = src;
733 strokeRec->setHairlineStyle();
734 return true;
735 }
736 void computeFastBounds(SkRect* dst, const SkRect& src) const override { *dst = src; }
737 static sk_sp<SkPathEffect> Make() {
738 return sk_sp<SkPathEffect>(new MakeHairlinePathEffect);
739 }
740 Factory getFactory() const override { return nullptr; }
741 void toString(SkString*) const override {}
742 private:
743 MakeHairlinePathEffect() {}
744 };
745
746 SkPaint fill;
747 SkPaint pe;
748 pe.setPathEffect(MakeHairlinePathEffect::Make());
749
750 TestCase peCase(geo, pe, reporter);
751
752 SkPath a, b;
753 peCase.baseShape().asPath(&a);
754 peCase.appliedPathEffectShape().asPath(&b);
755 REPORTER_ASSERT(reporter, a == b);
756 peCase.appliedFullStyleShape().asPath(&b);
757 REPORTER_ASSERT(reporter, a == b);
758 REPORTER_ASSERT(reporter, peCase.appliedPathEffectShape().style().isSimpleHairline());
759 REPORTER_ASSERT(reporter, peCase.appliedFullStyleShape().style().isSimpleHairline());
760 if (isNonPath) {
761 REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey() == peCase.baseKey());
762 REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey() == peCase.baseKey());
763 } else {
764 REPORTER_ASSERT(reporter, peCase.appliedPathEffectKey().empty());
765 REPORTER_ASSERT(reporter, peCase.appliedFullStyleKey().empty());
766 }
767}
768
bsalomon4eeccc92016-04-27 13:30:25 -0700769/**
770 * isNonPath indicates whether the initial shape made from the path is expected to be recognized
771 * as a simpler shape type (e.g. rrect)
772 */
773void test_volatile_path(skiatest::Reporter* reporter, const SkPath& path,
774 bool isNonPath) {
775 SkPath vPath(path);
776 vPath.setIsVolatile(true);
777
778 SkPaint dashAndStroke;
779 dashAndStroke.setPathEffect(make_dash());
780 dashAndStroke.setStrokeWidth(2.f);
781 dashAndStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -0700782 TestCase volatileCase(vPath, dashAndStroke, reporter);
bsalomon4eeccc92016-04-27 13:30:25 -0700783 // We expect a shape made from a volatile path to have a key iff the shape is recognized
784 // as a specialized geometry.
785 if (isNonPath) {
786 REPORTER_ASSERT(reporter, SkToBool(volatileCase.baseKey().count()));
787 // In this case all the keys should be identical to the non-volatile case.
bsalomonfb083272016-05-04 08:27:41 -0700788 TestCase nonVolatileCase(path, dashAndStroke, reporter);
bsalomon4eeccc92016-04-27 13:30:25 -0700789 volatileCase.compare(reporter, nonVolatileCase, TestCase::kAllSame_ComparisonExpecation);
790 } else {
791 // None of the keys should be valid.
792 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.baseKey().count()));
793 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectKey().count()));
794 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedFullStyleKey().count()));
795 REPORTER_ASSERT(reporter, !SkToBool(volatileCase.appliedPathEffectThenStrokeKey().count()));
796 }
797}
798
bsalomon409ed732016-04-27 12:36:02 -0700799template <typename GEO>
800void test_path_effect_makes_empty_shape(skiatest::Reporter* reporter, const GEO& geo) {
801 /**
802 * This path effect returns an empty path.
803 */
804 class EmptyPathEffect : SkPathEffect {
805 public:
806 bool filterPath(SkPath* dst, const SkPath& src, SkStrokeRec*,
807 const SkRect* cullR) const override {
808 dst->reset();
809 return true;
810 }
811 void computeFastBounds(SkRect* dst, const SkRect& src) const override {
812 dst->setEmpty();
813 }
814 static sk_sp<SkPathEffect> Make() { return sk_sp<SkPathEffect>(new EmptyPathEffect); }
815 Factory getFactory() const override { return nullptr; }
816 void toString(SkString*) const override {}
817 private:
818 EmptyPathEffect() {}
819 };
820
821 SkPath emptyPath;
822 GrShape emptyShape(emptyPath);
823 Key emptyKey;
824 make_key(&emptyKey, emptyShape);
bsalomon7c73a532016-05-11 15:15:56 -0700825 REPORTER_ASSERT(reporter, emptyShape.isEmpty());
bsalomon409ed732016-04-27 12:36:02 -0700826
827 SkPaint pe;
828 pe.setPathEffect(EmptyPathEffect::Make());
bsalomonfb083272016-05-04 08:27:41 -0700829 TestCase geoCase(geo, pe, reporter);
bsalomon409ed732016-04-27 12:36:02 -0700830 REPORTER_ASSERT(reporter, geoCase.appliedFullStyleKey() == emptyKey);
831 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectKey() == emptyKey);
832 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectThenStrokeKey() == emptyKey);
bsalomon7c73a532016-05-11 15:15:56 -0700833 REPORTER_ASSERT(reporter, geoCase.appliedPathEffectShape().isEmpty());
834 REPORTER_ASSERT(reporter, geoCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -0700835
836 SkPaint peStroke;
837 peStroke.setPathEffect(EmptyPathEffect::Make());
838 peStroke.setStrokeWidth(2.f);
839 peStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -0700840 TestCase geoPEStrokeCase(geo, peStroke, reporter);
bsalomon409ed732016-04-27 12:36:02 -0700841 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleKey() == emptyKey);
842 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectKey() == emptyKey);
843 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectThenStrokeKey() == emptyKey);
bsalomon7c73a532016-05-11 15:15:56 -0700844 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedPathEffectShape().isEmpty());
845 REPORTER_ASSERT(reporter, geoPEStrokeCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -0700846}
847
848void test_empty_shape(skiatest::Reporter* reporter) {
849 SkPath emptyPath;
850 SkPaint fill;
bsalomonfb083272016-05-04 08:27:41 -0700851 TestCase fillEmptyCase(emptyPath, fill, reporter);
bsalomon7c73a532016-05-11 15:15:56 -0700852 REPORTER_ASSERT(reporter, fillEmptyCase.baseShape().isEmpty());
853 REPORTER_ASSERT(reporter, fillEmptyCase.appliedPathEffectShape().isEmpty());
854 REPORTER_ASSERT(reporter, fillEmptyCase.appliedFullStyleShape().isEmpty());
bsalomon409ed732016-04-27 12:36:02 -0700855
856 Key emptyKey(fillEmptyCase.baseKey());
857 REPORTER_ASSERT(reporter, emptyKey.count());
858 TestCase::SelfExpectations expectations;
859 expectations.fStrokeApplies = false;
860 expectations.fPEHasEffect = false;
861 // This will test whether applying style preserves emptiness
862 fillEmptyCase.testExpectations(reporter, expectations);
863
864 // Stroking an empty path should have no effect
865 SkPath emptyPath2;
866 SkPaint stroke;
867 stroke.setStrokeWidth(2.f);
868 stroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -0700869 TestCase strokeEmptyCase(emptyPath2, stroke, reporter);
bsalomon409ed732016-04-27 12:36:02 -0700870 strokeEmptyCase.compare(reporter, fillEmptyCase, TestCase::kAllSame_ComparisonExpecation);
871
872 // Dashing and stroking an empty path should have no effect
873 SkPath emptyPath3;
874 SkPaint dashAndStroke;
875 dashAndStroke.setPathEffect(make_dash());
876 dashAndStroke.setStrokeWidth(2.f);
877 dashAndStroke.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -0700878 TestCase dashAndStrokeEmptyCase(emptyPath3, dashAndStroke, reporter);
bsalomon409ed732016-04-27 12:36:02 -0700879 dashAndStrokeEmptyCase.compare(reporter, fillEmptyCase,
880 TestCase::kAllSame_ComparisonExpecation);
bsalomon5e410b42016-04-28 09:30:46 -0700881
882 // A shape made from an empty rrect should behave the same as an empty path.
883 SkRRect emptyRRect = SkRRect::MakeRect(SkRect::MakeEmpty());
884 REPORTER_ASSERT(reporter, emptyRRect.getType() == SkRRect::kEmpty_Type);
bsalomonfb083272016-05-04 08:27:41 -0700885 TestCase dashAndStrokeEmptyRRectCase(emptyRRect, dashAndStroke, reporter);
bsalomon5e410b42016-04-28 09:30:46 -0700886 dashAndStrokeEmptyRRectCase.compare(reporter, fillEmptyCase,
887 TestCase::kAllSame_ComparisonExpecation);
888
889 // Same for a rect.
890 SkRect emptyRect = SkRect::MakeEmpty();
bsalomonfb083272016-05-04 08:27:41 -0700891 TestCase dashAndStrokeEmptyRectCase(emptyRect, dashAndStroke, reporter);
bsalomon5e410b42016-04-28 09:30:46 -0700892 dashAndStrokeEmptyRectCase.compare(reporter, fillEmptyCase,
893 TestCase::kAllSame_ComparisonExpecation);
bsalomon409ed732016-04-27 12:36:02 -0700894}
895
bsalomon47cc7692016-04-26 12:56:00 -0700896DEF_TEST(GrShape, reporter) {
897 sk_sp<SkPathEffect> dashPE = make_dash();
898
899 for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)),
900 SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4)}) {
901 test_basic(reporter, rr);
bsalomon97fd2d42016-05-09 13:02:01 -0700902 test_scale(reporter, rr);
bsalomon47cc7692016-04-26 12:56:00 -0700903 test_dash_fill(reporter, rr);
904 test_null_dash(reporter, rr);
905 // Test modifying various stroke params.
bsalomon72dc51c2016-04-27 06:46:23 -0700906 test_stroke_param<SkRRect, SkScalar>(
bsalomon47cc7692016-04-26 12:56:00 -0700907 reporter, rr,
908 [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
909 SkIntToScalar(2), SkIntToScalar(4));
bsalomon72dc51c2016-04-27 06:46:23 -0700910 test_stroke_param<SkRRect, SkPaint::Join>(
bsalomon47cc7692016-04-26 12:56:00 -0700911 reporter, rr,
912 [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
913 SkPaint::kMiter_Join, SkPaint::kRound_Join);
bsalomon06077562016-05-04 13:50:29 -0700914 test_stroke_cap(reporter, rr);
bsalomon47cc7692016-04-26 12:56:00 -0700915 test_miter_limit(reporter, rr);
bsalomon72dc51c2016-04-27 06:46:23 -0700916 test_path_effect_makes_rrect(reporter, rr);
917 test_unknown_path_effect(reporter, rr);
bsalomon409ed732016-04-27 12:36:02 -0700918 test_path_effect_makes_empty_shape(reporter, rr);
bsalomon9ad5d7c2016-05-04 08:44:15 -0700919 test_make_hairline_path_effect(reporter, rr, true);
bsalomon72dc51c2016-04-27 06:46:23 -0700920 }
921
922 struct TestPath {
923 TestPath(const SkPath& path, bool isRRectFill, bool isRRectStroke ,const SkRRect& rrect)
924 : fPath(path)
925 , fIsRRectForFill(isRRectFill)
926 , fIsRRectForStroke(isRRectStroke)
927 , fRRect(rrect) {}
928 SkPath fPath;
929 bool fIsRRectForFill;
930 bool fIsRRectForStroke;
931 SkRRect fRRect;
932 };
933 SkTArray<TestPath> paths;
934
935 SkPath circlePath;
936 circlePath.addCircle(10, 10, 10);
937 paths.emplace_back(circlePath, true, true, SkRRect::MakeOval(SkRect::MakeWH(20,20)));
938
939 SkPath rectPath;
940 rectPath.addRect(SkRect::MakeWH(10, 10));
941 paths.emplace_back(rectPath, true, true, SkRRect::MakeRect(SkRect::MakeWH(10, 10)));
942
943 SkPath openRectPath;
944 openRectPath.moveTo(0, 0);
945 openRectPath.lineTo(10, 0);
946 openRectPath.lineTo(10, 10);
947 openRectPath.lineTo(0, 10);
948 paths.emplace_back(openRectPath, true, false, SkRRect::MakeRect(SkRect::MakeWH(10, 10)));
949
950 SkPath quadPath;
951 quadPath.quadTo(10, 10, 5, 8);
952 paths.emplace_back(quadPath, false, false, SkRRect());
953
954 for (auto testPath : paths) {
955 const SkPath& path = testPath.fPath;
956 // These tests all assume that the original GrShape for fill and stroke will be the same.
957 // However, that is not the case in special cases (e.g. a unclosed rect becomes a RRect
958 // GrShape with a fill style but becomes a Path GrShape when stroked).
959 if (testPath.fIsRRectForFill == testPath.fIsRRectForStroke) {
960 test_basic(reporter, path);
961 test_null_dash(reporter, path);
962 test_path_effect_makes_rrect(reporter, path);
963 }
bsalomon97fd2d42016-05-09 13:02:01 -0700964 test_scale(reporter, path);
bsalomon4eeccc92016-04-27 13:30:25 -0700965 // This test uses a stroking paint, hence use of fIsRRectForStroke
966 test_volatile_path(reporter, path, testPath.fIsRRectForStroke);
bsalomon72dc51c2016-04-27 06:46:23 -0700967 test_dash_fill(reporter, path);
968 // Test modifying various stroke params.
969 test_stroke_param<SkPath, SkScalar>(
970 reporter, path,
971 [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
972 SkIntToScalar(2), SkIntToScalar(4));
bsalomon72dc51c2016-04-27 06:46:23 -0700973 test_stroke_param<SkPath, SkPaint::Join>(
974 reporter, path,
975 [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
976 SkPaint::kMiter_Join, SkPaint::kRound_Join);
bsalomon06077562016-05-04 13:50:29 -0700977 test_stroke_cap(reporter, path);
bsalomon72dc51c2016-04-27 06:46:23 -0700978 test_miter_limit(reporter, path);
979 test_unknown_path_effect(reporter, path);
bsalomon409ed732016-04-27 12:36:02 -0700980 test_path_effect_makes_empty_shape(reporter, path);
bsalomon9ad5d7c2016-05-04 08:44:15 -0700981 test_make_hairline_path_effect(reporter, path, testPath.fIsRRectForStroke);
bsalomon72dc51c2016-04-27 06:46:23 -0700982
983 SkPaint fillPaint;
bsalomonfb083272016-05-04 08:27:41 -0700984 TestCase fillPathCase(path, fillPaint, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700985 SkRRect rrect;
986 REPORTER_ASSERT(reporter, testPath.fIsRRectForFill ==
987 fillPathCase.baseShape().asRRect(&rrect));
988 if (testPath.fIsRRectForFill) {
989 REPORTER_ASSERT(reporter, rrect == testPath.fRRect);
bsalomonfb083272016-05-04 08:27:41 -0700990 TestCase fillRRectCase(rrect, fillPaint, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700991 fillPathCase.compare(reporter, fillRRectCase, TestCase::kAllSame_ComparisonExpecation);
992 }
993
994 SkPaint strokePaint;
995 strokePaint.setStrokeWidth(3.f);
996 strokePaint.setStyle(SkPaint::kStroke_Style);
bsalomonfb083272016-05-04 08:27:41 -0700997 TestCase strokePathCase(path, strokePaint, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -0700998 REPORTER_ASSERT(reporter, testPath.fIsRRectForStroke ==
999 strokePathCase.baseShape().asRRect(&rrect));
1000 if (testPath.fIsRRectForStroke) {
1001 REPORTER_ASSERT(reporter, rrect == testPath.fRRect);
bsalomonfb083272016-05-04 08:27:41 -07001002 TestCase strokeRRectCase(rrect, strokePaint, reporter);
bsalomon72dc51c2016-04-27 06:46:23 -07001003 strokePathCase.compare(reporter, strokeRRectCase,
1004 TestCase::kAllSame_ComparisonExpecation);
1005 }
bsalomon47cc7692016-04-26 12:56:00 -07001006 }
bsalomon409ed732016-04-27 12:36:02 -07001007
bsalomon4eeccc92016-04-27 13:30:25 -07001008 // Test a volatile empty path.
1009 test_volatile_path(reporter, SkPath(), true);
1010
bsalomon409ed732016-04-27 12:36:02 -07001011 test_empty_shape(reporter);
bsalomon47cc7692016-04-26 12:56:00 -07001012}
1013
1014#endif