• 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     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