skia2/tests/TriangulatingPathRendererTests.cpp

1023 lines
44 KiB
C++
Raw Normal View History

/*
* 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 "tests/Test.h"
#include "include/core/SkPath.h"
#include "include/effects/SkGradientShader.h"
#include "include/gpu/GrDirectContext.h"
#include "src/gpu/GrDirectContextPriv.h"
#include "src/gpu/GrEagerVertexAllocator.h"
#include "src/gpu/GrInnerFanTriangulator.h"
#include "src/gpu/GrStyle.h"
#include "src/gpu/GrSurfaceDrawContext.h"
#include "src/gpu/effects/GrPorterDuffXferProcessor.h"
Reland "Rename GrShape to GrStyledShape" This reverts commit 73b86c1ade1c002cfa4da66c33c25514737e2416. Reason for revert: wasn't the problem, relanding Original change's description: > Revert "Rename GrShape to GrStyledShape" > > This reverts commit f3f08af010eebdb9c1501bdf1b3d8e715298b05f. > > Reason for revert: maybe this is breaking the ios perf bot; it is the first CL that caused the bot to fail, but I can't really say why this would break them. > > Original change's description: > > Rename GrShape to GrStyledShape > > > > Change-Id: Ic457e634b4b95356f5615cff3fce1ca7d7677c26 > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/284036 > > Reviewed-by: Robert Phillips <robertphillips@google.com> > > Commit-Queue: Michael Ludwig <michaelludwig@google.com> > > TBR=bsalomon@google.com,robertphillips@google.com,michaelludwig@google.com > > # Not skipping CQ checks because original CL landed > 1 day ago. > > Change-Id: I2e5adbfc820811fbbde9cb57af28f86a7ba40bd9 > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/284231 > Reviewed-by: Michael Ludwig <michaelludwig@google.com> > Commit-Queue: Michael Ludwig <michaelludwig@google.com> TBR=bsalomon@google.com,robertphillips@google.com,michaelludwig@google.com Change-Id: Ifdd52f8bacb5d66a7bf58efd328675c4c443ac8a No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://skia-review.googlesource.com/c/skia/+/284376 Reviewed-by: Michael Ludwig <michaelludwig@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
2020-04-17 20:21:37 +00:00
#include "src/gpu/geometry/GrStyledShape.h"
#include "src/gpu/ops/GrTriangulatingPathRenderer.h"
#include "src/shaders/SkShaderBase.h"
#include "tools/ToolUtils.h"
#include <map>
/*
* These tests pass by not crashing, hanging or asserting in Debug.
*/
using CreatePathFn = SkPath(*)();
CreatePathFn kNonEdgeAAPaths[] = {
// Tests active edges made inactive by splitting.
// Also tests active edge list forced into an invalid ordering by
// splitting (mopped up in cleanup_active_edges()).
[]() -> SkPath {
SkPath path;
path.moveTo(229.127044677734375f, 67.34100341796875f);
path.lineTo(187.8097381591796875f, -6.7729740142822265625f);
path.lineTo(171.411407470703125f, 50.94266510009765625f);
path.lineTo(245.5253753662109375f, 9.6253643035888671875f);
path.moveTo(208.4683990478515625f, 30.284009933471679688f);
path.lineTo(171.411407470703125f, 50.94266510009765625f);
path.lineTo(187.8097381591796875f, -6.7729740142822265625f);
return path;
},
// Intersections which fall exactly on the current vertex, and require
// a restart of the intersection checking.
[]() -> SkPath {
SkPath path;
path.moveTo(314.483551025390625f, 486.246002197265625f);
path.lineTo(385.41949462890625f, 532.8087158203125f);
path.lineTo(373.232879638671875f, 474.05938720703125f);
path.lineTo(326.670166015625f, 544.995361328125f);
path.moveTo(349.951507568359375f, 509.52734375f);
path.lineTo(373.232879638671875f, 474.05938720703125f);
path.lineTo(385.41949462890625f, 532.8087158203125f);
return path;
},
// Tests active edges which are removed by splitting.
[]() -> SkPath {
SkPath path;
path.moveTo(343.107391357421875f, 613.62176513671875f);
path.lineTo(426.632415771484375f, 628.5740966796875f);
path.lineTo(392.3460693359375f, 579.33544921875f);
path.lineTo(377.39373779296875f, 662.86041259765625f);
path.moveTo(384.869873046875f, 621.097900390625f);
path.lineTo(392.3460693359375f, 579.33544921875f);
path.lineTo(426.632415771484375f, 628.5740966796875f);
return path;
},
// Collinear edges merged in set_top().
// Also, an intersection between left and right enclosing edges which
// falls above the current vertex.
[]() -> SkPath {
SkPath path;
path.moveTo(545.95751953125f, 791.69854736328125f);
path.lineTo(612.05816650390625f, 738.494140625f);
path.lineTo(552.4056396484375f, 732.0460205078125f);
path.lineTo(605.61004638671875f, 798.14666748046875f);
path.moveTo(579.00787353515625f, 765.0963134765625f);
path.lineTo(552.4056396484375f, 732.0460205078125f);
path.lineTo(612.05816650390625f, 738.494140625f);
return path;
},
// Tests active edges which are made inactive by set_top().
[]() -> SkPath {
SkPath path;
path.moveTo(819.2725830078125f, 751.77447509765625f);
path.lineTo(820.70904541015625f, 666.933837890625f);
path.lineTo(777.57049560546875f, 708.63592529296875f);
path.lineTo(862.4111328125f, 710.0723876953125f);
path.moveTo(819.99078369140625f, 709.3541259765625f);
path.lineTo(777.57049560546875f, 708.63592529296875f);
path.lineTo(820.70904541015625f, 666.933837890625f);
return path;
},
[]() -> SkPath {
SkPath path;
path.moveTo(823.33209228515625f, 749.052734375f);
path.lineTo(823.494873046875f, 664.20013427734375f);
path.lineTo(780.9871826171875f, 706.5450439453125f);
path.lineTo(865.8397216796875f, 706.70782470703125f);
path.moveTo(823.4134521484375f, 706.6263427734375f);
path.lineTo(780.9871826171875f, 706.5450439453125f);
path.lineTo(823.494873046875f, 664.20013427734375f);
return path;
},
[]() -> SkPath {
SkPath path;
path.moveTo(954.862548828125f, 562.8349609375f);
path.lineTo(899.32818603515625f, 498.679443359375f);
path.lineTo(895.017578125f, 558.52435302734375f);
path.lineTo(959.17315673828125f, 502.990081787109375f);
path.moveTo(927.0953369140625f, 530.7572021484375f);
path.lineTo(895.017578125f, 558.52435302734375f);
path.lineTo(899.32818603515625f, 498.679443359375f);
return path;
},
[]() -> SkPath {
SkPath path;
path.moveTo(958.5330810546875f, 547.35516357421875f);
path.lineTo(899.93109130859375f, 485.989013671875f);
path.lineTo(898.54901123046875f, 545.97308349609375f);
path.lineTo(959.9151611328125f, 487.37109375f);
path.moveTo(929.2320556640625f, 516.67205810546875f);
path.lineTo(898.54901123046875f, 545.97308349609375f);
path.lineTo(899.93109130859375f, 485.989013671875f);
return path;
},
[]() -> SkPath {
SkPath path;
path.moveTo(389.8609619140625f, 369.326873779296875f);
path.lineTo(470.6290283203125f, 395.33697509765625f);
path.lineTo(443.250030517578125f, 341.9478759765625f);
path.lineTo(417.239959716796875f, 422.7159423828125f);
path.moveTo(430.244964599609375f, 382.3319091796875f);
path.lineTo(443.250030517578125f, 341.9478759765625f);
path.lineTo(470.6290283203125f, 395.33697509765625f);
return path;
},
[]() -> SkPath {
SkPath path;
path.moveTo(20, 20);
path.lineTo(50, 80);
path.lineTo(20, 80);
path.moveTo(80, 50);
path.lineTo(50, 50);
path.lineTo(20, 50);
return path;
},
[]() -> SkPath {
SkPath path;
path.moveTo(257.19439697265625f, 320.876617431640625f);
path.lineTo(190.113037109375f, 320.58978271484375f);
path.lineTo(203.64404296875f, 293.8145751953125f);
path.moveTo(203.357177734375f, 360.896026611328125f);
path.lineTo(216.88824462890625f, 334.120819091796875f);
path.lineTo(230.41925048828125f, 307.345611572265625f);
return path;
},
// A degenerate segments case, where both upper and lower segments of
// a split edge must remain active.
[]() -> SkPath {
SkPath path;
path.moveTo(231.9331207275390625f, 306.2012939453125f);
path.lineTo(191.4859161376953125f, 306.04547119140625f);
path.lineTo(231.0659332275390625f, 300.2642822265625f);
path.moveTo(189.946807861328125f, 302.072265625f);
path.lineTo(179.79705810546875f, 294.859771728515625f);
path.lineTo(191.0016021728515625f, 296.165679931640625f);
path.moveTo(150.8942108154296875f, 304.900146484375f);
path.lineTo(179.708892822265625f, 297.849029541015625f);
path.lineTo(190.4742279052734375f, 299.11895751953125f);
return path;
},
// Handle the case where edge.dist(edge.fTop) != 0.0.
[]() -> SkPath {
SkPath path;
path.moveTo( 0.0f, 400.0f);
path.lineTo( 138.0f, 202.0f);
path.lineTo( 0.0f, 202.0f);
path.moveTo( 12.62693023681640625f, 250.57464599609375f);
path.lineTo( 8.13896942138671875f, 254.556884765625f);
path.lineTo(-18.15641021728515625f, 220.40203857421875f);
path.lineTo(-15.986493110656738281f, 219.6513519287109375f);
path.moveTo( 36.931194305419921875f, 282.485504150390625f);
path.lineTo( 15.617521286010742188f, 261.2901611328125f);
path.lineTo( 10.3829498291015625f, 252.565765380859375f);
path.lineTo(-16.165292739868164062f, 222.646026611328125f);
return path;
},
// A degenerate segments case which exercises inactive edges being
// made active by splitting.
[]() -> SkPath {
SkPath path;
path.moveTo(690.62127685546875f, 509.25555419921875f);
path.lineTo(99.336181640625f, 511.71405029296875f);
path.lineTo(708.362548828125f, 512.4349365234375f);
path.lineTo(729.9940185546875f, 516.3114013671875f);
path.lineTo(738.708984375f, 518.76995849609375f);
path.lineTo(678.3463134765625f, 510.0819091796875f);
path.lineTo(681.21795654296875f, 504.81378173828125f);
path.moveTo(758.52764892578125f, 521.55963134765625f);
path.lineTo(719.1549072265625f, 514.50372314453125f);
path.lineTo(689.59063720703125f, 512.0628662109375f);
path.lineTo(679.78216552734375f, 507.447845458984375f);
return path;
},
// Tests vertices which become "orphaned" (ie., no connected edges)
// after simplification.
[]() -> SkPath {
SkPath path;
path.moveTo(217.326019287109375f, 166.4752960205078125f);
path.lineTo(226.279266357421875f, 170.929473876953125f);
path.lineTo(234.3973388671875f, 177.0623626708984375f);
path.lineTo(262.0921630859375f, 188.746124267578125f);
path.moveTo(196.23638916015625f, 174.0722198486328125f);
path.lineTo(416.15277099609375f, 180.138214111328125f);
path.lineTo(192.651947021484375f, 304.0228271484375f);
return path;
},
[]() -> SkPath {
SkPath path;
path.moveTo( 0.0f, 0.0f);
path.lineTo(10000.0f, 0.0f);
path.lineTo( 0.0f, -1.0f);
path.lineTo(10000.0f, 0.000001f);
path.lineTo( 0.0f, -30.0f);
return path;
},
// Reduction of Nebraska-StateSeal.svg. Floating point error causes the
// same edge to be added to more than one poly on the same side.
[]() -> SkPath {
SkPath path;
path.moveTo(170.8199920654296875, 491.86700439453125);
path.lineTo(173.7649993896484375, 489.7340087890625);
path.lineTo(174.1450958251953125, 498.545989990234375);
path.lineTo( 171.998992919921875, 500.88201904296875);
path.moveTo(168.2922515869140625, 498.66265869140625);
path.lineTo(169.8589935302734375, 497.94500732421875);
path.lineTo( 172, 500.88299560546875);
path.moveTo( 169.555267333984375, 490.70111083984375);
path.lineTo(173.7649993896484375, 489.7340087890625);
path.lineTo( 170.82000732421875, 491.86700439453125);
return path;
},
// A shape with a vertex collinear to the right hand edge.
// This messes up find_enclosing_edges.
[]() -> SkPath {
SkPath path;
path.moveTo(80, 20);
path.lineTo(80, 60);
path.lineTo(20, 60);
path.moveTo(80, 50);
path.lineTo(80, 80);
path.lineTo(20, 80);
return path;
},
// Exercises the case where an edge becomes collinear with *two* of its
// adjacent neighbour edges after splitting.
// This is a reduction from
// http://mooooo.ooo/chebyshev-sine-approximation/horner_ulp.svg
[]() -> SkPath {
SkPath path;
path.moveTo( 351.99298095703125, 348.23046875);
path.lineTo( 351.91876220703125, 347.33984375);
path.lineTo( 351.91876220703125, 346.1953125);
path.lineTo( 351.90313720703125, 347.734375);
path.lineTo( 351.90313720703125, 346.1328125);
path.lineTo( 351.87579345703125, 347.93359375);
path.lineTo( 351.87579345703125, 345.484375);
path.lineTo( 351.86407470703125, 347.7890625);
path.lineTo( 351.86407470703125, 346.2109375);
path.lineTo( 351.84844970703125, 347.63763427734375);
path.lineTo( 351.84454345703125, 344.19232177734375);
path.lineTo( 351.78204345703125, 346.9483642578125);
path.lineTo( 351.758636474609375, 347.18310546875);
path.lineTo( 351.75469970703125, 346.75);
path.lineTo( 351.75469970703125, 345.46875);
path.lineTo( 352.5546875, 345.46875);
path.lineTo( 352.55078125, 347.01953125);
path.lineTo( 351.75079345703125, 347.02313232421875);
path.lineTo( 351.74688720703125, 346.15203857421875);
path.lineTo( 351.74688720703125, 347.646148681640625);
path.lineTo( 352.5390625, 346.94140625);
path.lineTo( 351.73907470703125, 346.94268798828125);
path.lineTo( 351.73516845703125, 344.48565673828125);
path.lineTo( 352.484375, 346.73828125);
path.lineTo( 351.68438720703125, 346.7401123046875);
path.lineTo( 352.4765625, 346.546875);
path.lineTo( 351.67657470703125, 346.54937744140625);
path.lineTo( 352.47265625, 346.75390625);
path.lineTo( 351.67266845703125, 346.756622314453125);
path.lineTo( 351.66876220703125, 345.612091064453125);
return path;
},
// A path which contains out-of-range colinear intersections.
[]() -> SkPath {
SkPath path;
path.moveTo( 0, 63.39080047607421875);
path.lineTo(-0.70804601907730102539, 63.14350128173828125);
path.lineTo(-7.8608899287380243391e-17, 64.14080047607421875);
path.moveTo( 0, 64.14080047607421875);
path.lineTo(44.285900115966796875, 64.14080047607421875);
path.lineTo( 0, 62.64080047607421875);
path.moveTo(21.434900283813476562, -0.24732701480388641357);
path.lineTo(-0.70804601907730102539, 63.14350128173828125);
path.lineTo(0.70804601907730102539, 63.6381988525390625);
return path;
},
// A path which results in infs and nans when conics are converted to quads.
[]() -> SkPath {
SkPath path;
path.moveTo(-2.20883e+37f, -1.02892e+37f);
path.conicTo(-2.00958e+38f, -9.36107e+37f, -1.7887e+38f, -8.33215e+37f, 0.707107f);
path.conicTo(-1.56782e+38f, -7.30323e+37f, 2.20883e+37f, 1.02892e+37f, 0.707107f);
path.conicTo(2.00958e+38f, 9.36107e+37f, 1.7887e+38f, 8.33215e+37f, 0.707107f);
path.conicTo(1.56782e+38f, 7.30323e+37f, -2.20883e+37f, -1.02892e+37f, 0.707107f);
return path;
},
// A quad which generates a huge number of points (>2B) when uniformly
// linearized. This should not hang or OOM.
[]() -> SkPath {
SkPath path;
path.moveTo(10, 0);
path.lineTo(0, 0);
path.quadTo(10, 0, 0, 8315084722602508288);
return path;
},
// A path which hangs during simplification. It produces an edge which is
// to the left of its own endpoints, which causes an infinite loop in the
// right-enclosing-edge splitting.
[]() -> SkPath {
SkPath path;
path.moveTo(0.75001740455627441406, 23.051967620849609375);
path.lineTo(5.8471612930297851562, 22.731662750244140625);
path.lineTo(10.749670028686523438, 22.253145217895507812);
path.lineTo(13.115868568420410156, 22.180681228637695312);
path.lineTo(15.418928146362304688, 22.340015411376953125);
path.lineTo( 17.654022216796875, 22.82159423828125);
path.lineTo(19.81632232666015625, 23.715869903564453125);
path.lineTo(40, 0);
path.lineTo(5.5635203441547955577e-15, 0);
path.lineTo(5.5635203441547955577e-15, 47);
path.lineTo(-1.4210854715202003717e-14, 21.713298797607421875);
path.lineTo(0.75001740455627441406, 21.694292068481445312);
path.lineTo(0.75001740455627441406, 23.051967620849609375);
return path;
},
// Reduction from skbug.com/7911 that causes a crash due to splitting a
// zombie edge.
[]() -> SkPath {
SkPath path;
path.moveTo( 0, 1.0927740941146660348e+24);
path.lineTo(2.9333931225865729333e+32, 16476101);
path.lineTo(1.0927731573659435417e+24, 1.0927740941146660348e+24);
path.lineTo(1.0927740941146660348e+24, 3.7616281094287041715e-37);
path.lineTo(1.0927740941146660348e+24, 1.0927740941146660348e+24);
path.lineTo(1.3061803026169399536e-33, 1.0927740941146660348e+24);
path.lineTo(4.7195362919941370727e-16, -8.4247545146051822591e+32);
return path;
},
// From crbug.com/844873. Crashes trying to merge a zombie edge.
[]() -> SkPath {
SkPath path;
path.moveTo( 316.000579833984375, -4338355948977389568);
path.lineTo(1.5069369808623501312e+20, 75180972320904708096.0);
path.lineTo(1.5069369808623501312e+20, 75180972320904708096.0);
path.lineTo( 771.21014404296875, -4338355948977389568.0);
path.lineTo( 316.000579833984375, -4338355948977389568.0);
path.moveTo( 354.208984375, -4338355948977389568.0);
path.lineTo( 773.00177001953125, -4338355948977389568.0);
path.lineTo(1.5069369808623501312e+20, 75180972320904708096.0);
path.lineTo(1.5069369808623501312e+20, 75180972320904708096.0);
path.lineTo( 354.208984375, -4338355948977389568.0);
return path;
},
// From crbug.com/844873. Hangs repeatedly splitting alternate vertices.
[]() -> SkPath {
SkPath path;
path.moveTo(10, -1e+20f);
path.lineTo(11, 25000);
path.lineTo(10, 25000);
path.lineTo(11, 25010);
return path;
},
// Reduction from circular_arcs_stroke_and_fill_round GM which
// repeatedly splits on the opposite edge from case 34 above.
[]() -> SkPath {
SkPath path;
path.moveTo( 16.25, 26.495191574096679688);
path.lineTo(32.420825958251953125, 37.377376556396484375);
path.lineTo(25.176382064819335938, 39.31851959228515625);
path.moveTo( 20, 20);
path.lineTo(28.847436904907226562, 37.940830230712890625);
path.lineTo(25.17638397216796875, 39.31851959228515625);
return path;
},
// Reduction from crbug.com/843135 where an intersection is found
// below the bottom of both intersected edges.
[]() -> SkPath {
SkPath path;
path.moveTo(-2791476679359332352, 2608107002026524672);
path.lineTo( 0, 11.95427703857421875);
path.lineTo(-2781824066779086848, 2599088532777598976);
path.lineTo( -7772.6875, 7274);
return path;
},
// Reduction from crbug.com/843135. Exercises a case where an intersection is missed.
// This causes bad ordering in the active edge list.
[]() -> SkPath {
SkPath path;
path.moveTo(-1.0662557646016024569e+23, 9.9621425197286319718e+22);
path.lineTo( -121806400, 113805032);
path.lineTo( -120098872, 112209680);
path.lineTo( 6.2832999862817380468e-36, 2.9885697364807128906);
return path;
},
// Reduction from crbug.com/851409. Exercises collinear last vertex.
[]() -> SkPath {
SkPath path;
path.moveTo(2072553216, 0);
path.lineTo(2072553216, 1);
path.lineTo(2072553472, -13.5);
path.lineTo(2072553216, 0);
path.lineTo(2072553472, -6.5);
return path;
},
// Another reduction from crbug.com/851409. Exercises two sequential collinear edges.
[]() -> SkPath {
SkPath path;
path.moveTo(2072553216, 0);
path.lineTo(2072553216, 1);
path.lineTo(2072553472, -13);
path.lineTo(2072553216, 0);
path.lineTo(2072553472, -6);
path.lineTo(2072553472, -13);
return path;
},
// Reduction from crbug.com/860655. Cause is three collinear edges discovered during
// sanitize_contours pass, before the vertices have been found coincident.
[]() -> SkPath {
SkPath path;
path.moveTo( 32572426382475264, -3053391034974208);
path.lineTo( 521289856, -48865776);
path.lineTo( 130322464, -12215873);
path.moveTo( 32572426382475264, -3053391034974208);
path.lineTo( 521289856, -48865776);
path.lineTo( 130322464, -12215873);
path.moveTo( 32572426382475264, -3053391034974208);
path.lineTo( 32114477642022912, -3010462031544320);
path.lineTo( 32111784697528320, -3010209702215680);
return path;
},
};
// A simple concave path. Test this with a non-invertible matrix.
static SkPath create_path_17() {
SkPath path;
path.moveTo(20, 20);
path.lineTo(80, 20);
path.lineTo(30, 30);
path.lineTo(20, 80);
return path;
}
// An intersection above the first vertex in the mesh.
// Reduction from http://crbug.com/730687
static SkPath create_path_20() {
SkPath path;
path.moveTo( 2822128.5, 235.026336669921875);
path.lineTo( 2819349.25, 235.3623504638671875);
path.lineTo( -340558688, 23.83478546142578125);
path.lineTo( -340558752, 25.510419845581054688);
path.lineTo( -340558720, 27.18605804443359375);
return path;
}
// An intersection whose result is NaN (due to rounded-to-inf endpoint).
static SkPath create_path_21() {
SkPath path;
path.moveTo(1.7889142061167663539e+38, 39338463358011572224.0);
path.lineTo( 1647.4193115234375, -522.603515625);
path.lineTo( 1677.74560546875, -529.0028076171875);
path.lineTo( 1678.29541015625, -528.7847900390625);
path.lineTo( 1637.5167236328125, -519.79266357421875);
path.lineTo( 1647.4193115234375, -522.603515625);
return path;
}
GrTessellator: implement straight skeleton, phase 2. This CL implements two major changes to the AA tessellating path renderer: 1) Fix inverted edges after stroke and simplify. Instead of detecting and fixing edges which invert on stroking during the stroking pass, we run the full simplify pass on both inner and outer contours, then create edge collapse events for the overlap regions. We then process the edge events in a priority queue and process them in order of decreasing alpha (this is the "edge event" part of the straight skeleton algorithm). By doing it after simplification, we ensure that there's a full-alpha intersection vertex to join the collapse edge to (which may have <1 alpha), so no spurious gradients appear in the rendered path. 2) "Pointy" vertices (defined as those which meet at an acute angle less than 14 degrees) are now properly bevelled off during stroking. This removes antialiasing artifacts which extend beyond the path boundary. Some ancillary changes: The extracted boundaries which are input to stroking have their line equations pre-normalized, and multiplied by winding. This simplifies a lot of code which was performing this computation on the fly. The workaround for the "intruding vertex" problem was removed, since the straight skeleton now moves the intruding vertex before it can cause problems. Bug: 756823 Change-Id: I271ed32be6847da55273b387e8c04bbf9b512b70 Reviewed-on: https://skia-review.googlesource.com/87341 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Stephen White <senorblanco@chromium.org>
2017-12-19 23:09:54 +00:00
// An edge collapse event which also collapses a neighbour, requiring
// its event to be removed.
static SkPath create_path_25() {
SkPath path;
path.moveTo( 43.44110107421875, 148.15106201171875);
path.lineTo( 44.64471435546875, 148.16748046875);
path.lineTo( 46.35009765625, 147.403076171875);
path.lineTo( 46.45404052734375, 148.34906005859375);
path.lineTo( 45.0400390625, 148.54205322265625);
path.lineTo( 44.624053955078125, 148.9810791015625);
path.lineTo( 44.59405517578125, 149.16107177734375);
path.lineTo( 44.877044677734375, 149.62005615234375);
path.lineTo(144.373016357421875, 68.8070068359375);
return path;
}
// An edge collapse event causes an edge to become collinear, requiring
// its event to be removed.
static SkPath create_path_26() {
SkPath path;
path.moveTo( 43.44110107421875, 148.15106201171875);
path.lineTo( 44.64471435546875, 148.16748046875);
path.lineTo( 46.35009765625, 147.403076171875);
path.lineTo( 46.45404052734375, 148.34906005859375);
path.lineTo( 45.0400390625, 148.54205322265625);
path.lineTo( 44.624053955078125, 148.9810791015625);
path.lineTo( 44.59405517578125, 149.16107177734375);
path.lineTo( 44.877044677734375, 149.62005615234375);
path.lineTo(144.373016357421875, 68.8070068359375);
return path;
}
// A path which results in non-finite points when stroked and bevelled for AA.
static SkPath create_path_27() {
SkPath path;
path.moveTo(8.5027233009104409507e+37, 1.7503381025241130639e+37);
path.lineTo(7.0923661737711584874e+37, 1.4600074517285415699e+37);
path.lineTo(7.0848733446033294691e+37, 1.4584649744781838604e+37);
path.lineTo(-2.0473916115129349496e+37, -4.2146796450364162012e+36);
path.lineTo(2.0473912312177548811e+37, 4.2146815465123165435e+36);
return path;
}
// AA stroking this path produces intersection failures on bevelling.
// This should skip the point, but not assert.
static SkPath create_path_28() {
SkPath path;
path.moveTo(-7.5952312625177475154e+21, -2.6819185100266674911e+24);
path.lineTo( 1260.3787841796875, 1727.7947998046875);
path.lineTo( 1260.5567626953125, 1728.0386962890625);
path.lineTo(1.1482511310557754163e+21, 4.054538502765980051e+23);
path.lineTo(-7.5952312625177475154e+21, -2.6819185100266674911e+24);
return path;
}
// A path with vertices which become infinite on AA stroking. Should not crash or assert.
static SkPath create_path_31() {
SkPath path;
path.moveTo(2.0257809259190991347e+36, -1244080640);
path.conicTo(2.0257809259190991347e+36, -1244080640,
2.0257809259190991347e+36, 0.10976474732160568237, 0.70710676908493041992);
path.lineTo(-10036566016, -1954718402215936);
path.conicTo(-1.1375507718551896064e+20, -1954721086570496,
10036566016, -1954721086570496, 0.70710676908493041992);
return path;
}
// Reduction from crbug.com/851914.
static SkPath create_path_38() {
SkPath path;
path.moveTo(14.400531768798828125, 17.711114883422851562);
path.lineTo(14.621990203857421875, 171563104293879808);
path.lineTo(14.027951240539550781, 872585759381520384);
path.lineTo( 14.0216827392578125, 872665817571917824);
path.lineTo(7.699314117431640625, -3417320793833472);
path.moveTo(11.606547355651855469, 17.40966796875);
path.lineTo( 7642114886926860288, 21.08358001708984375);
path.lineTo(11.606547355651855469, 21.08358001708984375);
return path;
}
// Reduction from crbug.com/860453. Tests a case where a "missing" intersection
// requires the active edge list to go out-of-order.
static SkPath create_path_41() {
SkPath path;
path.moveTo(72154931603311689728.0, 330.95965576171875);
path.lineTo(24053266013925408768.0, 78.11376953125);
path.lineTo(1.2031099003292404941e+20, 387.168731689453125);
path.lineTo(68859835992355373056.0, 346.55047607421875);
path.lineTo(76451708695451009024.0, 337.780029296875);
path.moveTo(-20815817797613387776.0, 18065700622522384384.0);
path.lineTo(-72144121204987396096.0, 142.855804443359375);
path.lineTo(72144121204987396096.0, 325.184783935546875);
path.lineTo(1.2347242901040791552e+20, 18065700622522384384.0);
return path;
}
// Reduction from crbug.com/866319. Cause is edges that are collinear when tested from
// one side, but non-collinear when tested from the other.
static SkPath create_path_43() {
SkPath path;
path.moveTo( 307316821852160, -28808363114496);
path.lineTo( 307165222928384, -28794154909696);
path.lineTo( 307013691113472, -28779948802048);
path.lineTo( 306862159298560, -28765744791552);
path.lineTo( 306870313025536, -28766508154880);
path.lineTo( 307049695019008, -28783327313920);
path.lineTo( 307408660332544, -28816974020608);
return path;
}
// Reduction from crbug.com/966696
static SkPath create_path_44() {
SkPath path;
path.moveTo(114.4606170654296875, 186.443878173828125);
path.lineTo( 91.5394744873046875, 185.4189453125);
path.lineTo(306.45538330078125, 3203.986083984375);
path.moveTo(16276206965409972224.0, 815.59393310546875);
path.lineTo(-3.541605062372533207e+20, 487.7236328125);
path.lineTo(-3.541605062372533207e+20, 168.204071044921875);
path.lineTo(16276206965409972224.0, 496.07427978515625);
path.moveTo(-3.541605062372533207e+20, 167.00958251953125);
path.lineTo(-3.541605062372533207e+20, 488.32086181640625);
path.lineTo(16276206965409972224.0, 816.78839111328125);
path.lineTo(16276206965409972224.0, 495.47705078125);
return path;
}
// Reduction from crbug.com/966274.
static SkPath create_path_45() {
SkPath path;
path.moveTo( 706471854080, 379003666432);
path.lineTo( 706503180288, 379020443648);
path.lineTo( 706595717120, 379070087168);
path.lineTo( 706626060288, 379086372864);
path.lineTo( 706656141312, 379102527488);
path.lineTo( 706774171648, 379165835264);
path.lineTo( 706803073024, 379181334528);
path.lineTo( 706831712256, 379196702720);
path.lineTo( 706860154880, 379211939840);
path.lineTo( 706888335360, 379227078656);
path.lineTo( 706916253696, 379242053632);
path.lineTo( 706956820480, 379263811584);
path.lineTo( 706929098752, 379248934912);
path.lineTo( 706901114880, 379233927168);
path.lineTo( 706872934400, 379218821120);
path.lineTo( 706844491776, 379203551232);
path.lineTo( 706815787008, 379188183040);
path.lineTo( 706786885632, 379172651008);
path.lineTo( 706757722112, 379156987904);
path.lineTo( 706728296448, 379141226496);
path.lineTo( 706698608640, 379125301248);
path.lineTo( 706668724224, 379109244928);
path.lineTo( 706638577664, 379093090304);
path.lineTo( 706608168960, 379076771840);
path.lineTo( 706484174848, 379010252800);
return path;
}
// Reduction from crbug.com/969359. Inf generated by intersections
// causes NaN in subsequent intersections, leading to assert or hang.
static SkPath create_path_46() {
SkPath path;
path.moveTo(1.0321827899075254821e+37, -5.1199920965387697886e+37);
path.lineTo(-1.0321827899075254821e+37, 5.1199920965387697886e+37);
path.lineTo(-1.0425214946728668754e+37, 4.5731834042267216669e+37);
path.moveTo(-9.5077331762291841872e+36, 8.1304868292377430302e+37);
path.lineTo(9.5077331762291841872e+36, -8.1304868292377430302e+37);
path.lineTo(1.0795449417808426232e+37, 1.2246856113744539311e+37);
path.moveTo(-165.8018341064453125, -44.859375);
path.lineTo(-9.558702871563160835e+36, -7.9814405281448285475e+37);
path.lineTo(-9.4147814283168490381e+36, -8.3935116522790983488e+37);
return path;
}
static std::unique_ptr<GrFragmentProcessor> create_linear_gradient_processor(
GrRecordingContext* rContext) {
GrTessellator: implement straight skeleton, phase 2. This CL implements two major changes to the AA tessellating path renderer: 1) Fix inverted edges after stroke and simplify. Instead of detecting and fixing edges which invert on stroking during the stroking pass, we run the full simplify pass on both inner and outer contours, then create edge collapse events for the overlap regions. We then process the edge events in a priority queue and process them in order of decreasing alpha (this is the "edge event" part of the straight skeleton algorithm). By doing it after simplification, we ensure that there's a full-alpha intersection vertex to join the collapse edge to (which may have <1 alpha), so no spurious gradients appear in the rendered path. 2) "Pointy" vertices (defined as those which meet at an acute angle less than 14 degrees) are now properly bevelled off during stroking. This removes antialiasing artifacts which extend beyond the path boundary. Some ancillary changes: The extracted boundaries which are input to stroking have their line equations pre-normalized, and multiplied by winding. This simplifies a lot of code which was performing this computation on the fly. The workaround for the "intruding vertex" problem was removed, since the straight skeleton now moves the intruding vertex before it can cause problems. Bug: 756823 Change-Id: I271ed32be6847da55273b387e8c04bbf9b512b70 Reviewed-on: https://skia-review.googlesource.com/87341 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Stephen White <senorblanco@chromium.org>
2017-12-19 23:09:54 +00:00
SkPoint pts[2] = { {0, 0}, {1, 1} };
SkColor colors[2] = { SK_ColorGREEN, SK_ColorBLUE };
sk_sp<SkShader> shader = SkGradientShader::MakeLinear(
pts, colors, nullptr, SK_ARRAY_COUNT(colors), SkTileMode::kClamp);
GrColorInfo colorInfo(GrColorType::kRGBA_8888, kPremul_SkAlphaType, nullptr);
SkSimpleMatrixProvider matrixProvider(SkMatrix::I());
return as_SB(shader)->asFragmentProcessor({rContext, matrixProvider, &colorInfo});
}
static void test_path(GrRecordingContext* rContext,
GrSurfaceDrawContext* surfaceDrawContext,
const SkPath& path,
const SkMatrix& matrix = SkMatrix::I(),
GrAAType aaType = GrAAType::kNone,
std::unique_ptr<GrFragmentProcessor> fp = nullptr) {
GrTriangulatingPathRenderer pr;
pr.setMaxVerbCount(100);
GrPaint paint;
paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
if (fp) {
paint.setColorFragmentProcessor(std::move(fp));
}
SkIRect clipConservativeBounds = SkIRect::MakeWH(surfaceDrawContext->width(),
surfaceDrawContext->height());
GrStyle style(SkStrokeRec::kFill_InitStyle);
Reland "Rename GrShape to GrStyledShape" This reverts commit 73b86c1ade1c002cfa4da66c33c25514737e2416. Reason for revert: wasn't the problem, relanding Original change's description: > Revert "Rename GrShape to GrStyledShape" > > This reverts commit f3f08af010eebdb9c1501bdf1b3d8e715298b05f. > > Reason for revert: maybe this is breaking the ios perf bot; it is the first CL that caused the bot to fail, but I can't really say why this would break them. > > Original change's description: > > Rename GrShape to GrStyledShape > > > > Change-Id: Ic457e634b4b95356f5615cff3fce1ca7d7677c26 > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/284036 > > Reviewed-by: Robert Phillips <robertphillips@google.com> > > Commit-Queue: Michael Ludwig <michaelludwig@google.com> > > TBR=bsalomon@google.com,robertphillips@google.com,michaelludwig@google.com > > # Not skipping CQ checks because original CL landed > 1 day ago. > > Change-Id: I2e5adbfc820811fbbde9cb57af28f86a7ba40bd9 > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/284231 > Reviewed-by: Michael Ludwig <michaelludwig@google.com> > Commit-Queue: Michael Ludwig <michaelludwig@google.com> TBR=bsalomon@google.com,robertphillips@google.com,michaelludwig@google.com Change-Id: Ifdd52f8bacb5d66a7bf58efd328675c4c443ac8a No-Presubmit: true No-Tree-Checks: true No-Try: true Reviewed-on: https://skia-review.googlesource.com/c/skia/+/284376 Reviewed-by: Michael Ludwig <michaelludwig@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
2020-04-17 20:21:37 +00:00
GrStyledShape shape(path, style);
GrPathRenderer::DrawPathArgs args{rContext,
std::move(paint),
&GrUserStencilSettings::kUnused,
surfaceDrawContext,
Reland "GrClips provided as pointers to GrRTC" This reverts commit 074414fed53efcab7f33b06454958707419e37d8. Reason for revert: updated to guard against nullptr before calling quickContains(rrect). Original change's description: > Revert "GrClips provided as pointers to GrRTC" > > This reverts commit 226b689471a0fbb7400bc166032458278957541b. > > Reason for revert: Breaks Android roller > > Original change's description: > > GrClips provided as pointers to GrRTC > > > > A null clip represents no high-level clipping is necessary (the implicit > > clip to the render target's logical dimensions is fine). > > > > This also removes GrNoClip and GrFixedClip::Disabled() since they are > > replaced with just nullptr. > > > > By allowing nullptr to represent no intended clipping, it makes it easier > > to require GrClip and GrAppliedClip objects to know about the dimensions > > of the device. If we required a non-null clip object to represent no > > clipping, we'd have to have an instance for each device based on its > > size and that just became cumbersome. > > > > Bug: skia:10205 > > Change-Id: Ie30cc71820b92d99356d393a4c98c8677082e761 > > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/290539 > > Commit-Queue: Michael Ludwig <michaelludwig@google.com> > > Reviewed-by: Brian Salomon <bsalomon@google.com> > > TBR=bsalomon@google.com,csmartdalton@google.com,michaelludwig@google.com > > Change-Id: I42c4828bcf016ee3d30d5c20b771be96e125817b > No-Presubmit: true > No-Tree-Checks: true > No-Try: true > Bug: skia:10205 > Reviewed-on: https://skia-review.googlesource.com/c/skia/+/292856 > Reviewed-by: Weston Tracey <westont@google.com> > Commit-Queue: Weston Tracey <westont@google.com> TBR=bsalomon@google.com,csmartdalton@google.com,michaelludwig@google.com,westont@google.com # Not skipping CQ checks because this is a reland. Bug: skia:10205 Change-Id: I5715a4de3b7c8847b73020dc4937d3816d879803 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/292876 Reviewed-by: Michael Ludwig <michaelludwig@google.com> Commit-Queue: Michael Ludwig <michaelludwig@google.com>
2020-05-29 13:54:07 +00:00
nullptr,
&clipConservativeBounds,
&matrix,
&shape,
aaType,
false};
pr.drawPath(args);
}
DEF_GPUTEST_FOR_ALL_CONTEXTS(TriangulatingPathRendererTests, reporter, ctxInfo) {
auto ctx = ctxInfo.directContext();
auto rtc = GrSurfaceDrawContext::Make(
ctx, GrColorType::kRGBA_8888, nullptr, SkBackingFit::kApprox, {800, 800}, 1,
GrMipmapped::kNo, GrProtected::kNo, kTopLeft_GrSurfaceOrigin);
if (!rtc) {
return;
}
ctx->flushAndSubmit();
// Adding discard to appease vulkan validation warning about loading uninitialized data on draw
rtc->discard();
for (CreatePathFn createPath : kNonEdgeAAPaths) {
test_path(ctx, rtc.get(), createPath());
}
SkMatrix nonInvertibleMatrix = SkMatrix::Scale(0, 0);
std::unique_ptr<GrFragmentProcessor> fp(create_linear_gradient_processor(ctx));
test_path(ctx, rtc.get(), create_path_17(), nonInvertibleMatrix, GrAAType::kCoverage,
std::move(fp));
test_path(ctx, rtc.get(), create_path_20(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_21(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_25(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_26(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_27(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_28(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_31(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_38(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_41(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_43(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_44(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_45(), SkMatrix(), GrAAType::kCoverage);
test_path(ctx, rtc.get(), create_path_46(), SkMatrix(), GrAAType::kCoverage);
}
namespace {
class SimpleVertexAllocator : public GrEagerVertexAllocator {
public:
void* lock(size_t stride, int eagerCount) override {
SkASSERT(!fPoints);
SkASSERT(stride == sizeof(SkPoint));
fPoints.reset(eagerCount);
return fPoints;
}
void unlock(int actualCount) override {}
SkPoint operator[](int idx) const { return fPoints[idx]; }
SkAutoTMalloc<SkPoint> fPoints;
};
} // namespace
struct Edge {
Edge reverse() const { return {fP1, fP0}; }
SkPoint fP0, fP1;
};
static bool operator<(const Edge& a, const Edge& b) {
if (a.fP0.fX != b.fP0.fX) {
return a.fP0.fX < b.fP0.fX;
}
if (a.fP0.fY != b.fP0.fY) {
return a.fP0.fY < b.fP0.fY;
}
if (a.fP1.fX != b.fP1.fX) {
return a.fP1.fX < b.fP1.fX;
}
if (a.fP1.fY != b.fP1.fY) {
return a.fP1.fY < b.fP1.fY;
}
return false;
}
using EdgeMap = std::map<Edge, int>;
static void add_edge(EdgeMap& edgeMap, SkPoint p0, SkPoint p1) {
Edge edge{p0, p1};
// First check if this edge already exists in reverse.
auto reverseIter = edgeMap.find(edge.reverse());
if (reverseIter != edgeMap.end()) {
--reverseIter->second;
} else {
++edgeMap[edge];
}
}
static void add_tri_edges(skiatest::Reporter* r, EdgeMap& edgeMap, const SkPoint pts[3]) {
for (int i = 0; i < 3; ++i) {
SkPoint p0=pts[i], p1=pts[(i+1)%3];
// The triangulator shouldn't output degenerate triangles.
REPORTER_ASSERT(r, p0 != p1);
add_edge(edgeMap, p0, p1);
}
}
static EdgeMap simplify(const EdgeMap& edges, SkPathFillType fillType) {
// Prune out the edges whose count went to zero, and reverse the edges whose count is negative.
EdgeMap simplifiedEdges;
for (auto [edge, count] : edges) {
// We should only have one ordering of any given edge.
SkASSERT(edges.find(edge.reverse()) == edges.end());
if (fillType == SkPathFillType::kEvenOdd) {
count = abs(count) & 1;
}
if (count > 0) {
simplifiedEdges[edge] = count;
} else if (count < 0) {
simplifiedEdges[edge.reverse()] = -count;
}
}
return simplifiedEdges;
}
static void verify_simple_inner_polygons(skiatest::Reporter* r, const char* shapeName,
SkPath path) {
for (auto fillType : {SkPathFillType::kWinding}) {
path.setFillType(fillType);
SkArenaAlloc arena(GrTriangulator::kArenaDefaultChunkSize);
GrInnerFanTriangulator::BreadcrumbTriangleList breadcrumbs;
SimpleVertexAllocator vertexAlloc;
int vertexCount;
{
bool isLinear;
GrInnerFanTriangulator triangulator(path, &arena);
vertexCount = triangulator.pathToTriangles(&vertexAlloc, &breadcrumbs, &isLinear);
}
// Count up all the triangulated edges.
EdgeMap trianglePlusBreadcrumbEdges;
for (int i = 0; i < vertexCount; i += 3) {
add_tri_edges(r, trianglePlusBreadcrumbEdges, vertexAlloc.fPoints.data() + i);
}
// Count up all the breadcrumb edges.
int breadcrumbCount = 0;
for (const auto* node = breadcrumbs.head(); node; node = node->fNext) {
add_tri_edges(r, trianglePlusBreadcrumbEdges, node->fPts);
++breadcrumbCount;
}
REPORTER_ASSERT(r, breadcrumbCount == breadcrumbs.count());
// The triangulated + breadcrumb edges should cancel out to the inner polygon edges.
trianglePlusBreadcrumbEdges = simplify(trianglePlusBreadcrumbEdges, path.getFillType());
// Build the inner polygon edges.
EdgeMap innerFanEdges;
SkPoint startPoint{}, lastPoint{};
for (auto [verb, pts, w] : SkPathPriv::Iterate(path)) {
switch (verb) {
case SkPathVerb::kMove:
if (lastPoint != startPoint) {
add_edge(innerFanEdges, lastPoint, startPoint);
}
lastPoint = startPoint = pts[0];
continue;
case SkPathVerb::kClose:
lastPoint = startPoint;
break;
case SkPathVerb::kLine:
lastPoint = pts[1];
break;
case SkPathVerb::kQuad:
case SkPathVerb::kConic:
lastPoint = pts[2];
break;
case SkPathVerb::kCubic:
lastPoint = pts[3];
break;
}
if (pts[0] != lastPoint) {
add_edge(innerFanEdges, pts[0], lastPoint);
}
}
if (lastPoint != startPoint) {
add_edge(innerFanEdges, lastPoint, startPoint);
}
innerFanEdges = simplify(innerFanEdges, path.getFillType());
// The triangulated + breadcrumb edges should cancel out to the inner polygon edges. First
// verify that every inner polygon edge can be found in the triangulation.
for (auto [edge, count] : innerFanEdges) {
auto it = trianglePlusBreadcrumbEdges.find(edge);
if (it != trianglePlusBreadcrumbEdges.end()) {
it->second -= count;
if (it->second == 0) {
trianglePlusBreadcrumbEdges.erase(it);
}
continue;
}
it = trianglePlusBreadcrumbEdges.find(edge.reverse());
if (it != trianglePlusBreadcrumbEdges.end()) {
it->second += count;
if (it->second == 0) {
trianglePlusBreadcrumbEdges.erase(it);
}
continue;
}
ERRORF(r, "error: %s: edge [%g,%g]:[%g,%g] not found in triangulation.",
shapeName, edge.fP0.fX, edge.fP0.fY, edge.fP1.fX, edge.fP1.fY);
return;
}
// Now verify that there are no spurious edges in the triangulation.
//
// NOTE: The triangulator's definition of wind isn't always correct for edges that run
// exactly parallel to the sweep (either vertical or horizontal edges). This doesn't
// actually matter though because T-junction artifacts don't happen on axis-aligned edges.
// Tolerate spurious edges that (1) come in pairs of 2, and (2) are either exactly
// horizontal or exactly vertical exclusively.
bool hasSpuriousHorz=false, hasSpuriousVert=false;
for (auto [edge, count] : trianglePlusBreadcrumbEdges) {
if (count % 2 == 0) {
if (edge.fP0.fX == edge.fP1.fX && !hasSpuriousVert) {
hasSpuriousHorz = true;
continue;
}
if (edge.fP0.fY == edge.fP1.fY && !hasSpuriousHorz) {
hasSpuriousVert = true;
continue;
}
}
ERRORF(r, "error: %s: spurious edge [%g,%g]:[%g,%g] found in triangulation.",
shapeName, edge.fP0.fX, edge.fP0.fY, edge.fP1.fX, edge.fP1.fY);
return;
}
}
}
DEF_TEST(GrInnerFanTriangulator, r) {
verify_simple_inner_polygons(r, "simple triangle", SkPath().lineTo(1,0).lineTo(0,1));
verify_simple_inner_polygons(r, "simple square", SkPath().lineTo(1,0).lineTo(1,1).lineTo(0,1));
verify_simple_inner_polygons(r, "concave polygon", SkPath()
.lineTo(1,0).lineTo(.5f,.5f).lineTo(1,1).lineTo(0,1));
verify_simple_inner_polygons(r, "double wound triangle", SkPath()
.lineTo(1,0).lineTo(0,1).lineTo(0,0).lineTo(1,0).lineTo(0,1));
verify_simple_inner_polygons(r, "self-intersecting bowtie", SkPath()
.lineTo(1,0).lineTo(0,1).lineTo(1,1));
verify_simple_inner_polygons(r, "asymmetrical bowtie", SkPath()
.lineTo(1,0).lineTo(0,1).lineTo(.1f,-.1f));
verify_simple_inner_polygons(r, "bowtie with extremely small section", SkPath()
.lineTo(1,0).lineTo(0,1).lineTo(1e-6f,-1e-6f));
verify_simple_inner_polygons(r, "intersecting squares", SkPath()
.lineTo(1,0).lineTo(1,1).lineTo(0,1)
.moveTo(.5f,.5f).lineTo(1.5f,.5f).lineTo(1.5f,1.5f).lineTo(.5f,1.5f).close());
verify_simple_inner_polygons(r, "6-point \"Star of David\"", SkPath()
.moveTo(cosf(-SK_ScalarPI/3), sinf(-SK_ScalarPI/3))
.lineTo(cosf(SK_ScalarPI/3), sinf(SK_ScalarPI/3))
.lineTo(cosf(SK_ScalarPI), sinf(SK_ScalarPI))
.moveTo(cosf(0), sinf(0))
.lineTo(cosf(2*SK_ScalarPI/3), sinf(2*SK_ScalarPI/3))
.lineTo(cosf(-2*SK_ScalarPI/3), sinf(-2*SK_ScalarPI/3)));
verify_simple_inner_polygons(r, "double wound \"Star of David\"", SkPath()
.moveTo(cosf(-SK_ScalarPI/3), sinf(-SK_ScalarPI/3))
.lineTo(cosf(SK_ScalarPI/3), sinf(SK_ScalarPI/3))
.lineTo(cosf(SK_ScalarPI), sinf(SK_ScalarPI))
.lineTo(cosf(-SK_ScalarPI/3), sinf(-SK_ScalarPI/3))
.lineTo(cosf(SK_ScalarPI/3), sinf(SK_ScalarPI/3))
.lineTo(cosf(SK_ScalarPI), sinf(SK_ScalarPI))
.moveTo(cosf(0), sinf(0))
.lineTo(cosf(2*SK_ScalarPI/3), sinf(2*SK_ScalarPI/3))
.lineTo(cosf(-2*SK_ScalarPI/3), sinf(-2*SK_ScalarPI/3)));
verify_simple_inner_polygons(r, "5-point star", ToolUtils::make_star(SkRect::MakeWH(100, 200)));
verify_simple_inner_polygons(r, "\"pointy\" intersecting triangles", SkPath()
.moveTo(0,-100).lineTo(-1e-6f,100).lineTo(1e-6f,100)
.moveTo(-100,0).lineTo(100,1e-6f).lineTo(100,-1e-6f));
verify_simple_inner_polygons(r, "overlapping rects with vertical collinear edges", SkPath()
.moveTo(0,0).lineTo(0,2).lineTo(1,2).lineTo(1,0)
.moveTo(0,1).lineTo(0,3).lineTo(1,3).lineTo(1,1));
verify_simple_inner_polygons(r, "overlapping rects with horizontal collinear edges", SkPath()
.lineTo(2,0).lineTo(2,1).lineTo(0,1)
.moveTo(1,0).lineTo(3,0).lineTo(3,1).lineTo(1,1).close());
for (int i = 0; i < (int)SK_ARRAY_COUNT(kNonEdgeAAPaths); ++i) {
verify_simple_inner_polygons(r, SkStringPrintf("kNonEdgeAAPaths[%i]", i).c_str(),
kNonEdgeAAPaths[i]());
}
SkRandom rand;
for (int i = 0; i < 50; ++i) {
auto randomPath = SkPath().moveTo(rand.nextF(), rand.nextF());
for (int j = 0; j < i; ++j) {
randomPath.lineTo(rand.nextF(), rand.nextF());
}
verify_simple_inner_polygons(r, SkStringPrintf("random_path_%i", i).c_str(), randomPath);
}
}