• 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.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         .lineTo(SkIntToScalar(80), SkIntToScalar(20))
19         .lineTo(SkIntToScalar(30), SkIntToScalar(30))
20         .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         .lineTo(SkIntToScalar(20), SkIntToScalar(80))
31         .lineTo(SkIntToScalar(30), SkIntToScalar(30))
32         .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         .lineTo(SkIntToScalar(80), SkIntToScalar(80))
44         .lineTo(SkIntToScalar(80), SkIntToScalar(20))
45         .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         .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));
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         .lineTo(50, 50)
74         .lineTo(68, 20)
75         .lineTo(68, 80)
76         .lineTo(50, 50)
77         .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         .lineTo(40,     80)
91         .lineTo(60,     20)
92         .lineTo(20,     20)
93         .lineTo(39.99f, 80)
94         .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         .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));
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         .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));
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.addPoly({{20,20}, {80,20}, {80,80}, {20,80}}, false)
136         .addPoly({{30,30}, {30,70}, {70,70}, {70,30}}, false);
137     canvas->drawPath(path, paint);
138     canvas->restore();
139 }
140 
141 // Star test (self-intersecting)
test_star(SkCanvas * canvas,const SkPaint & paint)142 void test_star(SkCanvas* canvas, const SkPaint& paint) {
143     canvas->save();
144     canvas->translate(300, 100);
145     canvas->drawPath(SkPath().addPoly({{30,20}, {50,80}, {70,20}, {20,57}, {80,57}}, false),
146                      paint);
147     canvas->restore();
148 }
149 
150 // Exercise a case where the intersection is below a bottom edge.
test_twist(SkCanvas * canvas,const SkPaint & paint)151 void 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 
164 // Stairstep with repeated vert (intersection)
test_stairstep(SkCanvas * canvas,const SkPaint & paint)165 void 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 
test_stairstep2(SkCanvas * canvas,const SkPaint & paint)179 void 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
test_overlapping(SkCanvas * canvas,const SkPaint & paint)193 void 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 
205 // Two "island" triangles inside a containing rect.
206 // This exercises the partnering code in the tessellator.
test_partners(SkCanvas * canvas,const SkPaint & paint)207 void 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 
225 // 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.
test_winding_merged_to_zero(SkCanvas * canvas,const SkPaint & paint)227 void 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 
243 // Monotone test 1 (point in the middle)
test_monotone_1(SkCanvas * canvas,const SkPaint & paint)244 void 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)
test_monotone_2(SkCanvas * canvas,const SkPaint & paint)258 void 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)
test_monotone_3(SkCanvas * canvas,const SkPaint & paint)271 void 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)
test_monotone_4(SkCanvas * canvas,const SkPaint & paint)284 void 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)
test_monotone_5(SkCanvas * canvas,const SkPaint & paint)299 void 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
test_degenerate(SkCanvas * canvas,const SkPaint & paint)311 void 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.
test_coincident_edge(SkCanvas * canvas,const SkPaint & paint)325 void 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).
test_bowtie_coincident_triangle(SkCanvas * canvas,const SkPaint & paint)343 void 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 
358 // 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.
test_collinear_outer_boundary_edge(SkCanvas * canvas,const SkPaint & paint)360 void 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 
374 // Coincident edges (big ones first, coincident vert on top).
test_coincident_edges_1(SkCanvas * canvas,const SkPaint & paint)375 void 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).
test_coincident_edges_2(SkCanvas * canvas,const SkPaint & paint)389 void 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).
test_coincident_edges_3(SkCanvas * canvas,const SkPaint & paint)403 void 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).
test_coincident_edges_4(SkCanvas * canvas,const SkPaint & paint)417 void 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 
433 DEF_SIMPLE_GM(concavepaths, canvas, 500, 600) {
434     SkPaint paint;
435 
436     paint.setAntiAlias(true);
437     paint.setStyle(SkPaint::kFill_Style);
438 
439     test_concave(canvas, paint);
440     test_reverse_concave(canvas, paint);
441     test_bowtie(canvas, paint);
442     test_fake_bowtie(canvas, paint);
443     test_intruding_vertex(canvas, paint);
444     test_fish(canvas, paint);
445     test_fast_forward(canvas, paint);
446     test_hole(canvas, paint);
447     test_star(canvas, paint);
448     test_twist(canvas, paint);
449     test_inversion_repeat_vertex(canvas, paint);
450     test_stairstep(canvas, paint);
451     test_stairstep2(canvas, paint);
452     test_overlapping(canvas, paint);
453     test_partners(canvas, paint);
454     test_winding_merged_to_zero(canvas, paint);
455     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);
463     test_collinear_outer_boundary_edge(canvas, paint);
464     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 }
469