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 #define WIDTH 400
13 #define HEIGHT 600
14
15 namespace {
16 // Concave test
test_concave(SkCanvas * canvas,const SkPaint & paint)17 void test_concave(SkCanvas* canvas, const SkPaint& paint) {
18 SkPath path;
19 canvas->translate(0, 0);
20 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
21 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
22 path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
23 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
24 canvas->drawPath(path, paint);
25 }
26
27 // Reverse concave test
test_reverse_concave(SkCanvas * canvas,const SkPaint & paint)28 void test_reverse_concave(SkCanvas* canvas, const SkPaint& paint) {
29 SkPath path;
30 canvas->save();
31 canvas->translate(100, 0);
32 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
33 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
34 path.lineTo(SkIntToScalar(30), SkIntToScalar(30));
35 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
36 canvas->drawPath(path, paint);
37 canvas->restore();
38 }
39
40 // Bowtie (intersection)
test_bowtie(SkCanvas * canvas,const SkPaint & paint)41 void test_bowtie(SkCanvas* canvas, const SkPaint& paint) {
42 SkPath path;
43 canvas->save();
44 canvas->translate(200, 0);
45 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
46 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
47 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
48 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
49 canvas->drawPath(path, paint);
50 canvas->restore();
51 }
52
53 // "fake" bowtie (concave, but no intersection)
test_fake_bowtie(SkCanvas * canvas,const SkPaint & paint)54 void test_fake_bowtie(SkCanvas* canvas, const SkPaint& paint) {
55 SkPath path;
56 canvas->save();
57 canvas->translate(300, 0);
58 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
59 path.lineTo(SkIntToScalar(50), SkIntToScalar(40));
60 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
61 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
62 path.lineTo(SkIntToScalar(50), SkIntToScalar(60));
63 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
64 canvas->drawPath(path, paint);
65 canvas->restore();
66 }
67
68 // Fish test (intersection/concave)
test_fish(SkCanvas * canvas,const SkPaint & paint)69 void test_fish(SkCanvas* canvas, const SkPaint& paint) {
70 SkPath path;
71 canvas->save();
72 canvas->translate(0, 100);
73 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
74 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
75 path.lineTo(SkIntToScalar(70), SkIntToScalar(50));
76 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
77 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
78 path.lineTo(SkIntToScalar(0), SkIntToScalar(50));
79 canvas->drawPath(path, paint);
80 canvas->restore();
81 }
82
83 // Collinear edges
test_collinear_edges(SkCanvas * canvas,const SkPaint & paint)84 void test_collinear_edges(SkCanvas* canvas, const SkPaint& paint) {
85 SkPath path;
86 canvas->save();
87 canvas->translate(100, 100);
88 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
89 path.lineTo(SkIntToScalar(50), SkIntToScalar(20));
90 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
91 path.lineTo(SkIntToScalar(50), SkIntToScalar(80));
92 canvas->drawPath(path, paint);
93 canvas->restore();
94 }
95
96 // Square polygon with a square hole.
test_hole(SkCanvas * canvas,const SkPaint & paint)97 void test_hole(SkCanvas* canvas, const SkPaint& paint) {
98 SkPath path;
99 canvas->save();
100 canvas->translate(200, 100);
101 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
102 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
103 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
104 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
105 path.moveTo(SkIntToScalar(30), SkIntToScalar(30));
106 path.lineTo(SkIntToScalar(30), SkIntToScalar(70));
107 path.lineTo(SkIntToScalar(70), SkIntToScalar(70));
108 path.lineTo(SkIntToScalar(70), SkIntToScalar(30));
109 canvas->drawPath(path, paint);
110 canvas->restore();
111 }
112
113 // Star test (self-intersecting)
test_star(SkCanvas * canvas,const SkPaint & paint)114 void test_star(SkCanvas* canvas, const SkPaint& paint) {
115 SkPath path;
116 canvas->save();
117 canvas->translate(300, 100);
118 path.moveTo(30, 20);
119 path.lineTo(50, 80);
120 path.lineTo(70, 20);
121 path.lineTo(20, 57);
122 path.lineTo(80, 57);
123 path.close();
124 canvas->drawPath(path, paint);
125 canvas->restore();
126 }
127
128 // Stairstep with repeated vert (intersection)
test_stairstep(SkCanvas * canvas,const SkPaint & paint)129 void test_stairstep(SkCanvas* canvas, const SkPaint& paint) {
130 SkPath path;
131 canvas->save();
132 canvas->translate(0, 200);
133 path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
134 path.lineTo(SkIntToScalar(50), SkIntToScalar(20));
135 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
136 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
137 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
138 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
139 canvas->drawPath(path, paint);
140 canvas->restore();
141 }
142
test_stairstep2(SkCanvas * canvas,const SkPaint & paint)143 void test_stairstep2(SkCanvas* canvas, const SkPaint& paint) {
144 SkPath path;
145 canvas->save();
146 canvas->translate(100, 200);
147 path.moveTo(20, 60);
148 path.lineTo(35, 80);
149 path.lineTo(50, 60);
150 path.lineTo(65, 80);
151 path.lineTo(80, 60);
152 canvas->drawPath(path, paint);
153 canvas->restore();
154 }
155
156 // Overlapping segments
test_overlapping(SkCanvas * canvas,const SkPaint & paint)157 void test_overlapping(SkCanvas* canvas, const SkPaint& paint) {
158 SkPath path;
159 canvas->save();
160 canvas->translate(200, 200);
161 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
162 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
163 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
164 path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
165 canvas->drawPath(path, paint);
166 canvas->restore();
167 }
168
169 // Monotone test 1 (point in the middle)
test_monotone_1(SkCanvas * canvas,const SkPaint & paint)170 void test_monotone_1(SkCanvas* canvas, const SkPaint& paint) {
171 SkPath path;
172 canvas->save();
173 canvas->translate(0, 300);
174 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
175 path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
176 SkIntToScalar(80), SkIntToScalar(50));
177 path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
178 SkIntToScalar(20), SkIntToScalar(80));
179 canvas->drawPath(path, paint);
180 canvas->restore();
181 }
182
183 // Monotone test 2 (point at the top)
test_monotone_2(SkCanvas * canvas,const SkPaint & paint)184 void test_monotone_2(SkCanvas* canvas, const SkPaint& paint) {
185 SkPath path;
186 canvas->save();
187 canvas->translate(100, 300);
188 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
189 path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
190 path.quadTo(SkIntToScalar(20), SkIntToScalar(20),
191 SkIntToScalar(20), SkIntToScalar(80));
192 canvas->drawPath(path, paint);
193 canvas->restore();
194 }
195
196 // Monotone test 3 (point at the bottom)
test_monotone_3(SkCanvas * canvas,const SkPaint & paint)197 void test_monotone_3(SkCanvas* canvas, const SkPaint& paint) {
198 SkPath path;
199 canvas->save();
200 canvas->translate(200, 300);
201 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
202 path.lineTo(SkIntToScalar(80), SkIntToScalar(70));
203 path.quadTo(SkIntToScalar(20), SkIntToScalar(80),
204 SkIntToScalar(20), SkIntToScalar(20));
205 canvas->drawPath(path, paint);
206 canvas->restore();
207 }
208
209 // Monotone test 4 (merging of two monotones)
test_monotone_4(SkCanvas * canvas,const SkPaint & paint)210 void test_monotone_4(SkCanvas* canvas, const SkPaint& paint) {
211 SkPath path;
212 canvas->save();
213 canvas->translate(300, 300);
214 path.moveTo(80, 25);
215 path.lineTo(50, 39);
216 path.lineTo(20, 25);
217 path.lineTo(40, 45);
218 path.lineTo(70, 50);
219 path.lineTo(80, 80);
220 canvas->drawPath(path, paint);
221 canvas->restore();
222 }
223
224 // Monotone test 5 (aborted merging of two monotones)
test_monotone_5(SkCanvas * canvas,const SkPaint & paint)225 void test_monotone_5(SkCanvas* canvas, const SkPaint& paint) {
226 SkPath path;
227 canvas->save();
228 canvas->translate(0, 400);
229 path.moveTo(50, 20);
230 path.lineTo(80, 80);
231 path.lineTo(50, 50);
232 path.lineTo(20, 80);
233 canvas->drawPath(path, paint);
234 canvas->restore();
235 }
236 // Degenerate intersection test
test_degenerate(SkCanvas * canvas,const SkPaint & paint)237 void test_degenerate(SkCanvas* canvas, const SkPaint& paint) {
238 SkPath path;
239 canvas->save();
240 canvas->translate(100, 400);
241 path.moveTo(50, 20);
242 path.lineTo(70, 30);
243 path.lineTo(20, 50);
244 path.moveTo(50, 20);
245 path.lineTo(80, 80);
246 path.lineTo(50, 80);
247 canvas->drawPath(path, paint);
248 canvas->restore();
249 }
250 // Two triangles with a coincident edge.
test_coincident_edge(SkCanvas * canvas,const SkPaint & paint)251 void test_coincident_edge(SkCanvas* canvas, const SkPaint& paint) {
252 SkPath path;
253 canvas->save();
254 canvas->translate(200, 400);
255
256 path.moveTo(80, 20);
257 path.lineTo(80, 80);
258 path.lineTo(20, 80);
259
260 path.moveTo(20, 20);
261 path.lineTo(80, 80);
262 path.lineTo(20, 80);
263
264 canvas->drawPath(path, paint);
265 canvas->restore();
266 }
267 // Bowtie with a coincident triangle (one triangle vertex coincident with the
268 // bowtie's intersection).
test_bowtie_coincident_triangle(SkCanvas * canvas,const SkPaint & paint)269 void test_bowtie_coincident_triangle(SkCanvas* canvas, const SkPaint& paint) {
270 SkPath path;
271 canvas->save();
272 canvas->translate(300, 400);
273 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
274 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
275 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
276 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
277 path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
278 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
279 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
280 canvas->drawPath(path, paint);
281 canvas->restore();
282 }
283
284 // Coincident edges (big ones first, coincident vert on top).
test_coincident_edges_1(SkCanvas * canvas,const SkPaint & paint)285 void test_coincident_edges_1(SkCanvas* canvas, const SkPaint& paint) {
286 SkPath path;
287 canvas->save();
288 canvas->translate(0, 500);
289 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
290 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
291 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
292 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
293 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
294 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
295 canvas->drawPath(path, paint);
296 canvas->restore();
297 }
298 // Coincident edges (small ones first, coincident vert on top).
test_coincident_edges_2(SkCanvas * canvas,const SkPaint & paint)299 void test_coincident_edges_2(SkCanvas* canvas, const SkPaint& paint) {
300 SkPath path;
301 canvas->save();
302 canvas->translate(100, 500);
303 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
304 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
305 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
306 path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
307 path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
308 path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
309 canvas->drawPath(path, paint);
310 canvas->restore();
311 }
312 // Coincident edges (small ones first, coincident vert on bottom).
test_coincident_edges_3(SkCanvas * canvas,const SkPaint & paint)313 void test_coincident_edges_3(SkCanvas* canvas, const SkPaint& paint) {
314 SkPath path;
315 canvas->save();
316 canvas->translate(200, 500);
317 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
318 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
319 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
320 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
321 path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
322 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
323 canvas->drawPath(path, paint);
324 canvas->restore();
325 }
326 // Coincident edges (big ones first, coincident vert on bottom).
test_coincident_edges_4(SkCanvas * canvas,const SkPaint & paint)327 void test_coincident_edges_4(SkCanvas* canvas, const SkPaint& paint) {
328 SkPath path;
329 canvas->save();
330 canvas->translate(300, 500);
331 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
332 path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
333 path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
334 path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
335 path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
336 path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
337 canvas->drawPath(path, paint);
338 canvas->restore();
339 }
340
341 };
342
343 class ConcavePathsGM : public skiagm::GM {
344 public:
ConcavePathsGM()345 ConcavePathsGM() {}
346
347 protected:
onShortName()348 SkString onShortName() override {
349 return SkString("concavepaths");
350 }
351
onISize()352 SkISize onISize() override {
353 return SkISize::Make(WIDTH, HEIGHT);
354 }
355
onDraw(SkCanvas * canvas)356 void onDraw(SkCanvas* canvas) override {
357 SkPaint paint;
358
359 paint.setAntiAlias(true);
360 paint.setStyle(SkPaint::kFill_Style);
361
362 test_concave(canvas, paint);
363 test_reverse_concave(canvas, paint);
364 test_bowtie(canvas, paint);
365 test_fake_bowtie(canvas, paint);
366 test_fish(canvas, paint);
367 test_collinear_edges(canvas, paint);
368 test_hole(canvas, paint);
369 test_star(canvas, paint);
370 test_stairstep(canvas, paint);
371 test_stairstep2(canvas, paint);
372 test_overlapping(canvas, paint);
373 test_monotone_1(canvas, paint);
374 test_monotone_2(canvas, paint);
375 test_monotone_3(canvas, paint);
376 test_monotone_4(canvas, paint);
377 test_monotone_5(canvas, paint);
378 test_degenerate(canvas, paint);
379 test_coincident_edge(canvas, paint);
380 test_bowtie_coincident_triangle(canvas, paint);
381 test_coincident_edges_1(canvas, paint);
382 test_coincident_edges_2(canvas, paint);
383 test_coincident_edges_3(canvas, paint);
384 test_coincident_edges_4(canvas, paint);
385 }
386
387 private:
388 typedef skiagm::GM INHERITED;
389 };
390
391 DEF_GM( return new ConcavePathsGM; )
392