123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470 |
- /*
- * Copyright 2015 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
- #include "gm/gm.h"
- #include "include/core/SkCanvas.h"
- #include "include/core/SkPaint.h"
- #include "include/core/SkPath.h"
- #include "include/core/SkScalar.h"
- namespace {
- // Concave test
- void test_concave(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->translate(0, 0);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
- .lineTo(SkIntToScalar(80), SkIntToScalar(20))
- .lineTo(SkIntToScalar(30), SkIntToScalar(30))
- .lineTo(SkIntToScalar(20), SkIntToScalar(80));
- canvas->drawPath(path, paint);
- }
- // Reverse concave test
- void test_reverse_concave(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(100, 0);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
- .lineTo(SkIntToScalar(20), SkIntToScalar(80))
- .lineTo(SkIntToScalar(30), SkIntToScalar(30))
- .lineTo(SkIntToScalar(80), SkIntToScalar(20));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Bowtie (intersection)
- void test_bowtie(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(200, 0);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
- .lineTo(SkIntToScalar(80), SkIntToScalar(80))
- .lineTo(SkIntToScalar(80), SkIntToScalar(20))
- .lineTo(SkIntToScalar(20), SkIntToScalar(80));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // "fake" bowtie (concave, but no intersection)
- void test_fake_bowtie(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(300, 0);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
- .lineTo(SkIntToScalar(50), SkIntToScalar(40))
- .lineTo(SkIntToScalar(80), SkIntToScalar(20))
- .lineTo(SkIntToScalar(80), SkIntToScalar(80))
- .lineTo(SkIntToScalar(50), SkIntToScalar(60))
- .lineTo(SkIntToScalar(20), SkIntToScalar(80));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Bowtie with a smaller right hand lobe. The outer vertex of the left hand
- // lobe intrudes into the interior of the right hand lobe.
- void test_intruding_vertex(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(400, 0);
- path.setIsVolatile(true);
- path.moveTo(20, 20)
- .lineTo(50, 50)
- .lineTo(68, 20)
- .lineTo(68, 80)
- .lineTo(50, 50)
- .lineTo(20, 80);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // A shape with an edge that becomes inverted on AA stroking and that also contains
- // a repeated start/end vertex.
- void test_inversion_repeat_vertex(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(400, 100);
- path.setIsVolatile(true);
- path.moveTo(80, 50)
- .lineTo(40, 80)
- .lineTo(60, 20)
- .lineTo(20, 20)
- .lineTo(39.99f, 80)
- .lineTo(80, 50);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Fish test (intersection/concave)
- void test_fish(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(0, 100);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
- .lineTo(SkIntToScalar(80), SkIntToScalar(80))
- .lineTo(SkIntToScalar(70), SkIntToScalar(50))
- .lineTo(SkIntToScalar(80), SkIntToScalar(20))
- .lineTo(SkIntToScalar(20), SkIntToScalar(80))
- .lineTo(SkIntToScalar(0), SkIntToScalar(50));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Overlapping "Fast-forward" icon: tests coincidence of inner and outer
- // vertices generated by intersection.
- void test_fast_forward(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(100, 100);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20))
- .lineTo(SkIntToScalar(60), SkIntToScalar(50))
- .lineTo(SkIntToScalar(20), SkIntToScalar(80))
- .moveTo(SkIntToScalar(40), SkIntToScalar(20))
- .lineTo(SkIntToScalar(40), SkIntToScalar(80))
- .lineTo(SkIntToScalar(80), SkIntToScalar(50));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Square polygon with a square hole.
- void test_hole(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(200, 100);
- path.addPoly({{20,20}, {80,20}, {80,80}, {20,80}}, false)
- .addPoly({{30,30}, {30,70}, {70,70}, {70,30}}, false);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Star test (self-intersecting)
- void test_star(SkCanvas* canvas, const SkPaint& paint) {
- canvas->save();
- canvas->translate(300, 100);
- canvas->drawPath(SkPath().addPoly({{30,20}, {50,80}, {70,20}, {20,57}, {80,57}}, false),
- paint);
- canvas->restore();
- }
- // Exercise a case where the intersection is below a bottom edge.
- void test_twist(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- path.moveTo( 0.5, 6);
- path.lineTo(5.8070392608642578125, 6.4612660408020019531);
- path.lineTo(-2.9186885356903076172, 2.811046600341796875);
- path.lineTo(0.49999994039535522461, -1.4124038219451904297);
- canvas->translate(420, 220);
- canvas->scale(10, 10);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Stairstep with repeated vert (intersection)
- void test_stairstep(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(0, 200);
- path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
- path.lineTo(SkIntToScalar(50), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- void test_stairstep2(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(100, 200);
- path.moveTo(20, 60);
- path.lineTo(35, 80);
- path.lineTo(50, 60);
- path.lineTo(65, 80);
- path.lineTo(80, 60);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Overlapping segments
- void test_overlapping(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(200, 200);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Two "island" triangles inside a containing rect.
- // This exercises the partnering code in the tessellator.
- void test_partners(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(300, 200);
- path.moveTo(20, 80);
- path.lineTo(80, 80);
- path.lineTo(80, 20);
- path.lineTo(20, 20);
- path.moveTo(30, 30);
- path.lineTo(45, 50);
- path.lineTo(30, 70);
- path.moveTo(70, 30);
- path.lineTo(70, 70);
- path.lineTo(55, 50);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // A split edge causes one half to be merged to zero winding (destroyed).
- // Test that the other half of the split doesn't also get zero winding.
- void test_winding_merged_to_zero(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(400, 350);
- path.moveTo(20, 80);
- path.moveTo(70, -0.000001f);
- path.lineTo(70, 0.0);
- path.lineTo(60, -30.0);
- path.lineTo(40, 20.0);
- path.moveTo(50, 50.0);
- path.lineTo(50, -50.0);
- path.lineTo(10, 50.0);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Monotone test 1 (point in the middle)
- void test_monotone_1(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(0, 300);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
- path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
- SkIntToScalar(80), SkIntToScalar(50));
- path.quadTo(SkIntToScalar(20), SkIntToScalar(50),
- SkIntToScalar(20), SkIntToScalar(80));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Monotone test 2 (point at the top)
- void test_monotone_2(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(100, 300);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(30));
- path.quadTo(SkIntToScalar(20), SkIntToScalar(20),
- SkIntToScalar(20), SkIntToScalar(80));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Monotone test 3 (point at the bottom)
- void test_monotone_3(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(200, 300);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(70));
- path.quadTo(SkIntToScalar(20), SkIntToScalar(80),
- SkIntToScalar(20), SkIntToScalar(20));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Monotone test 4 (merging of two monotones)
- void test_monotone_4(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(300, 300);
- path.moveTo(80, 25);
- path.lineTo(50, 39);
- path.lineTo(20, 25);
- path.lineTo(40, 45);
- path.lineTo(70, 50);
- path.lineTo(80, 80);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Monotone test 5 (aborted merging of two monotones)
- void test_monotone_5(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(0, 400);
- path.moveTo(50, 20);
- path.lineTo(80, 80);
- path.lineTo(50, 50);
- path.lineTo(20, 80);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Degenerate intersection test
- void test_degenerate(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(100, 400);
- path.moveTo(50, 20);
- path.lineTo(70, 30);
- path.lineTo(20, 50);
- path.moveTo(50, 20);
- path.lineTo(80, 80);
- path.lineTo(50, 80);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Two triangles with a coincident edge.
- void test_coincident_edge(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(200, 400);
- path.moveTo(80, 20);
- path.lineTo(80, 80);
- path.lineTo(20, 80);
- path.moveTo(20, 20);
- path.lineTo(80, 80);
- path.lineTo(20, 80);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Bowtie with a coincident triangle (one triangle vertex coincident with the
- // bowtie's intersection).
- void test_bowtie_coincident_triangle(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(300, 400);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
- path.moveTo(SkIntToScalar(50), SkIntToScalar(50));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Collinear outer boundary edges. In the edge-AA codepath, this creates an overlap region
- // which contains a boundary edge. It can't be removed, but it must have the correct winding.
- void test_collinear_outer_boundary_edge(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(400, 400);
- path.moveTo(20, 20);
- path.lineTo(20, 50);
- path.lineTo(50, 50);
- path.moveTo(80, 50);
- path.lineTo(50, 50);
- path.lineTo(80, 20);
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Coincident edges (big ones first, coincident vert on top).
- void test_coincident_edges_1(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(0, 500);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Coincident edges (small ones first, coincident vert on top).
- void test_coincident_edges_2(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(100, 500);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
- path.moveTo(SkIntToScalar(20), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(80));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(80));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Coincident edges (small ones first, coincident vert on bottom).
- void test_coincident_edges_3(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(200, 500);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
- path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
- path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- // Coincident edges (big ones first, coincident vert on bottom).
- void test_coincident_edges_4(SkCanvas* canvas, const SkPaint& paint) {
- SkPath path;
- canvas->save();
- canvas->translate(300, 500);
- path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(20));
- path.lineTo(SkIntToScalar(80), SkIntToScalar(20));
- path.moveTo(SkIntToScalar(20), SkIntToScalar(80));
- path.lineTo(SkIntToScalar(20), SkIntToScalar(50));
- path.lineTo(SkIntToScalar(50), SkIntToScalar(50));
- canvas->drawPath(path, paint);
- canvas->restore();
- }
- };
- DEF_SIMPLE_GM(concavepaths, canvas, 500, 600) {
- SkPaint paint;
- paint.setAntiAlias(true);
- paint.setStyle(SkPaint::kFill_Style);
- test_concave(canvas, paint);
- test_reverse_concave(canvas, paint);
- test_bowtie(canvas, paint);
- test_fake_bowtie(canvas, paint);
- test_intruding_vertex(canvas, paint);
- test_fish(canvas, paint);
- test_fast_forward(canvas, paint);
- test_hole(canvas, paint);
- test_star(canvas, paint);
- test_twist(canvas, paint);
- test_inversion_repeat_vertex(canvas, paint);
- test_stairstep(canvas, paint);
- test_stairstep2(canvas, paint);
- test_overlapping(canvas, paint);
- test_partners(canvas, paint);
- test_winding_merged_to_zero(canvas, paint);
- test_monotone_1(canvas, paint);
- test_monotone_2(canvas, paint);
- test_monotone_3(canvas, paint);
- test_monotone_4(canvas, paint);
- test_monotone_5(canvas, paint);
- test_degenerate(canvas, paint);
- test_coincident_edge(canvas, paint);
- test_bowtie_coincident_triangle(canvas, paint);
- test_collinear_outer_boundary_edge(canvas, paint);
- test_coincident_edges_1(canvas, paint);
- test_coincident_edges_2(canvas, paint);
- test_coincident_edges_3(canvas, paint);
- test_coincident_edges_4(canvas, paint);
- }
|