GrTessellator: fix intersection above the first vertex.

Handle the case where the an intersection falls not only above both
edge endpoints, but above the first vertex in the mesh. This requires
passing the mesh into check_for_intersection(), in order to modify the
head. We also need to rewind the mesh after insertion, since we need
to rewind to the newly-inserted vertex.

This also cleans up vertex ID computation a little (for logging), so
that vertices before the first vertex or after the last have a
reasonable ID. It also cleans up the intersection-on-endpoint 
special cases by refactoring the calls to split_edge().

BUG=730687

Change-Id: Idea736eca7b7c3c5d8a470b1373a16ad8e649e80
Reviewed-on: https://skia-review.googlesource.com/19069
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Stephen White <senorblanco@chromium.org>
This commit is contained in:
Stephen White 2017-06-08 14:41:01 -04:00 committed by Skia Commit-Bot
parent dc79270101
commit 0cb31675f3
2 changed files with 38 additions and 28 deletions

View File

@ -1006,6 +1006,9 @@ void merge_collinear_edges(Edge* edge, EdgeList* activeEdges, Vertex** current,
void split_edge(Edge* edge, Vertex* v, EdgeList* activeEdges, Vertex** current, Comparator& c,
SkArenaAlloc& alloc) {
if (v == edge->fTop || v == edge->fBottom) {
return;
}
LOG("splitting edge (%g -> %g) at vertex %g (%g, %g)\n",
edge->fTop->fID, edge->fBottom->fID,
v->fID, v->fPoint.fX, v->fPoint.fY);
@ -1073,7 +1076,7 @@ uint8_t max_edge_alpha(Edge* a, Edge* b) {
}
bool check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Vertex** current,
Comparator& c, SkArenaAlloc& alloc) {
VertexList* mesh, Comparator& c, SkArenaAlloc& alloc) {
if (!edge || !other) {
return false;
}
@ -1085,25 +1088,20 @@ bool check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Vert
Vertex* top = *current;
// If the intersection point is above the current vertex, rewind to the vertex above the
// intersection.
while (c.sweep_lt(p, top->fPoint) && top->fPrev) {
while (top && c.sweep_lt(p, top->fPoint)) {
top = top->fPrev;
}
rewind(activeEdges, current, top, c);
if (p == edge->fTop->fPoint) {
split_edge(other, edge->fTop, activeEdges, current, c, alloc);
v = edge->fTop;
} else if (p == edge->fBottom->fPoint) {
split_edge(other, edge->fBottom, activeEdges, current, c, alloc);
v = edge->fBottom;
} else if (p == other->fTop->fPoint) {
split_edge(edge, other->fTop, activeEdges, current, c, alloc);
v = other->fTop;
} else if (p == other->fBottom->fPoint) {
split_edge(edge, other->fBottom, activeEdges, current, c, alloc);
v = other->fBottom;
} else {
Vertex* prevV = top;
Vertex* nextV = top->fNext;
Vertex* nextV = top ? top->fNext : mesh->fHead;
while (nextV && c.sweep_lt(nextV->fPoint, p)) {
prevV = nextV;
nextV = nextV->fNext;
@ -1115,22 +1113,20 @@ bool check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Vert
} else {
v = alloc.make<Vertex>(p, alpha);
#if LOGGING_ENABLED
float prevID = prevV ? prevV->fID : 0.0f;
float nextID = nextV ? nextV->fID : prevV->fID + 1.0f;
v->fID = (prevID + nextID) * 0.5f;
if (!prevV) {
v->fID = mesh->fHead - 1.0f;
} else if (!nextV) {
v->fID = mesh->fTail + 1.0f;
} else {
v->fID = (prevV->fID + nextV->fID) * 0.5f;
}
#endif
v->fPrev = prevV;
v->fNext = nextV;
if (prevV) {
prevV->fNext = v;
}
if (nextV) {
nextV->fPrev = v;
}
mesh->insert(v, prevV, nextV);
}
split_edge(edge, v, activeEdges, current, c, alloc);
split_edge(other, v, activeEdges, current, c, alloc);
}
rewind(activeEdges, current, top ? top : v, c);
split_edge(edge, v, activeEdges, current, c, alloc);
split_edge(other, v, activeEdges, current, c, alloc);
v->fAlpha = SkTMax(v->fAlpha, alpha);
return true;
}
@ -1265,10 +1261,10 @@ void merge_sort(VertexList* vertices) {
// Stage 4: Simplify the mesh by inserting new vertices at intersecting edges.
void simplify(const VertexList& vertices, Comparator& c, SkArenaAlloc& alloc) {
void simplify(VertexList* mesh, Comparator& c, SkArenaAlloc& alloc) {
LOG("simplifying complex polygons\n");
EdgeList activeEdges;
for (Vertex* v = vertices.fHead; v != nullptr; v = v->fNext) {
for (Vertex* v = mesh->fHead; v != nullptr; v = v->fNext) {
if (!v->fFirstEdgeAbove && !v->fFirstEdgeBelow) {
continue;
}
@ -1289,12 +1285,12 @@ void simplify(const VertexList& vertices, Comparator& c, SkArenaAlloc& alloc) {
v->fRightEnclosingEdge = rightEnclosingEdge;
if (v->fFirstEdgeBelow) {
for (Edge* edge = v->fFirstEdgeBelow; edge; edge = edge->fNextEdgeBelow) {
if (check_for_intersection(edge, leftEnclosingEdge, &activeEdges, &v, c,
if (check_for_intersection(edge, leftEnclosingEdge, &activeEdges, &v, mesh, c,
alloc)) {
restartChecks = true;
break;
}
if (check_for_intersection(edge, rightEnclosingEdge, &activeEdges, &v, c,
if (check_for_intersection(edge, rightEnclosingEdge, &activeEdges, &v, mesh, c,
alloc)) {
restartChecks = true;
break;
@ -1302,7 +1298,7 @@ void simplify(const VertexList& vertices, Comparator& c, SkArenaAlloc& alloc) {
}
} else {
if (check_for_intersection(leftEnclosingEdge, rightEnclosingEdge,
&activeEdges, &v, c, alloc)) {
&activeEdges, &v, mesh, c, alloc)) {
restartChecks = true;
}
@ -1729,7 +1725,7 @@ Poly* contours_to_polys(VertexList* contours, int contourCnt, SkPath::FillType f
contours_to_mesh(contours, contourCnt, antialias, &mesh, c, alloc);
sort_mesh(&mesh, c, alloc);
merge_coincident_vertices(&mesh, c, alloc);
simplify(mesh, c, alloc);
simplify(&mesh, c, alloc);
if (antialias) {
VertexList innerMesh;
extract_boundaries(mesh, &innerMesh, outerMesh, fillType, c, alloc);
@ -1741,7 +1737,7 @@ Poly* contours_to_polys(VertexList* contours, int contourCnt, SkPath::FillType f
connect_partners(outerMesh, c, alloc);
sorted_merge(&innerMesh, outerMesh, &aaMesh, c);
merge_coincident_vertices(&aaMesh, c, alloc);
simplify(aaMesh, c, alloc);
simplify(&aaMesh, c, alloc);
outerMesh->fHead = outerMesh->fTail = nullptr;
return tessellate(aaMesh, alloc);
} else {

View File

@ -314,6 +314,19 @@ static SkPath create_path_19() {
return path;
}
// From clusterfuzz-testcase-minimized-6735316361936896
// FIXME: [add description here]
static SkPath create_path_20() {
SkPath path;
path.moveTo( 2822128.5, 235.026336669921875);
path.lineTo( 2819349.25, 235.3623504638671875);
path.lineTo( -340558688, 23.83478546142578125);
path.lineTo( -340558752, 25.510419845581054688);
path.lineTo( -340558720, 27.18605804443359375);
return path;
}
static sk_sp<GrFragmentProcessor> create_linear_gradient_processor(GrContext* ctx) {
SkPoint pts[2] = { {0, 0}, {1, 1} };
SkColor colors[2] = { SK_ColorGREEN, SK_ColorBLUE };
@ -390,5 +403,6 @@ DEF_GPUTEST_FOR_ALL_CONTEXTS(TessellatingPathRendererTests, reporter, ctxInfo) {
test_path(ctx, rtc.get(), create_path_17(), nonInvertibleMatrix, GrAAType::kCoverage, fp);
test_path(ctx, rtc.get(), create_path_18());
test_path(ctx, rtc.get(), create_path_19());
test_path(ctx, rtc.get(), create_path_20(), SkMatrix(), GrAAType::kCoverage);
}
#endif