/*
 * 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 "include/core/SkAlphaType.h"
#include "include/core/SkBlendMode.h"
#include "include/core/SkColor.h"
#include "include/core/SkColorSpace.h"
#include "include/core/SkMatrix.h"
#include "include/core/SkPath.h"
#include "include/core/SkPathBuilder.h"
#include "include/core/SkPathTypes.h"
#include "include/core/SkPoint.h"
#include "include/core/SkRect.h"
#include "include/core/SkRefCnt.h"
#include "include/core/SkScalar.h"
#include "include/core/SkString.h"
#include "include/core/SkStrokeRec.h"
#include "include/core/SkSurfaceProps.h"
#include "include/core/SkTileMode.h"
#include "include/core/SkTypes.h"
#include "include/effects/SkGradientShader.h"
#include "include/gpu/GpuTypes.h"
#include "include/gpu/ganesh/GrDirectContext.h"
#include "include/gpu/ganesh/GrTypes.h"
#include "include/private/base/SkTemplates.h"
#include "include/private/gpu/ganesh/GrTypesPriv.h"
#include "src/base/SkArenaAlloc.h"
#include "src/base/SkFloatBits.h"
#include "src/base/SkRandom.h"
#include "src/core/SkPathPriv.h"
#include "src/gpu/SkBackingFit.h"
#include "src/gpu/ganesh/GrColorInfo.h"
#include "src/gpu/ganesh/GrEagerVertexAllocator.h"
#include "src/gpu/ganesh/GrFPArgs.h"
#include "src/gpu/ganesh/GrFragmentProcessor.h"
#include "src/gpu/ganesh/GrFragmentProcessors.h"
#include "src/gpu/ganesh/GrPaint.h"
#include "src/gpu/ganesh/GrStyle.h"
#include "src/gpu/ganesh/GrUserStencilSettings.h"
#include "src/gpu/ganesh/PathRenderer.h"
#include "src/gpu/ganesh/SurfaceDrawContext.h"
#include "src/gpu/ganesh/effects/GrPorterDuffXferProcessor.h"
#include "src/gpu/ganesh/geometry/GrAATriangulator.h"
#include "src/gpu/ganesh/geometry/GrInnerFanTriangulator.h"
#include "src/gpu/ganesh/geometry/GrPathUtils.h"
#include "src/gpu/ganesh/geometry/GrStyledShape.h"
#include "src/gpu/ganesh/geometry/GrTriangulator.h"
#include "src/gpu/ganesh/ops/TriangulatingPathRenderer.h"
#include "tests/CtsEnforcement.h"
#include "tests/Test.h"
#include "tools/ToolUtils.h"

#include <algorithm>
#include <array>
#include <cmath>
#include <cstddef>
#include <initializer_list>
#include <limits>
#include <map>
#include <memory>
#include <utility>

using namespace skia_private;

class GrRecordingContext;
class SkShader;
struct GrContextOptions;

#if !defined(SK_ENABLE_OPTIMIZE_SIZE)

/*
 * These tests pass by not crashing, hanging or asserting in Debug.
 */

using CreatePathFn = void(*)(SkPathBuilder&);

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()).
    [](SkPathBuilder& 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);
    },

    // Intersections which fall exactly on the current vertex, and require
    // a restart of the intersection checking.
    [](SkPathBuilder& 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);
    },

    // Tests active edges which are removed by splitting.
    [](SkPathBuilder& 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);
    },

    // Collinear edges merged in set_top().
    // Also, an intersection between left and right enclosing edges which
    // falls above the current vertex.
    [](SkPathBuilder& 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);
    },

    // Tests active edges which are made inactive by set_top().
    [](SkPathBuilder& 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);
    },

    [](SkPathBuilder& 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);
    },

    [](SkPathBuilder& 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);
    },

    [](SkPathBuilder& 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);
    },

    [](SkPathBuilder& 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);
    },

    [](SkPathBuilder& 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);
    },

    [](SkPathBuilder& 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);
    },

    // A degenerate segments case, where both upper and lower segments of
    // a split edge must remain active.
    [](SkPathBuilder& 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);
    },

    // Handle the case where edge.dist(edge.fTop) != 0.0.
    [](SkPathBuilder& 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);
    },

    // A degenerate segments case which exercises inactive edges being
    // made active by splitting.
    [](SkPathBuilder& 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);
    },

    // Tests vertices which become "orphaned" (ie., no connected edges)
    // after simplification.
    [](SkPathBuilder& 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);
    },

    [](SkPathBuilder& 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);
    },

    // Reduction of Nebraska-StateSeal.svg. Floating point error causes the
    // same edge to be added to more than one poly on the same side.
    [](SkPathBuilder& 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);
    },

    // A shape with a vertex collinear to the right hand edge.
    // This messes up find_enclosing_edges.
    [](SkPathBuilder& 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);
    },

    // 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
    [](SkPathBuilder& 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);
    },

    // A path which contains out-of-range colinear intersections.
    [](SkPathBuilder& 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);
    },

    // A path which results in infs and nans when conics are converted to quads.
    [](SkPathBuilder& 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);
     },

    // A quad which generates a huge number of points (>2B) when uniformly
    // linearized. This should not hang or OOM.
    [](SkPathBuilder& path) {
        path.moveTo(10, 0);
        path.lineTo(0, 0);
        path.quadTo(10, 0, 0, 8315084722602508288);
    },

    // 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.
    [](SkPathBuilder& 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);
    },

    // Reduction from skbug.com/40039164 that causes a crash due to splitting a
    // zombie edge.
    [](SkPathBuilder& 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);
    },

    // From crbug.com/844873. Crashes trying to merge a zombie edge.
    [](SkPathBuilder& 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);
    },

    // From crbug.com/844873. Hangs repeatedly splitting alternate vertices.
    [](SkPathBuilder& path) {
        path.moveTo(10, -1e+20f);
        path.lineTo(11, 25000);
        path.lineTo(10, 25000);
        path.lineTo(11, 25010);
    },

    // Reduction from circular_arcs_stroke_and_fill_round GM which
    // repeatedly splits on the opposite edge from case 34 above.
    [](SkPathBuilder& 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);
    },

    // Reduction from crbug.com/843135 where an intersection is found
    // below the bottom of both intersected edges.
    [](SkPathBuilder& path) {
        path.moveTo(-2791476679359332352,  2608107002026524672);
        path.lineTo(                   0, 11.95427703857421875);
        path.lineTo(-2781824066779086848,  2599088532777598976);
        path.lineTo(          -7772.6875,                 7274);
    },

    // Reduction from crbug.com/843135. Exercises a case where an intersection is missed.
    // This causes bad ordering in the active edge list.
    [](SkPathBuilder& path) {
        path.moveTo(-1.0662557646016024569e+23, 9.9621425197286319718e+22);
        path.lineTo(                -121806400,                 113805032);
        path.lineTo(                -120098872,                 112209680);
        path.lineTo( 6.2832999862817380468e-36,     2.9885697364807128906);
    },

    // Reduction from crbug.com/851409. Exercises collinear last vertex.
    [](SkPathBuilder& path) {
        path.moveTo(2072553216, 0);
        path.lineTo(2072553216, 1);
        path.lineTo(2072553472, -13.5);
        path.lineTo(2072553216, 0);
        path.lineTo(2072553472, -6.5);
    },

    // Another reduction from crbug.com/851409. Exercises two sequential collinear edges.
    [](SkPathBuilder& 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);
    },

    // Reduction from crbug.com/860655. Cause is three collinear edges discovered during
    // sanitize_contours pass, before the vertices have been found coincident.
    [](SkPathBuilder& 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);
    },
};

#if defined(SK_GANESH)

// A simple concave path. Test this with a non-invertible matrix.
static SkPath create_path_17() {
    return SkPathBuilder()
           .moveTo(20, 20)
           .lineTo(80, 20)
           .lineTo(30, 30)
           .lineTo(20, 80)
           .detach();
}

// An intersection above the first vertex in the mesh.
// Reduction from http://crbug.com/730687
static SkPath create_path_20() {
    return SkPathBuilder()
           .moveTo( 2822128.5,  235.026336669921875)
           .lineTo(2819349.25, 235.3623504638671875)
           .lineTo(-340558688, 23.83478546142578125)
           .lineTo(-340558752, 25.510419845581054688)
           .lineTo(-340558720, 27.18605804443359375)
           .detach();
}

// An intersection whose result is NaN (due to rounded-to-inf endpoint).
static SkPath create_path_21() {
    return SkPathBuilder()
           .moveTo(1.7889142061167663539e+38, 39338463358011572224.0)
           .lineTo(  1647.4193115234375,       -522.603515625)
           .lineTo(    1677.74560546875,   -529.0028076171875)
           .lineTo(    1678.29541015625,   -528.7847900390625)
           .lineTo(  1637.5167236328125,  -519.79266357421875)
           .lineTo(  1647.4193115234375,       -522.603515625)
           .detach();
}

// An edge collapse event which also collapses a neighbour, requiring
// its event to be removed.
static SkPath create_path_25() {
    return SkPathBuilder()
           .moveTo( 43.44110107421875,  148.15106201171875)
           .lineTo( 44.64471435546875,  148.16748046875)
           .lineTo( 46.35009765625,     147.403076171875)
           .lineTo( 46.45404052734375,  148.34906005859375)
           .lineTo( 45.0400390625,      148.54205322265625)
           .lineTo( 44.624053955078125, 148.9810791015625)
           .lineTo( 44.59405517578125,  149.16107177734375)
           .lineTo( 44.877044677734375, 149.62005615234375)
           .lineTo(144.373016357421875,  68.8070068359375)
           .detach();
}

// An edge collapse event causes an edge to become collinear, requiring
// its event to be removed.
static SkPath create_path_26() {
    return SkPathBuilder()
           .moveTo( 43.44110107421875,  148.15106201171875)
           .lineTo( 44.64471435546875,  148.16748046875)
           .lineTo( 46.35009765625,     147.403076171875)
           .lineTo( 46.45404052734375,  148.34906005859375)
           .lineTo( 45.0400390625,      148.54205322265625)
           .lineTo( 44.624053955078125, 148.9810791015625)
           .lineTo( 44.59405517578125,  149.16107177734375)
           .lineTo( 44.877044677734375, 149.62005615234375)
           .lineTo(144.373016357421875,  68.8070068359375)
           .detach();
}

// A path which results in non-finite points when stroked and bevelled for AA.
static SkPath create_path_27() {
     return SkPathBuilder()
            .moveTo(8.5027233009104409507e+37, 1.7503381025241130639e+37)
            .lineTo(7.0923661737711584874e+37, 1.4600074517285415699e+37)
            .lineTo(7.0848733446033294691e+37, 1.4584649744781838604e+37)
            .lineTo(-2.0473916115129349496e+37, -4.2146796450364162012e+36)
            .lineTo(2.0473912312177548811e+37, 4.2146815465123165435e+36)
            .detach();
}

// AA stroking this path produces intersection failures on bevelling.
// This should skip the point, but not assert.
static SkPath create_path_28() {
    return SkPathBuilder()
           .moveTo(-7.5952312625177475154e+21, -2.6819185100266674911e+24)
           .lineTo(  1260.3787841796875,   1727.7947998046875)
           .lineTo(  1260.5567626953125,   1728.0386962890625)
           .lineTo(1.1482511310557754163e+21, 4.054538502765980051e+23)
           .lineTo(-7.5952312625177475154e+21, -2.6819185100266674911e+24)
           .detach();
}

// A path with vertices which become infinite on AA stroking. Should not crash or assert.
static SkPath create_path_31() {
    return SkPathBuilder()
           .moveTo(2.0257809259190991347e+36,  -1244080640)
           .conicTo(2.0257809259190991347e+36, -1244080640,
                    2.0257809259190991347e+36, 0.10976474732160568237, 0.70710676908493041992)
           .lineTo(-10036566016, -1954718402215936)
           .conicTo(-1.1375507718551896064e+20, -1954721086570496,
                    10036566016, -1954721086570496, 0.70710676908493041992)
           .detach();
}

// Reduction from crbug.com/851914.
static SkPath create_path_38() {
    return SkPathBuilder()
           .moveTo(14.400531768798828125, 17.711114883422851562)
           .lineTo(14.621990203857421875,   171563104293879808)
           .lineTo(14.027951240539550781,   872585759381520384)
           .lineTo( 14.0216827392578125,   872665817571917824)
           .lineTo(7.699314117431640625,    -3417320793833472)
           .moveTo(11.606547355651855469,       17.40966796875)
           .lineTo( 7642114886926860288, 21.08358001708984375)
           .lineTo(11.606547355651855469, 21.08358001708984375)
           .detach();
}

// 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() {
    return SkPathBuilder()
           .moveTo(72154931603311689728.0,   330.95965576171875)
           .lineTo(24053266013925408768.0,       78.11376953125)
           .lineTo(1.2031099003292404941e+20,  387.168731689453125)
           .lineTo(68859835992355373056.0,   346.55047607421875)
           .lineTo(76451708695451009024.0,     337.780029296875)
           .moveTo(-20815817797613387776.0, 18065700622522384384.0)
           .lineTo(-72144121204987396096.0,  142.855804443359375)
           .lineTo(72144121204987396096.0,  325.184783935546875)
           .lineTo(1.2347242901040791552e+20, 18065700622522384384.0)
           .detach();
}

// 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() {
    return SkPathBuilder()
           .moveTo(     307316821852160,      -28808363114496)
           .lineTo(     307165222928384,      -28794154909696)
           .lineTo(     307013691113472,      -28779948802048)
           .lineTo(     306862159298560,      -28765744791552)
           .lineTo(     306870313025536,      -28766508154880)
           .lineTo(     307049695019008,      -28783327313920)
           .lineTo(     307408660332544,      -28816974020608)
           .detach();
}

// Reduction from crbug.com/966696
static SkPath create_path_44() {
    return SkPathBuilder()
           .moveTo(114.4606170654296875,       186.443878173828125)
           .lineTo( 91.5394744873046875,       185.4189453125)
           .lineTo(306.45538330078125,        3203.986083984375)
           .moveTo(16276206965409972224.0,     815.59393310546875)
           .lineTo(-3.541605062372533207e+20,  487.7236328125)
           .lineTo(-3.541605062372533207e+20,  168.204071044921875)
           .lineTo(16276206965409972224.0,     496.07427978515625)
           .moveTo(-3.541605062372533207e+20,  167.00958251953125)
           .lineTo(-3.541605062372533207e+20,  488.32086181640625)
           .lineTo(16276206965409972224.0,     816.78839111328125)
           .lineTo(16276206965409972224.0,     495.47705078125)
           .detach();
}

// Reduction from crbug.com/966274.
static SkPath create_path_45() {
    return SkPathBuilder()
           .moveTo(        706471854080,         379003666432)
           .lineTo(        706503180288,         379020443648)
           .lineTo(        706595717120,         379070087168)
           .lineTo(        706626060288,         379086372864)
           .lineTo(        706656141312,         379102527488)
           .lineTo(        706774171648,         379165835264)
           .lineTo(        706803073024,         379181334528)
           .lineTo(        706831712256,         379196702720)
           .lineTo(        706860154880,         379211939840)
           .lineTo(        706888335360,         379227078656)
           .lineTo(        706916253696,         379242053632)
           .lineTo(        706956820480,         379263811584)
           .lineTo(        706929098752,         379248934912)
           .lineTo(        706901114880,         379233927168)
           .lineTo(        706872934400,         379218821120)
           .lineTo(        706844491776,         379203551232)
           .lineTo(        706815787008,         379188183040)
           .lineTo(        706786885632,         379172651008)
           .lineTo(        706757722112,         379156987904)
           .lineTo(        706728296448,         379141226496)
           .lineTo(        706698608640,         379125301248)
           .lineTo(        706668724224,         379109244928)
           .lineTo(        706638577664,         379093090304)
           .lineTo(        706608168960,         379076771840)
           .lineTo(        706484174848,         379010252800)
           .detach();
}

// Reduction from crbug.com/969359. Inf generated by intersections
// causes NaN in subsequent intersections, leading to assert or hang.

static SkPath create_path_46() {
    return SkPathBuilder()
           .moveTo(1.0321827899075254821e+37, -5.1199920965387697886e+37)
           .lineTo(-1.0321827899075254821e+37, 5.1199920965387697886e+37)
           .lineTo(-1.0425214946728668754e+37, 4.5731834042267216669e+37)
           .moveTo(-9.5077331762291841872e+36, 8.1304868292377430302e+37)
           .lineTo(9.5077331762291841872e+36, -8.1304868292377430302e+37)
           .lineTo(1.0795449417808426232e+37, 1.2246856113744539311e+37)
           .moveTo(-165.8018341064453125,           -44.859375)
           .lineTo(-9.558702871563160835e+36, -7.9814405281448285475e+37)
           .lineTo(-9.4147814283168490381e+36, -8.3935116522790983488e+37)
           .detach();
}

// Reduction from crbug.com/1245359
static SkPath create_path_47() {
    return SkPathBuilder(SkPathFillType::kWinding)
        .moveTo(SkBits2Float(0xdfb80000), SkBits2Float(0x4cb9b4a5))  // -2.65172e+19f,  9.73632e+07f
        .lineTo(SkBits2Float(0xdfb80000), SkBits2Float(0xe396b530))  // -2.65172e+19f, -5.56014e+21f
        .lineTo(SkBits2Float(0x5fb80000), SkBits2Float(0xe396b530))  //  2.65172e+19f, -5.56014e+21f
        .lineTo(SkBits2Float(0x5fb80000), SkBits2Float(0x6396b530))  //  2.65172e+19f,  5.56014e+21f
        .lineTo(SkBits2Float(0x4cc07742), SkBits2Float(0x6396b530))  //  1.00908e+08f,  5.56014e+21f
        .lineTo(SkBits2Float(0xdfb80000), SkBits2Float(0x6396b530))  // -2.65172e+19f,  5.56014e+21f
        .lineTo(SkBits2Float(0xdfb80000), SkBits2Float(0xe396b530))  // -2.65172e+19f, -5.56014e+21f
        .lineTo(SkBits2Float(0x4cc07742), SkBits2Float(0xe396b530))  //  1.00908e+08f, -5.56014e+21f
        .lineTo(SkBits2Float(0x4cc079c8), SkBits2Float(0xe396b530))  //  1.00913e+08f, -5.56014e+21f
        .lineTo(SkBits2Float(0x4cc079c8), SkBits2Float(0x4cb9b4a5))  //  1.00913e+08f,  9.73632e+07f
        .lineTo(SkBits2Float(0xdfb80000), SkBits2Float(0x4cb9b4a5))  // -2.65172e+19f,  9.73632e+07f
        .lineTo(SkBits2Float(0xdfb80000), SkBits2Float(0x4cb74d74))  // -2.65172e+19f,  9.61033e+07f
        .lineTo(SkBits2Float(0x4cc079c8), SkBits2Float(0x4cb74d74))  //  1.00913e+08f,  9.61033e+07f
        .lineTo(SkBits2Float(0x4cc079c8), SkBits2Float(0x6396b530))  //  1.00913e+08f,  5.56014e+21f
        .lineTo(SkBits2Float(0x4cc07742), SkBits2Float(0x6396b530))  //  1.00908e+08f,  5.56014e+21f
        .lineTo(SkBits2Float(0x4cc07742), SkBits2Float(0x4cb74d74))  //  1.00908e+08f,  9.61033e+07f
        .lineTo(SkBits2Float(0x5fb80000), SkBits2Float(0x4cb74d74))  //  2.65172e+19f,  9.61033e+07f
        .lineTo(SkBits2Float(0x5fb80000), SkBits2Float(0x6396b530))  //  2.65172e+19f,  5.56014e+21f
        .lineTo(SkBits2Float(0xdfb80000), SkBits2Float(0x6396b530))  // -2.65172e+19f,  5.56014e+21f
        .lineTo(SkBits2Float(0xdfb80000), SkBits2Float(0x4cb9b4a5))  // -2.65172e+19f,  9.73632e+07f
        .close()

        .moveTo(SkBits2Float(0xdfb39e51), SkBits2Float(0xe282c5bd))  // -2.58857e+19f, -1.20616e+21f
        .lineTo(SkBits2Float(0xdf8a47ec), SkBits2Float(0xe3b90de5))  // -1.99284e+19f, -6.8273e+21f
        .lineTo(SkBits2Float(0x5eb8b548), SkBits2Float(0xe391e278))  //  6.65481e+18f, -5.38219e+21f
        .quadTo(SkBits2Float(0x5eaa9855), SkBits2Float(0xe392a246),  //  6.14633e+18f, -5.40984e+21f
                SkBits2Float(0x5e9c5925), SkBits2Float(0xe39344a0))  //  5.63304e+18f, -5.43323e+21f
        .quadTo(SkBits2Float(0x5e89eefd), SkBits2Float(0xe3941678),  //  4.96958e+18f, -5.46347e+21f
                SkBits2Float(0x5e6ead5a), SkBits2Float(0xe394b6a4))  //  4.29963e+18f, -5.48656e+21f
        .quadTo(SkBits2Float(0x5e6c0307), SkBits2Float(0xe394c21f),  //  4.25161e+18f, -5.48821e+21f
                SkBits2Float(0x5e694ef2), SkBits2Float(0xe394cd7f))  //  4.20291e+18f, -5.48985e+21f
        .quadTo(SkBits2Float(0x5e67eeaa), SkBits2Float(0xe394d349),  //  4.17812e+18f, -5.49069e+21f
                SkBits2Float(0x5e669614), SkBits2Float(0xe394d8e2))  //  4.15387e+18f, -5.49149e+21f
        .quadTo(SkBits2Float(0x5e6534d4), SkBits2Float(0xe394de9e),  //  4.12901e+18f, -5.49232e+21f
                SkBits2Float(0x5e63d6a7), SkBits2Float(0xe394e43c))  //  4.10437e+18f, -5.49313e+21f
        .quadTo(SkBits2Float(0x5e610d59), SkBits2Float(0xe394efad),  //  4.05418e+18f, -5.49478e+21f
                SkBits2Float(0x5e5e43cb), SkBits2Float(0xe394fad6))  //  4.00397e+18f, -5.49639e+21f
        .quadTo(SkBits2Float(0x5e5b6ac0), SkBits2Float(0xe395063d),  //  3.95267e+18f, -5.49803e+21f
                SkBits2Float(0x5e5895ab), SkBits2Float(0xe3951148))  //  3.90164e+18f, -5.49962e+21f
        .quadTo(SkBits2Float(0x5e55b52e), SkBits2Float(0xe3951c7f),  //  3.84982e+18f, -5.50124e+21f
                SkBits2Float(0x5e52cb8e), SkBits2Float(0xe395278b))  //  3.79735e+18f, -5.50283e+21f
        .quadTo(SkBits2Float(0x5e514f61), SkBits2Float(0xe3952d2d),  //  3.7706e+18f,  -5.50364e+21f
                SkBits2Float(0x5e4fdbc5), SkBits2Float(0xe395329a))  //  3.74445e+18f, -5.50442e+21f
        .lineTo(SkBits2Float(0x5e4fdbc5), SkBits2Float(0xe395329a))  //  3.74445e+18f, -5.50442e+21f
        .lineTo(SkBits2Float(0x5e4fdbc5), SkBits2Float(0xe395329a))  //  3.74445e+18f, -5.50442e+21f
        .lineTo(SkBits2Float(0x5e4fdbc5), SkBits2Float(0xe395329a))  //  3.74445e+18f, -5.50442e+21f
        .lineTo(SkBits2Float(0x5e4fdbc5), SkBits2Float(0xe395329a))  //  3.74445e+18f, -5.50442e+21f
        .lineTo(SkBits2Float(0x5e4fdbc5), SkBits2Float(0xe395329a))  //  3.74445e+18f, -5.50442e+21f
        .lineTo(SkBits2Float(0x5e4fdbc5), SkBits2Float(0xe395329a))  //  3.74445e+18f, -5.50442e+21f
        .lineTo(SkBits2Float(0x5e4fdbc5), SkBits2Float(0xe395329a))  //  3.74445e+18f, -5.50442e+21f
        .lineTo(SkBits2Float(0x5e4fdbc5), SkBits2Float(0xe395329a))  //  3.74445e+18f, -5.50442e+21f
        .lineTo(SkBits2Float(0x5e4fdbc5), SkBits2Float(0xe395329a))  //  3.74445e+18f, -5.50442e+21f
        .lineTo(SkBits2Float(0xdfb80000), SkBits2Float(0xe396b530))  // -2.65172e+19f, -5.56014e+21f
        .lineTo(SkBits2Float(0x5fb80000), SkBits2Float(0xe396b530))  //  2.65172e+19f, -5.56014e+21f
        .lineTo(SkBits2Float(0x5fb80000), SkBits2Float(0x4cc8d35d))  //  2.65172e+19f,  1.0529e+08f
        .lineTo(SkBits2Float(0xdfe2ba48), SkBits2Float(0x63512f2f))  // -3.26749e+19f,  3.85877e+21f
        .lineTo(SkBits2Float(0xdf7f64f6), SkBits2Float(0xe3b9b457))  // -1.84031e+19f, -6.85129e+21f
        .lineTo(SkBits2Float(0xdfb80000), SkBits2Float(0x4cc8d35d))  // -2.65172e+19f,  1.0529e+08f
        .lineTo(SkBits2Float(0xdfb80000), SkBits2Float(0x4cbbf2a2))  // -2.65172e+19f,  9.85388e+07f
        .lineTo(SkBits2Float(0x4cc079c8), SkBits2Float(0x4cbbf2a2))  //  1.00913e+08f,  9.85388e+07f
        .lineTo(SkBits2Float(0x4cc079c8), SkBits2Float(0x6396b530))  //  1.00913e+08f,  5.56014e+21f
        .lineTo(SkBits2Float(0x4cc07742), SkBits2Float(0x6396b530))  //  1.00908e+08f,  5.56014e+21f
        .lineTo(SkBits2Float(0x4cc07742), SkBits2Float(0x4cbbf2a2))  //  1.00908e+08f,  9.85388e+07f
        .lineTo(SkBits2Float(0x5fb80000), SkBits2Float(0x4cbbf2a2))  //  2.65172e+19f,  9.85388e+07f
        .lineTo(SkBits2Float(0xdeb8b548), SkBits2Float(0x6391e278))  // -6.65481e+18f,  5.38219e+21f
        .lineTo(SkBits2Float(0x4cc07488), SkBits2Float(0x4ccb2302))  //  1.00902e+08f,  1.06502e+08f
        .lineTo(SkBits2Float(0x5fb39e51), SkBits2Float(0x6282c5bd))  //  2.58857e+19f,  1.20616e+21f
        .lineTo(SkBits2Float(0x5fb39e51), SkBits2Float(0x6282c5bd))  //  2.58857e+19f,  1.20616e+21f
        .lineTo(SkBits2Float(0x5f8bb406), SkBits2Float(0x63b3cfe4))  //  2.01334e+19f,  6.63389e+21f
        .lineTo(SkBits2Float(0xdfdb889b), SkBits2Float(0x6364da0b))  // -3.16381e+19f,  4.22157e+21f
        .lineTo(SkBits2Float(0xdfb39e51), SkBits2Float(0xe282c5bd))  // -2.58857e+19f, -1.20616e+21f
        .close()
        .detach();
}

static std::unique_ptr<GrFragmentProcessor>
create_linear_gradient_processor(skgpu::ganesh::SurfaceDrawContext* sdc, const SkMatrix& ctm) {
    SkPoint pts[2] = { {0, 0}, {1, 1} };
    SkColor colors[2] = { SK_ColorGREEN, SK_ColorBLUE };
    sk_sp<SkShader> shader = SkGradientShader::MakeLinear(
        pts, colors, nullptr, std::size(colors), SkTileMode::kClamp);
    GrColorInfo colorInfo(GrColorType::kRGBA_8888, kPremul_SkAlphaType, nullptr);
    SkSurfaceProps props; // default props for testing
    return GrFragmentProcessors::Make(
            shader.get(), {sdc, &colorInfo, props, GrFPArgs::Scope::kDefault}, ctm);
}

static void test_path(GrRecordingContext* rContext,
                      skgpu::ganesh::SurfaceDrawContext* sdc,
                      const SkPath& path,
                      const SkMatrix& matrix = SkMatrix::I(),
                      GrAAType aaType = GrAAType::kNone,
                      std::unique_ptr<GrFragmentProcessor> fp = nullptr) {
    skgpu::ganesh::TriangulatingPathRenderer pr;
    pr.setMaxVerbCount(100);

    GrPaint paint;
    paint.setXPFactory(GrPorterDuffXPFactory::Get(SkBlendMode::kSrc));
    if (fp) {
        paint.setColorFragmentProcessor(std::move(fp));
    }

    SkIRect clipConservativeBounds = SkIRect::MakeWH(sdc->width(), sdc->height());
    GrStyle style(SkStrokeRec::kFill_InitStyle);
    GrStyledShape shape(path, style);
    skgpu::ganesh::PathRenderer::DrawPathArgs args{rContext,
                                                   std::move(paint),
                                                   &GrUserStencilSettings::kUnused,
                                                   sdc,
                                                   nullptr,
                                                   &clipConservativeBounds,
                                                   &matrix,
                                                   &shape,
                                                   aaType,
                                                   false};
    pr.drawPath(args);
}

DEF_GANESH_TEST_FOR_ALL_CONTEXTS(TriangulatingPathRendererTests,
                                 reporter,
                                 ctxInfo,
                                 CtsEnforcement::kNever) {
    auto ctx = ctxInfo.directContext();
    auto sdc = skgpu::ganesh::SurfaceDrawContext::Make(ctx,
                                                       GrColorType::kRGBA_8888,
                                                       nullptr,
                                                       SkBackingFit::kApprox,
                                                       {800, 800},
                                                       SkSurfaceProps(),
                                                       /*label=*/{},
                                                       /* sampleCnt= */ 1,
                                                       skgpu::Mipmapped::kNo,
                                                       GrProtected::kNo,
                                                       kTopLeft_GrSurfaceOrigin);
    if (!sdc) {
        return;
    }

    ctx->flushAndSubmit();
    // Adding discard to appease vulkan validation warning about loading uninitialized data on draw
    sdc->discard();

    for (CreatePathFn createPath : kNonEdgeAAPaths) {
        SkPathBuilder builder;
        createPath(builder);
        test_path(ctx, sdc.get(), builder.detach());
    }
    SkMatrix nonInvertibleMatrix = SkMatrix::Scale(0, 0);
    std::unique_ptr<GrFragmentProcessor> fp(create_linear_gradient_processor(sdc.get(),
                                                                             SkMatrix()));
    test_path(ctx, sdc.get(), create_path_17(), nonInvertibleMatrix, GrAAType::kCoverage,
              std::move(fp));
    test_path(ctx, sdc.get(), create_path_20(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_21(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_25(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_26(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_27(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_28(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_31(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_38(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_41(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_43(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_44(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_45(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_46(), SkMatrix(), GrAAType::kCoverage);
    test_path(ctx, sdc.get(), create_path_47(), SkMatrix(), GrAAType::kCoverage);
}

#endif // defined(SK_GANESH)

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]; }
    AutoTMalloc<SkPoint> fPoints;
};

class SimplerVertexAllocator : public GrEagerVertexAllocator {
public:
    void* lock(size_t stride, int eagerCount) override {
        size_t allocSize = eagerCount * stride;
        if (allocSize > fVertexAllocSize) {
            fVertexData.reset(allocSize);
        }
        return fVertexData;
    }

    void unlock(int) override {}

    AutoTMalloc<char> fVertexData;
    size_t fVertexAllocSize = 0;
};

}  // 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,
                                         const SkPathBuilder& builder) {
    SkPath path = builder.snapshot();
    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", SkPathBuilder().lineTo(1,0).lineTo(0,1));
    verify_simple_inner_polygons(r, "simple square", SkPathBuilder().lineTo(1,0).lineTo(1,1).lineTo(0,1));
    verify_simple_inner_polygons(r,  "concave polygon", SkPathBuilder()
            .lineTo(1,0).lineTo(.5f,.5f).lineTo(1,1).lineTo(0,1));
    verify_simple_inner_polygons(r, "double wound triangle", SkPathBuilder()
            .lineTo(1,0).lineTo(0,1).lineTo(0,0).lineTo(1,0).lineTo(0,1));
    verify_simple_inner_polygons(r, "self-intersecting bowtie", SkPathBuilder()
            .lineTo(1,0).lineTo(0,1).lineTo(1,1));
    verify_simple_inner_polygons(r, "asymmetrical bowtie", SkPathBuilder()
            .lineTo(1,0).lineTo(0,1).lineTo(.1f,-.1f));
    verify_simple_inner_polygons(r, "bowtie with extremely small section", SkPathBuilder()
            .lineTo(1,0).lineTo(0,1).lineTo(1e-6f,-1e-6f));
    verify_simple_inner_polygons(r, "intersecting squares", SkPathBuilder()
            .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\"", SkPathBuilder()
            .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\"", SkPathBuilder()
            .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", SkPathBuilder(ToolUtils::make_star(SkRect::MakeWH(100, 200))));
    verify_simple_inner_polygons(r, "\"pointy\" intersecting triangles", SkPathBuilder()
            .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", SkPathBuilder()
            .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", SkPathBuilder()
            .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)std::size(kNonEdgeAAPaths); ++i) {
        SkPathBuilder builder;
        kNonEdgeAAPaths[i](builder);
        verify_simple_inner_polygons(r, SkStringPrintf("kNonEdgeAAPaths[%i]", i).c_str(), builder);
    }
    SkRandom rand;
    for (int i = 0; i < 50; ++i) {
        SkPathBuilder randomBuilder;
        randomBuilder.moveTo(rand.nextF(), rand.nextF());
        for (int j = 0; j < i; ++j) {
            randomBuilder.lineTo(rand.nextF(), rand.nextF());
        }
        verify_simple_inner_polygons(r, SkStringPrintf("random_path_%i", i).c_str(), randomBuilder);
    }
}

DEF_TEST(Triangulator_Crbug1262444, r) {
    SkPathBuilder path(SkPathFillType::kWinding);

    path.moveTo(SkBits2Float(0x3fe0633f), SkBits2Float(0x3d04a60d));  // 1.75303f, 0.0323849f
    path.cubicTo(SkBits2Float(0x3fe27540), SkBits2Float(0x3dff593f), SkBits2Float(0x3fe45241),
                 SkBits2Float(0x3e5e2fbb), SkBits2Float(0x3fe55b41), SkBits2Float(
                    0x3e9e596d));  // 1.7692f, 0.124682f, 1.78376f, 0.216979f, 1.79185f, 0.309276f
    path.cubicTo(SkBits2Float(0x3fe5fa41), SkBits2Float(0x3eb3e79c), SkBits2Float(0x3fe62f41),
                 SkBits2Float(0x3ec975cb), SkBits2Float(0x3fe69941), SkBits2Float(
                    0x3edfd837));  // 1.7967f, 0.351376f, 1.79832f, 0.393477f, 1.80155f, 0.437196f
    path.cubicTo(SkBits2Float(0x3fe70341), SkBits2Float(0x3f064e87), SkBits2Float(0x3fe6ce41),
                 SkBits2Float(0x3f1cb0f2), SkBits2Float(0x3fe59041), SkBits2Float(
                    0x3f33135e));  // 1.80479f, 0.524636f, 1.80317f, 0.612075f, 1.79346f, 0.699514f
    path.cubicTo(SkBits2Float(0x3fe48740), SkBits2Float(0x3f468ef5), SkBits2Float(0x3fe2df40),
                 SkBits2Float(0x3f59a06d), SkBits2Float(0x3fe02e3f), SkBits2Float(
                    0x3f6cb1e6));  // 1.78538f, 0.775619f, 1.77244f, 0.850104f, 1.75141f, 0.92459f
    path.cubicTo(SkBits2Float(0x3fde863f), SkBits2Float(0x3f78b759), SkBits2Float(0x3fdc743e),
                 SkBits2Float(0x3f822957), SkBits2Float(0x3fd9c33e), SkBits2Float(
                    0x3f87f701));  // 1.73847f, 0.971548f, 1.7223f, 1.01689f, 1.70127f, 1.06223f
    path.cubicTo(SkBits2Float(0x3fd98e3e), SkBits2Float(0x3f88611f), SkBits2Float(0x3fd9593e),
                 SkBits2Float(0x3f88cb3e), SkBits2Float(0x3fd9243d), SkBits2Float(
                    0x3f896a6b));  // 1.69965f, 1.06546f, 1.69804f, 1.0687f, 1.69642f, 1.07356f
    path.cubicTo(SkBits2Float(0x3fd63e3c), SkBits2Float(0x3f8fa234), SkBits2Float(0x3fd2ee3b),
                 SkBits2Float(0x3f95d9fd), SkBits2Float(0x3fd2ee3b), SkBits2Float(
                    0x3f9ce602));  // 1.67377f, 1.12214f, 1.6479f, 1.17071f, 1.6479f, 1.22577f
    path.cubicTo(SkBits2Float(0x3fd3233b), SkBits2Float(0x3f9cb0f3), SkBits2Float(0x3fd3583b),
                 SkBits2Float(0x3f9cb0f3), SkBits2Float(0x3fd3c23c), SkBits2Float(
                    0x3f9c7be4));  // 1.64951f, 1.22415f, 1.65113f, 1.22415f, 1.65437f, 1.22253f
    path.cubicTo(SkBits2Float(0x3fd3c23c), SkBits2Float(0x3f9cb0f3), SkBits2Float(0x3fd3c23c),
                 SkBits2Float(0x3f9cb0f3), SkBits2Float(0x3fd3c23c), SkBits2Float(
                    0x3f9ce602));  // 1.65437f, 1.22415f, 1.65437f, 1.22415f, 1.65437f, 1.22577f
    path.cubicTo(SkBits2Float(0x3fd5353c), SkBits2Float(0x3f9c46d4), SkBits2Float(0x3fd6dd3d),
                 SkBits2Float(0x3f9bdcb6), SkBits2Float(0x3fd7b13d), SkBits2Float(
                    0x3f9ad36a));  // 1.66569f, 1.22091f, 1.67863f, 1.21767f, 1.6851f, 1.20958f
    path.cubicTo(SkBits2Float(0x3fda623e), SkBits2Float(0x3f96ae3a), SkBits2Float(0x3fdca93f),
                 SkBits2Float(0x3f921eeb), SkBits2Float(0x3fdf253f), SkBits2Float(
                    0x3f8dc4ab));  // 1.70612f, 1.17719f, 1.72391f, 1.14157f, 1.74332f, 1.10756f
    path.cubicTo(SkBits2Float(0x3fe0983f), SkBits2Float(0x3f8b12e5), SkBits2Float(0x3fe1d640),
                 SkBits2Float(0x3f87f700), SkBits2Float(0x3fe3b340), SkBits2Float(
                    0x3f857a4a));  // 1.75465f, 1.08651f, 1.76435f, 1.06223f, 1.77891f, 1.04279f
    path.cubicTo(SkBits2Float(0x3fe48740), SkBits2Float(0x3f8470fe), SkBits2Float(0x3fe62f40),
                 SkBits2Float(0x3f8470fe), SkBits2Float(0x3fe7d741), SkBits2Float(
                    0x3f843bef));  // 1.78538f, 1.0347f, 1.79832f, 1.0347f, 1.81126f, 1.03308f
    path.cubicTo(SkBits2Float(0x3fe2aa40), SkBits2Float(0x3f943182), SkBits2Float(0x3fda623d),
                 SkBits2Float(0x3fa2498e), SkBits2Float(0x3fceff3a), SkBits2Float(
                    0x3fae4f01));  // 1.77082f, 1.15776f, 1.70612f, 1.26787f, 1.61716f, 1.36179f
    path.cubicTo(SkBits2Float(0x3fce6039), SkBits2Float(0x3faf233e), SkBits2Float(0x3fcd2239),
                 SkBits2Float(0x3faf584d), SkBits2Float(0x3fcc1939), SkBits2Float(
                    0x3fafc26b));  // 1.61231f, 1.36826f, 1.60261f, 1.36988f, 1.59452f, 1.37312f
    path.cubicTo(SkBits2Float(0x3fcc1939), SkBits2Float(0x3faff77a), SkBits2Float(0x3fcc1939),
                 SkBits2Float(0x3faff77a), SkBits2Float(0x3fcc4e39), SkBits2Float(
                    0x3fb02c89));  // 1.59452f, 1.37474f, 1.59452f, 1.37474f, 1.59614f, 1.37636f
    path.cubicTo(SkBits2Float(0x3fcc1939), SkBits2Float(0x3fb02c89), SkBits2Float(0x3fcc1939),
                 SkBits2Float(0x3fb02c89), SkBits2Float(0x3fcbe439), SkBits2Float(
                    0x3fb02c89));  // 1.59452f, 1.37636f, 1.59452f, 1.37636f, 1.5929f, 1.37636f
    path.cubicTo(SkBits2Float(0x3fcbe439), SkBits2Float(0x3fb20a12), SkBits2Float(0x3fcb4539),
                 SkBits2Float(0x3fb37d7d), SkBits2Float(0x3fc99d39), SkBits2Float(
                    0x3fb3b28c));  // 1.5929f, 1.39093f, 1.58805f, 1.40227f, 1.57511f, 1.40389f
    path.cubicTo(SkBits2Float(0x3fc93339), SkBits2Float(0x3fb3e79b), SkBits2Float(0x3fc8c938),
                 SkBits2Float(0x3fb41caa), SkBits2Float(0x3fc7f538), SkBits2Float(
                    0x3fb41caa));  // 1.57188f, 1.40551f, 1.56864f, 1.40712f, 1.56217f, 1.40712f
    path.cubicTo(SkBits2Float(0x3fc7f538), SkBits2Float(0x3fb3e79b), SkBits2Float(0x3fc7f538),
                 SkBits2Float(0x3fb3e79b), SkBits2Float(0x3fc7f538), SkBits2Float(
                    0x3fb3b28c));  // 1.56217f, 1.40551f, 1.56217f, 1.40551f, 1.56217f, 1.40389f
    path.lineTo(SkBits2Float(0x3fc7c038), SkBits2Float(0x3fb3b28c));  // 1.56055f, 1.40389f
    path.cubicTo(SkBits2Float(0x3fc7c038), SkBits2Float(0x3fb4f0e7), SkBits2Float(0x3fc7f538),
                 SkBits2Float(0x3fb66452), SkBits2Float(0x3fc78b38), SkBits2Float(
                    0x3fb76d9e));  // 1.56055f, 1.4136f, 1.56217f, 1.42494f, 1.55894f, 1.43303f
    path.cubicTo(SkBits2Float(0x3fc3d137), SkBits2Float(0x3fbe4495), SkBits2Float(0x3fbf4336),
                 SkBits2Float(0x3fc4123e), SkBits2Float(0x3fb80434), SkBits2Float(
                    0x3fc76331));  // 1.52982f, 1.48647f, 1.49424f, 1.53181f, 1.43763f, 1.55771f
    path.cubicTo(SkBits2Float(0x3fb47f33), SkBits2Float(0x3fc90bac), SkBits2Float(0x3fb19932),
                 SkBits2Float(0x3fcb5353), SkBits2Float(0x3faf1d31), SkBits2Float(
                    0x3fce6f37));  // 1.41013f, 1.57067f, 1.38749f, 1.58848f, 1.36808f, 1.61277f
    path.cubicTo(SkBits2Float(0x3fa4592e), SkBits2Float(0x3fdb13d7), SkBits2Float(0x3f974e2a),
                 SkBits2Float(0x3fe53bc1), SkBits2Float(0x3f896f25), SkBits2Float(
                    0x3fee5a5f));  // 1.28397f, 1.71154f, 1.18207f, 1.79089f, 1.0737f, 1.86213f
    path.cubicTo(SkBits2Float(0x3f6b883f), SkBits2Float(0x3ffb691f), SkBits2Float(0x3f42f434),
                 SkBits2Float(0x400367b2), SkBits2Float(0x3f184e28), SkBits2Float(
                    0x4008611f));  // 0.920048f, 1.96415f, 0.761539f, 2.0532f, 0.594943f, 2.13093f
    path.cubicTo(SkBits2Float(0x3f184e28), SkBits2Float(0x4008611f), SkBits2Float(0x3f17e428),
                 SkBits2Float(0x4008611f), SkBits2Float(0x3f17e428), SkBits2Float(
                    0x40087ba7));  // 0.594943f, 2.13093f, 0.593325f, 2.13093f, 0.593325f, 2.13255f
    path.cubicTo(SkBits2Float(0x3effc044), SkBits2Float(0x400b47f5), SkBits2Float(0x3ed08c36),
                 SkBits2Float(0x400e2eca), SkBits2Float(0x3e9edc28), SkBits2Float(
                    0x401090f9));  // 0.499514f, 2.17627f, 0.40732f, 2.22161f, 0.310273f, 2.25885f
    path.cubicTo(SkBits2Float(0x3e5a5832), SkBits2Float(0x4012f328), SkBits2Float(0x3de40030),
                 SkBits2Float(0x4014811a), SkBits2Float(0x3c1a7f9e), SkBits2Float(
                    0x40158a66));  // 0.213227f, 2.29609f, 0.111328f, 2.32038f, 0.00942984f, 2.33657f
    path.lineTo(SkBits2Float(0x3c1a7f9e), SkBits2Float(0x401bf73d));  // 0.00942984f, 2.43697f
    path.cubicTo(SkBits2Float(0x3dc98028), SkBits2Float(0x401b580f), SkBits2Float(0x3e3fd82e),
                 SkBits2Float(0x401a694b), SkBits2Float(0x3e8ca424), SkBits2Float(
                    0x40191068));  // 0.098389f, 2.42725f, 0.187348f, 2.41268f, 0.27469f, 2.39163f
    path.cubicTo(SkBits2Float(0x3e94ec27), SkBits2Float(0x4018db59), SkBits2Float(0x3e9d3429),
                 SkBits2Float(0x40188bc2), SkBits2Float(0x3ea4a82b), SkBits2Float(
                    0x401856b3));  // 0.290864f, 2.38839f, 0.307039f, 2.38353f, 0.321596f, 2.38029f
    path.cubicTo(SkBits2Float(0x3eae982e), SkBits2Float(0x4018071c), SkBits2Float(0x3eb95c31),
                 SkBits2Float(0x40179cfe), SkBits2Float(0x3ec34c34), SkBits2Float(
                    0x40174d67));  // 0.341005f, 2.37543f, 0.362031f, 2.36896f, 0.381441f, 2.3641f
    path.cubicTo(SkBits2Float(0x3ec9ec36), SkBits2Float(0x40171858), SkBits2Float(0x3ed08c38),
                 SkBits2Float(0x4016c8c1), SkBits2Float(0x3ed8003a), SkBits2Float(
                    0x401693b2));  // 0.39438f, 2.36086f, 0.40732f, 2.356f, 0.421877f, 2.35276f
    path.cubicTo(SkBits2Float(0x3eda7c3a), SkBits2Float(0x4016792a), SkBits2Float(0x3eddcc3c),
                 SkBits2Float(0x40165ea3), SkBits2Float(0x3ee0483c), SkBits2Float(
                    0x4016441b));  // 0.426729f, 2.35115f, 0.433199f, 2.34953f, 0.438051f, 2.34791f
    path.cubicTo(SkBits2Float(0x3ee2c43d), SkBits2Float(0x40162993), SkBits2Float(0x3ee5403e),
                 SkBits2Float(0x40160f0c), SkBits2Float(0x3ee8903f), SkBits2Float(
                    0x4015f484));  // 0.442903f, 2.34629f, 0.447756f, 2.34467f, 0.454226f, 2.34305f
    path.cubicTo(SkBits2Float(0x3f1c082a), SkBits2Float(0x4012be17), SkBits2Float(0x3f422036),
                 SkBits2Float(0x400e63d8), SkBits2Float(0x3f66fa40), SkBits2Float(
                    0x40096a6a));  // 0.6095f, 2.29285f, 0.758304f, 2.22484f, 0.902256f, 2.14712f
    path.cubicTo(SkBits2Float(0x3f6a4a41), SkBits2Float(0x4009004c), SkBits2Float(0x3f6d3042),
                 SkBits2Float(0x4008962d), SkBits2Float(0x3f708043), SkBits2Float(
                    0x40081187));  // 0.915196f, 2.14064f, 0.926518f, 2.13417f, 0.939457f, 2.12607f
    path.cubicTo(SkBits2Float(0x3f7efe47), SkBits2Float(0x4005feef), SkBits2Float(0x3f868925),
                 SkBits2Float(0x4003b748), SkBits2Float(0x3f8d5e28), SkBits2Float(
                    0x40015519));  // 0.996067f, 2.09368f, 1.05106f, 2.05806f, 1.10444f, 2.02082f
    path.cubicTo(SkBits2Float(0x3f97b82b), SkBits2Float(0x3ffb691d), SkBits2Float(0x3fa1a82e),
                 SkBits2Float(0x3ff388da), SkBits2Float(0x3fab9830), SkBits2Float(
                    0x3feb7389));  // 1.18531f, 1.96415f, 1.26294f, 1.90261f, 1.34058f, 1.83946f
    path.cubicTo(SkBits2Float(0x3fb20332), SkBits2Float(0x3fe6450c), SkBits2Float(0x3fb80434),
                 SkBits2Float(0x3fe0e181), SkBits2Float(0x3fbd6635), SkBits2Float(
                    0x3fda3f99));  // 1.39072f, 1.79898f, 1.43763f, 1.75688f, 1.47968f, 1.70507f
    path.cubicTo(SkBits2Float(0x3fbf4336), SkBits2Float(0x3fd7f7f2), SkBits2Float(0x3fc12037),
                 SkBits2Float(0x3fd5b04b), SkBits2Float(0x3fc2fd36), SkBits2Float(
                    0x3fd33394));  // 1.49424f, 1.68725f, 1.5088f, 1.66944f, 1.52335f, 1.65001f
    path.cubicTo(SkBits2Float(0x3fc5e337), SkBits2Float(0x3fcf7881), SkBits2Float(0x3fc8c938),
                 SkBits2Float(0x3fcbbd70), SkBits2Float(0x3fcbaf38), SkBits2Float(
                    0x3fc8025d));  // 1.546f, 1.62086f, 1.56864f, 1.59172f, 1.59128f, 1.56257f
    path.cubicTo(SkBits2Float(0x3fceff39), SkBits2Float(0x3fc3a81e), SkBits2Float(0x3fd2843b),
                 SkBits2Float(0x3fbf18cf), SkBits2Float(0x3fd5d43b), SkBits2Float(
                    0x3fbabe8f));  // 1.61716f, 1.52857f, 1.64466f, 1.49294f, 1.67054f, 1.45894f
    path.cubicTo(SkBits2Float(0x3fd8503c), SkBits2Float(0x3fb7a2ab), SkBits2Float(0x3fda973d),
                 SkBits2Float(0x3fb486c7), SkBits2Float(0x3fdca93e), SkBits2Float(
                    0x3fb135d3));  // 1.68995f, 1.43465f, 1.70774f, 1.41036f, 1.72391f, 1.38446f
    path.cubicTo(SkBits2Float(0x3fe5c541), SkBits2Float(0x3fa2b3aa), SkBits2Float(0x3feb5c42),
                 SkBits2Float(0x3f92be16), SkBits2Float(0x3ff15d44), SkBits2Float(
                    0x3f82c882));  // 1.79508f, 1.27111f, 1.83875f, 1.14643f, 1.88566f, 1.02174f
    path.cubicTo(SkBits2Float(0x3ff1fc44), SkBits2Float(0x3f812008), SkBits2Float(0x3ff23144),
                 SkBits2Float(0x3f7e1adf), SkBits2Float(0x3ff29b44), SkBits2Float(
                    0x3f7a5fcc));  // 1.89051f, 1.00879f, 1.89213f, 0.992598f, 1.89536f, 0.978024f
    path.cubicTo(SkBits2Float(0x3ff47845), SkBits2Float(0x3f5fd830), SkBits2Float(0x3ff65545),
                 SkBits2Float(0x3f455094), SkBits2Float(0x3ff6bf45), SkBits2Float(
                    0x3f2a5ed9));  // 1.90992f, 0.874393f, 1.92448f, 0.770761f, 1.92771f, 0.66551f
    path.cubicTo(SkBits2Float(0x3ff33a44), SkBits2Float(0x3f0d5a87), SkBits2Float(0x3ff08943),
                 SkBits2Float(0x3edf03ee), SkBits2Float(0x3fee7743), SkBits2Float(
                    0x3ea352cf));  // 1.90022f, 0.552163f, 1.87919f, 0.435577f, 1.86301f, 0.318991f
    path.cubicTo(SkBits2Float(0x3feccf42), SkBits2Float(0x3e5c872d), SkBits2Float(0x3feb9142),
                 SkBits2Float(0x3de4d179), SkBits2Float(0x3feaf242), SkBits2Float(
                    0x3c04a4ae));  // 1.85008f, 0.215359f, 1.84037f, 0.111728f, 1.83552f, 0.0080959f
    path.lineTo(SkBits2Float(0x3fe02e3f), SkBits2Float(0x3c04a4ae));  // 1.75141f, 0.0080959f
    path.cubicTo(SkBits2Float(0x3fdff93f), SkBits2Float(0x3c6ec47e), SkBits2Float(0x3fe02e3f),
                 SkBits2Float(0x3cb9b545), SkBits2Float(0x3fe0633f), SkBits2Float(
                    0x3d04a60d));  // 1.74979f, 0.0145732f, 1.75141f, 0.0226694f, 1.75303f, 0.0323849f
    path.close();
    path.moveTo(SkBits2Float(0x3fe97f42), SkBits2Float(0x3f7b9e2e));  // 1.8242f, 0.982882f
    path.cubicTo(SkBits2Float(0x3fe91542), SkBits2Float(0x3f7eef21), SkBits2Float(0x3fe87642),
                 SkBits2Float(0x3f81551a), SkBits2Float(0x3fe7d741), SkBits2Float(
                    0x3f82fd94));  // 1.82096f, 0.995836f, 1.81611f, 1.01041f, 1.81126f, 1.02336f
    path.cubicTo(SkBits2Float(0x3fe6ce41), SkBits2Float(0x3f81bf39), SkBits2Float(0x3fe66441),
                 SkBits2Float(0x3f8080dd), SkBits2Float(0x3fe66441), SkBits2Float(
                    0x3f7e1ae4));  // 1.80317f, 1.01365f, 1.79993f, 1.00393f, 1.79993f, 0.992598f
    path.cubicTo(SkBits2Float(0x3fe66441), SkBits2Float(0x3f7c726a), SkBits2Float(0x3fe69941),
                 SkBits2Float(0x3f7b340e), SkBits2Float(0x3fe6ce41), SkBits2Float(
                    0x3f798b95));  // 1.79993f, 0.986121f, 1.80155f, 0.981263f, 1.80317f, 0.974786f
    path.cubicTo(SkBits2Float(0x3fe70341), SkBits2Float(0x3f78b758), SkBits2Float(0x3fe76d41),
                 SkBits2Float(0x3f770edf), SkBits2Float(0x3fe7d741), SkBits2Float(
                    0x3f770edf));  // 1.80479f, 0.971548f, 1.80802f, 0.965071f, 1.81126f, 0.965071f
    path.cubicTo(SkBits2Float(0x3fe84141), SkBits2Float(0x3f770edf), SkBits2Float(0x3fe8ab42),
                 SkBits2Float(0x3f770edf), SkBits2Float(0x3fe8e041), SkBits2Float(
                    0x3f7778fd));  // 1.81449f, 0.965071f, 1.81773f, 0.965071f, 1.81934f, 0.96669f
    path.cubicTo(SkBits2Float(0x3fe97f42), SkBits2Float(0x3f77e31b), SkBits2Float(0x3fe9e942),
                 SkBits2Float(0x3f798b95), SkBits2Float(0x3fe97f42), SkBits2Float(
                    0x3f7b9e2e));  // 1.8242f, 0.968309f, 1.82743f, 0.974786f, 1.8242f, 0.982882f
    path.close();

    SkRect clipBounds = SkRect::MakeLTRB(0, 0, 14, 14);
    SimplerVertexAllocator alloc;
    int vertexCount = GrAATriangulator::PathToAATriangles(
            path.detach(), GrPathUtils::kDefaultTolerance, clipBounds, &alloc);
    REPORTER_ASSERT(r, vertexCount == 0);
}

DEF_TEST(Triangulator_Crbug337080025, r) {
    SkPathBuilder path(SkPathFillType::kWinding);

    path.moveTo(SkBits2Float(0x71ec7478),
                SkBits2Float(0xe18cc1ac));  // 2.34173646e+30f, -3.24562462e+20f
    path.lineTo(SkBits2Float(0x71ec7478),
                SkBits2Float(0xe189ac7b));  // 2.34173646e+30f, -3.17454058e+20f
    path.lineTo(SkBits2Float(0x71ec7478),
                SkBits2Float(0x618fd6dd));  // 2.34173646e+30f, 3.31670867e+20f
    path.lineTo(SkBits2Float(0x71ec7478),
                SkBits2Float(0x6192ec0e));  // 2.34173646e+30f, 3.38779271e+20f
    path.conicTo(SkBits2Float(0x71ec7478),
                 SkBits2Float(0x71ec7478),
                 SkBits2Float(0xe18cc1ac),
                 SkBits2Float(0x71ec7478),
                 SkBits2Float(0x3f3504f3));  // 2.34173646e+30f, 2.34173646e+30f,
                                             // -3.24562462e+20f, 2.34173646e+30f, 0.707106769f
    path.conicTo(SkBits2Float(0xf1ec7478),
                 SkBits2Float(0x71ec7478),
                 SkBits2Float(0xf1ec7478),
                 SkBits2Float(0x6192ec0e),
                 SkBits2Float(0x3f3504f3));  // -2.34173646e+30f, 2.34173646e+30f,
                                             // -2.34173646e+30f, 3.38779271e+20f, 0.707106769f
    path.lineTo(SkBits2Float(0xf1ec7478),
                SkBits2Float(0x618fd6dd));  // -2.34173646e+30f, 3.31670867e+20f
    path.lineTo(SkBits2Float(0xf1ec7478),
                SkBits2Float(0xe189ac7b));  // -2.34173646e+30f, -3.17454058e+20f
    path.lineTo(SkBits2Float(0xf1ec7478),
                SkBits2Float(0xe18cc1ac));  // -2.34173646e+30f, -3.24562462e+20f
    path.conicTo(SkBits2Float(0xf1ec7478),
                 SkBits2Float(0xf1ec7478),
                 SkBits2Float(0x4e775b1f),
                 SkBits2Float(0xf1ec7478),
                 SkBits2Float(0x3f3504f3));  // -2.34173646e+30f, -2.34173646e+30f, 1.03748602e+09f,
                                             // -2.34173646e+30f, 0.707106769f
    path.lineTo(SkBits2Float(0x4e775b20),
                SkBits2Float(0xf1ec7478));  // 1.03748608e+09f, -2.34173646e+30f
    path.lineTo(SkBits2Float(0x71ec7478),
                SkBits2Float(0xe18cc1ac));  // 2.34173646e+30f, -3.24562462e+20f
    path.close();
    path.moveTo(SkBits2Float(0xf1ec7478),
                SkBits2Float(0x618cc1ac));  // -2.34173646e+30f, 3.24562462e+20f
    path.lineTo(SkBits2Float(0x4e775b20),
                SkBits2Float(0x02230001));  // 1.03748608e+09f, 1.19753498e-37f
    path.lineTo(SkBits2Float(0x4e775b20),
                SkBits2Float(0x71ec7478));  // 1.03748608e+09f, 2.34173646e+30f
    path.lineTo(SkBits2Float(0x4e775b1f),
                SkBits2Float(0x71ec7478));  // 1.03748602e+09f, 2.34173646e+30f
    path.lineTo(SkBits2Float(0x4e775b1f), SkBits2Float(0x00000000));  // 1.03748602e+09f, 0
    path.lineTo(SkBits2Float(0x71ec7478),
                SkBits2Float(0x618cc1ac));  // 2.34173646e+30f, 3.24562462e+20f
    path.lineTo(SkBits2Float(0x71ec7478),
                SkBits2Float(0x618fd6dd));  // 2.34173646e+30f, 3.31670867e+20f
    path.lineTo(SkBits2Float(0x4c476265), SkBits2Float(0x5ec54c47));  // 52267412, 7.10840809e+18f
    path.lineTo(SkBits2Float(0x71ec7478),
                SkBits2Float(0xe189ac7b));  // 2.34173646e+30f, -3.17454058e+20f
    path.lineTo(SkBits2Float(0x71ec7478),
                SkBits2Float(0xe186974a));  // 2.34173646e+30f, -3.10345654e+20f
    path.lineTo(SkBits2Float(0x4e775b20),
                SkBits2Float(0x5f454c47));  // 1.03748608e+09f, 1.42168162e+19f
    path.lineTo(SkBits2Float(0xf1ec7478),
                SkBits2Float(0xe186974a));  // -2.34173646e+30f, -3.10345654e+20f
    path.lineTo(SkBits2Float(0xf1ec7478),
                SkBits2Float(0xe189ac7b));  // -2.34173646e+30f, -3.17454058e+20f
    path.lineTo(SkBits2Float(0x4ef1200d),
                SkBits2Float(0x5ec54c47));  // 2.02270477e+09f, 7.10840809e+18f
    path.lineTo(SkBits2Float(0xf1ec7478),
                SkBits2Float(0x618fd6dd));  // -2.34173646e+30f, 3.31670867e+20f
    path.lineTo(SkBits2Float(0xf1ec7478),
                SkBits2Float(0x618cc1ac));  // -2.34173646e+30f, 3.24562462e+20f
    path.close();
    path.moveTo(SkBits2Float(0x4e775b20),
                SkBits2Float(0x02230001));  // 1.03748608e+09f, 1.19753498e-37f
    path.conicTo(SkBits2Float(0x4ef1200d),
                 SkBits2Float(0x02230001),
                 SkBits2Float(0x4ef1200d),
                 SkBits2Float(0x5ec54c47),
                 SkBits2Float(0x3f3504f3));  // 2.02270477e+09f, 1.19753498e-37f, 2.02270477e+09f,
                                             // 7.10840809e+18f, 0.707106769f
    path.conicTo(SkBits2Float(0x4ef1200d),
                 SkBits2Float(0x5f454c47),
                 SkBits2Float(0x4e775b20),
                 SkBits2Float(0x5f454c47),
                 SkBits2Float(0x3f3504f3));  // 2.02270477e+09f, 1.42168162e+19f, 1.03748608e+09f,
                                             // 1.42168162e+19f,0.707106769f
    path.conicTo(SkBits2Float(0x4c476265),
                 SkBits2Float(0x5f454c47),
                 SkBits2Float(0x4c476265),
                 SkBits2Float(0x5ec54c47),
                 SkBits2Float(0x3f3504f3));  // 52267412, 1.42168162e+19f, 52267412,
                                             // 7.10840809e+18f, 0.707106769f
    path.conicTo(SkBits2Float(0x4c476265),
                 SkBits2Float(0x02230001),
                 SkBits2Float(0x4e775b20),
                 SkBits2Float(0x02230001),
                 SkBits2Float(0x3f3504f3));  // 52267412, 1.19753498e-37f, 1.03748608e+09f,
                                             // 1.19753498e-37f, 0.707106769f
    path.close();

    SkRect clipBounds = SkRect::MakeLTRB(0, 0, 256, 256);
    SimplerVertexAllocator alloc;

    int vertexCount = GrAATriangulator::PathToAATriangles(
            path.detach(), GrPathUtils::kDefaultTolerance, clipBounds, &alloc);
    REPORTER_ASSERT(r, vertexCount == 0);
}

// From b/421959607, fixes regression from b/40577206 (crbug.com/844873 above)
//
// TODO: b/425655494. The purpose of this test is to reproduce the nullptr dereference found by the
// fuzzer. Although this test should successfully terminate on Release builds, it is disabled for
// Debug builds because it triggers the ordering asserts in validate_edge_list()
//
// Attempted to solve this ordering issue by adding a variable epsilon in
// https://skia-review.googlesource.com/c/skia/+/1004396, but this had to be removed as it caused
// regressions in the fuzzer.
#ifndef SK_DEBUG
DEF_TEST(Triangulator_bug421959607, r) {
    SkPathBuilder path(SkPathFillType::kWinding);
    path.moveTo(SkBits2Float(0x529423dc),    // 3.1812839e+11f
                SkBits2Float(0xd1f322c7));   // -1.30532565e+11f
    path.conicTo(SkBits2Float(0x20777b4a),   // 2.09625067e-19f
                 SkBits2Float(0x203553b2),   // 1.53589888e-19f
                 SkBits2Float(0x20777b4a),   // 2.09625067e-19f
                 SkBits2Float(0x203553b2),   // 1.53589888e-19f
                 SkBits2Float(0x3f3504f3));  // 0.707106769f
    path.conicTo(SkBits2Float(0x20777b4a),   // 2.09625067e-19f
                 SkBits2Float(0x203553b2),   // 1.53589888e-19f
                 SkBits2Float(0x529423dc),   // 3.1812839e+11f
                 SkBits2Float(0xd1f322c7),   // -1.30532565e+11f
                 SkBits2Float(0x3f3504f3));  // 0.707106769f
    path.conicTo(SkBits2Float(0x531423dc),   // 6.3625678e+11f
                 SkBits2Float(0xd27322c7),   // -2.6106513e+11f
                 SkBits2Float(0x531423dc),   // 6.3625678e+11f
                 SkBits2Float(0xd27322c7),   // -2.6106513e+11f
                 SkBits2Float(0x3f3504f3));  // 0.707106769f
    path.conicTo(SkBits2Float(0x531423dc),   // 6.3625678e+11f
                 SkBits2Float(0xd27322c7),   // -2.6106513e+11f
                 SkBits2Float(0x529423dc),   // 3.1812839e+11f
                 SkBits2Float(0xd1f322c7),   // -1.30532565e+11f
                 SkBits2Float(0x3f3504f3));  // 0.707106769f
    path.close();
    path.moveTo(SkBits2Float(0x20784999),    // 2.10307685e-19f
                SkBits2Float(0x2034ff0c));   // 1.5330981e-19f
    path.cubicTo(SkBits2Float(0xcdec0781),   // -494989344
                 SkBits2Float(0x4d41b13b),   // 203101104
                 SkBits2Float(0x20777b4a),   // 2.09625067e-19f
                 SkBits2Float(0x203553b2),   // 1.53589888e-19f
                 SkBits2Float(0x55144bd5),   // 1.01908386e+13f
                 SkBits2Float(0xd4736460));  // -4.18144426e+12f
    path.close();
    SkRect clipBounds = SkRect::MakeLTRB(0, 0, 128, 160);
    SimplerVertexAllocator alloc;

    int vertexCount = GrAATriangulator::PathToAATriangles(
            path.detach(), GrPathUtils::kDefaultTolerance, clipBounds, &alloc);
    REPORTER_ASSERT(r, vertexCount);
}
#endif

DEF_TEST(Triangulator_bug424666603, r) {
    SkPathBuilder path(SkPathFillType::kWinding);

    path.moveTo(SkBits2Float(0x524ae681),
                SkBits2Float(0xd277c639));  // 2.17862652e+11f, -2.66045637e+11f
    path.conicTo(SkBits2Float(0x2009b044),
                 SkBits2Float(0x9fbc6c7a),
                 SkBits2Float(0x2009b044),
                 SkBits2Float(0x9fbc6c7a),
                 SkBits2Float(0x3f3504f3));  // 1.16626728e-19f, -7.98005565e-20f, 1.16626728e-19f,
                                             // -7.98005565e-20f, 0.707106769f
    path.conicTo(SkBits2Float(0x2009b044),
                 SkBits2Float(0x9fbc6c7a),
                 SkBits2Float(0x524ae681),
                 SkBits2Float(0xd277c639),
                 SkBits2Float(0x3f3504f3));  // 1.16626728e-19f, -7.98005565e-20f, 2.17862652e+11f,
                                             // -2.66045637e+11f, 0.707106769f
    path.conicTo(SkBits2Float(0x52cae681),
                 SkBits2Float(0xd2f7c639),
                 SkBits2Float(0x52cae681),
                 SkBits2Float(0xd2f7c639),
                 SkBits2Float(0x3f3504f3));  // 4.35725304e+11f, -5.32091273e+11f, 4.35725304e+11f,
                                             // -5.32091273e+11f, 0.707106769f
    path.conicTo(SkBits2Float(0x52cae681),
                 SkBits2Float(0xd2f7c639),
                 SkBits2Float(0x524ae681),
                 SkBits2Float(0xd277c639),
                 SkBits2Float(0x3f3504f3));  // 4.35725304e+11f, -5.32091273e+11f, 2.17862652e+11f,
                                             // -2.66045637e+11f, 0.707106769f
    path.close();
    path.moveTo(SkBits2Float(0x200a3d8d),
                SkBits2Float(0x9fbdc58a));  // 1.17094201e-19f, -8.03714145e-20f
    path.cubicTo(SkBits2Float(0xcda1a399),
                 SkBits2Float(0x4dc56333),
                 SkBits2Float(0x2009b044),
                 SkBits2Float(0x9fbc6c7a),
                 SkBits2Float(0x5507077d),
                 SkBits2Float(0xd524e482));  // -338981664, 413951584, 1.16626728e-19f,
                                             // -7.98005565e-20f, 9.27913948e+12f, -1.13313338e+13f
    path.close();
    SkRect clipBounds = SkRect::MakeLTRB(0, 0, 128, 160);
    SimplerVertexAllocator alloc;
    int vertexCount = GrAATriangulator::PathToAATriangles(
            path.detach(), GrPathUtils::kDefaultTolerance, clipBounds, &alloc);
    REPORTER_ASSERT(r, vertexCount);
}

DEF_TEST(GrPathUtils_bug451448218, r) {
    std::array<SkPoint, 128> pts_storage;

    {
        SkPoint* pts_ptr = pts_storage.data();
        auto pts = SkSpan(pts_storage.data(), GrPathUtils::generateQuadraticPoints(
            {std::numeric_limits<float>::min()  , std::numeric_limits<float>::max()  },
            {std::numeric_limits<float>::min()/2, std::numeric_limits<float>::max()/4},
            {std::numeric_limits<float>::min()/4, std::numeric_limits<float>::max()/2},
            0.25f, &pts_ptr, std::size(pts_storage)));
        REPORTER_ASSERT(r, !pts.empty());
        REPORTER_ASSERT(r, std::find_if(pts.begin(),
                                        pts.end(),
                                        [](const SkPoint& p) { return !p.isFinite(); }) ==
                            pts.end());
    }
    {
        SkPoint* pts_ptr = pts_storage.data();
        auto pts = SkSpan(pts_storage.data(), GrPathUtils::generateQuadraticPoints(
            {-2622205555521528300272877568.f, -170344960829757120064436701749554708480.f},
            {-2547580949471791090489098240.f, -170036485662095181808958888004745691136.f},
            {-2473589583252616182193192960.f, -169626344916294939555481038662865518592.f},
            0.25f, &pts_ptr, std::size(pts_storage)));
        REPORTER_ASSERT(r, !pts.empty());
        REPORTER_ASSERT(r, std::find_if(pts.begin(),
                                        pts.end(),
                                        [](const SkPoint& p) { return !p.isFinite(); }) ==
                            pts.end());
    }
}

#endif // SK_ENABLE_OPTIMIZE_SIZE
