concavepaths.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  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. #include "gm/gm.h"
  8. #include "include/core/SkCanvas.h"
  9. #include "include/core/SkPaint.h"
  10. #include "include/core/SkPath.h"
  11. #include "include/core/SkScalar.h"
  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. .lineTo(SkIntToScalar(80), SkIntToScalar(20))
  19. .lineTo(SkIntToScalar(30), SkIntToScalar(30))
  20. .lineTo(SkIntToScalar(20), SkIntToScalar(80));
  21. canvas->drawPath(path, paint);
  22. }
  23. // Reverse concave test
  24. void test_reverse_concave(SkCanvas* canvas, const SkPaint& paint) {
  25. SkPath path;
  26. canvas->save();
  27. canvas->translate(100, 0);
  28. path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
  29. .lineTo(SkIntToScalar(20), SkIntToScalar(80))
  30. .lineTo(SkIntToScalar(30), SkIntToScalar(30))
  31. .lineTo(SkIntToScalar(80), SkIntToScalar(20));
  32. canvas->drawPath(path, paint);
  33. canvas->restore();
  34. }
  35. // Bowtie (intersection)
  36. void test_bowtie(SkCanvas* canvas, const SkPaint& paint) {
  37. SkPath path;
  38. canvas->save();
  39. canvas->translate(200, 0);
  40. path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
  41. .lineTo(SkIntToScalar(80), SkIntToScalar(80))
  42. .lineTo(SkIntToScalar(80), SkIntToScalar(20))
  43. .lineTo(SkIntToScalar(20), SkIntToScalar(80));
  44. canvas->drawPath(path, paint);
  45. canvas->restore();
  46. }
  47. // "fake" bowtie (concave, but no intersection)
  48. void test_fake_bowtie(SkCanvas* canvas, const SkPaint& paint) {
  49. SkPath path;
  50. canvas->save();
  51. canvas->translate(300, 0);
  52. path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
  53. .lineTo(SkIntToScalar(50), SkIntToScalar(40))
  54. .lineTo(SkIntToScalar(80), SkIntToScalar(20))
  55. .lineTo(SkIntToScalar(80), SkIntToScalar(80))
  56. .lineTo(SkIntToScalar(50), SkIntToScalar(60))
  57. .lineTo(SkIntToScalar(20), SkIntToScalar(80));
  58. canvas->drawPath(path, paint);
  59. canvas->restore();
  60. }
  61. // Bowtie with a smaller right hand lobe. The outer vertex of the left hand
  62. // lobe intrudes into the interior of the right hand lobe.
  63. void test_intruding_vertex(SkCanvas* canvas, const SkPaint& paint) {
  64. SkPath path;
  65. canvas->save();
  66. canvas->translate(400, 0);
  67. path.setIsVolatile(true);
  68. path.moveTo(20, 20)
  69. .lineTo(50, 50)
  70. .lineTo(68, 20)
  71. .lineTo(68, 80)
  72. .lineTo(50, 50)
  73. .lineTo(20, 80);
  74. canvas->drawPath(path, paint);
  75. canvas->restore();
  76. }
  77. // A shape with an edge that becomes inverted on AA stroking and that also contains
  78. // a repeated start/end vertex.
  79. void test_inversion_repeat_vertex(SkCanvas* canvas, const SkPaint& paint) {
  80. SkPath path;
  81. canvas->save();
  82. canvas->translate(400, 100);
  83. path.setIsVolatile(true);
  84. path.moveTo(80, 50)
  85. .lineTo(40, 80)
  86. .lineTo(60, 20)
  87. .lineTo(20, 20)
  88. .lineTo(39.99f, 80)
  89. .lineTo(80, 50);
  90. canvas->drawPath(path, paint);
  91. canvas->restore();
  92. }
  93. // Fish test (intersection/concave)
  94. void test_fish(SkCanvas* canvas, const SkPaint& paint) {
  95. SkPath path;
  96. canvas->save();
  97. canvas->translate(0, 100);
  98. path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
  99. .lineTo(SkIntToScalar(80), SkIntToScalar(80))
  100. .lineTo(SkIntToScalar(70), SkIntToScalar(50))
  101. .lineTo(SkIntToScalar(80), SkIntToScalar(20))
  102. .lineTo(SkIntToScalar(20), SkIntToScalar(80))
  103. .lineTo(SkIntToScalar(0), SkIntToScalar(50));
  104. canvas->drawPath(path, paint);
  105. canvas->restore();
  106. }
  107. // Overlapping "Fast-forward" icon: tests coincidence of inner and outer
  108. // vertices generated by intersection.
  109. void test_fast_forward(SkCanvas* canvas, const SkPaint& paint) {
  110. SkPath path;
  111. canvas->save();
  112. canvas->translate(100, 100);
  113. path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
  114. .lineTo(SkIntToScalar(60), SkIntToScalar(50))
  115. .lineTo(SkIntToScalar(20), SkIntToScalar(80))
  116. .moveTo(SkIntToScalar(40), SkIntToScalar(20))
  117. .lineTo(SkIntToScalar(40), SkIntToScalar(80))
  118. .lineTo(SkIntToScalar(80), SkIntToScalar(50));
  119. canvas->drawPath(path, paint);
  120. canvas->restore();
  121. }
  122. // Square polygon with a square hole.
  123. void test_hole(SkCanvas* canvas, const SkPaint& paint) {
  124. SkPath path;
  125. canvas->save();
  126. canvas->translate(200, 100);
  127. path.addPoly({{20,20}, {80,20}, {80,80}, {20,80}}, false)
  128. .addPoly({{30,30}, {30,70}, {70,70}, {70,30}}, false);
  129. canvas->drawPath(path, paint);
  130. canvas->restore();
  131. }
  132. // Star test (self-intersecting)
  133. void test_star(SkCanvas* canvas, const SkPaint& paint) {
  134. canvas->save();
  135. canvas->translate(300, 100);
  136. canvas->drawPath(SkPath().addPoly({{30,20}, {50,80}, {70,20}, {20,57}, {80,57}}, false),
  137. paint);
  138. canvas->restore();
  139. }
  140. // Exercise a case where the intersection is below a bottom edge.
  141. void test_twist(SkCanvas* canvas, const SkPaint& paint) {
  142. SkPath path;
  143. canvas->save();
  144. path.moveTo( 0.5, 6);
  145. path.lineTo(5.8070392608642578125, 6.4612660408020019531);
  146. path.lineTo(-2.9186885356903076172, 2.811046600341796875);
  147. path.lineTo(0.49999994039535522461, -1.4124038219451904297);
  148. canvas->translate(420, 220);
  149. canvas->scale(10, 10);
  150. canvas->drawPath(path, paint);
  151. canvas->restore();
  152. }
  153. // Stairstep with repeated vert (intersection)
  154. void test_stairstep(SkCanvas* canvas, const SkPaint& paint) {
  155. SkPath path;
  156. canvas->save();
  157. canvas->translate(0, 200);
  158. path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
  159. path.lineTo(SkIntToScalar(50), SkIntToScalar(20));
  160. path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
  161. path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
  162. path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
  163. path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
  164. canvas->drawPath(path, paint);
  165. canvas->restore();
  166. }
  167. void test_stairstep2(SkCanvas* canvas, const SkPaint& paint) {
  168. SkPath path;
  169. canvas->save();
  170. canvas->translate(100, 200);
  171. path.moveTo(20, 60);
  172. path.lineTo(35, 80);
  173. path.lineTo(50, 60);
  174. path.lineTo(65, 80);
  175. path.lineTo(80, 60);
  176. canvas->drawPath(path, paint);
  177. canvas->restore();
  178. }
  179. // Overlapping segments
  180. void test_overlapping(SkCanvas* canvas, const SkPaint& paint) {
  181. SkPath path;
  182. canvas->save();
  183. canvas->translate(200, 200);
  184. path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
  185. path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
  186. path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
  187. path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
  188. canvas->drawPath(path, paint);
  189. canvas->restore();
  190. }
  191. // Two "island" triangles inside a containing rect.
  192. // This exercises the partnering code in the tessellator.
  193. void test_partners(SkCanvas* canvas, const SkPaint& paint) {
  194. SkPath path;
  195. canvas->save();
  196. canvas->translate(300, 200);
  197. path.moveTo(20, 80);
  198. path.lineTo(80, 80);
  199. path.lineTo(80, 20);
  200. path.lineTo(20, 20);
  201. path.moveTo(30, 30);
  202. path.lineTo(45, 50);
  203. path.lineTo(30, 70);
  204. path.moveTo(70, 30);
  205. path.lineTo(70, 70);
  206. path.lineTo(55, 50);
  207. canvas->drawPath(path, paint);
  208. canvas->restore();
  209. }
  210. // A split edge causes one half to be merged to zero winding (destroyed).
  211. // Test that the other half of the split doesn't also get zero winding.
  212. void test_winding_merged_to_zero(SkCanvas* canvas, const SkPaint& paint) {
  213. SkPath path;
  214. canvas->save();
  215. canvas->translate(400, 350);
  216. path.moveTo(20, 80);
  217. path.moveTo(70, -0.000001f);
  218. path.lineTo(70, 0.0);
  219. path.lineTo(60, -30.0);
  220. path.lineTo(40, 20.0);
  221. path.moveTo(50, 50.0);
  222. path.lineTo(50, -50.0);
  223. path.lineTo(10, 50.0);
  224. canvas->drawPath(path, paint);
  225. canvas->restore();
  226. }
  227. // Monotone test 1 (point in the middle)
  228. void test_monotone_1(SkCanvas* canvas, const SkPaint& paint) {
  229. SkPath path;
  230. canvas->save();
  231. canvas->translate(0, 300);
  232. path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
  233. path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
  234. SkIntToScalar(80), SkIntToScalar(50));
  235. path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
  236. SkIntToScalar(20), SkIntToScalar(80));
  237. canvas->drawPath(path, paint);
  238. canvas->restore();
  239. }
  240. // Monotone test 2 (point at the top)
  241. void test_monotone_2(SkCanvas* canvas, const SkPaint& paint) {
  242. SkPath path;
  243. canvas->save();
  244. canvas->translate(100, 300);
  245. path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
  246. path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
  247. path.quadTo(SkIntToScalar(20), SkIntToScalar(20),
  248. SkIntToScalar(20), SkIntToScalar(80));
  249. canvas->drawPath(path, paint);
  250. canvas->restore();
  251. }
  252. // Monotone test 3 (point at the bottom)
  253. void test_monotone_3(SkCanvas* canvas, const SkPaint& paint) {
  254. SkPath path;
  255. canvas->save();
  256. canvas->translate(200, 300);
  257. path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
  258. path.lineTo(SkIntToScalar(80), SkIntToScalar(70));
  259. path.quadTo(SkIntToScalar(20), SkIntToScalar(80),
  260. SkIntToScalar(20), SkIntToScalar(20));
  261. canvas->drawPath(path, paint);
  262. canvas->restore();
  263. }
  264. // Monotone test 4 (merging of two monotones)
  265. void test_monotone_4(SkCanvas* canvas, const SkPaint& paint) {
  266. SkPath path;
  267. canvas->save();
  268. canvas->translate(300, 300);
  269. path.moveTo(80, 25);
  270. path.lineTo(50, 39);
  271. path.lineTo(20, 25);
  272. path.lineTo(40, 45);
  273. path.lineTo(70, 50);
  274. path.lineTo(80, 80);
  275. canvas->drawPath(path, paint);
  276. canvas->restore();
  277. }
  278. // Monotone test 5 (aborted merging of two monotones)
  279. void test_monotone_5(SkCanvas* canvas, const SkPaint& paint) {
  280. SkPath path;
  281. canvas->save();
  282. canvas->translate(0, 400);
  283. path.moveTo(50, 20);
  284. path.lineTo(80, 80);
  285. path.lineTo(50, 50);
  286. path.lineTo(20, 80);
  287. canvas->drawPath(path, paint);
  288. canvas->restore();
  289. }
  290. // Degenerate intersection test
  291. void test_degenerate(SkCanvas* canvas, const SkPaint& paint) {
  292. SkPath path;
  293. canvas->save();
  294. canvas->translate(100, 400);
  295. path.moveTo(50, 20);
  296. path.lineTo(70, 30);
  297. path.lineTo(20, 50);
  298. path.moveTo(50, 20);
  299. path.lineTo(80, 80);
  300. path.lineTo(50, 80);
  301. canvas->drawPath(path, paint);
  302. canvas->restore();
  303. }
  304. // Two triangles with a coincident edge.
  305. void test_coincident_edge(SkCanvas* canvas, const SkPaint& paint) {
  306. SkPath path;
  307. canvas->save();
  308. canvas->translate(200, 400);
  309. path.moveTo(80, 20);
  310. path.lineTo(80, 80);
  311. path.lineTo(20, 80);
  312. path.moveTo(20, 20);
  313. path.lineTo(80, 80);
  314. path.lineTo(20, 80);
  315. canvas->drawPath(path, paint);
  316. canvas->restore();
  317. }
  318. // Bowtie with a coincident triangle (one triangle vertex coincident with the
  319. // bowtie's intersection).
  320. void test_bowtie_coincident_triangle(SkCanvas* canvas, const SkPaint& paint) {
  321. SkPath path;
  322. canvas->save();
  323. canvas->translate(300, 400);
  324. path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
  325. path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
  326. path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
  327. path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
  328. path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
  329. path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
  330. path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
  331. canvas->drawPath(path, paint);
  332. canvas->restore();
  333. }
  334. // Collinear outer boundary edges. In the edge-AA codepath, this creates an overlap region
  335. // which contains a boundary edge. It can't be removed, but it must have the correct winding.
  336. void test_collinear_outer_boundary_edge(SkCanvas* canvas, const SkPaint& paint) {
  337. SkPath path;
  338. canvas->save();
  339. canvas->translate(400, 400);
  340. path.moveTo(20, 20);
  341. path.lineTo(20, 50);
  342. path.lineTo(50, 50);
  343. path.moveTo(80, 50);
  344. path.lineTo(50, 50);
  345. path.lineTo(80, 20);
  346. canvas->drawPath(path, paint);
  347. canvas->restore();
  348. }
  349. // Coincident edges (big ones first, coincident vert on top).
  350. void test_coincident_edges_1(SkCanvas* canvas, const SkPaint& paint) {
  351. SkPath path;
  352. canvas->save();
  353. canvas->translate(0, 500);
  354. path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
  355. path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
  356. path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
  357. path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
  358. path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
  359. path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
  360. canvas->drawPath(path, paint);
  361. canvas->restore();
  362. }
  363. // Coincident edges (small ones first, coincident vert on top).
  364. void test_coincident_edges_2(SkCanvas* canvas, const SkPaint& paint) {
  365. SkPath path;
  366. canvas->save();
  367. canvas->translate(100, 500);
  368. path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
  369. path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
  370. path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
  371. path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
  372. path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
  373. path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
  374. canvas->drawPath(path, paint);
  375. canvas->restore();
  376. }
  377. // Coincident edges (small ones first, coincident vert on bottom).
  378. void test_coincident_edges_3(SkCanvas* canvas, const SkPaint& paint) {
  379. SkPath path;
  380. canvas->save();
  381. canvas->translate(200, 500);
  382. path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
  383. path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
  384. path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
  385. path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
  386. path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
  387. path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
  388. canvas->drawPath(path, paint);
  389. canvas->restore();
  390. }
  391. // Coincident edges (big ones first, coincident vert on bottom).
  392. void test_coincident_edges_4(SkCanvas* canvas, const SkPaint& paint) {
  393. SkPath path;
  394. canvas->save();
  395. canvas->translate(300, 500);
  396. path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
  397. path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
  398. path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
  399. path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
  400. path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
  401. path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
  402. canvas->drawPath(path, paint);
  403. canvas->restore();
  404. }
  405. };
  406. DEF_SIMPLE_GM(concavepaths, canvas, 500, 600) {
  407. SkPaint paint;
  408. paint.setAntiAlias(true);
  409. paint.setStyle(SkPaint::kFill_Style);
  410. test_concave(canvas, paint);
  411. test_reverse_concave(canvas, paint);
  412. test_bowtie(canvas, paint);
  413. test_fake_bowtie(canvas, paint);
  414. test_intruding_vertex(canvas, paint);
  415. test_fish(canvas, paint);
  416. test_fast_forward(canvas, paint);
  417. test_hole(canvas, paint);
  418. test_star(canvas, paint);
  419. test_twist(canvas, paint);
  420. test_inversion_repeat_vertex(canvas, paint);
  421. test_stairstep(canvas, paint);
  422. test_stairstep2(canvas, paint);
  423. test_overlapping(canvas, paint);
  424. test_partners(canvas, paint);
  425. test_winding_merged_to_zero(canvas, paint);
  426. test_monotone_1(canvas, paint);
  427. test_monotone_2(canvas, paint);
  428. test_monotone_3(canvas, paint);
  429. test_monotone_4(canvas, paint);
  430. test_monotone_5(canvas, paint);
  431. test_degenerate(canvas, paint);
  432. test_coincident_edge(canvas, paint);
  433. test_bowtie_coincident_triangle(canvas, paint);
  434. test_collinear_outer_boundary_edge(canvas, paint);
  435. test_coincident_edges_1(canvas, paint);
  436. test_coincident_edges_2(canvas, paint);
  437. test_coincident_edges_3(canvas, paint);
  438. test_coincident_edges_4(canvas, paint);
  439. }