Move GrTriangulator nested struct defs into the .h file
This makes them reusable for the AA triangulator when it gets its own file. Bug: skia:10419 Change-Id: Ibf77b9a77aabc57898c0526b3a6c02ab9c46e2cc Reviewed-on: https://skia-review.googlesource.com/c/skia/+/351876 Commit-Queue: Chris Dalton <csmartdalton@google.com> Reviewed-by: Jim Van Verth <jvanverth@google.com>
This commit is contained in:
parent
50940aba5d
commit
5045de33e7
@ -21,9 +21,7 @@
|
|||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
|
||||||
#define LOGGING_ENABLED 0
|
#if TRIANGULATOR_LOGGING
|
||||||
|
|
||||||
#if LOGGING_ENABLED
|
|
||||||
#define TESS_LOG printf
|
#define TESS_LOG printf
|
||||||
#else
|
#else
|
||||||
#define TESS_LOG(...)
|
#define TESS_LOG(...)
|
||||||
@ -74,49 +72,6 @@ static void list_remove(T* t, T** head, T** tail) {
|
|||||||
t->*Prev = t->*Next = nullptr;
|
t->*Prev = t->*Next = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Vertices are used in three ways: first, the path contours are converted into a
|
|
||||||
* circularly-linked list of Vertices for each contour. After edge construction, the same Vertices
|
|
||||||
* are re-ordered by the merge sort according to the sweep_lt comparator (usually, increasing
|
|
||||||
* in Y) using the same fPrev/fNext pointers that were used for the contours, to avoid
|
|
||||||
* reallocation. Finally, MonotonePolys are built containing a circularly-linked list of
|
|
||||||
* Vertices. (Currently, those Vertices are newly-allocated for the MonotonePolys, since
|
|
||||||
* an individual Vertex from the path mesh may belong to multiple
|
|
||||||
* MonotonePolys, so the original Vertices cannot be re-used.
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct GrTriangulator::Vertex {
|
|
||||||
Vertex(const SkPoint& point, uint8_t alpha)
|
|
||||||
: fPoint(point), fPrev(nullptr), fNext(nullptr)
|
|
||||||
, fFirstEdgeAbove(nullptr), fLastEdgeAbove(nullptr)
|
|
||||||
, fFirstEdgeBelow(nullptr), fLastEdgeBelow(nullptr)
|
|
||||||
, fLeftEnclosingEdge(nullptr), fRightEnclosingEdge(nullptr)
|
|
||||||
, fPartner(nullptr)
|
|
||||||
, fAlpha(alpha)
|
|
||||||
, fSynthetic(false)
|
|
||||||
#if LOGGING_ENABLED
|
|
||||||
, fID (-1.0f)
|
|
||||||
#endif
|
|
||||||
{}
|
|
||||||
SkPoint fPoint; // Vertex position
|
|
||||||
Vertex* fPrev; // Linked list of contours, then Y-sorted vertices.
|
|
||||||
Vertex* fNext; // "
|
|
||||||
Edge* fFirstEdgeAbove; // Linked list of edges above this vertex.
|
|
||||||
Edge* fLastEdgeAbove; // "
|
|
||||||
Edge* fFirstEdgeBelow; // Linked list of edges below this vertex.
|
|
||||||
Edge* fLastEdgeBelow; // "
|
|
||||||
Edge* fLeftEnclosingEdge; // Nearest edge in the AEL left of this vertex.
|
|
||||||
Edge* fRightEnclosingEdge; // Nearest edge in the AEL right of this vertex.
|
|
||||||
Vertex* fPartner; // Corresponding inner or outer vertex (for AA).
|
|
||||||
uint8_t fAlpha;
|
|
||||||
bool fSynthetic; // Is this a synthetic vertex?
|
|
||||||
#if LOGGING_ENABLED
|
|
||||||
float fID; // Identifier used for logging.
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
/***************************************************************************************/
|
|
||||||
|
|
||||||
typedef bool (*CompareFunc)(const SkPoint& a, const SkPoint& b);
|
typedef bool (*CompareFunc)(const SkPoint& a, const SkPoint& b);
|
||||||
|
|
||||||
static bool sweep_lt_horiz(const SkPoint& a, const SkPoint& b) {
|
static bool sweep_lt_horiz(const SkPoint& a, const SkPoint& b) {
|
||||||
@ -127,14 +82,9 @@ static bool sweep_lt_vert(const SkPoint& a, const SkPoint& b) {
|
|||||||
return a.fY < b.fY || (a.fY == b.fY && a.fX < b.fX);
|
return a.fY < b.fY || (a.fY == b.fY && a.fX < b.fX);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GrTriangulator::Comparator {
|
bool GrTriangulator::Comparator::sweep_lt(const SkPoint& a, const SkPoint& b) const {
|
||||||
enum class Direction { kVertical, kHorizontal };
|
return fDirection == Direction::kHorizontal ? sweep_lt_horiz(a, b) : sweep_lt_vert(a, b);
|
||||||
Comparator(Direction direction) : fDirection(direction) {}
|
}
|
||||||
bool sweep_lt(const SkPoint& a, const SkPoint& b) const {
|
|
||||||
return fDirection == Direction::kHorizontal ? sweep_lt_horiz(a, b) : sweep_lt_vert(a, b);
|
|
||||||
}
|
|
||||||
Direction fDirection;
|
|
||||||
};
|
|
||||||
|
|
||||||
static inline void* emit_vertex(Vertex* v, bool emitCoverage, void* data) {
|
static inline void* emit_vertex(Vertex* v, bool emitCoverage, void* data) {
|
||||||
GrVertexWriter verts{data};
|
GrVertexWriter verts{data};
|
||||||
@ -166,42 +116,13 @@ static void* emit_triangle(Vertex* v0, Vertex* v1, Vertex* v2, bool emitCoverage
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GrTriangulator::VertexList {
|
void GrTriangulator::VertexList::insert(Vertex* v, Vertex* prev, Vertex* next) {
|
||||||
VertexList() : fHead(nullptr), fTail(nullptr) {}
|
list_insert<Vertex, &Vertex::fPrev, &Vertex::fNext>(v, prev, next, &fHead, &fTail);
|
||||||
VertexList(Vertex* head, Vertex* tail) : fHead(head), fTail(tail) {}
|
}
|
||||||
Vertex* fHead;
|
|
||||||
Vertex* fTail;
|
void GrTriangulator::VertexList::remove(Vertex* v) {
|
||||||
void insert(Vertex* v, Vertex* prev, Vertex* next) {
|
list_remove<Vertex, &Vertex::fPrev, &Vertex::fNext>(v, &fHead, &fTail);
|
||||||
list_insert<Vertex, &Vertex::fPrev, &Vertex::fNext>(v, prev, next, &fHead, &fTail);
|
}
|
||||||
}
|
|
||||||
void append(Vertex* v) {
|
|
||||||
insert(v, fTail, nullptr);
|
|
||||||
}
|
|
||||||
void append(const VertexList& list) {
|
|
||||||
if (!list.fHead) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (fTail) {
|
|
||||||
fTail->fNext = list.fHead;
|
|
||||||
list.fHead->fPrev = fTail;
|
|
||||||
} else {
|
|
||||||
fHead = list.fHead;
|
|
||||||
}
|
|
||||||
fTail = list.fTail;
|
|
||||||
}
|
|
||||||
void prepend(Vertex* v) {
|
|
||||||
insert(v, nullptr, fHead);
|
|
||||||
}
|
|
||||||
void remove(Vertex* v) {
|
|
||||||
list_remove<Vertex, &Vertex::fPrev, &Vertex::fNext>(v, &fHead, &fTail);
|
|
||||||
}
|
|
||||||
void close() {
|
|
||||||
if (fHead && fTail) {
|
|
||||||
fTail->fNext = fHead;
|
|
||||||
fHead->fPrev = fTail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Round to nearest quarter-pixel. This is used for screenspace tessellation.
|
// Round to nearest quarter-pixel. This is used for screenspace tessellation.
|
||||||
|
|
||||||
@ -214,163 +135,56 @@ static inline SkScalar double_to_clamped_scalar(double d) {
|
|||||||
return SkDoubleToScalar(std::min((double) SK_ScalarMax, std::max(d, (double) -SK_ScalarMax)));
|
return SkDoubleToScalar(std::min((double) SK_ScalarMax, std::max(d, (double) -SK_ScalarMax)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// A line equation in implicit form. fA * x + fB * y + fC = 0, for all points (x, y) on the line.
|
bool GrTriangulator::Line::intersect(const Line& other, SkPoint* point) const {
|
||||||
struct GrTriangulator::Line {
|
double denom = fA * other.fB - fB * other.fA;
|
||||||
Line(double a, double b, double c) : fA(a), fB(b), fC(c) {}
|
if (denom == 0.0) {
|
||||||
Line(Vertex* p, Vertex* q) : Line(p->fPoint, q->fPoint) {}
|
return false;
|
||||||
Line(const SkPoint& p, const SkPoint& q)
|
|
||||||
: fA(static_cast<double>(q.fY) - p.fY) // a = dY
|
|
||||||
, fB(static_cast<double>(p.fX) - q.fX) // b = -dX
|
|
||||||
, fC(static_cast<double>(p.fY) * q.fX - // c = cross(q, p)
|
|
||||||
static_cast<double>(p.fX) * q.fY) {}
|
|
||||||
double dist(const SkPoint& p) const {
|
|
||||||
return fA * p.fX + fB * p.fY + fC;
|
|
||||||
}
|
|
||||||
Line operator*(double v) const {
|
|
||||||
return Line(fA * v, fB * v, fC * v);
|
|
||||||
}
|
|
||||||
double magSq() const {
|
|
||||||
return fA * fA + fB * fB;
|
|
||||||
}
|
|
||||||
void normalize() {
|
|
||||||
double len = sqrt(this->magSq());
|
|
||||||
if (len == 0.0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
double scale = 1.0f / len;
|
|
||||||
fA *= scale;
|
|
||||||
fB *= scale;
|
|
||||||
fC *= scale;
|
|
||||||
}
|
|
||||||
bool nearParallel(const Line& o) const {
|
|
||||||
return fabs(o.fA - fA) < 0.00001 && fabs(o.fB - fB) < 0.00001;
|
|
||||||
}
|
}
|
||||||
|
double scale = 1.0 / denom;
|
||||||
|
point->fX = double_to_clamped_scalar((fB * other.fC - other.fB * fC) * scale);
|
||||||
|
point->fY = double_to_clamped_scalar((other.fA * fC - fA * other.fC) * scale);
|
||||||
|
round(point);
|
||||||
|
return point->isFinite();
|
||||||
|
}
|
||||||
|
|
||||||
// Compute the intersection of two (infinite) Lines.
|
bool GrTriangulator::Edge::intersect(const Edge& other, SkPoint* p, uint8_t* alpha) const {
|
||||||
bool intersect(const Line& other, SkPoint* point) const {
|
TESS_LOG("intersecting %g -> %g with %g -> %g\n",
|
||||||
double denom = fA * other.fB - fB * other.fA;
|
fTop->fID, fBottom->fID, other.fTop->fID, other.fBottom->fID);
|
||||||
if (denom == 0.0) {
|
if (fTop == other.fTop || fBottom == other.fBottom) {
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
double scale = 1.0 / denom;
|
|
||||||
point->fX = double_to_clamped_scalar((fB * other.fC - other.fB * fC) * scale);
|
|
||||||
point->fY = double_to_clamped_scalar((other.fA * fC - fA * other.fC) * scale);
|
|
||||||
round(point);
|
|
||||||
return point->isFinite();
|
|
||||||
}
|
}
|
||||||
double fA, fB, fC;
|
double denom = fLine.fA * other.fLine.fB - fLine.fB * other.fLine.fA;
|
||||||
};
|
if (denom == 0.0) {
|
||||||
|
return false;
|
||||||
/**
|
|
||||||
* An Edge joins a top Vertex to a bottom Vertex. Edge ordering for the list of "edges above" and
|
|
||||||
* "edge below" a vertex as well as for the active edge list is handled by isLeftOf()/isRightOf().
|
|
||||||
* Note that an Edge will give occasionally dist() != 0 for its own endpoints (because floating
|
|
||||||
* point). For speed, that case is only tested by the callers that require it (e.g.,
|
|
||||||
* rewind_if_necessary()). Edges also handle checking for intersection with other edges.
|
|
||||||
* Currently, this converts the edges to the parametric form, in order to avoid doing a division
|
|
||||||
* until an intersection has been confirmed. This is slightly slower in the "found" case, but
|
|
||||||
* a lot faster in the "not found" case.
|
|
||||||
*
|
|
||||||
* The coefficients of the line equation stored in double precision to avoid catastrphic
|
|
||||||
* cancellation in the isLeftOf() and isRightOf() checks. Using doubles ensures that the result is
|
|
||||||
* correct in float, since it's a polynomial of degree 2. The intersect() function, being
|
|
||||||
* degree 5, is still subject to catastrophic cancellation. We deal with that by assuming its
|
|
||||||
* output may be incorrect, and adjusting the mesh topology to match (see comment at the top of
|
|
||||||
* this file).
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct GrTriangulator::Edge {
|
|
||||||
Edge(Vertex* top, Vertex* bottom, int winding, EdgeType type)
|
|
||||||
: fWinding(winding)
|
|
||||||
, fTop(top)
|
|
||||||
, fBottom(bottom)
|
|
||||||
, fType(type)
|
|
||||||
, fLeft(nullptr)
|
|
||||||
, fRight(nullptr)
|
|
||||||
, fPrevEdgeAbove(nullptr)
|
|
||||||
, fNextEdgeAbove(nullptr)
|
|
||||||
, fPrevEdgeBelow(nullptr)
|
|
||||||
, fNextEdgeBelow(nullptr)
|
|
||||||
, fLeftPoly(nullptr)
|
|
||||||
, fRightPoly(nullptr)
|
|
||||||
, fLeftPolyPrev(nullptr)
|
|
||||||
, fLeftPolyNext(nullptr)
|
|
||||||
, fRightPolyPrev(nullptr)
|
|
||||||
, fRightPolyNext(nullptr)
|
|
||||||
, fUsedInLeftPoly(false)
|
|
||||||
, fUsedInRightPoly(false)
|
|
||||||
, fLine(top, bottom) {
|
|
||||||
}
|
|
||||||
int fWinding; // 1 == edge goes downward; -1 = edge goes upward.
|
|
||||||
Vertex* fTop; // The top vertex in vertex-sort-order (sweep_lt).
|
|
||||||
Vertex* fBottom; // The bottom vertex in vertex-sort-order.
|
|
||||||
EdgeType fType;
|
|
||||||
Edge* fLeft; // The linked list of edges in the active edge list.
|
|
||||||
Edge* fRight; // "
|
|
||||||
Edge* fPrevEdgeAbove; // The linked list of edges in the bottom Vertex's "edges above".
|
|
||||||
Edge* fNextEdgeAbove; // "
|
|
||||||
Edge* fPrevEdgeBelow; // The linked list of edges in the top Vertex's "edges below".
|
|
||||||
Edge* fNextEdgeBelow; // "
|
|
||||||
Poly* fLeftPoly; // The Poly to the left of this edge, if any.
|
|
||||||
Poly* fRightPoly; // The Poly to the right of this edge, if any.
|
|
||||||
Edge* fLeftPolyPrev;
|
|
||||||
Edge* fLeftPolyNext;
|
|
||||||
Edge* fRightPolyPrev;
|
|
||||||
Edge* fRightPolyNext;
|
|
||||||
bool fUsedInLeftPoly;
|
|
||||||
bool fUsedInRightPoly;
|
|
||||||
Line fLine;
|
|
||||||
double dist(const SkPoint& p) const {
|
|
||||||
return fLine.dist(p);
|
|
||||||
}
|
}
|
||||||
bool isRightOf(Vertex* v) const {
|
double dx = static_cast<double>(other.fTop->fPoint.fX) - fTop->fPoint.fX;
|
||||||
return fLine.dist(v->fPoint) < 0.0;
|
double dy = static_cast<double>(other.fTop->fPoint.fY) - fTop->fPoint.fY;
|
||||||
|
double sNumer = dy * other.fLine.fB + dx * other.fLine.fA;
|
||||||
|
double tNumer = dy * fLine.fB + dx * fLine.fA;
|
||||||
|
// If (sNumer / denom) or (tNumer / denom) is not in [0..1], exit early.
|
||||||
|
// This saves us doing the divide below unless absolutely necessary.
|
||||||
|
if (denom > 0.0 ? (sNumer < 0.0 || sNumer > denom || tNumer < 0.0 || tNumer > denom)
|
||||||
|
: (sNumer > 0.0 || sNumer < denom || tNumer > 0.0 || tNumer < denom)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
bool isLeftOf(Vertex* v) const {
|
double s = sNumer / denom;
|
||||||
return fLine.dist(v->fPoint) > 0.0;
|
SkASSERT(s >= 0.0 && s <= 1.0);
|
||||||
|
p->fX = SkDoubleToScalar(fTop->fPoint.fX - s * fLine.fB);
|
||||||
|
p->fY = SkDoubleToScalar(fTop->fPoint.fY + s * fLine.fA);
|
||||||
|
if (alpha) {
|
||||||
|
if (fType == EdgeType::kConnector) {
|
||||||
|
*alpha = (1.0 - s) * fTop->fAlpha + s * fBottom->fAlpha;
|
||||||
|
} else if (other.fType == EdgeType::kConnector) {
|
||||||
|
double t = tNumer / denom;
|
||||||
|
*alpha = (1.0 - t) * other.fTop->fAlpha + t * other.fBottom->fAlpha;
|
||||||
|
} else if (fType == EdgeType::kOuter && other.fType == EdgeType::kOuter) {
|
||||||
|
*alpha = 0;
|
||||||
|
} else {
|
||||||
|
*alpha = 255;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
void recompute() {
|
return true;
|
||||||
fLine = Line(fTop, fBottom);
|
}
|
||||||
}
|
|
||||||
bool intersect(const Edge& other, SkPoint* p, uint8_t* alpha = nullptr) const {
|
|
||||||
TESS_LOG("intersecting %g -> %g with %g -> %g\n",
|
|
||||||
fTop->fID, fBottom->fID, other.fTop->fID, other.fBottom->fID);
|
|
||||||
if (fTop == other.fTop || fBottom == other.fBottom) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
double denom = fLine.fA * other.fLine.fB - fLine.fB * other.fLine.fA;
|
|
||||||
if (denom == 0.0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
double dx = static_cast<double>(other.fTop->fPoint.fX) - fTop->fPoint.fX;
|
|
||||||
double dy = static_cast<double>(other.fTop->fPoint.fY) - fTop->fPoint.fY;
|
|
||||||
double sNumer = dy * other.fLine.fB + dx * other.fLine.fA;
|
|
||||||
double tNumer = dy * fLine.fB + dx * fLine.fA;
|
|
||||||
// If (sNumer / denom) or (tNumer / denom) is not in [0..1], exit early.
|
|
||||||
// This saves us doing the divide below unless absolutely necessary.
|
|
||||||
if (denom > 0.0 ? (sNumer < 0.0 || sNumer > denom || tNumer < 0.0 || tNumer > denom)
|
|
||||||
: (sNumer > 0.0 || sNumer < denom || tNumer > 0.0 || tNumer < denom)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
double s = sNumer / denom;
|
|
||||||
SkASSERT(s >= 0.0 && s <= 1.0);
|
|
||||||
p->fX = SkDoubleToScalar(fTop->fPoint.fX - s * fLine.fB);
|
|
||||||
p->fY = SkDoubleToScalar(fTop->fPoint.fY + s * fLine.fA);
|
|
||||||
if (alpha) {
|
|
||||||
if (fType == EdgeType::kConnector) {
|
|
||||||
*alpha = (1.0 - s) * fTop->fAlpha + s * fBottom->fAlpha;
|
|
||||||
} else if (other.fType == EdgeType::kConnector) {
|
|
||||||
double t = tNumer / denom;
|
|
||||||
*alpha = (1.0 - t) * other.fTop->fAlpha + t * other.fBottom->fAlpha;
|
|
||||||
} else if (fType == EdgeType::kOuter && other.fType == EdgeType::kOuter) {
|
|
||||||
*alpha = 0;
|
|
||||||
} else {
|
|
||||||
*alpha = 255;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct SSEdge;
|
struct SSEdge;
|
||||||
|
|
||||||
@ -394,34 +208,12 @@ struct SSEdge {
|
|||||||
typedef std::unordered_map<Vertex*, SSVertex*> SSVertexMap;
|
typedef std::unordered_map<Vertex*, SSVertex*> SSVertexMap;
|
||||||
typedef std::vector<SSEdge*> SSEdgeList;
|
typedef std::vector<SSEdge*> SSEdgeList;
|
||||||
|
|
||||||
struct GrTriangulator::EdgeList {
|
void GrTriangulator::EdgeList::insert(Edge* edge, Edge* prev, Edge* next) {
|
||||||
EdgeList() : fHead(nullptr), fTail(nullptr) {}
|
list_insert<Edge, &Edge::fLeft, &Edge::fRight>(edge, prev, next, &fHead, &fTail);
|
||||||
Edge* fHead;
|
}
|
||||||
Edge* fTail;
|
void GrTriangulator::EdgeList::remove(Edge* edge) {
|
||||||
void insert(Edge* edge, Edge* prev, Edge* next) {
|
list_remove<Edge, &Edge::fLeft, &Edge::fRight>(edge, &fHead, &fTail);
|
||||||
list_insert<Edge, &Edge::fLeft, &Edge::fRight>(edge, prev, next, &fHead, &fTail);
|
}
|
||||||
}
|
|
||||||
void append(Edge* e) {
|
|
||||||
insert(e, fTail, nullptr);
|
|
||||||
}
|
|
||||||
void remove(Edge* edge) {
|
|
||||||
list_remove<Edge, &Edge::fLeft, &Edge::fRight>(edge, &fHead, &fTail);
|
|
||||||
}
|
|
||||||
void removeAll() {
|
|
||||||
while (fHead) {
|
|
||||||
this->remove(fHead);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void close() {
|
|
||||||
if (fHead && fTail) {
|
|
||||||
fTail->fRight = fHead;
|
|
||||||
fHead->fLeft = fTail;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
bool contains(Edge* edge) const {
|
|
||||||
return edge->fLeft || edge->fRight || fHead == edge;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct EventList;
|
struct EventList;
|
||||||
|
|
||||||
@ -497,173 +289,127 @@ static void create_event(SSEdge* edge, Vertex* v, SSEdge* other, Vertex* dest, E
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GrTriangulator::MonotonePoly {
|
void GrTriangulator::MonotonePoly::addEdge(Edge* edge) {
|
||||||
MonotonePoly(Edge* edge, Side side, int winding)
|
if (fSide == kRight_Side) {
|
||||||
: fSide(side)
|
SkASSERT(!edge->fUsedInRightPoly);
|
||||||
, fFirstEdge(nullptr)
|
list_insert<Edge, &Edge::fRightPolyPrev, &Edge::fRightPolyNext>(
|
||||||
, fLastEdge(nullptr)
|
edge, fLastEdge, nullptr, &fFirstEdge, &fLastEdge);
|
||||||
, fPrev(nullptr)
|
edge->fUsedInRightPoly = true;
|
||||||
, fNext(nullptr)
|
} else {
|
||||||
, fWinding(winding) {
|
SkASSERT(!edge->fUsedInLeftPoly);
|
||||||
this->addEdge(edge);
|
list_insert<Edge, &Edge::fLeftPolyPrev, &Edge::fLeftPolyNext>(
|
||||||
}
|
edge, fLastEdge, nullptr, &fFirstEdge, &fLastEdge);
|
||||||
Side fSide;
|
edge->fUsedInLeftPoly = true;
|
||||||
Edge* fFirstEdge;
|
|
||||||
Edge* fLastEdge;
|
|
||||||
MonotonePoly* fPrev;
|
|
||||||
MonotonePoly* fNext;
|
|
||||||
int fWinding;
|
|
||||||
void addEdge(Edge* edge) {
|
|
||||||
if (fSide == kRight_Side) {
|
|
||||||
SkASSERT(!edge->fUsedInRightPoly);
|
|
||||||
list_insert<Edge, &Edge::fRightPolyPrev, &Edge::fRightPolyNext>(
|
|
||||||
edge, fLastEdge, nullptr, &fFirstEdge, &fLastEdge);
|
|
||||||
edge->fUsedInRightPoly = true;
|
|
||||||
} else {
|
|
||||||
SkASSERT(!edge->fUsedInLeftPoly);
|
|
||||||
list_insert<Edge, &Edge::fLeftPolyPrev, &Edge::fLeftPolyNext>(
|
|
||||||
edge, fLastEdge, nullptr, &fFirstEdge, &fLastEdge);
|
|
||||||
edge->fUsedInLeftPoly = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void* emit(bool emitCoverage, void* data) {
|
void* GrTriangulator::MonotonePoly::emit(bool emitCoverage, void* data) {
|
||||||
Edge* e = fFirstEdge;
|
Edge* e = fFirstEdge;
|
||||||
VertexList vertices;
|
VertexList vertices;
|
||||||
vertices.append(e->fTop);
|
vertices.append(e->fTop);
|
||||||
int count = 1;
|
int count = 1;
|
||||||
while (e != nullptr) {
|
while (e != nullptr) {
|
||||||
if (kRight_Side == fSide) {
|
if (kRight_Side == fSide) {
|
||||||
vertices.append(e->fBottom);
|
vertices.append(e->fBottom);
|
||||||
e = e->fRightPolyNext;
|
e = e->fRightPolyNext;
|
||||||
} else {
|
} else {
|
||||||
vertices.prepend(e->fBottom);
|
vertices.prepend(e->fBottom);
|
||||||
e = e->fLeftPolyNext;
|
e = e->fLeftPolyNext;
|
||||||
}
|
|
||||||
count++;
|
|
||||||
}
|
}
|
||||||
Vertex* first = vertices.fHead;
|
count++;
|
||||||
Vertex* v = first->fNext;
|
}
|
||||||
while (v != vertices.fTail) {
|
Vertex* first = vertices.fHead;
|
||||||
SkASSERT(v && v->fPrev && v->fNext);
|
Vertex* v = first->fNext;
|
||||||
Vertex* prev = v->fPrev;
|
while (v != vertices.fTail) {
|
||||||
Vertex* curr = v;
|
SkASSERT(v && v->fPrev && v->fNext);
|
||||||
Vertex* next = v->fNext;
|
Vertex* prev = v->fPrev;
|
||||||
if (count == 3) {
|
Vertex* curr = v;
|
||||||
return this->emitTriangle(prev, curr, next, emitCoverage, data);
|
Vertex* next = v->fNext;
|
||||||
}
|
if (count == 3) {
|
||||||
double ax = static_cast<double>(curr->fPoint.fX) - prev->fPoint.fX;
|
return this->emitTriangle(prev, curr, next, emitCoverage, data);
|
||||||
double ay = static_cast<double>(curr->fPoint.fY) - prev->fPoint.fY;
|
}
|
||||||
double bx = static_cast<double>(next->fPoint.fX) - curr->fPoint.fX;
|
double ax = static_cast<double>(curr->fPoint.fX) - prev->fPoint.fX;
|
||||||
double by = static_cast<double>(next->fPoint.fY) - curr->fPoint.fY;
|
double ay = static_cast<double>(curr->fPoint.fY) - prev->fPoint.fY;
|
||||||
if (ax * by - ay * bx >= 0.0) {
|
double bx = static_cast<double>(next->fPoint.fX) - curr->fPoint.fX;
|
||||||
data = this->emitTriangle(prev, curr, next, emitCoverage, data);
|
double by = static_cast<double>(next->fPoint.fY) - curr->fPoint.fY;
|
||||||
v->fPrev->fNext = v->fNext;
|
if (ax * by - ay * bx >= 0.0) {
|
||||||
v->fNext->fPrev = v->fPrev;
|
data = this->emitTriangle(prev, curr, next, emitCoverage, data);
|
||||||
count--;
|
v->fPrev->fNext = v->fNext;
|
||||||
if (v->fPrev == first) {
|
v->fNext->fPrev = v->fPrev;
|
||||||
v = v->fNext;
|
count--;
|
||||||
} else {
|
if (v->fPrev == first) {
|
||||||
v = v->fPrev;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
v = v->fNext;
|
v = v->fNext;
|
||||||
}
|
|
||||||
}
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
void* emitTriangle(Vertex* prev, Vertex* curr, Vertex* next, bool emitCoverage,
|
|
||||||
void* data) const {
|
|
||||||
if (fWinding < 0) {
|
|
||||||
// Ensure our triangles always wind in the same direction as if the path had been
|
|
||||||
// triangulated as a simple fan (a la red book).
|
|
||||||
std::swap(prev, next);
|
|
||||||
}
|
|
||||||
return emit_triangle(next, curr, prev, emitCoverage, data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct GrTriangulator::Poly {
|
|
||||||
Poly(Vertex* v, int winding)
|
|
||||||
: fFirstVertex(v)
|
|
||||||
, fWinding(winding)
|
|
||||||
, fHead(nullptr)
|
|
||||||
, fTail(nullptr)
|
|
||||||
, fNext(nullptr)
|
|
||||||
, fPartner(nullptr)
|
|
||||||
, fCount(0)
|
|
||||||
{
|
|
||||||
#if LOGGING_ENABLED
|
|
||||||
static int gID = 0;
|
|
||||||
fID = gID++;
|
|
||||||
TESS_LOG("*** created Poly %d\n", fID);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
Poly* addEdge(Edge* e, Side side, SkArenaAlloc& alloc) {
|
|
||||||
TESS_LOG("addEdge (%g -> %g) to poly %d, %s side\n",
|
|
||||||
e->fTop->fID, e->fBottom->fID, fID, side == kLeft_Side ? "left" : "right");
|
|
||||||
Poly* partner = fPartner;
|
|
||||||
Poly* poly = this;
|
|
||||||
if (side == kRight_Side) {
|
|
||||||
if (e->fUsedInRightPoly) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (e->fUsedInLeftPoly) {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (partner) {
|
|
||||||
fPartner = partner->fPartner = nullptr;
|
|
||||||
}
|
|
||||||
if (!fTail) {
|
|
||||||
fHead = fTail = alloc.make<MonotonePoly>(e, side, fWinding);
|
|
||||||
fCount += 2;
|
|
||||||
} else if (e->fBottom == fTail->fLastEdge->fBottom) {
|
|
||||||
return poly;
|
|
||||||
} else if (side == fTail->fSide) {
|
|
||||||
fTail->addEdge(e);
|
|
||||||
fCount++;
|
|
||||||
} else {
|
|
||||||
e = alloc.make<Edge>(fTail->fLastEdge->fBottom, e->fBottom, 1, EdgeType::kInner);
|
|
||||||
fTail->addEdge(e);
|
|
||||||
fCount++;
|
|
||||||
if (partner) {
|
|
||||||
partner->addEdge(e, side, alloc);
|
|
||||||
poly = partner;
|
|
||||||
} else {
|
} else {
|
||||||
MonotonePoly* m = alloc.make<MonotonePoly>(e, side, fWinding);
|
v = v->fPrev;
|
||||||
m->fPrev = fTail;
|
|
||||||
fTail->fNext = m;
|
|
||||||
fTail = m;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
v = v->fNext;
|
||||||
}
|
}
|
||||||
return poly;
|
|
||||||
}
|
}
|
||||||
void* emit(bool emitCoverage, void *data) {
|
return data;
|
||||||
if (fCount < 3) {
|
}
|
||||||
return data;
|
|
||||||
|
void* GrTriangulator::MonotonePoly::emitTriangle(Vertex* prev, Vertex* curr, Vertex* next,
|
||||||
|
bool emitCoverage, void* data) const {
|
||||||
|
if (fWinding < 0) {
|
||||||
|
// Ensure our triangles always wind in the same direction as if the path had been
|
||||||
|
// triangulated as a simple fan (a la red book).
|
||||||
|
std::swap(prev, next);
|
||||||
|
}
|
||||||
|
return emit_triangle(next, curr, prev, emitCoverage, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
Poly* GrTriangulator::Poly::addEdge(Edge* e, Side side, SkArenaAlloc& alloc) {
|
||||||
|
TESS_LOG("addEdge (%g -> %g) to poly %d, %s side\n",
|
||||||
|
e->fTop->fID, e->fBottom->fID, fID, side == kLeft_Side ? "left" : "right");
|
||||||
|
Poly* partner = fPartner;
|
||||||
|
Poly* poly = this;
|
||||||
|
if (side == kRight_Side) {
|
||||||
|
if (e->fUsedInRightPoly) {
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
TESS_LOG("emit() %d, size %d\n", fID, fCount);
|
} else {
|
||||||
for (MonotonePoly* m = fHead; m != nullptr; m = m->fNext) {
|
if (e->fUsedInLeftPoly) {
|
||||||
data = m->emit(emitCoverage, data);
|
return this;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
if (partner) {
|
||||||
|
fPartner = partner->fPartner = nullptr;
|
||||||
|
}
|
||||||
|
if (!fTail) {
|
||||||
|
fHead = fTail = alloc.make<MonotonePoly>(e, side, fWinding);
|
||||||
|
fCount += 2;
|
||||||
|
} else if (e->fBottom == fTail->fLastEdge->fBottom) {
|
||||||
|
return poly;
|
||||||
|
} else if (side == fTail->fSide) {
|
||||||
|
fTail->addEdge(e);
|
||||||
|
fCount++;
|
||||||
|
} else {
|
||||||
|
e = alloc.make<Edge>(fTail->fLastEdge->fBottom, e->fBottom, 1, EdgeType::kInner);
|
||||||
|
fTail->addEdge(e);
|
||||||
|
fCount++;
|
||||||
|
if (partner) {
|
||||||
|
partner->addEdge(e, side, alloc);
|
||||||
|
poly = partner;
|
||||||
|
} else {
|
||||||
|
MonotonePoly* m = alloc.make<MonotonePoly>(e, side, fWinding);
|
||||||
|
m->fPrev = fTail;
|
||||||
|
fTail->fNext = m;
|
||||||
|
fTail = m;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return poly;
|
||||||
|
}
|
||||||
|
void* GrTriangulator::Poly::emit(bool emitCoverage, void *data) {
|
||||||
|
if (fCount < 3) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
Vertex* lastVertex() const { return fTail ? fTail->fLastEdge->fBottom : fFirstVertex; }
|
TESS_LOG("emit() %d, size %d\n", fID, fCount);
|
||||||
Vertex* fFirstVertex;
|
for (MonotonePoly* m = fHead; m != nullptr; m = m->fNext) {
|
||||||
int fWinding;
|
data = m->emit(emitCoverage, data);
|
||||||
MonotonePoly* fHead;
|
}
|
||||||
MonotonePoly* fTail;
|
return data;
|
||||||
Poly* fNext;
|
}
|
||||||
Poly* fPartner;
|
|
||||||
int fCount;
|
|
||||||
#if LOGGING_ENABLED
|
|
||||||
int fID;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
/***************************************************************************************/
|
|
||||||
|
|
||||||
static bool coincident(const SkPoint& a, const SkPoint& b) {
|
static bool coincident(const SkPoint& a, const SkPoint& b) {
|
||||||
return a == b;
|
return a == b;
|
||||||
@ -678,7 +424,7 @@ static Poly* new_poly(Poly** head, Vertex* v, int winding, SkArenaAlloc& alloc)
|
|||||||
|
|
||||||
void GrTriangulator::appendPointToContour(const SkPoint& p, VertexList* contour) {
|
void GrTriangulator::appendPointToContour(const SkPoint& p, VertexList* contour) {
|
||||||
Vertex* v = fAlloc.make<Vertex>(p, 255);
|
Vertex* v = fAlloc.make<Vertex>(p, 255);
|
||||||
#if LOGGING_ENABLED
|
#if TRIANGULATOR_LOGGING
|
||||||
static float gID = 0.0f;
|
static float gID = 0.0f;
|
||||||
v->fID = gID++;
|
v->fID = gID++;
|
||||||
#endif
|
#endif
|
||||||
@ -1212,7 +958,7 @@ static Vertex* create_sorted_vertex(const SkPoint& p, uint8_t alpha, VertexList*
|
|||||||
v = nextV;
|
v = nextV;
|
||||||
} else {
|
} else {
|
||||||
v = alloc.make<Vertex>(p, alpha);
|
v = alloc.make<Vertex>(p, alpha);
|
||||||
#if LOGGING_ENABLED
|
#if TRIANGULATOR_LOGGING
|
||||||
if (!prevV) {
|
if (!prevV) {
|
||||||
v->fID = mesh->fHead->fID - 1.0f;
|
v->fID = mesh->fHead->fID - 1.0f;
|
||||||
} else if (!nextV) {
|
} else if (!nextV) {
|
||||||
@ -1416,7 +1162,7 @@ static void sorted_merge(VertexList* front, VertexList* back, VertexList* result
|
|||||||
} else {
|
} else {
|
||||||
sorted_merge<sweep_lt_vert>(front, back, result);
|
sorted_merge<sweep_lt_vert>(front, back, result);
|
||||||
}
|
}
|
||||||
#if LOGGING_ENABLED
|
#if TRIANGULATOR_LOGGING
|
||||||
float id = 0.0f;
|
float id = 0.0f;
|
||||||
for (Vertex* v = result->fHead; v; v = v->fNext) {
|
for (Vertex* v = result->fHead; v; v = v->fNext) {
|
||||||
v->fID = id++;
|
v->fID = id++;
|
||||||
@ -1455,7 +1201,7 @@ static void merge_sort(VertexList* vertices) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void dump_mesh(const VertexList& mesh) {
|
static void dump_mesh(const VertexList& mesh) {
|
||||||
#if LOGGING_ENABLED
|
#if TRIANGULATOR_LOGGING
|
||||||
for (Vertex* v = mesh.fHead; v; v = v->fNext) {
|
for (Vertex* v = mesh.fHead; v; v = v->fNext) {
|
||||||
TESS_LOG("vertex %g (%g, %g) alpha %d", v->fID, v->fPoint.fX, v->fPoint.fY, v->fAlpha);
|
TESS_LOG("vertex %g (%g, %g) alpha %d", v->fID, v->fPoint.fX, v->fPoint.fY, v->fAlpha);
|
||||||
if (Vertex* p = v->fPartner) {
|
if (Vertex* p = v->fPartner) {
|
||||||
@ -1475,7 +1221,7 @@ static void dump_mesh(const VertexList& mesh) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void dump_skel(const SSEdgeList& ssEdges) {
|
static void dump_skel(const SSEdgeList& ssEdges) {
|
||||||
#if LOGGING_ENABLED
|
#if TRIANGULATOR_LOGGING
|
||||||
for (SSEdge* edge : ssEdges) {
|
for (SSEdge* edge : ssEdges) {
|
||||||
if (edge->fEdge) {
|
if (edge->fEdge) {
|
||||||
TESS_LOG("skel edge %g -> %g",
|
TESS_LOG("skel edge %g -> %g",
|
||||||
@ -1608,7 +1354,7 @@ Poly* GrTriangulator::tessellate(const VertexList& vertices) {
|
|||||||
if (!connected(v)) {
|
if (!connected(v)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
#if LOGGING_ENABLED
|
#if TRIANGULATOR_LOGGING
|
||||||
TESS_LOG("\nvertex %g: (%g,%g), alpha %d\n", v->fID, v->fPoint.fX, v->fPoint.fY, v->fAlpha);
|
TESS_LOG("\nvertex %g: (%g,%g), alpha %d\n", v->fID, v->fPoint.fX, v->fPoint.fY, v->fAlpha);
|
||||||
#endif
|
#endif
|
||||||
Edge* leftEnclosingEdge;
|
Edge* leftEnclosingEdge;
|
||||||
@ -1623,7 +1369,7 @@ Poly* GrTriangulator::tessellate(const VertexList& vertices) {
|
|||||||
leftPoly = leftEnclosingEdge ? leftEnclosingEdge->fRightPoly : nullptr;
|
leftPoly = leftEnclosingEdge ? leftEnclosingEdge->fRightPoly : nullptr;
|
||||||
rightPoly = rightEnclosingEdge ? rightEnclosingEdge->fLeftPoly : nullptr;
|
rightPoly = rightEnclosingEdge ? rightEnclosingEdge->fLeftPoly : nullptr;
|
||||||
}
|
}
|
||||||
#if LOGGING_ENABLED
|
#if TRIANGULATOR_LOGGING
|
||||||
TESS_LOG("edges above:\n");
|
TESS_LOG("edges above:\n");
|
||||||
for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) {
|
for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) {
|
||||||
TESS_LOG("%g -> %g, lpoly %d, rpoly %d\n",
|
TESS_LOG("%g -> %g, lpoly %d, rpoly %d\n",
|
||||||
@ -1704,7 +1450,7 @@ Poly* GrTriangulator::tessellate(const VertexList& vertices) {
|
|||||||
}
|
}
|
||||||
v->fLastEdgeBelow->fRightPoly = rightPoly;
|
v->fLastEdgeBelow->fRightPoly = rightPoly;
|
||||||
}
|
}
|
||||||
#if LOGGING_ENABLED
|
#if TRIANGULATOR_LOGGING
|
||||||
TESS_LOG("\nactive edges:\n");
|
TESS_LOG("\nactive edges:\n");
|
||||||
for (Edge* e = activeEdges.fHead; e != nullptr; e = e->fRight) {
|
for (Edge* e = activeEdges.fHead; e != nullptr; e = e->fRight) {
|
||||||
TESS_LOG("%g -> %g, lpoly %d, rpoly %d\n",
|
TESS_LOG("%g -> %g, lpoly %d, rpoly %d\n",
|
||||||
@ -2191,7 +1937,7 @@ static void extract_boundaries(const VertexList& inMesh, VertexList* innerVertic
|
|||||||
|
|
||||||
void GrTriangulator::contoursToMesh(VertexList* contours, int contourCnt, VertexList* mesh,
|
void GrTriangulator::contoursToMesh(VertexList* contours, int contourCnt, VertexList* mesh,
|
||||||
const Comparator& c) {
|
const Comparator& c) {
|
||||||
#if LOGGING_ENABLED
|
#if TRIANGULATOR_LOGGING
|
||||||
for (int i = 0; i < contourCnt; ++i) {
|
for (int i = 0; i < contourCnt; ++i) {
|
||||||
Vertex* v = contours[i].fHead;
|
Vertex* v = contours[i].fHead;
|
||||||
SkASSERT(v);
|
SkASSERT(v);
|
||||||
@ -2216,7 +1962,7 @@ void GrTriangulator::SortMesh(VertexList* vertices, const Comparator& c) {
|
|||||||
} else {
|
} else {
|
||||||
merge_sort<sweep_lt_vert>(vertices);
|
merge_sort<sweep_lt_vert>(vertices);
|
||||||
}
|
}
|
||||||
#if LOGGING_ENABLED
|
#if TRIANGULATOR_LOGGING
|
||||||
for (Vertex* v = vertices->fHead; v != nullptr; v = v->fNext) {
|
for (Vertex* v = vertices->fHead; v != nullptr; v = v->fNext) {
|
||||||
static float gID = 0.0f;
|
static float gID = 0.0f;
|
||||||
v->fID = gID++;
|
v->fID = gID++;
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
class GrEagerVertexAllocator;
|
class GrEagerVertexAllocator;
|
||||||
struct SkRect;
|
struct SkRect;
|
||||||
|
|
||||||
|
#define TRIANGULATOR_LOGGING 0
|
||||||
#define TRIANGULATOR_WIREFRAME 0
|
#define TRIANGULATOR_WIREFRAME 0
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -200,4 +201,252 @@ private:
|
|||||||
bool fSimpleInnerPolygons = false;
|
bool fSimpleInnerPolygons = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vertices are used in three ways: first, the path contours are converted into a
|
||||||
|
* circularly-linked list of Vertices for each contour. After edge construction, the same Vertices
|
||||||
|
* are re-ordered by the merge sort according to the sweep_lt comparator (usually, increasing
|
||||||
|
* in Y) using the same fPrev/fNext pointers that were used for the contours, to avoid
|
||||||
|
* reallocation. Finally, MonotonePolys are built containing a circularly-linked list of
|
||||||
|
* Vertices. (Currently, those Vertices are newly-allocated for the MonotonePolys, since
|
||||||
|
* an individual Vertex from the path mesh may belong to multiple
|
||||||
|
* MonotonePolys, so the original Vertices cannot be re-used.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct GrTriangulator::Vertex {
|
||||||
|
Vertex(const SkPoint& point, uint8_t alpha)
|
||||||
|
: fPoint(point), fPrev(nullptr), fNext(nullptr)
|
||||||
|
, fFirstEdgeAbove(nullptr), fLastEdgeAbove(nullptr)
|
||||||
|
, fFirstEdgeBelow(nullptr), fLastEdgeBelow(nullptr)
|
||||||
|
, fLeftEnclosingEdge(nullptr), fRightEnclosingEdge(nullptr)
|
||||||
|
, fPartner(nullptr)
|
||||||
|
, fAlpha(alpha)
|
||||||
|
, fSynthetic(false)
|
||||||
|
#if TRIANGULATOR_LOGGING
|
||||||
|
, fID (-1.0f)
|
||||||
|
#endif
|
||||||
|
{}
|
||||||
|
SkPoint fPoint; // Vertex position
|
||||||
|
Vertex* fPrev; // Linked list of contours, then Y-sorted vertices.
|
||||||
|
Vertex* fNext; // "
|
||||||
|
Edge* fFirstEdgeAbove; // Linked list of edges above this vertex.
|
||||||
|
Edge* fLastEdgeAbove; // "
|
||||||
|
Edge* fFirstEdgeBelow; // Linked list of edges below this vertex.
|
||||||
|
Edge* fLastEdgeBelow; // "
|
||||||
|
Edge* fLeftEnclosingEdge; // Nearest edge in the AEL left of this vertex.
|
||||||
|
Edge* fRightEnclosingEdge; // Nearest edge in the AEL right of this vertex.
|
||||||
|
Vertex* fPartner; // Corresponding inner or outer vertex (for AA).
|
||||||
|
uint8_t fAlpha;
|
||||||
|
bool fSynthetic; // Is this a synthetic vertex?
|
||||||
|
#if TRIANGULATOR_LOGGING
|
||||||
|
float fID; // Identifier used for logging.
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GrTriangulator::VertexList {
|
||||||
|
VertexList() : fHead(nullptr), fTail(nullptr) {}
|
||||||
|
VertexList(Vertex* head, Vertex* tail) : fHead(head), fTail(tail) {}
|
||||||
|
Vertex* fHead;
|
||||||
|
Vertex* fTail;
|
||||||
|
void insert(Vertex* v, Vertex* prev, Vertex* next);
|
||||||
|
void append(Vertex* v) { insert(v, fTail, nullptr); }
|
||||||
|
void append(const VertexList& list) {
|
||||||
|
if (!list.fHead) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (fTail) {
|
||||||
|
fTail->fNext = list.fHead;
|
||||||
|
list.fHead->fPrev = fTail;
|
||||||
|
} else {
|
||||||
|
fHead = list.fHead;
|
||||||
|
}
|
||||||
|
fTail = list.fTail;
|
||||||
|
}
|
||||||
|
void prepend(Vertex* v) { insert(v, nullptr, fHead); }
|
||||||
|
void remove(Vertex* v);
|
||||||
|
void close() {
|
||||||
|
if (fHead && fTail) {
|
||||||
|
fTail->fNext = fHead;
|
||||||
|
fHead->fPrev = fTail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A line equation in implicit form. fA * x + fB * y + fC = 0, for all points (x, y) on the line.
|
||||||
|
struct GrTriangulator::Line {
|
||||||
|
Line(double a, double b, double c) : fA(a), fB(b), fC(c) {}
|
||||||
|
Line(Vertex* p, Vertex* q) : Line(p->fPoint, q->fPoint) {}
|
||||||
|
Line(const SkPoint& p, const SkPoint& q)
|
||||||
|
: fA(static_cast<double>(q.fY) - p.fY) // a = dY
|
||||||
|
, fB(static_cast<double>(p.fX) - q.fX) // b = -dX
|
||||||
|
, fC(static_cast<double>(p.fY) * q.fX - // c = cross(q, p)
|
||||||
|
static_cast<double>(p.fX) * q.fY) {}
|
||||||
|
double dist(const SkPoint& p) const { return fA * p.fX + fB * p.fY + fC; }
|
||||||
|
Line operator*(double v) const { return Line(fA * v, fB * v, fC * v); }
|
||||||
|
double magSq() const { return fA * fA + fB * fB; }
|
||||||
|
void normalize() {
|
||||||
|
double len = sqrt(this->magSq());
|
||||||
|
if (len == 0.0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
double scale = 1.0f / len;
|
||||||
|
fA *= scale;
|
||||||
|
fB *= scale;
|
||||||
|
fC *= scale;
|
||||||
|
}
|
||||||
|
bool nearParallel(const Line& o) const {
|
||||||
|
return fabs(o.fA - fA) < 0.00001 && fabs(o.fB - fB) < 0.00001;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compute the intersection of two (infinite) Lines.
|
||||||
|
bool intersect(const Line& other, SkPoint* point) const;
|
||||||
|
double fA, fB, fC;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An Edge joins a top Vertex to a bottom Vertex. Edge ordering for the list of "edges above" and
|
||||||
|
* "edge below" a vertex as well as for the active edge list is handled by isLeftOf()/isRightOf().
|
||||||
|
* Note that an Edge will give occasionally dist() != 0 for its own endpoints (because floating
|
||||||
|
* point). For speed, that case is only tested by the callers that require it (e.g.,
|
||||||
|
* rewind_if_necessary()). Edges also handle checking for intersection with other edges.
|
||||||
|
* Currently, this converts the edges to the parametric form, in order to avoid doing a division
|
||||||
|
* until an intersection has been confirmed. This is slightly slower in the "found" case, but
|
||||||
|
* a lot faster in the "not found" case.
|
||||||
|
*
|
||||||
|
* The coefficients of the line equation stored in double precision to avoid catastrophic
|
||||||
|
* cancellation in the isLeftOf() and isRightOf() checks. Using doubles ensures that the result is
|
||||||
|
* correct in float, since it's a polynomial of degree 2. The intersect() function, being
|
||||||
|
* degree 5, is still subject to catastrophic cancellation. We deal with that by assuming its
|
||||||
|
* output may be incorrect, and adjusting the mesh topology to match (see comment at the top of
|
||||||
|
* this file).
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct GrTriangulator::Edge {
|
||||||
|
Edge(Vertex* top, Vertex* bottom, int winding, EdgeType type)
|
||||||
|
: fWinding(winding)
|
||||||
|
, fTop(top)
|
||||||
|
, fBottom(bottom)
|
||||||
|
, fType(type)
|
||||||
|
, fLeft(nullptr)
|
||||||
|
, fRight(nullptr)
|
||||||
|
, fPrevEdgeAbove(nullptr)
|
||||||
|
, fNextEdgeAbove(nullptr)
|
||||||
|
, fPrevEdgeBelow(nullptr)
|
||||||
|
, fNextEdgeBelow(nullptr)
|
||||||
|
, fLeftPoly(nullptr)
|
||||||
|
, fRightPoly(nullptr)
|
||||||
|
, fLeftPolyPrev(nullptr)
|
||||||
|
, fLeftPolyNext(nullptr)
|
||||||
|
, fRightPolyPrev(nullptr)
|
||||||
|
, fRightPolyNext(nullptr)
|
||||||
|
, fUsedInLeftPoly(false)
|
||||||
|
, fUsedInRightPoly(false)
|
||||||
|
, fLine(top, bottom) {
|
||||||
|
}
|
||||||
|
int fWinding; // 1 == edge goes downward; -1 = edge goes upward.
|
||||||
|
Vertex* fTop; // The top vertex in vertex-sort-order (sweep_lt).
|
||||||
|
Vertex* fBottom; // The bottom vertex in vertex-sort-order.
|
||||||
|
EdgeType fType;
|
||||||
|
Edge* fLeft; // The linked list of edges in the active edge list.
|
||||||
|
Edge* fRight; // "
|
||||||
|
Edge* fPrevEdgeAbove; // The linked list of edges in the bottom Vertex's "edges above".
|
||||||
|
Edge* fNextEdgeAbove; // "
|
||||||
|
Edge* fPrevEdgeBelow; // The linked list of edges in the top Vertex's "edges below".
|
||||||
|
Edge* fNextEdgeBelow; // "
|
||||||
|
Poly* fLeftPoly; // The Poly to the left of this edge, if any.
|
||||||
|
Poly* fRightPoly; // The Poly to the right of this edge, if any.
|
||||||
|
Edge* fLeftPolyPrev;
|
||||||
|
Edge* fLeftPolyNext;
|
||||||
|
Edge* fRightPolyPrev;
|
||||||
|
Edge* fRightPolyNext;
|
||||||
|
bool fUsedInLeftPoly;
|
||||||
|
bool fUsedInRightPoly;
|
||||||
|
Line fLine;
|
||||||
|
double dist(const SkPoint& p) const { return fLine.dist(p); }
|
||||||
|
bool isRightOf(Vertex* v) const { return fLine.dist(v->fPoint) < 0.0; }
|
||||||
|
bool isLeftOf(Vertex* v) const { return fLine.dist(v->fPoint) > 0.0; }
|
||||||
|
void recompute() { fLine = Line(fTop, fBottom); }
|
||||||
|
bool intersect(const Edge& other, SkPoint* p, uint8_t* alpha = nullptr) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GrTriangulator::EdgeList {
|
||||||
|
EdgeList() : fHead(nullptr), fTail(nullptr) {}
|
||||||
|
Edge* fHead;
|
||||||
|
Edge* fTail;
|
||||||
|
void insert(Edge* edge, Edge* prev, Edge* next);
|
||||||
|
void append(Edge* e) { insert(e, fTail, nullptr); }
|
||||||
|
void remove(Edge* edge);
|
||||||
|
void removeAll() {
|
||||||
|
while (fHead) {
|
||||||
|
this->remove(fHead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void close() {
|
||||||
|
if (fHead && fTail) {
|
||||||
|
fTail->fRight = fHead;
|
||||||
|
fHead->fLeft = fTail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool contains(Edge* edge) const { return edge->fLeft || edge->fRight || fHead == edge; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GrTriangulator::MonotonePoly {
|
||||||
|
MonotonePoly(Edge* edge, Side side, int winding)
|
||||||
|
: fSide(side)
|
||||||
|
, fFirstEdge(nullptr)
|
||||||
|
, fLastEdge(nullptr)
|
||||||
|
, fPrev(nullptr)
|
||||||
|
, fNext(nullptr)
|
||||||
|
, fWinding(winding) {
|
||||||
|
this->addEdge(edge);
|
||||||
|
}
|
||||||
|
Side fSide;
|
||||||
|
Edge* fFirstEdge;
|
||||||
|
Edge* fLastEdge;
|
||||||
|
MonotonePoly* fPrev;
|
||||||
|
MonotonePoly* fNext;
|
||||||
|
int fWinding;
|
||||||
|
void addEdge(Edge*);
|
||||||
|
void* emit(bool emitCoverage, void* data);
|
||||||
|
void* emitTriangle(Vertex* prev, Vertex* curr, Vertex* next, bool emitCoverage,
|
||||||
|
void* data) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GrTriangulator::Poly {
|
||||||
|
Poly(Vertex* v, int winding)
|
||||||
|
: fFirstVertex(v)
|
||||||
|
, fWinding(winding)
|
||||||
|
, fHead(nullptr)
|
||||||
|
, fTail(nullptr)
|
||||||
|
, fNext(nullptr)
|
||||||
|
, fPartner(nullptr)
|
||||||
|
, fCount(0)
|
||||||
|
{
|
||||||
|
#if TRIANGULATOR_LOGGING
|
||||||
|
static int gID = 0;
|
||||||
|
fID = gID++;
|
||||||
|
TESS_LOG("*** created Poly %d\n", fID);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
Poly* addEdge(Edge* e, Side side, SkArenaAlloc& alloc);
|
||||||
|
void* emit(bool emitCoverage, void *data);
|
||||||
|
Vertex* lastVertex() const { return fTail ? fTail->fLastEdge->fBottom : fFirstVertex; }
|
||||||
|
Vertex* fFirstVertex;
|
||||||
|
int fWinding;
|
||||||
|
MonotonePoly* fHead;
|
||||||
|
MonotonePoly* fTail;
|
||||||
|
Poly* fNext;
|
||||||
|
Poly* fPartner;
|
||||||
|
int fCount;
|
||||||
|
#if TRIANGULATOR_LOGGING
|
||||||
|
int fID;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GrTriangulator::Comparator {
|
||||||
|
enum class Direction { kVertical, kHorizontal };
|
||||||
|
Comparator(Direction direction) : fDirection(direction) {}
|
||||||
|
bool sweep_lt(const SkPoint& a, const SkPoint& b) const;
|
||||||
|
Direction fDirection;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
Reference in New Issue
Block a user