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 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 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) 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) 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. 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. 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) 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. 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. 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) 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. 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) 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 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 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. 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) 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) 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) 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) 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) 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 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. 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). 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). 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). 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). 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). 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