| Index: src/gpu/GrTessellator.cpp
|
| diff --git a/src/gpu/GrTessellator.cpp b/src/gpu/GrTessellator.cpp
|
| index ccffa9ffbeed21b29156e96c8d8e4cffe82e06c7..9ce1a739df8dff27b24d3364da825fa35928ca24 100644
|
| --- a/src/gpu/GrTessellator.cpp
|
| +++ b/src/gpu/GrTessellator.cpp
|
| @@ -7,6 +7,7 @@
|
|
|
| #include "GrTessellator.h"
|
|
|
| +#include "GrDefaultGeoProcFactory.h"
|
| #include "GrPathUtils.h"
|
|
|
| #include "SkChunkAlloc.h"
|
| @@ -16,7 +17,7 @@
|
| #include <stdio.h>
|
|
|
| /*
|
| - * There are six stages to the algorithm:
|
| + * There are six stages to the basic algorithm:
|
| *
|
| * 1) Linearize the path contours into piecewise linear segments (path_to_contours()).
|
| * 2) Build a mesh of edges connecting the vertices (build_edges()).
|
| @@ -25,6 +26,16 @@
|
| * 5) Tessellate the simplified mesh into monotone polygons (tessellate()).
|
| * 6) Triangulate the monotone polygons directly into a vertex buffer (polys_to_triangles()).
|
| *
|
| + * 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 which 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 (boundary_to_aa_mesh()).
|
| + * 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).
|
| *
|
| @@ -130,11 +141,12 @@ void list_remove(T* t, T** head, T** tail) {
|
| */
|
|
|
| struct Vertex {
|
| - Vertex(const SkPoint& point)
|
| + Vertex(const SkPoint& point, uint8_t alpha)
|
| : fPoint(point), fPrev(nullptr), fNext(nullptr)
|
| , fFirstEdgeAbove(nullptr), fLastEdgeAbove(nullptr)
|
| , fFirstEdgeBelow(nullptr), fLastEdgeBelow(nullptr)
|
| , fProcessed(false)
|
| + , fAlpha(alpha)
|
| #if LOGGING_ENABLED
|
| , fID (-1.0f)
|
| #endif
|
| @@ -147,6 +159,7 @@ struct Vertex {
|
| Edge* fFirstEdgeBelow; // Linked list of edges below this vertex.
|
| Edge* fLastEdgeBelow; // "
|
| bool fProcessed; // Has this vertex been seen in simplify()?
|
| + uint8_t fAlpha;
|
| #if LOGGING_ENABLED
|
| float fID; // Identifier used for logging.
|
| #endif
|
| @@ -154,6 +167,11 @@ struct Vertex {
|
|
|
| /***************************************************************************************/
|
|
|
| +struct AAParams {
|
| + bool fTweakAlpha;
|
| + GrColor fColor;
|
| +};
|
| +
|
| typedef bool (*CompareFunc)(const SkPoint& a, const SkPoint& b);
|
|
|
| struct Comparator {
|
| @@ -177,33 +195,43 @@ bool sweep_gt_vert(const SkPoint& a, const SkPoint& b) {
|
| return a.fY == b.fY ? a.fX > b.fX : a.fY > b.fY;
|
| }
|
|
|
| -inline SkPoint* emit_vertex(Vertex* v, SkPoint* data) {
|
| - *data++ = v->fPoint;
|
| - return data;
|
| +inline void* emit_vertex(Vertex* v, const AAParams* aaParams, void* data) {
|
| + if (!aaParams) {
|
| + SkPoint* d = static_cast<SkPoint*>(data);
|
| + *d++ = v->fPoint;
|
| + return d;
|
| + }
|
| + if (aaParams->fTweakAlpha) {
|
| + auto d = static_cast<GrDefaultGeoProcFactory::PositionColorAttr*>(data);
|
| + d->fPosition = v->fPoint;
|
| + d->fColor = SkAlphaMulQ(aaParams->fColor, v->fAlpha);
|
| + d++;
|
| + return d;
|
| + }
|
| + auto d = static_cast<GrDefaultGeoProcFactory::PositionColorCoverageAttr*>(data);
|
| + d->fPosition = v->fPoint;
|
| + d->fColor = aaParams->fColor;
|
| + d->fCoverage = GrNormalizeByteToFloat(v->fAlpha);
|
| + d++;
|
| + return d;
|
| }
|
|
|
| -SkPoint* emit_triangle(Vertex* v0, Vertex* v1, Vertex* v2, SkPoint* data) {
|
| -#if WIREFRAME
|
| - data = emit_vertex(v0, data);
|
| - data = emit_vertex(v1, data);
|
| - data = emit_vertex(v1, data);
|
| - data = emit_vertex(v2, data);
|
| - data = emit_vertex(v2, data);
|
| - data = emit_vertex(v0, data);
|
| +void* emit_triangle(Vertex* v0, Vertex* v1, Vertex* v2, const AAParams* aaParams, void* data) {
|
| +#if TESSELLATOR_WIREFRAME
|
| + data = emit_vertex(v0, aaParams, data);
|
| + data = emit_vertex(v1, aaParams, data);
|
| + data = emit_vertex(v1, aaParams, data);
|
| + data = emit_vertex(v2, aaParams, data);
|
| + data = emit_vertex(v2, aaParams, data);
|
| + data = emit_vertex(v0, aaParams, data);
|
| #else
|
| - data = emit_vertex(v0, data);
|
| - data = emit_vertex(v1, data);
|
| - data = emit_vertex(v2, data);
|
| + data = emit_vertex(v0, aaParams, data);
|
| + data = emit_vertex(v1, aaParams, data);
|
| + data = emit_vertex(v2, aaParams, data);
|
| #endif
|
| return data;
|
| }
|
|
|
| -struct EdgeList {
|
| - EdgeList() : fHead(nullptr), fTail(nullptr) {}
|
| - Edge* fHead;
|
| - Edge* fTail;
|
| -};
|
| -
|
| struct VertexList {
|
| VertexList() : fHead(nullptr), fTail(nullptr) {}
|
| Vertex* fHead;
|
| @@ -217,8 +245,21 @@ struct VertexList {
|
| void prepend(Vertex* v) {
|
| insert(v, nullptr, fHead);
|
| }
|
| + void close() {
|
| + if (fHead && fTail) {
|
| + fTail->fNext = fHead;
|
| + fHead->fPrev = fTail;
|
| + }
|
| + }
|
| };
|
|
|
| +// Round to nearest quarter-pixel. This is used for screenspace tessellation.
|
| +
|
| +inline void round(SkPoint* p) {
|
| + p->fX = SkScalarRoundToScalar(p->fX * SkFloatToScalar(4.0f)) * SkFloatToScalar(0.25f);
|
| + p->fY = SkScalarRoundToScalar(p->fY * SkFloatToScalar(4.0f)) * SkFloatToScalar(0.25f);
|
| +}
|
| +
|
| /**
|
| * 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().
|
| @@ -320,8 +361,33 @@ struct Edge {
|
| p->fY = SkDoubleToScalar(fTop->fPoint.fY + s * fDY);
|
| return true;
|
| }
|
| - bool isActive(EdgeList* activeEdges) const {
|
| - return activeEdges && (fLeft || fRight || activeEdges->fHead == this);
|
| +};
|
| +
|
| +struct EdgeList {
|
| + EdgeList() : fHead(nullptr), fTail(nullptr), fNext(nullptr), fCount(0) {}
|
| + Edge* fHead;
|
| + Edge* fTail;
|
| + EdgeList* fNext;
|
| + int fCount;
|
| + void insert(Edge* edge, Edge* prev, Edge* next) {
|
| + list_insert<Edge, &Edge::fLeft, &Edge::fRight>(edge, prev, next, &fHead, &fTail);
|
| + fCount++;
|
| + }
|
| + void append(Edge* e) {
|
| + insert(e, fTail, nullptr);
|
| + }
|
| + void remove(Edge* edge) {
|
| + list_remove<Edge, &Edge::fLeft, &Edge::fRight>(edge, &fHead, &fTail);
|
| + fCount--;
|
| + }
|
| + void close() {
|
| + if (fHead && fTail) {
|
| + fTail->fRight = fHead;
|
| + fHead->fLeft = fTail;
|
| + }
|
| + }
|
| + bool contains(Edge* edge) const {
|
| + return edge->fLeft || edge->fRight || fHead == edge;
|
| }
|
| };
|
|
|
| @@ -372,7 +438,7 @@ struct Poly {
|
| }
|
| }
|
|
|
| - SkPoint* emit(SkPoint* data) {
|
| + void* emit(const AAParams* aaParams, void* data) {
|
| Edge* e = fFirstEdge;
|
| e->fTop->fPrev = e->fTop->fNext = nullptr;
|
| VertexList vertices;
|
| @@ -399,7 +465,7 @@ struct Poly {
|
| double bx = static_cast<double>(next->fPoint.fX) - curr->fPoint.fX;
|
| double by = static_cast<double>(next->fPoint.fY) - curr->fPoint.fY;
|
| if (ax * by - ay * bx >= 0.0) {
|
| - data = emit_triangle(prev, curr, next, data);
|
| + data = emit_triangle(prev, curr, next, aaParams, data);
|
| v->fPrev->fNext = v->fNext;
|
| v->fNext->fPrev = v->fPrev;
|
| if (v->fPrev == first) {
|
| @@ -455,13 +521,13 @@ struct Poly {
|
| }
|
| return poly;
|
| }
|
| - SkPoint* emit(SkPoint *data) {
|
| + void* emit(const AAParams* aaParams, void *data) {
|
| if (fCount < 3) {
|
| return data;
|
| }
|
| LOG("emit() %d, size %d\n", fID, fCount);
|
| for (MonotonePoly* m = fHead; m != nullptr; m = m->fNext) {
|
| - data = m->emit(data);
|
| + data = m->emit(aaParams, data);
|
| }
|
| return data;
|
| }
|
| @@ -491,9 +557,16 @@ Poly* new_poly(Poly** head, Vertex* v, int winding, SkChunkAlloc& alloc) {
|
| return poly;
|
| }
|
|
|
| +EdgeList* new_contour(EdgeList** head, SkChunkAlloc& alloc) {
|
| + EdgeList* contour = ALLOC_NEW(EdgeList, (), alloc);
|
| + contour->fNext = *head;
|
| + *head = contour;
|
| + return contour;
|
| +}
|
| +
|
| Vertex* append_point_to_contour(const SkPoint& p, Vertex* prev, Vertex** head,
|
| SkChunkAlloc& alloc) {
|
| - Vertex* v = ALLOC_NEW(Vertex, (p), alloc);
|
| + Vertex* v = ALLOC_NEW(Vertex, (p, 255), alloc);
|
| #if LOGGING_ENABLED
|
| static float gID = 0.0f;
|
| v->fID = gID++;
|
| @@ -578,7 +651,7 @@ void path_to_contours(const SkPath& path, SkScalar tolerance, const SkRect& clip
|
| if (path.isInverseFillType()) {
|
| SkPoint quad[4];
|
| clipBounds.toQuad(quad);
|
| - for (int i = 3; i >= 0; i--) {
|
| + for (int i = 0; i < 4; i++) {
|
| prev = append_point_to_contour(quad[i], prev, &head, alloc);
|
| }
|
| head->fPrev = prev;
|
| @@ -649,14 +722,18 @@ void path_to_contours(const SkPath& path, SkScalar tolerance, const SkRect& clip
|
| }
|
| }
|
|
|
| -inline bool apply_fill_type(SkPath::FillType fillType, int winding) {
|
| +inline bool apply_fill_type(SkPath::FillType fillType, Poly* poly) {
|
| + if (!poly) {
|
| + return false;
|
| + }
|
| + int winding = poly->fWinding;
|
| switch (fillType) {
|
| case SkPath::kWinding_FillType:
|
| return winding != 0;
|
| case SkPath::kEvenOdd_FillType:
|
| return (winding & 1) != 0;
|
| case SkPath::kInverseWinding_FillType:
|
| - return winding == 1;
|
| + return winding == -1;
|
| case SkPath::kInverseEvenOdd_FillType:
|
| return (winding & 1) == 1;
|
| default:
|
| @@ -665,8 +742,9 @@ inline bool apply_fill_type(SkPath::FillType fillType, int winding) {
|
| }
|
| }
|
|
|
| -Edge* new_edge(Vertex* prev, Vertex* next, SkChunkAlloc& alloc, Comparator& c) {
|
| - int winding = c.sweep_lt(prev->fPoint, next->fPoint) ? 1 : -1;
|
| +Edge* new_edge(Vertex* prev, Vertex* next, SkChunkAlloc& alloc, Comparator& c,
|
| + int winding_scale = 1) {
|
| + int winding = c.sweep_lt(prev->fPoint, next->fPoint) ? winding_scale : -winding_scale;
|
| Vertex* top = winding < 0 ? next : prev;
|
| Vertex* bottom = winding < 0 ? prev : next;
|
| return ALLOC_NEW(Edge, (top, bottom, winding), alloc);
|
| @@ -674,15 +752,15 @@ Edge* new_edge(Vertex* prev, Vertex* next, SkChunkAlloc& alloc, Comparator& c) {
|
|
|
| void remove_edge(Edge* edge, EdgeList* edges) {
|
| LOG("removing edge %g -> %g\n", edge->fTop->fID, edge->fBottom->fID);
|
| - SkASSERT(edge->isActive(edges));
|
| - list_remove<Edge, &Edge::fLeft, &Edge::fRight>(edge, &edges->fHead, &edges->fTail);
|
| + SkASSERT(edges->contains(edge));
|
| + edges->remove(edge);
|
| }
|
|
|
| void insert_edge(Edge* edge, Edge* prev, EdgeList* edges) {
|
| LOG("inserting edge %g -> %g\n", edge->fTop->fID, edge->fBottom->fID);
|
| - SkASSERT(!edge->isActive(edges));
|
| + SkASSERT(!edges->contains(edge));
|
| Edge* next = prev ? prev->fRight : edges->fHead;
|
| - list_insert<Edge, &Edge::fLeft, &Edge::fRight>(edge, prev, next, &edges->fHead, &edges->fTail);
|
| + edges->insert(edge, prev, next);
|
| }
|
|
|
| void find_enclosing_edges(Vertex* v, EdgeList* edges, Edge** left, Edge** right) {
|
| @@ -722,7 +800,7 @@ void find_enclosing_edges(Edge* edge, EdgeList* edges, Comparator& c, Edge** lef
|
| }
|
|
|
| void fix_active_state(Edge* edge, EdgeList* activeEdges, Comparator& c) {
|
| - if (edge->isActive(activeEdges)) {
|
| + if (activeEdges && activeEdges->contains(edge)) {
|
| if (edge->fBottom->fProcessed || !edge->fTop->fProcessed) {
|
| remove_edge(edge, activeEdges);
|
| }
|
| @@ -791,7 +869,7 @@ void erase_edge_if_zero_winding(Edge* edge, EdgeList* edges) {
|
| LOG("erasing edge (%g -> %g)\n", edge->fTop->fID, edge->fBottom->fID);
|
| remove_edge_above(edge);
|
| remove_edge_below(edge);
|
| - if (edge->isActive(edges)) {
|
| + if (edges && edges->contains(edge)) {
|
| remove_edge(edge, edges);
|
| }
|
| }
|
| @@ -928,9 +1006,24 @@ void split_edge(Edge* edge, Vertex* v, EdgeList* activeEdges, Comparator& c, SkC
|
| }
|
| }
|
|
|
| +Edge* connect(Vertex* prev, Vertex* next, SkChunkAlloc& alloc, Comparator c,
|
| + int winding_scale = 1) {
|
| + Edge* edge = new_edge(prev, next, alloc, c, winding_scale);
|
| + if (edge->fWinding > 0) {
|
| + insert_edge_below(edge, prev, c);
|
| + insert_edge_above(edge, next, c);
|
| + } else {
|
| + insert_edge_below(edge, next, c);
|
| + insert_edge_above(edge, prev, c);
|
| + }
|
| + merge_collinear_edges(edge, nullptr, c);
|
| + return edge;
|
| +}
|
| +
|
| void merge_vertices(Vertex* src, Vertex* dst, Vertex** head, Comparator& c, SkChunkAlloc& alloc) {
|
| LOG("found coincident verts at %g, %g; merging %g into %g\n", src->fPoint.fX, src->fPoint.fY,
|
| src->fID, dst->fID);
|
| + dst->fAlpha = SkTMax(src->fAlpha, dst->fAlpha);
|
| for (Edge* edge = src->fFirstEdgeAbove; edge;) {
|
| Edge* next = edge->fNextEdgeAbove;
|
| set_bottom(edge, dst, nullptr, c);
|
| @@ -944,6 +1037,11 @@ void merge_vertices(Vertex* src, Vertex* dst, Vertex** head, Comparator& c, SkCh
|
| list_remove<Vertex, &Vertex::fPrev, &Vertex::fNext>(src, head, nullptr);
|
| }
|
|
|
| +uint8_t max_edge_alpha(Edge* a, Edge* b) {
|
| + return SkTMax(SkTMax(a->fTop->fAlpha, a->fBottom->fAlpha),
|
| + SkTMax(b->fTop->fAlpha, b->fBottom->fAlpha));
|
| +}
|
| +
|
| Vertex* check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, Comparator& c,
|
| SkChunkAlloc& alloc) {
|
| SkPoint p;
|
| @@ -979,7 +1077,8 @@ Vertex* check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, C
|
| } else if (coincident(nextV->fPoint, p)) {
|
| v = nextV;
|
| } else {
|
| - v = ALLOC_NEW(Vertex, (p), alloc);
|
| + uint8_t alpha = max_edge_alpha(edge, other);
|
| + v = ALLOC_NEW(Vertex, (p, alpha), alloc);
|
| LOG("inserting between %g (%g, %g) and %g (%g, %g)\n",
|
| prevV->fID, prevV->fPoint.fX, prevV->fPoint.fY,
|
| nextV->fID, nextV->fPoint.fX, nextV->fPoint.fY);
|
| @@ -999,10 +1098,13 @@ Vertex* check_for_intersection(Edge* edge, Edge* other, EdgeList* activeEdges, C
|
| return nullptr;
|
| }
|
|
|
| -void sanitize_contours(Vertex** contours, int contourCnt) {
|
| +void sanitize_contours(Vertex** contours, int contourCnt, bool approximate) {
|
| for (int i = 0; i < contourCnt; ++i) {
|
| SkASSERT(contours[i]);
|
| for (Vertex* v = contours[i];;) {
|
| + if (approximate) {
|
| + round(&v->fPoint);
|
| + }
|
| if (coincident(v->fPrev->fPoint, v->fPoint)) {
|
| LOG("vertex %g,%g coincident; removing\n", v->fPoint.fX, v->fPoint.fY);
|
| if (v->fPrev == v) {
|
| @@ -1042,15 +1144,7 @@ Vertex* build_edges(Vertex** contours, int contourCnt, Comparator& c, SkChunkAll
|
| for (int i = 0; i < contourCnt; ++i) {
|
| for (Vertex* v = contours[i]; v != nullptr;) {
|
| Vertex* vNext = v->fNext;
|
| - Edge* edge = new_edge(v->fPrev, v, alloc, c);
|
| - if (edge->fWinding > 0) {
|
| - insert_edge_below(edge, v->fPrev, c);
|
| - insert_edge_above(edge, v, c);
|
| - } else {
|
| - insert_edge_below(edge, v, c);
|
| - insert_edge_above(edge, v->fPrev, c);
|
| - }
|
| - merge_collinear_edges(edge, nullptr, c);
|
| + connect(v->fPrev, v, alloc, c);
|
| if (prev) {
|
| prev->fNext = v;
|
| v->fPrev = prev;
|
| @@ -1145,7 +1239,7 @@ void simplify(Vertex* vertices, Comparator& c, SkChunkAlloc& alloc) {
|
| continue;
|
| }
|
| #if LOGGING_ENABLED
|
| - LOG("\nvertex %g: (%g,%g)\n", v->fID, v->fPoint.fX, v->fPoint.fY);
|
| + LOG("\nvertex %g: (%g,%g), alpha %d\n", v->fID, v->fPoint.fX, v->fPoint.fY, v->fAlpha);
|
| #endif
|
| Edge* leftEnclosingEdge = nullptr;
|
| Edge* rightEnclosingEdge = nullptr;
|
| @@ -1175,6 +1269,12 @@ void simplify(Vertex* vertices, Comparator& c, SkChunkAlloc& alloc) {
|
|
|
| }
|
| } while (restartChecks);
|
| + if (v->fAlpha == 0) {
|
| + if ((leftEnclosingEdge && leftEnclosingEdge->fWinding < 0) &&
|
| + (rightEnclosingEdge && rightEnclosingEdge->fWinding > 0)) {
|
| + v->fAlpha = max_edge_alpha(leftEnclosingEdge, rightEnclosingEdge);
|
| + }
|
| + }
|
| for (Edge* e = v->fFirstEdgeAbove; e; e = e->fNextEdgeAbove) {
|
| remove_edge(e, &activeEdges);
|
| }
|
| @@ -1198,7 +1298,7 @@ Poly* tessellate(Vertex* vertices, SkChunkAlloc& alloc) {
|
| continue;
|
| }
|
| #if LOGGING_ENABLED
|
| - LOG("\nvertex %g: (%g,%g)\n", v->fID, v->fPoint.fX, v->fPoint.fY);
|
| + LOG("\nvertex %g: (%g,%g), alpha %d\n", v->fID, v->fPoint.fX, v->fPoint.fY, v->fAlpha);
|
| #endif
|
| Edge* leftEnclosingEdge = nullptr;
|
| Edge* rightEnclosingEdge = nullptr;
|
| @@ -1298,18 +1398,220 @@ Poly* tessellate(Vertex* vertices, SkChunkAlloc& alloc) {
|
| return polys;
|
| }
|
|
|
| -// This is a driver function which calls stages 2-5 in turn.
|
| +bool is_boundary_edge(Edge* edge, SkPath::FillType fillType) {
|
| + return apply_fill_type(fillType, edge->fLeftPoly) !=
|
| + apply_fill_type(fillType, edge->fRightPoly);
|
| +}
|
|
|
| -Poly* contours_to_polys(Vertex** contours, int contourCnt, const SkRect& pathBounds,
|
| - SkChunkAlloc& alloc) {
|
| - Comparator c;
|
| - if (pathBounds.width() > pathBounds.height()) {
|
| - c.sweep_lt = sweep_lt_horiz;
|
| - c.sweep_gt = sweep_gt_horiz;
|
| - } else {
|
| - c.sweep_lt = sweep_lt_vert;
|
| - c.sweep_gt = sweep_gt_vert;
|
| +bool is_boundary_start(Edge* edge, SkPath::FillType fillType) {
|
| + return !apply_fill_type(fillType, edge->fLeftPoly) &&
|
| + apply_fill_type(fillType, edge->fRightPoly);
|
| +}
|
| +
|
| +Vertex* remove_non_boundary_edges(Vertex* vertices, SkPath::FillType fillType,
|
| + SkChunkAlloc& alloc) {
|
| + for (Vertex* v = vertices; v != nullptr; v = v->fNext) {
|
| + for (Edge* e = v->fFirstEdgeBelow; e != nullptr;) {
|
| + Edge* next = e->fNextEdgeBelow;
|
| + if (!is_boundary_edge(e, fillType)) {
|
| + remove_edge_above(e);
|
| + remove_edge_below(e);
|
| + }
|
| + e = next;
|
| + }
|
| }
|
| + return vertices;
|
| +}
|
| +
|
| +// This is different from Edge::intersect, in that it intersects lines, not line segments.
|
| +bool intersect(const Edge& a, const Edge& b, SkPoint* point) {
|
| + double denom = a.fDX * b.fDY - a.fDY * b.fDX;
|
| + if (denom == 0.0) {
|
| + return false;
|
| + }
|
| + double scale = 1.0f / denom;
|
| + point->fX = SkDoubleToScalar((b.fDX * a.fC - a.fDX * b.fC) * scale);
|
| + point->fY = SkDoubleToScalar((b.fDY * a.fC - a.fDY * b.fC) * scale);
|
| + round(point);
|
| + return true;
|
| +}
|
| +
|
| +void get_edge_normal(const Edge* e, SkVector* normal) {
|
| + normal->setNormalize(SkDoubleToScalar(e->fDX) * e->fWinding,
|
| + SkDoubleToScalar(e->fDY) * e->fWinding);
|
| +}
|
| +
|
| +// Stage 5c: detect and remove "pointy" vertices whose edge normals point in opposite directions
|
| +// and whose adjacent vertices are less than a quarter pixel from an edge. These are guaranteed to
|
| +// invert on stroking.
|
| +
|
| +void simplify_boundary(EdgeList* boundary, Comparator& c, SkChunkAlloc& alloc) {
|
| + Edge* prevEdge = boundary->fTail;
|
| + SkVector prevNormal;
|
| + get_edge_normal(prevEdge, &prevNormal);
|
| + for (Edge* e = boundary->fHead; e != nullptr;) {
|
| + Vertex* prev = prevEdge->fWinding == 1 ? prevEdge->fTop : prevEdge->fBottom;
|
| + Vertex* next = e->fWinding == 1 ? e->fBottom : e->fTop;
|
| + double dist = e->dist(prev->fPoint);
|
| + SkVector normal;
|
| + get_edge_normal(e, &normal);
|
| + float denom = 0.25f * static_cast<float>(e->fDX * e->fDX + e->fDY * e->fDY);
|
| + if (prevNormal.dot(normal) < 0.0 && (dist * dist) <= denom) {
|
| + Edge* join = new_edge(prev, next, alloc, c);
|
| + insert_edge(join, e, boundary);
|
| + remove_edge(prevEdge, boundary);
|
| + remove_edge(e, boundary);
|
| + if (join->fLeft && join->fRight) {
|
| + prevEdge = join->fLeft;
|
| + e = join;
|
| + } else {
|
| + prevEdge = boundary->fTail;
|
| + e = boundary->fHead; // join->fLeft ? join->fLeft : join;
|
| + }
|
| + get_edge_normal(prevEdge, &prevNormal);
|
| + } else {
|
| + prevEdge = e;
|
| + prevNormal = normal;
|
| + e = e->fRight;
|
| + }
|
| + }
|
| +}
|
| +
|
| +// Stage 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.
|
| +
|
| +void boundary_to_aa_mesh(EdgeList* boundary, VertexList* mesh, Comparator& c, SkChunkAlloc& alloc) {
|
| + EdgeList outerContour;
|
| + Edge* prevEdge = boundary->fTail;
|
| + float radius = 0.5f;
|
| + double offset = radius * sqrt(prevEdge->fDX * prevEdge->fDX + prevEdge->fDY * prevEdge->fDY)
|
| + * prevEdge->fWinding;
|
| + Edge prevInner(prevEdge->fTop, prevEdge->fBottom, prevEdge->fWinding);
|
| + prevInner.fC -= offset;
|
| + Edge prevOuter(prevEdge->fTop, prevEdge->fBottom, prevEdge->fWinding);
|
| + prevOuter.fC += offset;
|
| + VertexList innerVertices;
|
| + VertexList outerVertices;
|
| + SkScalar innerCount = SK_Scalar1, outerCount = SK_Scalar1;
|
| + for (Edge* e = boundary->fHead; e != nullptr; e = e->fRight) {
|
| + double offset = radius * sqrt(e->fDX * e->fDX + e->fDY * e->fDY) * e->fWinding;
|
| + Edge inner(e->fTop, e->fBottom, e->fWinding);
|
| + inner.fC -= offset;
|
| + Edge outer(e->fTop, e->fBottom, e->fWinding);
|
| + outer.fC += offset;
|
| + SkPoint innerPoint, outerPoint;
|
| + if (intersect(prevInner, inner, &innerPoint) &&
|
| + intersect(prevOuter, outer, &outerPoint)) {
|
| + Vertex* innerVertex = ALLOC_NEW(Vertex, (innerPoint, 255), alloc);
|
| + Vertex* outerVertex = ALLOC_NEW(Vertex, (outerPoint, 0), alloc);
|
| + if (innerVertices.fTail && outerVertices.fTail) {
|
| + Edge innerEdge(innerVertices.fTail, innerVertex, 1);
|
| + Edge outerEdge(outerVertices.fTail, outerVertex, 1);
|
| + SkVector innerNormal;
|
| + get_edge_normal(&innerEdge, &innerNormal);
|
| + SkVector outerNormal;
|
| + get_edge_normal(&outerEdge, &outerNormal);
|
| + SkVector normal;
|
| + get_edge_normal(prevEdge, &normal);
|
| + if (normal.dot(innerNormal) < 0) {
|
| + innerPoint += innerVertices.fTail->fPoint * innerCount;
|
| + innerCount++;
|
| + innerPoint *= SkScalarInvert(innerCount);
|
| + innerVertices.fTail->fPoint = innerVertex->fPoint = innerPoint;
|
| + } else {
|
| + innerCount = SK_Scalar1;
|
| + }
|
| + if (normal.dot(outerNormal) < 0) {
|
| + outerPoint += outerVertices.fTail->fPoint * outerCount;
|
| + outerCount++;
|
| + outerPoint *= SkScalarInvert(outerCount);
|
| + outerVertices.fTail->fPoint = outerVertex->fPoint = outerPoint;
|
| + } else {
|
| + outerCount = SK_Scalar1;
|
| + }
|
| + }
|
| + innerVertices.append(innerVertex);
|
| + outerVertices.append(outerVertex);
|
| + prevEdge = e;
|
| + }
|
| + prevInner = inner;
|
| + prevOuter = outer;
|
| + }
|
| + innerVertices.close();
|
| + outerVertices.close();
|
| +
|
| + Vertex* innerVertex = innerVertices.fHead;
|
| + Vertex* outerVertex = outerVertices.fHead;
|
| + // Alternate clockwise and counterclockwise polys, so the tesselator
|
| + // doesn't cancel out the interior edges.
|
| + if (!innerVertex || !outerVertex) {
|
| + return;
|
| + }
|
| + do {
|
| + connect(outerVertex->fNext, outerVertex, alloc, c);
|
| + connect(innerVertex->fNext, innerVertex, alloc, c, 2);
|
| + connect(innerVertex, outerVertex->fNext, alloc, c, 2);
|
| + connect(outerVertex, innerVertex, alloc, c, 2);
|
| + Vertex* innerNext = innerVertex->fNext;
|
| + Vertex* outerNext = outerVertex->fNext;
|
| + mesh->append(innerVertex);
|
| + mesh->append(outerVertex);
|
| + innerVertex = innerNext;
|
| + outerVertex = outerNext;
|
| + } while (innerVertex != innerVertices.fHead && outerVertex != outerVertices.fHead);
|
| +}
|
| +
|
| +void extract_boundary(EdgeList* boundary, Edge* e, SkPath::FillType fillType, SkChunkAlloc& alloc) {
|
| + bool down = is_boundary_start(e, fillType);
|
| + while (e) {
|
| + e->fWinding = down ? 1 : -1;
|
| + Edge* next;
|
| + boundary->append(e);
|
| + if (down) {
|
| + // Find outgoing edge, in clockwise order.
|
| + if ((next = e->fNextEdgeAbove)) {
|
| + down = false;
|
| + } else if ((next = e->fBottom->fLastEdgeBelow)) {
|
| + down = true;
|
| + } else if ((next = e->fPrevEdgeAbove)) {
|
| + down = false;
|
| + }
|
| + } else {
|
| + // Find outgoing edge, in counter-clockwise order.
|
| + if ((next = e->fPrevEdgeBelow)) {
|
| + down = true;
|
| + } else if ((next = e->fTop->fFirstEdgeAbove)) {
|
| + down = false;
|
| + } else if ((next = e->fNextEdgeBelow)) {
|
| + down = true;
|
| + }
|
| + }
|
| + remove_edge_above(e);
|
| + remove_edge_below(e);
|
| + e = next;
|
| + }
|
| +}
|
| +
|
| +// Stage 5b: Extract boundary edges.
|
| +
|
| +EdgeList* extract_boundaries(Vertex* vertices, SkPath::FillType fillType, SkChunkAlloc& alloc) {
|
| + LOG("extracting boundaries\n");
|
| + vertices = remove_non_boundary_edges(vertices, fillType, alloc);
|
| + EdgeList* boundaries = nullptr;
|
| + for (Vertex* v = vertices; v != nullptr; v = v->fNext) {
|
| + while (v->fFirstEdgeBelow) {
|
| + EdgeList* boundary = new_contour(&boundaries, alloc);
|
| + extract_boundary(boundary, v->fFirstEdgeBelow, fillType, alloc);
|
| + }
|
| + }
|
| + return boundaries;
|
| +}
|
| +
|
| +// This is a driver function which calls stages 2-5 in turn.
|
| +
|
| +Vertex* contours_to_mesh(Vertex** contours, int contourCnt, bool antialias,
|
| + Comparator& c, SkChunkAlloc& alloc) {
|
| #if LOGGING_ENABLED
|
| for (int i = 0; i < contourCnt; ++i) {
|
| Vertex* v = contours[i];
|
| @@ -1320,27 +1622,69 @@ Poly* contours_to_polys(Vertex** contours, int contourCnt, const SkRect& pathBou
|
| }
|
| }
|
| #endif
|
| - sanitize_contours(contours, contourCnt);
|
| - Vertex* vertices = build_edges(contours, contourCnt, c, alloc);
|
| - if (!vertices) {
|
| + sanitize_contours(contours, contourCnt, antialias);
|
| + return build_edges(contours, contourCnt, c, alloc);
|
| +}
|
| +
|
| +Poly* mesh_to_polys(Vertex** vertices, SkPath::FillType fillType, Comparator& c,
|
| + SkChunkAlloc& alloc) {
|
| + if (!vertices || !*vertices) {
|
| return nullptr;
|
| }
|
|
|
| // Sort vertices in Y (secondarily in X).
|
| - merge_sort(&vertices, c);
|
| - merge_coincident_vertices(&vertices, c, alloc);
|
| + merge_sort(vertices, c);
|
| + merge_coincident_vertices(vertices, c, alloc);
|
| #if LOGGING_ENABLED
|
| - for (Vertex* v = vertices; v != nullptr; v = v->fNext) {
|
| + for (Vertex* v = *vertices; v != nullptr; v = v->fNext) {
|
| static float gID = 0.0f;
|
| v->fID = gID++;
|
| }
|
| #endif
|
| - simplify(vertices, c, alloc);
|
| - return tessellate(vertices, alloc);
|
| + simplify(*vertices, c, alloc);
|
| + return tessellate(*vertices, alloc);
|
| +}
|
| +
|
| +Poly* contours_to_polys(Vertex** contours, int contourCnt, SkPath::FillType fillType,
|
| + const SkRect& pathBounds, bool antialias,
|
| + SkChunkAlloc& alloc) {
|
| + Comparator c;
|
| + if (pathBounds.width() > pathBounds.height()) {
|
| + c.sweep_lt = sweep_lt_horiz;
|
| + c.sweep_gt = sweep_gt_horiz;
|
| + } else {
|
| + c.sweep_lt = sweep_lt_vert;
|
| + c.sweep_gt = sweep_gt_vert;
|
| + }
|
| + Vertex* mesh = contours_to_mesh(contours, contourCnt, antialias, c, alloc);
|
| + Poly* polys = mesh_to_polys(&mesh, fillType, c, alloc);
|
| + if (antialias) {
|
| + EdgeList* boundaries = extract_boundaries(mesh, fillType, alloc);
|
| + VertexList aaMesh;
|
| + for (EdgeList* boundary = boundaries; boundary != nullptr; boundary = boundary->fNext) {
|
| + simplify_boundary(boundary, c, alloc);
|
| + if (boundary->fCount > 2) {
|
| + boundary_to_aa_mesh(boundary, &aaMesh, c, alloc);
|
| + }
|
| + }
|
| + return mesh_to_polys(&aaMesh.fHead, SkPath::kWinding_FillType, c, alloc);
|
| + }
|
| + return polys;
|
| +}
|
| +
|
| +// Stage 6: Triangulate the monotone polygons into a vertex buffer.
|
| +void* polys_to_triangles(Poly* polys, SkPath::FillType fillType, const AAParams* aaParams,
|
| + void* data) {
|
| + for (Poly* poly = polys; poly; poly = poly->fNext) {
|
| + if (apply_fill_type(fillType, poly)) {
|
| + data = poly->emit(aaParams, data);
|
| + }
|
| + }
|
| + return data;
|
| }
|
|
|
| Poly* path_to_polys(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
|
| - int contourCnt, SkChunkAlloc& alloc, bool* isLinear) {
|
| + int contourCnt, SkChunkAlloc& alloc, bool antialias, bool* isLinear) {
|
| SkPath::FillType fillType = path.getFillType();
|
| if (SkPath::IsInverseFillType(fillType)) {
|
| contourCnt++;
|
| @@ -1348,7 +1692,8 @@ Poly* path_to_polys(const SkPath& path, SkScalar tolerance, const SkRect& clipBo
|
| SkAutoTDeleteArray<Vertex*> contours(new Vertex* [contourCnt]);
|
|
|
| path_to_contours(path, tolerance, clipBounds, contours.get(), alloc, isLinear);
|
| - return contours_to_polys(contours.get(), contourCnt, path.getBounds(), alloc);
|
| + return contours_to_polys(contours.get(), contourCnt, path.getFillType(), path.getBounds(),
|
| + antialias, alloc);
|
| }
|
|
|
| void get_contour_count_and_size_estimate(const SkPath& path, SkScalar tolerance, int* contourCnt,
|
| @@ -1373,7 +1718,7 @@ void get_contour_count_and_size_estimate(const SkPath& path, SkScalar tolerance,
|
| int count_points(Poly* polys, SkPath::FillType fillType) {
|
| int count = 0;
|
| for (Poly* poly = polys; poly; poly = poly->fNext) {
|
| - if (apply_fill_type(fillType, poly->fWinding) && poly->fCount >= 3) {
|
| + if (apply_fill_type(fillType, poly) && poly->fCount >= 3) {
|
| count += (poly->fCount - 2) * (TESSELLATOR_WIREFRAME ? 6 : 3);
|
| }
|
| }
|
| @@ -1387,7 +1732,8 @@ namespace GrTessellator {
|
| // Stage 6: Triangulate the monotone polygons into a vertex buffer.
|
|
|
| int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds,
|
| - VertexAllocator* vertexAllocator, bool* isLinear) {
|
| + VertexAllocator* vertexAllocator, bool antialias, const GrColor& color,
|
| + bool canTweakAlphaForCoverage, bool* isLinear) {
|
| int contourCnt;
|
| int sizeEstimate;
|
| get_contour_count_and_size_estimate(path, tolerance, &contourCnt, &sizeEstimate);
|
| @@ -1396,26 +1742,28 @@ int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBo
|
| return 0;
|
| }
|
| SkChunkAlloc alloc(sizeEstimate);
|
| - Poly* polys = path_to_polys(path, tolerance, clipBounds, contourCnt, alloc, isLinear);
|
| + Poly* polys = path_to_polys(path, tolerance, clipBounds, contourCnt, alloc, antialias,
|
| + isLinear);
|
| SkPath::FillType fillType = path.getFillType();
|
| int count = count_points(polys, fillType);
|
| if (0 == count) {
|
| return 0;
|
| }
|
|
|
| - SkPoint* verts = vertexAllocator->lock(count);
|
| + void* verts = vertexAllocator->lock(count);
|
| if (!verts) {
|
| SkDebugf("Could not allocate vertices\n");
|
| return 0;
|
| }
|
| - SkPoint* end = verts;
|
| - for (Poly* poly = polys; poly; poly = poly->fNext) {
|
| - if (apply_fill_type(fillType, poly->fWinding)) {
|
| - end = poly->emit(end);
|
| - }
|
| - }
|
| - int actualCount = static_cast<int>(end - verts);
|
| - LOG("actual count: %d\n", actualCount);
|
| +
|
| + LOG("emitting %d verts\n", count);
|
| + AAParams aaParams;
|
| + aaParams.fTweakAlpha = canTweakAlphaForCoverage;
|
| + aaParams.fColor = color;
|
| +
|
| + void* end = polys_to_triangles(polys, fillType, antialias ? &aaParams : nullptr, verts);
|
| + int actualCount = static_cast<int>((static_cast<uint8_t*>(end) - static_cast<uint8_t*>(verts))
|
| + / vertexAllocator->stride());
|
| SkASSERT(actualCount <= count);
|
| vertexAllocator->unlock(actualCount);
|
| return actualCount;
|
| @@ -1431,7 +1779,7 @@ int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBou
|
| }
|
| SkChunkAlloc alloc(sizeEstimate);
|
| bool isLinear;
|
| - Poly* polys = path_to_polys(path, tolerance, clipBounds, contourCnt, alloc, &isLinear);
|
| + Poly* polys = path_to_polys(path, tolerance, clipBounds, contourCnt, alloc, false, &isLinear);
|
| SkPath::FillType fillType = path.getFillType();
|
| int count = count_points(polys, fillType);
|
| if (0 == count) {
|
| @@ -1444,9 +1792,9 @@ int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBou
|
| SkPoint* points = new SkPoint[count];
|
| SkPoint* pointsEnd = points;
|
| for (Poly* poly = polys; poly; poly = poly->fNext) {
|
| - if (apply_fill_type(fillType, poly->fWinding)) {
|
| + if (apply_fill_type(fillType, poly)) {
|
| SkPoint* start = pointsEnd;
|
| - pointsEnd = poly->emit(pointsEnd);
|
| + pointsEnd = static_cast<SkPoint*>(poly->emit(nullptr, pointsEnd));
|
| while (start != pointsEnd) {
|
| vertsEnd->fPos = *start;
|
| vertsEnd->fWinding = poly->fWinding;
|
|
|