Convert GrTriangulator to a class
This simplifies the argument lists for functions and will also allow us to extract the AA logic into its own subclass. Bug: skia:10419 Change-Id: If51e86a7633da7a3ee9352c0236258a0a21f2ebe Reviewed-on: https://skia-review.googlesource.com/c/skia/+/347976 Commit-Queue: Chris Dalton <csmartdalton@google.com> Reviewed-by: Robert Phillips <robertphillips@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
parent
736c992966
commit
57ea1fcb74
File diff suppressed because it is too large
Load Diff
@ -8,55 +8,174 @@
|
||||
#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;
|
||||
class SkPath;
|
||||
struct SkRect;
|
||||
|
||||
#define TRIANGULATOR_WIREFRAME 0
|
||||
|
||||
/**
|
||||
* Provides utility functions for converting paths to a collection of triangles.
|
||||
*/
|
||||
class GrTriangulator {
|
||||
public:
|
||||
enum class Mode {
|
||||
kNormal,
|
||||
|
||||
#define TRIANGULATOR_WIREFRAME 0
|
||||
// Surround path edges with coverage ramps for antialiasing.
|
||||
kEdgeAntialias,
|
||||
|
||||
namespace GrTriangulator {
|
||||
// Triangulate only each contour's inner polygon. The inner polygons connect the endpoints
|
||||
// of each verb. (i.e., they are the path that would result from collapsing all curves to
|
||||
// single lines.)
|
||||
//
|
||||
// If the inner polygons are not simple (e.g., self intersection, double winding), then the
|
||||
// tessellator aborts and returns 0.
|
||||
kSimpleInnerPolygons
|
||||
};
|
||||
|
||||
struct WindingVertex {
|
||||
SkPoint fPos;
|
||||
int fWinding;
|
||||
};
|
||||
constexpr static size_t GetVertexStride(Mode mode) {
|
||||
return sizeof(SkPoint) + ((Mode::kEdgeAntialias == mode) ? sizeof(float) : 0);
|
||||
}
|
||||
|
||||
// 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!
|
||||
int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
|
||||
WindingVertex** verts);
|
||||
static int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
|
||||
GrEagerVertexAllocator*, Mode, bool *isLinear);
|
||||
|
||||
enum class Mode {
|
||||
kNormal,
|
||||
struct WindingVertex {
|
||||
SkPoint fPos;
|
||||
int fWinding;
|
||||
};
|
||||
|
||||
// Surround path edges with coverage ramps for antialiasing.
|
||||
kEdgeAntialias,
|
||||
|
||||
// Triangulate only each contour's inner polygon. The inner polygons connect the endpoints of
|
||||
// each verb. (i.e., they are the path that would result from collapsing all curves to single
|
||||
// lines.)
|
||||
// *DEPRECATED*: Once CCPR is removed this method will go away.
|
||||
//
|
||||
// If the inner polygons are not simple (e.g., self intersection, double winding), then the
|
||||
// tessellator aborts and returns 0.
|
||||
kSimpleInnerPolygons
|
||||
// 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, Mode mode) : fPath(path), fMode(mode) {}
|
||||
|
||||
// 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;
|
||||
const Mode fMode;
|
||||
bool fIsLinear = false;
|
||||
};
|
||||
|
||||
constexpr size_t GetVertexStride(Mode mode) {
|
||||
return sizeof(SkPoint) + ((Mode::kEdgeAntialias == mode) ? sizeof(float) : 0);
|
||||
}
|
||||
|
||||
int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
|
||||
GrEagerVertexAllocator*, Mode, bool *isLinear);
|
||||
} // namespace GrTriangulator
|
||||
|
||||
#endif
|
||||
|
@ -172,7 +172,7 @@ bool GrPathTessellateOp::prePrepareInnerPolygonTriangulation(const PrePrepareArg
|
||||
SkASSERT(!fStencilTrianglesProgram);
|
||||
SkASSERT(!fFillTrianglesProgram);
|
||||
|
||||
using GrTriangulator::Mode;
|
||||
using Mode = GrTriangulator::Mode;
|
||||
|
||||
fTriangleVertexCount = GrTriangulator::PathToTriangles(fPath, 0, SkRect::MakeEmpty(),
|
||||
args.fInnerTriangleAllocator,
|
||||
|
Loading…
Reference in New Issue
Block a user