• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/gm.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkPaint.h"
11 #include "include/core/SkPath.h"
12 #include "include/core/SkScalar.h"
13 
14 namespace {
15 // Concave test
test_concave(SkCanvas * canvas,const SkPaint & paint)16 void test_concave(SkCanvas* canvas, const SkPaint& paint) {
17     SkPath path;
18     canvas->translate(0, 0);
19     path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
20         .lineTo(SkIntToScalar(80), SkIntToScalar(20))
21         .lineTo(SkIntToScalar(30), SkIntToScalar(30))
22         .lineTo(SkIntToScalar(20), SkIntToScalar(80));
23     canvas->drawPath(path, paint);
24 }
25 
26 // Reverse concave test
test_reverse_concave(SkCanvas * canvas,const SkPaint & paint)27 void test_reverse_concave(SkCanvas* canvas, const SkPaint& paint) {
28     SkPath path;
29     canvas->save();
30     canvas->translate(100, 0);
31     path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
32         .lineTo(SkIntToScalar(20), SkIntToScalar(80))
33         .lineTo(SkIntToScalar(30), SkIntToScalar(30))
34         .lineTo(SkIntToScalar(80), SkIntToScalar(20));
35     canvas->drawPath(path, paint);
36     canvas->restore();
37 }
38 
39 // Bowtie (intersection)
test_bowtie(SkCanvas * canvas,const SkPaint & paint)40 void test_bowtie(SkCanvas* canvas, const SkPaint& paint) {
41     SkPath path;
42     canvas->save();
43     canvas->translate(200, 0);
44     path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
45         .lineTo(SkIntToScalar(80), SkIntToScalar(80))
46         .lineTo(SkIntToScalar(80), SkIntToScalar(20))
47         .lineTo(SkIntToScalar(20), SkIntToScalar(80));
48     canvas->drawPath(path, paint);
49     canvas->restore();
50 }
51 
52 // "fake" bowtie (concave, but no intersection)
test_fake_bowtie(SkCanvas * canvas,const SkPaint & paint)53 void test_fake_bowtie(SkCanvas* canvas, const SkPaint& paint) {
54     SkPath path;
55     canvas->save();
56     canvas->translate(300, 0);
57     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));
63     canvas->drawPath(path, paint);
64     canvas->restore();
65 }
66 
67 // 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.
test_intruding_vertex(SkCanvas * canvas,const SkPaint & paint)69 void test_intruding_vertex(SkCanvas* canvas, const SkPaint& paint) {
70     SkPath path;
71     canvas->save();
72     canvas->translate(400, 0);
73     path.setIsVolatile(true);
74     path.moveTo(20, 20)
75         .lineTo(50, 50)
76         .lineTo(68, 20)
77         .lineTo(68, 80)
78         .lineTo(50, 50)
79         .lineTo(20, 80);
80     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.
test_inversion_repeat_vertex(SkCanvas * canvas,const SkPaint & paint)86 void test_inversion_repeat_vertex(SkCanvas* canvas, const SkPaint& paint) {
87     SkPath path;
88     canvas->save();
89     canvas->translate(400, 100);
90     path.setIsVolatile(true);
91     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);
97     canvas->drawPath(path, paint);
98     canvas->restore();
99 }
100 
101 // Fish test (intersection/concave)
test_fish(SkCanvas * canvas,const SkPaint & paint)102 void test_fish(SkCanvas* canvas, const SkPaint& paint) {
103     SkPath path;
104     canvas->save();
105     canvas->translate(0, 100);
106     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));
112     canvas->drawPath(path, paint);
113     canvas->restore();
114 }
115 
116 // Overlapping "Fast-forward" icon: tests coincidence of inner and outer
117 // vertices generated by intersection.
test_fast_forward(SkCanvas * canvas,const SkPaint & paint)118 void test_fast_forward(SkCanvas* canvas, const SkPaint& paint) {
119     SkPath path;
120     canvas->save();
121     canvas->translate(100, 100);
122     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));
128     canvas->drawPath(path, paint);
129     canvas->restore();
130 }
131 
132 // Square polygon with a square hole.
test_hole(SkCanvas * canvas,const SkPaint & paint)133 void test_hole(SkCanvas* canvas, const SkPaint& paint) {
134     SkPath path;
135     canvas->save();
136     canvas->translate(200, 100);
137     path.addPoly({{20,20}, {80,20}, {80,80}, {20,80}}, false)
138         .addPoly({{30,30}, {30,70}, {70,70}, {70,30}}, false);
139     canvas->drawPath(path, paint);
140     canvas->restore();
141 }
142 
143 // Star test (self-intersecting)
test_star(SkCanvas * canvas,const SkPaint & paint)144 void test_star(SkCanvas* canvas, const SkPaint& paint) {
145     canvas->save();
146     canvas->translate(300, 100);
147     canvas->drawPath(SkPath().addPoly({{30,20}, {50,80}, {70,20}, {20,57}, {80,57}}, false),
148                      paint);
149     canvas->restore();
150 }
151 
152 // Exercise a case where the intersection is below a bottom edge.
test_twist(SkCanvas * canvas,const SkPaint & paint)153 void 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 
166 // Stairstep with repeated vert (intersection)
test_stairstep(SkCanvas * canvas,const SkPaint & paint)167 void 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 
test_stairstep2(SkCanvas * canvas,const SkPaint & paint)181 void 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
test_overlapping(SkCanvas * canvas,const SkPaint & paint)195 void 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 
207 // Two "island" triangles inside a containing rect.
208 // This exercises the partnering code in the tessellator.
test_partners(SkCanvas * canvas,const SkPaint & paint)209 void 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 
227 // 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.
test_winding_merged_to_zero(SkCanvas * canvas,const SkPaint & paint)229 void 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 
245 // Monotone test 1 (point in the middle)
test_monotone_1(SkCanvas * canvas,const SkPaint & paint)246 void 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)
test_monotone_2(SkCanvas * canvas,const SkPaint & paint)260 void 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)
test_monotone_3(SkCanvas * canvas,const SkPaint & paint)273 void 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)
test_monotone_4(SkCanvas * canvas,const SkPaint & paint)286 void 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)
test_monotone_5(SkCanvas * canvas,const SkPaint & paint)301 void 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
test_degenerate(SkCanvas * canvas,const SkPaint & paint)313 void 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.
test_coincident_edge(SkCanvas * canvas,const SkPaint & paint)327 void 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).
test_bowtie_coincident_triangle(SkCanvas * canvas,const SkPaint & paint)345 void 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 
360 // 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.
test_collinear_outer_boundary_edge(SkCanvas * canvas,const SkPaint & paint)362 void 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 
376 // Coincident edges (big ones first, coincident vert on top).
test_coincident_edges_1(SkCanvas * canvas,const SkPaint & paint)377 void 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).
test_coincident_edges_2(SkCanvas * canvas,const SkPaint & paint)391 void 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).
test_coincident_edges_3(SkCanvas * canvas,const SkPaint & paint)405 void 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).
test_coincident_edges_4(SkCanvas * canvas,const SkPaint & paint)419 void 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 
435 DEF_SIMPLE_GM(concavepaths, canvas, 500, 600) {
436     SkPaint paint;
437 
438     paint.setAntiAlias(true);
439     paint.setStyle(SkPaint::kFill_Style);
440 
441     test_concave(canvas, paint);
442     test_reverse_concave(canvas, paint);
443     test_bowtie(canvas, paint);
444     test_fake_bowtie(canvas, paint);
445     test_intruding_vertex(canvas, paint);
446     test_fish(canvas, paint);
447     test_fast_forward(canvas, paint);
448     test_hole(canvas, paint);
449     test_star(canvas, paint);
450     test_twist(canvas, paint);
451     test_inversion_repeat_vertex(canvas, paint);
452     test_stairstep(canvas, paint);
453     test_stairstep2(canvas, paint);
454     test_overlapping(canvas, paint);
455     test_partners(canvas, paint);
456     test_winding_merged_to_zero(canvas, paint);
457     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);
465     test_collinear_outer_boundary_edge(canvas, paint);
466     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 }
471