1 /*
2 * Copyright 2015 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 "gm.h"
9 #include "SkCanvas.h"
10 #include "SkPath.h"
11
12 namespace {
13 // Concave test
test_concave(SkCanvas * canvas,const SkPaint & paint)14 void test_concave(SkCanvas* canvas, const SkPaint& paint) {
15 SkPath path;
16 canvas->translate(0, 0);
17 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
18 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
19 path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
20 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
21 canvas->drawPath(path, paint);
22 }
23
24 // Reverse concave test
test_reverse_concave(SkCanvas * canvas,const SkPaint & paint)25 void test_reverse_concave(SkCanvas* canvas, const SkPaint& paint) {
26 SkPath path;
27 canvas->save();
28 canvas->translate(100, 0);
29 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
30 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
31 path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
32 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
33 canvas->drawPath(path, paint);
34 canvas->restore();
35 }
36
37 // Bowtie (intersection)
test_bowtie(SkCanvas * canvas,const SkPaint & paint)38 void test_bowtie(SkCanvas* canvas, const SkPaint& paint) {
39 SkPath path;
40 canvas->save();
41 canvas->translate(200, 0);
42 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
43 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
44 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
45 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
46 canvas->drawPath(path, paint);
47 canvas->restore();
48 }
49
50 // "fake" bowtie (concave, but no intersection)
test_fake_bowtie(SkCanvas * canvas,const SkPaint & paint)51 void test_fake_bowtie(SkCanvas* canvas, const SkPaint& paint) {
52 SkPath path;
53 canvas->save();
54 canvas->translate(300, 0);
55 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
56 path.lineTo(SkIntToScalar(50), SkIntToScalar(40));
57 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
58 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
59 path.lineTo(SkIntToScalar(50), SkIntToScalar(60));
60 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
61 canvas->drawPath(path, paint);
62 canvas->restore();
63 }
64
65 // Bowtie with a smaller right hand lobe. The outer vertex of the left hand
66 // lobe intrudes into the interior of the right hand lobe.
test_intruding_vertex(SkCanvas * canvas,const SkPaint & paint)67 void test_intruding_vertex(SkCanvas* canvas, const SkPaint& paint) {
68 SkPath path;
69 canvas->save();
70 canvas->translate(400, 0);
71 path.setIsVolatile(true);
72 path.moveTo(20, 20);
73 path.lineTo(50, 50);
74 path.lineTo(68, 20);
75 path.lineTo(68, 80);
76 path.lineTo(50, 50);
77 path.lineTo(20, 80);
78 canvas->drawPath(path, paint);
79 canvas->restore();
80 }
81
82 // A shape with an edge that becomes inverted on AA stroking and that also contains
83 // a repeated start/end vertex.
test_inversion_repeat_vertex(SkCanvas * canvas,const SkPaint & paint)84 void test_inversion_repeat_vertex(SkCanvas* canvas, const SkPaint& paint) {
85 SkPath path;
86 canvas->save();
87 canvas->translate(400, 100);
88 path.setIsVolatile(true);
89 path.moveTo(80, 50);
90 path.lineTo(40, 80);
91 path.lineTo(60, 20);
92 path.lineTo(20, 20);
93 path.lineTo(39.99f, 80);
94 path.lineTo(80, 50);
95 canvas->drawPath(path, paint);
96 canvas->restore();
97 }
98
99 // Fish test (intersection/concave)
test_fish(SkCanvas * canvas,const SkPaint & paint)100 void test_fish(SkCanvas* canvas, const SkPaint& paint) {
101 SkPath path;
102 canvas->save();
103 canvas->translate(0, 100);
104 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
105 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
106 path.lineTo(SkIntToScalar(70), SkIntToScalar(50));
107 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
108 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
109 path.lineTo(SkIntToScalar(0), SkIntToScalar(50));
110 canvas->drawPath(path, paint);
111 canvas->restore();
112 }
113
114 // Overlapping "Fast-forward" icon: tests coincidence of inner and outer
115 // vertices generated by intersection.
test_fast_forward(SkCanvas * canvas,const SkPaint & paint)116 void test_fast_forward(SkCanvas* canvas, const SkPaint& paint) {
117 SkPath path;
118 canvas->save();
119 canvas->translate(100, 100);
120 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
121 path.lineTo(SkIntToScalar(60), SkIntToScalar(50));
122 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
123 path.moveTo(SkIntToScalar(40), SkIntToScalar(20));
124 path.lineTo(SkIntToScalar(40), SkIntToScalar(80));
125 path.lineTo(SkIntToScalar(80), SkIntToScalar(50));
126 canvas->drawPath(path, paint);
127 canvas->restore();
128 }
129
130 // Square polygon with a square hole.
test_hole(SkCanvas * canvas,const SkPaint & paint)131 void test_hole(SkCanvas* canvas, const SkPaint& paint) {
132 SkPath path;
133 canvas->save();
134 canvas->translate(200, 100);
135 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
136 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
137 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
138 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
139 path.moveTo(SkIntToScalar(30), SkIntToScalar(30));
140 path.lineTo(SkIntToScalar(30), SkIntToScalar(70));
141 path.lineTo(SkIntToScalar(70), SkIntToScalar(70));
142 path.lineTo(SkIntToScalar(70), SkIntToScalar(30));
143 canvas->drawPath(path, paint);
144 canvas->restore();
145 }
146
147 // Star test (self-intersecting)
test_star(SkCanvas * canvas,const SkPaint & paint)148 void test_star(SkCanvas* canvas, const SkPaint& paint) {
149 SkPath path;
150 canvas->save();
151 canvas->translate(300, 100);
152 path.moveTo(30, 20);
153 path.lineTo(50, 80);
154 path.lineTo(70, 20);
155 path.lineTo(20, 57);
156 path.lineTo(80, 57);
157 path.close();
158 canvas->drawPath(path, paint);
159 canvas->restore();
160 }
161
162 // Exercise a case where the intersection is below a bottom edge.
test_twist(SkCanvas * canvas,const SkPaint & paint)163 void test_twist(SkCanvas* canvas, const SkPaint& paint) {
164 SkPath path;
165 canvas->save();
166 path.moveTo( 0.5, 6);
167 path.lineTo(5.8070392608642578125, 6.4612660408020019531);
168 path.lineTo(-2.9186885356903076172, 2.811046600341796875);
169 path.lineTo(0.49999994039535522461, -1.4124038219451904297);
170 canvas->translate(420, 220);
171 canvas->scale(10, 10);
172 canvas->drawPath(path, paint);
173 canvas->restore();
174 }
175
176 // Stairstep with repeated vert (intersection)
test_stairstep(SkCanvas * canvas,const SkPaint & paint)177 void test_stairstep(SkCanvas* canvas, const SkPaint& paint) {
178 SkPath path;
179 canvas->save();
180 canvas->translate(0, 200);
181 path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
182 path.lineTo(SkIntToScalar(50), SkIntToScalar(20));
183 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
184 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
185 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
186 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
187 canvas->drawPath(path, paint);
188 canvas->restore();
189 }
190
test_stairstep2(SkCanvas * canvas,const SkPaint & paint)191 void test_stairstep2(SkCanvas* canvas, const SkPaint& paint) {
192 SkPath path;
193 canvas->save();
194 canvas->translate(100, 200);
195 path.moveTo(20, 60);
196 path.lineTo(35, 80);
197 path.lineTo(50, 60);
198 path.lineTo(65, 80);
199 path.lineTo(80, 60);
200 canvas->drawPath(path, paint);
201 canvas->restore();
202 }
203
204 // Overlapping segments
test_overlapping(SkCanvas * canvas,const SkPaint & paint)205 void test_overlapping(SkCanvas* canvas, const SkPaint& paint) {
206 SkPath path;
207 canvas->save();
208 canvas->translate(200, 200);
209 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
210 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
211 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
212 path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
213 canvas->drawPath(path, paint);
214 canvas->restore();
215 }
216
217 // Two "island" triangles inside a containing rect.
218 // This exercises the partnering code in the tessellator.
test_partners(SkCanvas * canvas,const SkPaint & paint)219 void test_partners(SkCanvas* canvas, const SkPaint& paint) {
220 SkPath path;
221 canvas->save();
222 canvas->translate(300, 200);
223 path.moveTo(20, 80);
224 path.lineTo(80, 80);
225 path.lineTo(80, 20);
226 path.lineTo(20, 20);
227 path.moveTo(30, 30);
228 path.lineTo(45, 50);
229 path.lineTo(30, 70);
230 path.moveTo(70, 30);
231 path.lineTo(70, 70);
232 path.lineTo(55, 50);
233 canvas->drawPath(path, paint);
234 canvas->restore();
235 }
236
237 // Monotone test 1 (point in the middle)
test_monotone_1(SkCanvas * canvas,const SkPaint & paint)238 void test_monotone_1(SkCanvas* canvas, const SkPaint& paint) {
239 SkPath path;
240 canvas->save();
241 canvas->translate(0, 300);
242 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
243 path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
244 SkIntToScalar(80), SkIntToScalar(50));
245 path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
246 SkIntToScalar(20), SkIntToScalar(80));
247 canvas->drawPath(path, paint);
248 canvas->restore();
249 }
250
251 // Monotone test 2 (point at the top)
test_monotone_2(SkCanvas * canvas,const SkPaint & paint)252 void test_monotone_2(SkCanvas* canvas, const SkPaint& paint) {
253 SkPath path;
254 canvas->save();
255 canvas->translate(100, 300);
256 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
257 path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
258 path.quadTo(SkIntToScalar(20), SkIntToScalar(20),
259 SkIntToScalar(20), SkIntToScalar(80));
260 canvas->drawPath(path, paint);
261 canvas->restore();
262 }
263
264 // Monotone test 3 (point at the bottom)
test_monotone_3(SkCanvas * canvas,const SkPaint & paint)265 void test_monotone_3(SkCanvas* canvas, const SkPaint& paint) {
266 SkPath path;
267 canvas->save();
268 canvas->translate(200, 300);
269 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
270 path.lineTo(SkIntToScalar(80), SkIntToScalar(70));
271 path.quadTo(SkIntToScalar(20), SkIntToScalar(80),
272 SkIntToScalar(20), SkIntToScalar(20));
273 canvas->drawPath(path, paint);
274 canvas->restore();
275 }
276
277 // Monotone test 4 (merging of two monotones)
test_monotone_4(SkCanvas * canvas,const SkPaint & paint)278 void test_monotone_4(SkCanvas* canvas, const SkPaint& paint) {
279 SkPath path;
280 canvas->save();
281 canvas->translate(300, 300);
282 path.moveTo(80, 25);
283 path.lineTo(50, 39);
284 path.lineTo(20, 25);
285 path.lineTo(40, 45);
286 path.lineTo(70, 50);
287 path.lineTo(80, 80);
288 canvas->drawPath(path, paint);
289 canvas->restore();
290 }
291
292 // Monotone test 5 (aborted merging of two monotones)
test_monotone_5(SkCanvas * canvas,const SkPaint & paint)293 void test_monotone_5(SkCanvas* canvas, const SkPaint& paint) {
294 SkPath path;
295 canvas->save();
296 canvas->translate(0, 400);
297 path.moveTo(50, 20);
298 path.lineTo(80, 80);
299 path.lineTo(50, 50);
300 path.lineTo(20, 80);
301 canvas->drawPath(path, paint);
302 canvas->restore();
303 }
304 // Degenerate intersection test
test_degenerate(SkCanvas * canvas,const SkPaint & paint)305 void test_degenerate(SkCanvas* canvas, const SkPaint& paint) {
306 SkPath path;
307 canvas->save();
308 canvas->translate(100, 400);
309 path.moveTo(50, 20);
310 path.lineTo(70, 30);
311 path.lineTo(20, 50);
312 path.moveTo(50, 20);
313 path.lineTo(80, 80);
314 path.lineTo(50, 80);
315 canvas->drawPath(path, paint);
316 canvas->restore();
317 }
318 // Two triangles with a coincident edge.
test_coincident_edge(SkCanvas * canvas,const SkPaint & paint)319 void test_coincident_edge(SkCanvas* canvas, const SkPaint& paint) {
320 SkPath path;
321 canvas->save();
322 canvas->translate(200, 400);
323
324 path.moveTo(80, 20);
325 path.lineTo(80, 80);
326 path.lineTo(20, 80);
327
328 path.moveTo(20, 20);
329 path.lineTo(80, 80);
330 path.lineTo(20, 80);
331
332 canvas->drawPath(path, paint);
333 canvas->restore();
334 }
335 // Bowtie with a coincident triangle (one triangle vertex coincident with the
336 // bowtie's intersection).
test_bowtie_coincident_triangle(SkCanvas * canvas,const SkPaint & paint)337 void test_bowtie_coincident_triangle(SkCanvas* canvas, const SkPaint& paint) {
338 SkPath path;
339 canvas->save();
340 canvas->translate(300, 400);
341 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
342 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
343 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
344 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
345 path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
346 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
347 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
348 canvas->drawPath(path, paint);
349 canvas->restore();
350 }
351
352 // Coincident edges (big ones first, coincident vert on top).
test_coincident_edges_1(SkCanvas * canvas,const SkPaint & paint)353 void test_coincident_edges_1(SkCanvas* canvas, const SkPaint& paint) {
354 SkPath path;
355 canvas->save();
356 canvas->translate(0, 500);
357 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
358 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
359 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
360 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
361 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
362 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
363 canvas->drawPath(path, paint);
364 canvas->restore();
365 }
366 // Coincident edges (small ones first, coincident vert on top).
test_coincident_edges_2(SkCanvas * canvas,const SkPaint & paint)367 void test_coincident_edges_2(SkCanvas* canvas, const SkPaint& paint) {
368 SkPath path;
369 canvas->save();
370 canvas->translate(100, 500);
371 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
372 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
373 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
374 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
375 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
376 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
377 canvas->drawPath(path, paint);
378 canvas->restore();
379 }
380 // Coincident edges (small ones first, coincident vert on bottom).
test_coincident_edges_3(SkCanvas * canvas,const SkPaint & paint)381 void test_coincident_edges_3(SkCanvas* canvas, const SkPaint& paint) {
382 SkPath path;
383 canvas->save();
384 canvas->translate(200, 500);
385 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
386 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
387 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
388 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
389 path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
390 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
391 canvas->drawPath(path, paint);
392 canvas->restore();
393 }
394 // Coincident edges (big ones first, coincident vert on bottom).
test_coincident_edges_4(SkCanvas * canvas,const SkPaint & paint)395 void test_coincident_edges_4(SkCanvas* canvas, const SkPaint& paint) {
396 SkPath path;
397 canvas->save();
398 canvas->translate(300, 500);
399 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
400 path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
401 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
402 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
403 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
404 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
405 canvas->drawPath(path, paint);
406 canvas->restore();
407 }
408
409 };
410
411 DEF_SIMPLE_GM(concavepaths, canvas, 500, 600) {
412 SkPaint paint;
413
414 paint.setAntiAlias(true);
415 paint.setStyle(SkPaint::kFill_Style);
416
417 test_concave(canvas, paint);
418 test_reverse_concave(canvas, paint);
419 test_bowtie(canvas, paint);
420 test_fake_bowtie(canvas, paint);
421 test_intruding_vertex(canvas, paint);
422 test_fish(canvas, paint);
423 test_fast_forward(canvas, paint);
424 test_hole(canvas, paint);
425 test_star(canvas, paint);
426 test_twist(canvas, paint);
427 test_inversion_repeat_vertex(canvas, paint);
428 test_stairstep(canvas, paint);
429 test_stairstep2(canvas, paint);
430 test_overlapping(canvas, paint);
431 test_partners(canvas, paint);
432 test_monotone_1(canvas, paint);
433 test_monotone_2(canvas, paint);
434 test_monotone_3(canvas, paint);
435 test_monotone_4(canvas, paint);
436 test_monotone_5(canvas, paint);
437 test_degenerate(canvas, paint);
438 test_coincident_edge(canvas, paint);
439 test_bowtie_coincident_triangle(canvas, paint);
440 test_coincident_edges_1(canvas, paint);
441 test_coincident_edges_2(canvas, paint);
442 test_coincident_edges_3(canvas, paint);
443 test_coincident_edges_4(canvas, paint);
444 }
445