blob: 8c81c51ed32447fea39b0cedbfc0087d3bf7f864 [file] [log] [blame]
bsalomonc885dac2016-04-25 13:37:22 -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"
13#include "SkPath.h"
14#include "SkDashPathEffect.h"
15
16namespace {
17class TestCase {
18public:
19 TestCase(const SkRRect& rrect, const SkPaint& paint) : fBase(rrect, paint) {
20 this->init();
21 }
22
23 struct SelfExpectations {
24 bool fPEHasEffect;
25 bool fPEHasValidKey;
26 bool fStrokeApplies;
27 };
28
29 void testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const;
30
31 enum ComparisonExpecation {
32 kAllDifferent_ComparisonExpecation,
33 kSameUpToPE_ComparisonExpecation,
34 kSameUpToStroke_ComparisonExpecation,
35 kAllSame_ComparisonExpecation,
36 };
37
38 void compare(skiatest::Reporter*, const TestCase& that, ComparisonExpecation) const;
39
40private:
41 void init() {
42 fAppliedPE = fBase.applyPathEffect();
43 fAppliedPEThenStroke = fAppliedPE.applyFullStyle();
44 fAppliedFull = fBase.applyFullStyle();
45
46 fBaseKeyIsValid = MakeKey(&fBaseKey, fBase);
47 fAppliedPEKeyIsValid = MakeKey(&fAppliedPEKey, fAppliedPE);
48 fAppliedPEThenStrokeKeyIsValid = MakeKey(&fAppliedPEThenStrokeKey, fAppliedPEThenStroke);
49 fAppliedFullKeyIsValid = MakeKey(&fAppliedFullKey, fAppliedFull);
50 }
51
52 using Key = SkTArray<uint32_t>;
53
54 static bool MakeKey(Key* key, const GrShape& shape) {
55 int size = shape.unstyledKeySize();
56 if (size <= 0) {
57 return false;
58 }
59 key->reset(size);
60 shape.writeUnstyledKey(key->begin());
61 return true;
62 }
63
64 GrShape fBase;
65 GrShape fAppliedPE;
66 GrShape fAppliedPEThenStroke;
67 GrShape fAppliedFull;
68
69 Key fBaseKey;
70 Key fAppliedPEKey;
71 Key fAppliedPEThenStrokeKey;
72 Key fAppliedFullKey;
73
74 bool fBaseKeyIsValid;
75 bool fAppliedPEKeyIsValid;
76 bool fAppliedPEThenStrokeKeyIsValid;
77 bool fAppliedFullKeyIsValid;
78};
79
80void TestCase::testExpectations(skiatest::Reporter* reporter, SelfExpectations expectations) const {
81 // Applying the path effect and then the stroke should always be the same as applying
82 // both in one go.
83 REPORTER_ASSERT(reporter, fAppliedPEThenStrokeKey == fAppliedFullKey);
84 // The base's key should always be valid (unless the path is volatile)
85 REPORTER_ASSERT(reporter, fBaseKeyIsValid);
86 if (expectations.fPEHasEffect) {
87 REPORTER_ASSERT(reporter, fBaseKey != fAppliedPEKey);
88 REPORTER_ASSERT(reporter, expectations.fPEHasEffect == fAppliedPEKeyIsValid);
89 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
90 REPORTER_ASSERT(reporter, expectations.fPEHasEffect == fAppliedFullKeyIsValid);
91 if (expectations.fStrokeApplies && expectations.fPEHasValidKey) {
92 REPORTER_ASSERT(reporter, fAppliedPEKey != fAppliedFullKey);
93 REPORTER_ASSERT(reporter, expectations.fPEHasEffect == fAppliedFullKeyIsValid);
94 }
95 } else {
96 REPORTER_ASSERT(reporter, fBaseKey == fAppliedPEKey);
97 if (expectations.fStrokeApplies) {
98 REPORTER_ASSERT(reporter, fBaseKey != fAppliedFullKey);
99 } else {
100 REPORTER_ASSERT(reporter, fBaseKey == fAppliedFullKey);
101 }
102 }
103}
104
105void TestCase::compare(skiatest::Reporter* reporter, const TestCase& that,
106 ComparisonExpecation expectation) const {
107 switch (expectation) {
108 case kAllDifferent_ComparisonExpecation:
109 REPORTER_ASSERT(reporter, fBaseKey != that.fBaseKey);
110 REPORTER_ASSERT(reporter, fAppliedPEKey != that.fAppliedPEKey);
111 REPORTER_ASSERT(reporter, fAppliedFullKey != that.fAppliedFullKey);
112 break;
113 case kSameUpToPE_ComparisonExpecation:
114 REPORTER_ASSERT(reporter, fBaseKey == that.fBaseKey);
115 REPORTER_ASSERT(reporter, fAppliedPEKey != that.fAppliedPEKey);
116 REPORTER_ASSERT(reporter, fAppliedFullKey != that.fAppliedFullKey);
117 break;
118 case kSameUpToStroke_ComparisonExpecation:
119 REPORTER_ASSERT(reporter, fBaseKey == that.fBaseKey);
120 REPORTER_ASSERT(reporter, fAppliedPEKey == that.fAppliedPEKey);
121 REPORTER_ASSERT(reporter, fAppliedFullKey != that.fAppliedFullKey);
122 break;
123 case kAllSame_ComparisonExpecation:
124 REPORTER_ASSERT(reporter, fBaseKey == that.fBaseKey);
125 REPORTER_ASSERT(reporter, fAppliedPEKey == that.fAppliedPEKey);
126 REPORTER_ASSERT(reporter, fAppliedFullKey == that.fAppliedFullKey);
127 break;
128 }
129}
130} // namespace
131
132static sk_sp<SkPathEffect> make_dash() {
133 static const SkScalar kIntervals[] = { 0.25, 3.f, 0.5, 2.f };
134 static const SkScalar kPhase = 0.75;
135 return SkDashPathEffect::Make(kIntervals, SK_ARRAY_COUNT(kIntervals), kPhase);
136}
137
138static sk_sp<SkPathEffect> make_null_dash() {
139 static const SkScalar kNullIntervals[] = {0, 0, 0, 0, 0, 0};
140 return SkDashPathEffect::Make(kNullIntervals, SK_ARRAY_COUNT(kNullIntervals), 0.f);
141}
142
143static void test_basic(skiatest::Reporter* reporter, const SkRRect& rrect) {
144 sk_sp<SkPathEffect> dashPE = make_dash();
145
146 TestCase::SelfExpectations expectations;
147 SkPaint fill;
148
149 TestCase fillCase(rrect, fill);
150 expectations.fPEHasEffect = false;
151 expectations.fPEHasValidKey = false;
152 expectations.fStrokeApplies = false;
153 fillCase.testExpectations(reporter, expectations);
154 // Test that another GrShape instance built from the same primitive is the same.
155 TestCase(rrect, fill).compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
156
157 SkPaint stroke2RoundBevel;
158 stroke2RoundBevel.setStyle(SkPaint::kStroke_Style);
159 stroke2RoundBevel.setStrokeCap(SkPaint::kRound_Cap);
160 stroke2RoundBevel.setStrokeJoin(SkPaint::kBevel_Join);
161 stroke2RoundBevel.setStrokeWidth(2.f);
162 TestCase stroke2RoundBevelCase(rrect, stroke2RoundBevel);
163 expectations.fPEHasValidKey = true;
164 expectations.fPEHasEffect = false;
165 expectations.fStrokeApplies = true;
166 stroke2RoundBevelCase.testExpectations(reporter, expectations);
167 TestCase(rrect, stroke2RoundBevel).compare(reporter, stroke2RoundBevelCase,
168 TestCase::kAllSame_ComparisonExpecation);
169
170 SkPaint stroke2RoundBevelDash = stroke2RoundBevel;
171 stroke2RoundBevelDash.setPathEffect(make_dash());
172 TestCase stroke2RoundBevelDashCase(rrect, stroke2RoundBevelDash);
173 expectations.fPEHasValidKey = true;
174 expectations.fPEHasEffect = true;
175 expectations.fStrokeApplies = true;
176 stroke2RoundBevelDashCase.testExpectations(reporter, expectations);
177 TestCase(rrect, stroke2RoundBevelDash).compare(reporter, stroke2RoundBevelDashCase,
178 TestCase::kAllSame_ComparisonExpecation);
179
180 fillCase.compare(reporter, stroke2RoundBevelCase,
181 TestCase::kSameUpToStroke_ComparisonExpecation);
182 fillCase.compare(reporter, stroke2RoundBevelDashCase,
183 TestCase::kSameUpToPE_ComparisonExpecation);
184 stroke2RoundBevelCase.compare(reporter, stroke2RoundBevelDashCase,
185 TestCase::kSameUpToPE_ComparisonExpecation);
186}
187
188template <typename T>
189static void test_stroke_param(skiatest::Reporter* reporter, const SkRRect& rrect,
190 std::function<void(SkPaint*, T)> setter, T a, T b) {
191 // Set the stroke width so that we don't get hairline. However, call the function second so that
192 // it can override.
193 SkPaint strokeA;
194 strokeA.setStyle(SkPaint::kStroke_Style);
195 strokeA.setStrokeWidth(2.f);
196 setter(&strokeA, a);
197 SkPaint strokeB;
198 strokeB.setStyle(SkPaint::kStroke_Style);
199 strokeB.setStrokeWidth(2.f);
200 setter(&strokeB, b);
201
202 TestCase strokeACase(rrect, strokeA);
203 TestCase strokeBCase(rrect, strokeB);
204 strokeACase.compare(reporter, strokeBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
205
206 // Make sure stroking params don't affect fill style.
207 SkPaint fillA = strokeA, fillB = strokeB;
208 fillA.setStyle(SkPaint::kFill_Style);
209 fillB.setStyle(SkPaint::kFill_Style);
210 TestCase fillACase(rrect, fillA);
211 TestCase fillBCase(rrect, fillB);
212 fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
213
214 // Make sure just applying the dash but not stroke gives the same key for both stroking
215 // variations.
216 SkPaint dashA = strokeA, dashB = strokeB;
217 dashA.setPathEffect(make_dash());
218 dashB.setPathEffect(make_dash());
219 TestCase dashACase(rrect, dashA);
220 TestCase dashBCase(rrect, dashB);
221 dashACase.compare(reporter, dashBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
222}
223
224static void test_miter_limit(skiatest::Reporter* reporter, const SkRRect& rrect) {
225 // Miter limit should only matter when stroking with miter joins. It shouldn't affect other
226 // joins or fills.
227 SkPaint miterA;
228 miterA.setStyle(SkPaint::kStroke_Style);
229 miterA.setStrokeWidth(2.f);
230 miterA.setStrokeJoin(SkPaint::kMiter_Join);
231 miterA.setStrokeMiter(0.5f);
232 SkPaint miterB = miterA;
233 miterA.setStrokeMiter(0.6f);
234
235 TestCase miterACase(rrect, miterA);
236 TestCase miterBCase(rrect, miterB);
237 miterACase.compare(reporter, miterBCase, TestCase::kSameUpToStroke_ComparisonExpecation);
238
239 SkPaint noMiterA = miterA, noMiterB = miterB;
240 noMiterA.setStrokeJoin(SkPaint::kRound_Join);
241 noMiterB.setStrokeJoin(SkPaint::kRound_Join);
242 TestCase noMiterACase(rrect, noMiterA);
243 TestCase noMiterBCase(rrect, noMiterB);
244 noMiterACase.compare(reporter, noMiterBCase, TestCase::kAllSame_ComparisonExpecation);
245
246 SkPaint fillA = miterA, fillB = miterB;
247 fillA.setStyle(SkPaint::kFill_Style);
248 fillB.setStyle(SkPaint::kFill_Style);
249 TestCase fillACase(rrect, fillA);
250 TestCase fillBCase(rrect, fillB);
251 fillACase.compare(reporter, fillBCase, TestCase::kAllSame_ComparisonExpecation);
252}
253
254static void test_dash_fill(skiatest::Reporter* reporter, const SkRRect& rrect) {
255 // A dash with no stroke should have no effect
256 for (auto md : {make_dash, make_null_dash}) {
257 SkPaint dashFill;
258 dashFill.setPathEffect(md());
259 TestCase dashFillCase(rrect, dashFill);
260
261 TestCase fillCase(rrect, SkPaint());
262 dashFillCase.compare(reporter, fillCase, TestCase::kAllSame_ComparisonExpecation);
263 }
264}
265
266void test_null_dash(skiatest::Reporter* reporter, const SkRRect& rrect) {
267 SkPaint fill;
268 SkPaint stroke;
269 stroke.setStyle(SkPaint::kStroke_Style);
270 stroke.setStrokeWidth(1.f);
271 SkPaint dash;
272 dash.setStyle(SkPaint::kStroke_Style);
273 dash.setStrokeWidth(1.f);
274 dash.setPathEffect(make_dash());
275 SkPaint nullDash;
276 nullDash.setStyle(SkPaint::kStroke_Style);
277 nullDash.setStrokeWidth(1.f);
278 nullDash.setPathEffect(make_null_dash());
279
280 TestCase fillCase(rrect, fill);
281 TestCase strokeCase(rrect, stroke);
282 TestCase dashCase(rrect, dash);
283 TestCase nullDashCase(rrect, nullDash);
284
285 nullDashCase.compare(reporter, fillCase, TestCase::kSameUpToStroke_ComparisonExpecation);
286 nullDashCase.compare(reporter, strokeCase, TestCase::kAllSame_ComparisonExpecation);
287 nullDashCase.compare(reporter, dashCase, TestCase::kSameUpToPE_ComparisonExpecation);
288}
289
290DEF_TEST(GrShape, reporter) {
291 sk_sp<SkPathEffect> dashPE = make_dash();
292
293 for (auto rr : { SkRRect::MakeRect(SkRect::MakeWH(10, 10)),
294 SkRRect::MakeRectXY(SkRect::MakeWH(10, 10), 3, 4)}) {
295 test_basic(reporter, rr);
296 test_dash_fill(reporter, rr);
297 test_null_dash(reporter, rr);
298 // Test modifying various stroke params.
299 test_stroke_param<SkScalar>(
300 reporter, rr,
301 [](SkPaint* p, SkScalar w) { p->setStrokeWidth(w);},
302 SkIntToScalar(2), SkIntToScalar(4));
303 test_stroke_param<SkPaint::Cap>(
304 reporter, rr,
305 [](SkPaint* p, SkPaint::Cap c) { p->setStrokeCap(c);},
306 SkPaint::kButt_Cap, SkPaint::kRound_Cap);
307 test_stroke_param<SkPaint::Join>(
308 reporter, rr,
309 [](SkPaint* p, SkPaint::Join j) { p->setStrokeJoin(j);},
310 SkPaint::kMiter_Join, SkPaint::kRound_Join);
311 test_miter_limit(reporter, rr);
312 }
313}
314
315#endif