blob: a1abea5ec2ddfe41e1c957ce48553edaa349dc16 [file] [log] [blame]
/*
* Copyright 2015 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifndef GrTriangulator_DEFINED
#define GrTriangulator_DEFINED
#include "include/core/SkPath.h"
#include "include/core/SkPoint.h"
#include "include/private/SkColorData.h"
#include "src/core/SkArenaAlloc.h"
#include "src/gpu/GrColor.h"
class GrEagerVertexAllocator;
struct SkRect;
#define TRIANGULATOR_WIREFRAME 0
/**
* Provides utility functions for converting paths to a collection of triangles.
*/
class GrTriangulator {
public:
static int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
GrEagerVertexAllocator* vertexAllocator, bool* isLinear) {
GrTriangulator triangulator(path);
int count = triangulator.pathToTriangles(tolerance, clipBounds, vertexAllocator,
path.getFillType());
*isLinear = triangulator.fIsLinear;
return count;
}
static int PathToAATriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
GrEagerVertexAllocator* vertexAllocator, bool* isLinear) {
GrTriangulator triangulator(path);
triangulator.fRoundVerticesToQuarterPixel = true;
triangulator.fEmitCoverage = true;
int count = triangulator.pathToTriangles(tolerance, clipBounds, vertexAllocator,
SkPathFillType::kWinding);
*isLinear = triangulator.fIsLinear;
return count;
}
static int TriangulateSimpleInnerPolygons(const SkPath& path,
GrEagerVertexAllocator* vertexAllocator,
bool *isLinear) {
GrTriangulator triangulator(path);
triangulator.fCullCollinearVertices = false;
triangulator.fSimpleInnerPolygons = true;
int count = triangulator.pathToTriangles(0, SkRect::MakeEmpty(), vertexAllocator,
path.getFillType());
*isLinear = triangulator.fIsLinear;
return count;
}
struct WindingVertex {
SkPoint fPos;
int fWinding;
};
// *DEPRECATED*: Once CCPR is removed this method will go away.
//
// Triangulates a path to an array of vertices. Each triangle is represented as a set of three
// WindingVertex entries, each of which contains the position and winding count (which is the
// same for all three vertices of a triangle). The 'verts' out parameter is set to point to the
// resultant vertex array. CALLER IS RESPONSIBLE for deleting this buffer to avoid a memory
// leak!
static int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
WindingVertex** verts);
// Structs used by GrTriangulator internals.
struct Vertex;
struct VertexList;
struct Edge;
struct EdgeList;
struct Poly;
struct Comparator;
private:
GrTriangulator(const SkPath& path) : fPath(path) {}
// There are six stages to the basic algorithm:
//
// 1) Linearize the path contours into piecewise linear segments:
void pathToContours(float tolerance, const SkRect& clipBounds, VertexList* contours);
// 2) Build a mesh of edges connecting the vertices:
void contoursToMesh(VertexList* contours, int contourCnt, VertexList* mesh, Comparator&);
// 3) Sort the vertices in Y (and secondarily in X) (merge_sort()).
static void SortMesh(VertexList* vertices, const Comparator&);
// 4) Simplify the mesh by inserting new vertices at intersecting edges:
enum class SimplifyResult {
kAlreadySimple,
kFoundSelfIntersection,
kAbort
};
SimplifyResult simplify(VertexList* mesh, Comparator&);
// 5) Tessellate the simplified mesh into monotone polygons:
Poly* tessellate(const VertexList& vertices);
// 6) Triangulate the monotone polygons directly into a vertex buffer:
void* polysToTriangles(Poly* polys, void* data, SkPathFillType overrideFillType);
// For screenspace antialiasing, the algorithm is modified as follows:
//
// Run steps 1-5 above to produce polygons.
// 5b) Apply fill rules to extract boundary contours from the polygons (extract_boundaries()).
// 5c) Simplify boundaries to remove "pointy" vertices that cause inversions
// (simplify_boundary()).
// 5d) Displace edges by half a pixel inward and outward along their normals. Intersect to find
// new vertices, and set zero alpha on the exterior and one alpha on the interior. Build a
// new antialiased mesh from those vertices (stroke_boundary()).
// Run steps 3-6 above on the new mesh, and produce antialiased triangles.
//
// The vertex sorting in step (3) is a merge sort, since it plays well with the linked list
// of vertices (and the necessity of inserting new vertices on intersection).
//
// Stages (4) and (5) use an active edge list -- a list of all edges for which the
// sweep line has crossed the top vertex, but not the bottom vertex. It's sorted
// left-to-right based on the point where both edges are active (when both top vertices
// have been seen, so the "lower" top vertex of the two). If the top vertices are equal
// (shared), it's sorted based on the last point where both edges are active, so the
// "upper" bottom vertex.
//
// The most complex step is the simplification (4). It's based on the Bentley-Ottman
// line-sweep algorithm, but due to floating point inaccuracy, the intersection points are
// not exact and may violate the mesh topology or active edge list ordering. We
// accommodate this by adjusting the topology of the mesh and AEL to match the intersection
// points. This occurs in two ways:
//
// A) Intersections may cause a shortened edge to no longer be ordered with respect to its
// neighbouring edges at the top or bottom vertex. This is handled by merging the
// edges (merge_collinear_edges()).
// B) Intersections may cause an edge to violate the left-to-right ordering of the
// active edge list. This is handled by detecting potential violations and rewinding
// the active edge list to the vertex before they occur (rewind() during merging,
// rewind_if_necessary() during splitting).
//
// The tessellation steps (5) and (6) are based on "Triangulating Simple Polygons and
// Equivalent Problems" (Fournier and Montuno); also a line-sweep algorithm. Note that it
// currently uses a linked list for the active edge list, rather than a 2-3 tree as the
// paper describes. The 2-3 tree gives O(lg N) lookups, but insertion and removal also
// become O(lg N). In all the test cases, it was found that the cost of frequent O(lg N)
// insertions and removals was greater than the cost of infrequent O(N) lookups with the
// linked list implementation. With the latter, all removals are O(1), and most insertions
// are O(1), since we know the adjacent edge in the active edge list based on the topology.
// Only type 2 vertices (see paper) require the O(N) lookups, and these are much less
// frequent. There may be other data structures worth investigating, however.
//
// Note that the orientation of the line sweep algorithms is determined by the aspect ratio of
// the path bounds. When the path is taller than it is wide, we sort vertices based on
// increasing Y coordinate, and secondarily by increasing X coordinate. When the path is wider
// than it is tall, we sort by increasing X coordinate, but secondarily by *decreasing* Y
// coordinate. This is so that the "left" and "right" orientation in the code remains correct
// (edges to the left are increasing in Y; edges to the right are decreasing in Y). That is, the
// setting rotates 90 degrees counterclockwise, rather that transposing.
// Additional helpers and driver functions.
void appendPointToContour(const SkPoint& p, VertexList* contour);
void appendQuadraticToContour(const SkPoint[3], SkScalar toleranceSqd, VertexList* contour);
void generateCubicPoints(const SkPoint&, const SkPoint&, const SkPoint&, const SkPoint&,
SkScalar tolSqd, VertexList* contour, int pointsLeft);
bool splitEdge(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current, Comparator&);
bool intersectEdgePair(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
Comparator&);
bool checkForIntersection(Edge* left, Edge* right, EdgeList* activeEdges, Vertex** current,
VertexList* mesh, Comparator&);
void sanitizeContours(VertexList* contours, int contourCnt);
bool mergeCoincidentVertices(VertexList* mesh, Comparator&);
void buildEdges(VertexList* contours, int contourCnt, VertexList* mesh, Comparator&);
Poly* contoursToPolys(VertexList* contours, int contourCnt, VertexList* outerMesh);
Poly* pathToPolys(float tolerance, const SkRect& clipBounds, int contourCnt,
VertexList* outerMesh);
int pathToTriangles(float tolerance, const SkRect& clipBounds, GrEagerVertexAllocator*,
SkPathFillType overrideFillType);
constexpr static int kArenaChunkSize = 16 * 1024;
SkArenaAlloc fAlloc{kArenaChunkSize};
const SkPath fPath;
bool fIsLinear = false;
// Flags.
bool fRoundVerticesToQuarterPixel = false;
bool fEmitCoverage = false;
bool fCullCollinearVertices = true;
bool fSimpleInnerPolygons = false;
};
#endif