blob: adbc2c422484d07885017fc5e4f6ca9cf804dc8d [file] [log] [blame]
senorblancod6ed19c2015-02-26 06:58:17 -08001/*
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"
bungemand3ebb482015-08-05 13:57:49 -070010#include "SkPath.h"
senorblancod6ed19c2015-02-26 06:58:17 -080011
senorblancod6ed19c2015-02-26 06:58:17 -080012namespace {
13// Concave test
14void test_concave(SkCanvas* canvas, const SkPaint& paint) {
15 SkPath path;
16 canvas->translate(0, 0);
Mike Reedb6317422018-08-15 10:23:39 -040017 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
18 .lineTo(SkIntToScalar(80), SkIntToScalar(20))
19 .lineTo(SkIntToScalar(30), SkIntToScalar(30))
20 .lineTo(SkIntToScalar(20), SkIntToScalar(80));
senorblancod6ed19c2015-02-26 06:58:17 -080021 canvas->drawPath(path, paint);
22}
23
24// Reverse concave test
25void test_reverse_concave(SkCanvas* canvas, const SkPaint& paint) {
26 SkPath path;
27 canvas->save();
28 canvas->translate(100, 0);
Mike Reedb6317422018-08-15 10:23:39 -040029 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
30 .lineTo(SkIntToScalar(20), SkIntToScalar(80))
31 .lineTo(SkIntToScalar(30), SkIntToScalar(30))
32 .lineTo(SkIntToScalar(80), SkIntToScalar(20));
senorblancod6ed19c2015-02-26 06:58:17 -080033 canvas->drawPath(path, paint);
34 canvas->restore();
35}
36
37// Bowtie (intersection)
38void test_bowtie(SkCanvas* canvas, const SkPaint& paint) {
39 SkPath path;
40 canvas->save();
41 canvas->translate(200, 0);
Mike Reedb6317422018-08-15 10:23:39 -040042 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
43 .lineTo(SkIntToScalar(80), SkIntToScalar(80))
44 .lineTo(SkIntToScalar(80), SkIntToScalar(20))
45 .lineTo(SkIntToScalar(20), SkIntToScalar(80));
senorblancod6ed19c2015-02-26 06:58:17 -080046 canvas->drawPath(path, paint);
47 canvas->restore();
48}
49
50// "fake" bowtie (concave, but no intersection)
51void test_fake_bowtie(SkCanvas* canvas, const SkPaint& paint) {
52 SkPath path;
53 canvas->save();
54 canvas->translate(300, 0);
Mike Reedb6317422018-08-15 10:23:39 -040055 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
56 .lineTo(SkIntToScalar(50), SkIntToScalar(40))
57 .lineTo(SkIntToScalar(80), SkIntToScalar(20))
58 .lineTo(SkIntToScalar(80), SkIntToScalar(80))
59 .lineTo(SkIntToScalar(50), SkIntToScalar(60))
60 .lineTo(SkIntToScalar(20), SkIntToScalar(80));
senorblancod6ed19c2015-02-26 06:58:17 -080061 canvas->drawPath(path, paint);
62 canvas->restore();
63}
64
Stephen White5926f2d2017-02-13 13:55:42 -050065// 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.
67void test_intruding_vertex(SkCanvas* canvas, const SkPaint& paint) {
68 SkPath path;
69 canvas->save();
70 canvas->translate(400, 0);
71 path.setIsVolatile(true);
Mike Reedb6317422018-08-15 10:23:39 -040072 path.moveTo(20, 20)
73 .lineTo(50, 50)
74 .lineTo(68, 20)
75 .lineTo(68, 80)
76 .lineTo(50, 50)
77 .lineTo(20, 80);
Stephen White5926f2d2017-02-13 13:55:42 -050078 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.
84void test_inversion_repeat_vertex(SkCanvas* canvas, const SkPaint& paint) {
85 SkPath path;
86 canvas->save();
87 canvas->translate(400, 100);
88 path.setIsVolatile(true);
Mike Reedb6317422018-08-15 10:23:39 -040089 path.moveTo(80, 50)
90 .lineTo(40, 80)
91 .lineTo(60, 20)
92 .lineTo(20, 20)
93 .lineTo(39.99f, 80)
94 .lineTo(80, 50);
Stephen White5926f2d2017-02-13 13:55:42 -050095 canvas->drawPath(path, paint);
96 canvas->restore();
97}
98
senorblancod6ed19c2015-02-26 06:58:17 -080099// Fish test (intersection/concave)
100void test_fish(SkCanvas* canvas, const SkPaint& paint) {
101 SkPath path;
102 canvas->save();
103 canvas->translate(0, 100);
Mike Reedb6317422018-08-15 10:23:39 -0400104 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
105 .lineTo(SkIntToScalar(80), SkIntToScalar(80))
106 .lineTo(SkIntToScalar(70), SkIntToScalar(50))
107 .lineTo(SkIntToScalar(80), SkIntToScalar(20))
108 .lineTo(SkIntToScalar(20), SkIntToScalar(80))
109 .lineTo(SkIntToScalar(0), SkIntToScalar(50));
senorblancod6ed19c2015-02-26 06:58:17 -0800110 canvas->drawPath(path, paint);
111 canvas->restore();
112}
113
Stephen White2f4686f2017-01-03 16:20:01 -0500114// Overlapping "Fast-forward" icon: tests coincidence of inner and outer
115// vertices generated by intersection.
116void test_fast_forward(SkCanvas* canvas, const SkPaint& paint) {
senorblancod6ed19c2015-02-26 06:58:17 -0800117 SkPath path;
118 canvas->save();
119 canvas->translate(100, 100);
Mike Reedb6317422018-08-15 10:23:39 -0400120 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
121 .lineTo(SkIntToScalar(60), SkIntToScalar(50))
122 .lineTo(SkIntToScalar(20), SkIntToScalar(80))
123 .moveTo(SkIntToScalar(40), SkIntToScalar(20))
124 .lineTo(SkIntToScalar(40), SkIntToScalar(80))
125 .lineTo(SkIntToScalar(80), SkIntToScalar(50));
senorblancod6ed19c2015-02-26 06:58:17 -0800126 canvas->drawPath(path, paint);
127 canvas->restore();
128}
129
130// Square polygon with a square hole.
131void test_hole(SkCanvas* canvas, const SkPaint& paint) {
132 SkPath path;
133 canvas->save();
134 canvas->translate(200, 100);
Mike Reedb6317422018-08-15 10:23:39 -0400135 path.addPoly({{20,20}, {80,20}, {80,80}, {20,80}}, false)
136 .addPoly({{30,30}, {30,70}, {70,70}, {70,30}}, false);
senorblancod6ed19c2015-02-26 06:58:17 -0800137 canvas->drawPath(path, paint);
138 canvas->restore();
139}
140
141// Star test (self-intersecting)
142void test_star(SkCanvas* canvas, const SkPaint& paint) {
senorblancod6ed19c2015-02-26 06:58:17 -0800143 canvas->save();
144 canvas->translate(300, 100);
Mike Reedb6317422018-08-15 10:23:39 -0400145 canvas->drawPath(SkPath().addPoly({{30,20}, {50,80}, {70,20}, {20,57}, {80,57}}, false),
146 paint);
senorblancod6ed19c2015-02-26 06:58:17 -0800147 canvas->restore();
148}
149
Stephen White3b5a3fa2017-06-06 14:51:19 -0400150// Exercise a case where the intersection is below a bottom edge.
151void test_twist(SkCanvas* canvas, const SkPaint& paint) {
152 SkPath path;
153 canvas->save();
154 path.moveTo( 0.5, 6);
155 path.lineTo(5.8070392608642578125, 6.4612660408020019531);
156 path.lineTo(-2.9186885356903076172, 2.811046600341796875);
157 path.lineTo(0.49999994039535522461, -1.4124038219451904297);
158 canvas->translate(420, 220);
159 canvas->scale(10, 10);
160 canvas->drawPath(path, paint);
161 canvas->restore();
162}
163
senorblancod6ed19c2015-02-26 06:58:17 -0800164// Stairstep with repeated vert (intersection)
165void test_stairstep(SkCanvas* canvas, const SkPaint& paint) {
166 SkPath path;
167 canvas->save();
168 canvas->translate(0, 200);
169 path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
170 path.lineTo(SkIntToScalar(50), SkIntToScalar(20));
171 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
172 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
173 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
174 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
175 canvas->drawPath(path, paint);
176 canvas->restore();
177}
178
179void test_stairstep2(SkCanvas* canvas, const SkPaint& paint) {
180 SkPath path;
181 canvas->save();
182 canvas->translate(100, 200);
183 path.moveTo(20, 60);
184 path.lineTo(35, 80);
185 path.lineTo(50, 60);
186 path.lineTo(65, 80);
187 path.lineTo(80, 60);
188 canvas->drawPath(path, paint);
189 canvas->restore();
190}
191
192// Overlapping segments
193void test_overlapping(SkCanvas* canvas, const SkPaint& paint) {
194 SkPath path;
195 canvas->save();
196 canvas->translate(200, 200);
197 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
198 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
199 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
200 path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
201 canvas->drawPath(path, paint);
202 canvas->restore();
203}
204
senorblanco531237e2016-06-02 11:36:48 -0700205// Two "island" triangles inside a containing rect.
206// This exercises the partnering code in the tessellator.
207void test_partners(SkCanvas* canvas, const SkPaint& paint) {
208 SkPath path;
209 canvas->save();
210 canvas->translate(300, 200);
211 path.moveTo(20, 80);
212 path.lineTo(80, 80);
213 path.lineTo(80, 20);
214 path.lineTo(20, 20);
215 path.moveTo(30, 30);
216 path.lineTo(45, 50);
217 path.lineTo(30, 70);
218 path.moveTo(70, 30);
219 path.lineTo(70, 70);
220 path.lineTo(55, 50);
221 canvas->drawPath(path, paint);
222 canvas->restore();
223}
224
Stephen White531a48e2018-06-01 09:49:39 -0400225// A split edge causes one half to be merged to zero winding (destroyed).
226// Test that the other half of the split doesn't also get zero winding.
227void test_winding_merged_to_zero(SkCanvas* canvas, const SkPaint& paint) {
228 SkPath path;
229 canvas->save();
230 canvas->translate(400, 350);
231 path.moveTo(20, 80);
232 path.moveTo(70, -0.000001f);
233 path.lineTo(70, 0.0);
234 path.lineTo(60, -30.0);
235 path.lineTo(40, 20.0);
236 path.moveTo(50, 50.0);
237 path.lineTo(50, -50.0);
238 path.lineTo(10, 50.0);
239 canvas->drawPath(path, paint);
240 canvas->restore();
241}
242
senorblancod6ed19c2015-02-26 06:58:17 -0800243// Monotone test 1 (point in the middle)
244void test_monotone_1(SkCanvas* canvas, const SkPaint& paint) {
245 SkPath path;
246 canvas->save();
247 canvas->translate(0, 300);
248 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
249 path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
250 SkIntToScalar(80), SkIntToScalar(50));
251 path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
252 SkIntToScalar(20), SkIntToScalar(80));
253 canvas->drawPath(path, paint);
254 canvas->restore();
255}
256
257// Monotone test 2 (point at the top)
258void test_monotone_2(SkCanvas* canvas, const SkPaint& paint) {
259 SkPath path;
260 canvas->save();
261 canvas->translate(100, 300);
262 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
263 path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
264 path.quadTo(SkIntToScalar(20), SkIntToScalar(20),
265 SkIntToScalar(20), SkIntToScalar(80));
266 canvas->drawPath(path, paint);
267 canvas->restore();
268}
269
270// Monotone test 3 (point at the bottom)
271void test_monotone_3(SkCanvas* canvas, const SkPaint& paint) {
272 SkPath path;
273 canvas->save();
274 canvas->translate(200, 300);
275 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
276 path.lineTo(SkIntToScalar(80), SkIntToScalar(70));
277 path.quadTo(SkIntToScalar(20), SkIntToScalar(80),
278 SkIntToScalar(20), SkIntToScalar(20));
279 canvas->drawPath(path, paint);
280 canvas->restore();
281}
282
283// Monotone test 4 (merging of two monotones)
284void test_monotone_4(SkCanvas* canvas, const SkPaint& paint) {
285 SkPath path;
286 canvas->save();
287 canvas->translate(300, 300);
288 path.moveTo(80, 25);
289 path.lineTo(50, 39);
290 path.lineTo(20, 25);
291 path.lineTo(40, 45);
292 path.lineTo(70, 50);
293 path.lineTo(80, 80);
294 canvas->drawPath(path, paint);
295 canvas->restore();
296}
297
298// Monotone test 5 (aborted merging of two monotones)
299void test_monotone_5(SkCanvas* canvas, const SkPaint& paint) {
300 SkPath path;
301 canvas->save();
302 canvas->translate(0, 400);
303 path.moveTo(50, 20);
304 path.lineTo(80, 80);
305 path.lineTo(50, 50);
306 path.lineTo(20, 80);
307 canvas->drawPath(path, paint);
308 canvas->restore();
309}
310// Degenerate intersection test
311void test_degenerate(SkCanvas* canvas, const SkPaint& paint) {
312 SkPath path;
313 canvas->save();
314 canvas->translate(100, 400);
315 path.moveTo(50, 20);
316 path.lineTo(70, 30);
317 path.lineTo(20, 50);
318 path.moveTo(50, 20);
319 path.lineTo(80, 80);
320 path.lineTo(50, 80);
321 canvas->drawPath(path, paint);
322 canvas->restore();
323}
324// Two triangles with a coincident edge.
325void test_coincident_edge(SkCanvas* canvas, const SkPaint& paint) {
326 SkPath path;
327 canvas->save();
328 canvas->translate(200, 400);
329
330 path.moveTo(80, 20);
331 path.lineTo(80, 80);
332 path.lineTo(20, 80);
333
334 path.moveTo(20, 20);
335 path.lineTo(80, 80);
336 path.lineTo(20, 80);
337
338 canvas->drawPath(path, paint);
339 canvas->restore();
340}
341// Bowtie with a coincident triangle (one triangle vertex coincident with the
342// bowtie's intersection).
343void test_bowtie_coincident_triangle(SkCanvas* canvas, const SkPaint& paint) {
344 SkPath path;
345 canvas->save();
346 canvas->translate(300, 400);
347 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
348 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
349 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
350 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
351 path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
352 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
353 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
354 canvas->drawPath(path, paint);
355 canvas->restore();
356}
357
Stephen White85dcf6b2018-07-17 16:14:31 -0400358// Collinear outer boundary edges. In the edge-AA codepath, this creates an overlap region
359// which contains a boundary edge. It can't be removed, but it must have the correct winding.
360void test_collinear_outer_boundary_edge(SkCanvas* canvas, const SkPaint& paint) {
361 SkPath path;
362 canvas->save();
363 canvas->translate(400, 400);
364 path.moveTo(20, 20);
365 path.lineTo(20, 50);
366 path.lineTo(50, 50);
367 path.moveTo(80, 50);
368 path.lineTo(50, 50);
369 path.lineTo(80, 20);
370 canvas->drawPath(path, paint);
371 canvas->restore();
372}
373
senorblancod6ed19c2015-02-26 06:58:17 -0800374// Coincident edges (big ones first, coincident vert on top).
375void test_coincident_edges_1(SkCanvas* canvas, const SkPaint& paint) {
376 SkPath path;
377 canvas->save();
378 canvas->translate(0, 500);
379 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
380 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
381 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
382 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
383 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
384 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
385 canvas->drawPath(path, paint);
386 canvas->restore();
387}
388// Coincident edges (small ones first, coincident vert on top).
389void test_coincident_edges_2(SkCanvas* canvas, const SkPaint& paint) {
390 SkPath path;
391 canvas->save();
392 canvas->translate(100, 500);
393 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
394 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
395 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
396 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
397 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
398 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
399 canvas->drawPath(path, paint);
400 canvas->restore();
401}
402// Coincident edges (small ones first, coincident vert on bottom).
403void test_coincident_edges_3(SkCanvas* canvas, const SkPaint& paint) {
404 SkPath path;
405 canvas->save();
406 canvas->translate(200, 500);
407 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
408 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
409 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
410 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
411 path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
412 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
413 canvas->drawPath(path, paint);
414 canvas->restore();
415}
416// Coincident edges (big ones first, coincident vert on bottom).
417void test_coincident_edges_4(SkCanvas* canvas, const SkPaint& paint) {
418 SkPath path;
419 canvas->save();
420 canvas->translate(300, 500);
421 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
422 path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
423 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
424 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
425 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
426 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
427 canvas->drawPath(path, paint);
428 canvas->restore();
429}
430
431};
432
Stephen White5926f2d2017-02-13 13:55:42 -0500433DEF_SIMPLE_GM(concavepaths, canvas, 500, 600) {
Stephen White930f69e2017-01-12 17:15:50 -0500434 SkPaint paint;
senorblancod6ed19c2015-02-26 06:58:17 -0800435
Stephen White930f69e2017-01-12 17:15:50 -0500436 paint.setAntiAlias(true);
437 paint.setStyle(SkPaint::kFill_Style);
senorblancod6ed19c2015-02-26 06:58:17 -0800438
Stephen White930f69e2017-01-12 17:15:50 -0500439 test_concave(canvas, paint);
440 test_reverse_concave(canvas, paint);
441 test_bowtie(canvas, paint);
442 test_fake_bowtie(canvas, paint);
Stephen White5926f2d2017-02-13 13:55:42 -0500443 test_intruding_vertex(canvas, paint);
Stephen White930f69e2017-01-12 17:15:50 -0500444 test_fish(canvas, paint);
445 test_fast_forward(canvas, paint);
446 test_hole(canvas, paint);
447 test_star(canvas, paint);
Stephen White3b5a3fa2017-06-06 14:51:19 -0400448 test_twist(canvas, paint);
Stephen White5926f2d2017-02-13 13:55:42 -0500449 test_inversion_repeat_vertex(canvas, paint);
Stephen White930f69e2017-01-12 17:15:50 -0500450 test_stairstep(canvas, paint);
451 test_stairstep2(canvas, paint);
452 test_overlapping(canvas, paint);
453 test_partners(canvas, paint);
Stephen White531a48e2018-06-01 09:49:39 -0400454 test_winding_merged_to_zero(canvas, paint);
Stephen White930f69e2017-01-12 17:15:50 -0500455 test_monotone_1(canvas, paint);
456 test_monotone_2(canvas, paint);
457 test_monotone_3(canvas, paint);
458 test_monotone_4(canvas, paint);
459 test_monotone_5(canvas, paint);
460 test_degenerate(canvas, paint);
461 test_coincident_edge(canvas, paint);
462 test_bowtie_coincident_triangle(canvas, paint);
Stephen White85dcf6b2018-07-17 16:14:31 -0400463 test_collinear_outer_boundary_edge(canvas, paint);
Stephen White930f69e2017-01-12 17:15:50 -0500464 test_coincident_edges_1(canvas, paint);
465 test_coincident_edges_2(canvas, paint);
466 test_coincident_edges_3(canvas, paint);
467 test_coincident_edges_4(canvas, paint);
468}