blob: 1082a56c674e2963c52af052f6a9697ab60cde91 [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
Mike Kleinc0bd9f92019-04-23 12:05:21 -05008#include "gm/gm.h"
9#include "include/core/SkCanvas.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040010#include "include/core/SkPaint.h"
Mike Kleinc0bd9f92019-04-23 12:05:21 -050011#include "include/core/SkPath.h"
Ben Wagner7fde8e12019-05-01 17:28:53 -040012#include "include/core/SkScalar.h"
senorblancod6ed19c2015-02-26 06:58:17 -080013
senorblancod6ed19c2015-02-26 06:58:17 -080014namespace {
15// Concave test
16void test_concave(SkCanvas* canvas, const SkPaint& paint) {
17 SkPath path;
18 canvas->translate(0, 0);
Mike Reedb6317422018-08-15 10:23:39 -040019 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
20 .lineTo(SkIntToScalar(80), SkIntToScalar(20))
21 .lineTo(SkIntToScalar(30), SkIntToScalar(30))
22 .lineTo(SkIntToScalar(20), SkIntToScalar(80));
senorblancod6ed19c2015-02-26 06:58:17 -080023 canvas->drawPath(path, paint);
24}
25
26// Reverse concave test
27void test_reverse_concave(SkCanvas* canvas, const SkPaint& paint) {
28 SkPath path;
29 canvas->save();
30 canvas->translate(100, 0);
Mike Reedb6317422018-08-15 10:23:39 -040031 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
32 .lineTo(SkIntToScalar(20), SkIntToScalar(80))
33 .lineTo(SkIntToScalar(30), SkIntToScalar(30))
34 .lineTo(SkIntToScalar(80), SkIntToScalar(20));
senorblancod6ed19c2015-02-26 06:58:17 -080035 canvas->drawPath(path, paint);
36 canvas->restore();
37}
38
39// Bowtie (intersection)
40void test_bowtie(SkCanvas* canvas, const SkPaint& paint) {
41 SkPath path;
42 canvas->save();
43 canvas->translate(200, 0);
Mike Reedb6317422018-08-15 10:23:39 -040044 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
45 .lineTo(SkIntToScalar(80), SkIntToScalar(80))
46 .lineTo(SkIntToScalar(80), SkIntToScalar(20))
47 .lineTo(SkIntToScalar(20), SkIntToScalar(80));
senorblancod6ed19c2015-02-26 06:58:17 -080048 canvas->drawPath(path, paint);
49 canvas->restore();
50}
51
52// "fake" bowtie (concave, but no intersection)
53void test_fake_bowtie(SkCanvas* canvas, const SkPaint& paint) {
54 SkPath path;
55 canvas->save();
56 canvas->translate(300, 0);
Mike Reedb6317422018-08-15 10:23:39 -040057 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
58 .lineTo(SkIntToScalar(50), SkIntToScalar(40))
59 .lineTo(SkIntToScalar(80), SkIntToScalar(20))
60 .lineTo(SkIntToScalar(80), SkIntToScalar(80))
61 .lineTo(SkIntToScalar(50), SkIntToScalar(60))
62 .lineTo(SkIntToScalar(20), SkIntToScalar(80));
senorblancod6ed19c2015-02-26 06:58:17 -080063 canvas->drawPath(path, paint);
64 canvas->restore();
65}
66
Stephen White5926f2d2017-02-13 13:55:42 -050067// Bowtie with a smaller right hand lobe. The outer vertex of the left hand
68// lobe intrudes into the interior of the right hand lobe.
69void test_intruding_vertex(SkCanvas* canvas, const SkPaint& paint) {
70 SkPath path;
71 canvas->save();
72 canvas->translate(400, 0);
73 path.setIsVolatile(true);
Mike Reedb6317422018-08-15 10:23:39 -040074 path.moveTo(20, 20)
75 .lineTo(50, 50)
76 .lineTo(68, 20)
77 .lineTo(68, 80)
78 .lineTo(50, 50)
79 .lineTo(20, 80);
Stephen White5926f2d2017-02-13 13:55:42 -050080 canvas->drawPath(path, paint);
81 canvas->restore();
82}
83
84// A shape with an edge that becomes inverted on AA stroking and that also contains
85// a repeated start/end vertex.
86void test_inversion_repeat_vertex(SkCanvas* canvas, const SkPaint& paint) {
87 SkPath path;
88 canvas->save();
89 canvas->translate(400, 100);
90 path.setIsVolatile(true);
Mike Reedb6317422018-08-15 10:23:39 -040091 path.moveTo(80, 50)
92 .lineTo(40, 80)
93 .lineTo(60, 20)
94 .lineTo(20, 20)
95 .lineTo(39.99f, 80)
96 .lineTo(80, 50);
Stephen White5926f2d2017-02-13 13:55:42 -050097 canvas->drawPath(path, paint);
98 canvas->restore();
99}
100
senorblancod6ed19c2015-02-26 06:58:17 -0800101// Fish test (intersection/concave)
102void test_fish(SkCanvas* canvas, const SkPaint& paint) {
103 SkPath path;
104 canvas->save();
105 canvas->translate(0, 100);
Mike Reedb6317422018-08-15 10:23:39 -0400106 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
107 .lineTo(SkIntToScalar(80), SkIntToScalar(80))
108 .lineTo(SkIntToScalar(70), SkIntToScalar(50))
109 .lineTo(SkIntToScalar(80), SkIntToScalar(20))
110 .lineTo(SkIntToScalar(20), SkIntToScalar(80))
111 .lineTo(SkIntToScalar(0), SkIntToScalar(50));
senorblancod6ed19c2015-02-26 06:58:17 -0800112 canvas->drawPath(path, paint);
113 canvas->restore();
114}
115
Stephen White2f4686f2017-01-03 16:20:01 -0500116// Overlapping "Fast-forward" icon: tests coincidence of inner and outer
117// vertices generated by intersection.
118void test_fast_forward(SkCanvas* canvas, const SkPaint& paint) {
senorblancod6ed19c2015-02-26 06:58:17 -0800119 SkPath path;
120 canvas->save();
121 canvas->translate(100, 100);
Mike Reedb6317422018-08-15 10:23:39 -0400122 path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
123 .lineTo(SkIntToScalar(60), SkIntToScalar(50))
124 .lineTo(SkIntToScalar(20), SkIntToScalar(80))
125 .moveTo(SkIntToScalar(40), SkIntToScalar(20))
126 .lineTo(SkIntToScalar(40), SkIntToScalar(80))
127 .lineTo(SkIntToScalar(80), SkIntToScalar(50));
senorblancod6ed19c2015-02-26 06:58:17 -0800128 canvas->drawPath(path, paint);
129 canvas->restore();
130}
131
132// Square polygon with a square hole.
133void test_hole(SkCanvas* canvas, const SkPaint& paint) {
134 SkPath path;
135 canvas->save();
136 canvas->translate(200, 100);
Mike Reedb6317422018-08-15 10:23:39 -0400137 path.addPoly({{20,20}, {80,20}, {80,80}, {20,80}}, false)
138 .addPoly({{30,30}, {30,70}, {70,70}, {70,30}}, false);
senorblancod6ed19c2015-02-26 06:58:17 -0800139 canvas->drawPath(path, paint);
140 canvas->restore();
141}
142
143// Star test (self-intersecting)
144void test_star(SkCanvas* canvas, const SkPaint& paint) {
senorblancod6ed19c2015-02-26 06:58:17 -0800145 canvas->save();
146 canvas->translate(300, 100);
Mike Reedb6317422018-08-15 10:23:39 -0400147 canvas->drawPath(SkPath().addPoly({{30,20}, {50,80}, {70,20}, {20,57}, {80,57}}, false),
148 paint);
senorblancod6ed19c2015-02-26 06:58:17 -0800149 canvas->restore();
150}
151
Stephen White3b5a3fa2017-06-06 14:51:19 -0400152// Exercise a case where the intersection is below a bottom edge.
153void test_twist(SkCanvas* canvas, const SkPaint& paint) {
154 SkPath path;
155 canvas->save();
156 path.moveTo( 0.5, 6);
157 path.lineTo(5.8070392608642578125, 6.4612660408020019531);
158 path.lineTo(-2.9186885356903076172, 2.811046600341796875);
159 path.lineTo(0.49999994039535522461, -1.4124038219451904297);
160 canvas->translate(420, 220);
161 canvas->scale(10, 10);
162 canvas->drawPath(path, paint);
163 canvas->restore();
164}
165
senorblancod6ed19c2015-02-26 06:58:17 -0800166// Stairstep with repeated vert (intersection)
167void test_stairstep(SkCanvas* canvas, const SkPaint& paint) {
168 SkPath path;
169 canvas->save();
170 canvas->translate(0, 200);
171 path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
172 path.lineTo(SkIntToScalar(50), SkIntToScalar(20));
173 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
174 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
175 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
176 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
177 canvas->drawPath(path, paint);
178 canvas->restore();
179}
180
181void test_stairstep2(SkCanvas* canvas, const SkPaint& paint) {
182 SkPath path;
183 canvas->save();
184 canvas->translate(100, 200);
185 path.moveTo(20, 60);
186 path.lineTo(35, 80);
187 path.lineTo(50, 60);
188 path.lineTo(65, 80);
189 path.lineTo(80, 60);
190 canvas->drawPath(path, paint);
191 canvas->restore();
192}
193
194// Overlapping segments
195void test_overlapping(SkCanvas* canvas, const SkPaint& paint) {
196 SkPath path;
197 canvas->save();
198 canvas->translate(200, 200);
199 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
200 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
201 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
202 path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
203 canvas->drawPath(path, paint);
204 canvas->restore();
205}
206
senorblanco531237e2016-06-02 11:36:48 -0700207// Two "island" triangles inside a containing rect.
208// This exercises the partnering code in the tessellator.
209void test_partners(SkCanvas* canvas, const SkPaint& paint) {
210 SkPath path;
211 canvas->save();
212 canvas->translate(300, 200);
213 path.moveTo(20, 80);
214 path.lineTo(80, 80);
215 path.lineTo(80, 20);
216 path.lineTo(20, 20);
217 path.moveTo(30, 30);
218 path.lineTo(45, 50);
219 path.lineTo(30, 70);
220 path.moveTo(70, 30);
221 path.lineTo(70, 70);
222 path.lineTo(55, 50);
223 canvas->drawPath(path, paint);
224 canvas->restore();
225}
226
Stephen White531a48e2018-06-01 09:49:39 -0400227// A split edge causes one half to be merged to zero winding (destroyed).
228// Test that the other half of the split doesn't also get zero winding.
229void test_winding_merged_to_zero(SkCanvas* canvas, const SkPaint& paint) {
230 SkPath path;
231 canvas->save();
232 canvas->translate(400, 350);
233 path.moveTo(20, 80);
234 path.moveTo(70, -0.000001f);
235 path.lineTo(70, 0.0);
236 path.lineTo(60, -30.0);
237 path.lineTo(40, 20.0);
238 path.moveTo(50, 50.0);
239 path.lineTo(50, -50.0);
240 path.lineTo(10, 50.0);
241 canvas->drawPath(path, paint);
242 canvas->restore();
243}
244
senorblancod6ed19c2015-02-26 06:58:17 -0800245// Monotone test 1 (point in the middle)
246void test_monotone_1(SkCanvas* canvas, const SkPaint& paint) {
247 SkPath path;
248 canvas->save();
249 canvas->translate(0, 300);
250 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
251 path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
252 SkIntToScalar(80), SkIntToScalar(50));
253 path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
254 SkIntToScalar(20), SkIntToScalar(80));
255 canvas->drawPath(path, paint);
256 canvas->restore();
257}
258
259// Monotone test 2 (point at the top)
260void test_monotone_2(SkCanvas* canvas, const SkPaint& paint) {
261 SkPath path;
262 canvas->save();
263 canvas->translate(100, 300);
264 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
265 path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
266 path.quadTo(SkIntToScalar(20), SkIntToScalar(20),
267 SkIntToScalar(20), SkIntToScalar(80));
268 canvas->drawPath(path, paint);
269 canvas->restore();
270}
271
272// Monotone test 3 (point at the bottom)
273void test_monotone_3(SkCanvas* canvas, const SkPaint& paint) {
274 SkPath path;
275 canvas->save();
276 canvas->translate(200, 300);
277 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
278 path.lineTo(SkIntToScalar(80), SkIntToScalar(70));
279 path.quadTo(SkIntToScalar(20), SkIntToScalar(80),
280 SkIntToScalar(20), SkIntToScalar(20));
281 canvas->drawPath(path, paint);
282 canvas->restore();
283}
284
285// Monotone test 4 (merging of two monotones)
286void test_monotone_4(SkCanvas* canvas, const SkPaint& paint) {
287 SkPath path;
288 canvas->save();
289 canvas->translate(300, 300);
290 path.moveTo(80, 25);
291 path.lineTo(50, 39);
292 path.lineTo(20, 25);
293 path.lineTo(40, 45);
294 path.lineTo(70, 50);
295 path.lineTo(80, 80);
296 canvas->drawPath(path, paint);
297 canvas->restore();
298}
299
300// Monotone test 5 (aborted merging of two monotones)
301void test_monotone_5(SkCanvas* canvas, const SkPaint& paint) {
302 SkPath path;
303 canvas->save();
304 canvas->translate(0, 400);
305 path.moveTo(50, 20);
306 path.lineTo(80, 80);
307 path.lineTo(50, 50);
308 path.lineTo(20, 80);
309 canvas->drawPath(path, paint);
310 canvas->restore();
311}
312// Degenerate intersection test
313void test_degenerate(SkCanvas* canvas, const SkPaint& paint) {
314 SkPath path;
315 canvas->save();
316 canvas->translate(100, 400);
317 path.moveTo(50, 20);
318 path.lineTo(70, 30);
319 path.lineTo(20, 50);
320 path.moveTo(50, 20);
321 path.lineTo(80, 80);
322 path.lineTo(50, 80);
323 canvas->drawPath(path, paint);
324 canvas->restore();
325}
326// Two triangles with a coincident edge.
327void test_coincident_edge(SkCanvas* canvas, const SkPaint& paint) {
328 SkPath path;
329 canvas->save();
330 canvas->translate(200, 400);
331
332 path.moveTo(80, 20);
333 path.lineTo(80, 80);
334 path.lineTo(20, 80);
335
336 path.moveTo(20, 20);
337 path.lineTo(80, 80);
338 path.lineTo(20, 80);
339
340 canvas->drawPath(path, paint);
341 canvas->restore();
342}
343// Bowtie with a coincident triangle (one triangle vertex coincident with the
344// bowtie's intersection).
345void test_bowtie_coincident_triangle(SkCanvas* canvas, const SkPaint& paint) {
346 SkPath path;
347 canvas->save();
348 canvas->translate(300, 400);
349 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
350 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
351 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
352 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
353 path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
354 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
355 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
356 canvas->drawPath(path, paint);
357 canvas->restore();
358}
359
Stephen White85dcf6b2018-07-17 16:14:31 -0400360// Collinear outer boundary edges. In the edge-AA codepath, this creates an overlap region
361// which contains a boundary edge. It can't be removed, but it must have the correct winding.
362void test_collinear_outer_boundary_edge(SkCanvas* canvas, const SkPaint& paint) {
363 SkPath path;
364 canvas->save();
365 canvas->translate(400, 400);
366 path.moveTo(20, 20);
367 path.lineTo(20, 50);
368 path.lineTo(50, 50);
369 path.moveTo(80, 50);
370 path.lineTo(50, 50);
371 path.lineTo(80, 20);
372 canvas->drawPath(path, paint);
373 canvas->restore();
374}
375
senorblancod6ed19c2015-02-26 06:58:17 -0800376// Coincident edges (big ones first, coincident vert on top).
377void test_coincident_edges_1(SkCanvas* canvas, const SkPaint& paint) {
378 SkPath path;
379 canvas->save();
380 canvas->translate(0, 500);
381 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
382 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
383 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
384 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
385 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
386 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
387 canvas->drawPath(path, paint);
388 canvas->restore();
389}
390// Coincident edges (small ones first, coincident vert on top).
391void test_coincident_edges_2(SkCanvas* canvas, const SkPaint& paint) {
392 SkPath path;
393 canvas->save();
394 canvas->translate(100, 500);
395 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
396 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
397 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
398 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
399 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
400 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
401 canvas->drawPath(path, paint);
402 canvas->restore();
403}
404// Coincident edges (small ones first, coincident vert on bottom).
405void test_coincident_edges_3(SkCanvas* canvas, const SkPaint& paint) {
406 SkPath path;
407 canvas->save();
408 canvas->translate(200, 500);
409 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
410 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
411 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
412 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
413 path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
414 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
415 canvas->drawPath(path, paint);
416 canvas->restore();
417}
418// Coincident edges (big ones first, coincident vert on bottom).
419void test_coincident_edges_4(SkCanvas* canvas, const SkPaint& paint) {
420 SkPath path;
421 canvas->save();
422 canvas->translate(300, 500);
423 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
424 path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
425 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
426 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
427 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
428 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
429 canvas->drawPath(path, paint);
430 canvas->restore();
431}
432
433};
434
Stephen White5926f2d2017-02-13 13:55:42 -0500435DEF_SIMPLE_GM(concavepaths, canvas, 500, 600) {
Stephen White930f69e2017-01-12 17:15:50 -0500436 SkPaint paint;
senorblancod6ed19c2015-02-26 06:58:17 -0800437
Stephen White930f69e2017-01-12 17:15:50 -0500438 paint.setAntiAlias(true);
439 paint.setStyle(SkPaint::kFill_Style);
senorblancod6ed19c2015-02-26 06:58:17 -0800440
Stephen White930f69e2017-01-12 17:15:50 -0500441 test_concave(canvas, paint);
442 test_reverse_concave(canvas, paint);
443 test_bowtie(canvas, paint);
444 test_fake_bowtie(canvas, paint);
Stephen White5926f2d2017-02-13 13:55:42 -0500445 test_intruding_vertex(canvas, paint);
Stephen White930f69e2017-01-12 17:15:50 -0500446 test_fish(canvas, paint);
447 test_fast_forward(canvas, paint);
448 test_hole(canvas, paint);
449 test_star(canvas, paint);
Stephen White3b5a3fa2017-06-06 14:51:19 -0400450 test_twist(canvas, paint);
Stephen White5926f2d2017-02-13 13:55:42 -0500451 test_inversion_repeat_vertex(canvas, paint);
Stephen White930f69e2017-01-12 17:15:50 -0500452 test_stairstep(canvas, paint);
453 test_stairstep2(canvas, paint);
454 test_overlapping(canvas, paint);
455 test_partners(canvas, paint);
Stephen White531a48e2018-06-01 09:49:39 -0400456 test_winding_merged_to_zero(canvas, paint);
Stephen White930f69e2017-01-12 17:15:50 -0500457 test_monotone_1(canvas, paint);
458 test_monotone_2(canvas, paint);
459 test_monotone_3(canvas, paint);
460 test_monotone_4(canvas, paint);
461 test_monotone_5(canvas, paint);
462 test_degenerate(canvas, paint);
463 test_coincident_edge(canvas, paint);
464 test_bowtie_coincident_triangle(canvas, paint);
Stephen White85dcf6b2018-07-17 16:14:31 -0400465 test_collinear_outer_boundary_edge(canvas, paint);
Stephen White930f69e2017-01-12 17:15:50 -0500466 test_coincident_edges_1(canvas, paint);
467 test_coincident_edges_2(canvas, paint);
468 test_coincident_edges_3(canvas, paint);
469 test_coincident_edges_4(canvas, paint);
470}