diff --git a/src/gpu/GrDefaultGeoProcFactory.h b/src/gpu/GrDefaultGeoProcFactory.h index f7ddb061fd..022930b659 100644 --- a/src/gpu/GrDefaultGeoProcFactory.h +++ b/src/gpu/GrDefaultGeoProcFactory.h @@ -24,7 +24,7 @@ namespace GrDefaultGeoProcFactory { struct PositionCoverageAttr { SkPoint fPosition; - GrColor fCoverage; + float fCoverage; }; struct PositionColorAttr { @@ -35,7 +35,7 @@ namespace GrDefaultGeoProcFactory { struct PositionColorCoverageAttr { SkPoint fPosition; SkColor fColor; - GrColor fCoverage; + float fCoverage; }; struct PositionLocalCoordAttr { @@ -46,7 +46,7 @@ namespace GrDefaultGeoProcFactory { struct PositionLocalCoordCoverageAttr { SkPoint fPosition; SkPoint fLocalCoord; - GrColor fCoverage; + float fCoverage; }; struct PositionColorLocalCoordAttr { @@ -59,7 +59,7 @@ namespace GrDefaultGeoProcFactory { SkPoint fPosition; GrColor fColor; SkPoint fLocalCoord; - GrColor fCoverage; + float fCoverage; }; struct Color { diff --git a/src/gpu/GrPathRendererChain.cpp b/src/gpu/GrPathRendererChain.cpp index bed56f8b86..44dc343952 100644 --- a/src/gpu/GrPathRendererChain.cpp +++ b/src/gpu/GrPathRendererChain.cpp @@ -36,7 +36,6 @@ GrPathRendererChain::GrPathRendererChain(GrContext* context) { if (caps.sampleShadingSupport()) { this->addPathRenderer(new GrMSAAPathRenderer)->unref(); } - this->addPathRenderer(new GrTessellatingPathRenderer)->unref(); this->addPathRenderer(new GrAAHairLinePathRenderer)->unref(); this->addPathRenderer(new GrAAConvexPathRenderer)->unref(); this->addPathRenderer(new GrAALinearizingConvexPathRenderer)->unref(); @@ -44,6 +43,7 @@ GrPathRendererChain::GrPathRendererChain(GrContext* context) { this->addPathRenderer(new GrPLSPathRenderer)->unref(); } this->addPathRenderer(new GrAADistanceFieldPathRenderer)->unref(); + this->addPathRenderer(new GrTessellatingPathRenderer)->unref(); this->addPathRenderer(new GrDefaultPathRenderer(caps.twoSidedStencilSupport(), caps.stencilWrapOpsSupport()))->unref(); } diff --git a/src/gpu/GrTessellator.cpp b/src/gpu/GrTessellator.cpp index ccffa9ffbe..9ce1a739df 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 /* - * 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(data); + *d++ = v->fPoint; + return d; + } + if (aaParams->fTweakAlpha) { + auto d = static_cast(data); + d->fPosition = v->fPoint; + d->fColor = SkAlphaMulQ(aaParams->fColor, v->fAlpha); + d++; + return d; + } + auto d = static_cast(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, prev, next, &fHead, &fTail); + fCount++; + } + void append(Edge* e) { + insert(e, fTail, nullptr); + } + void remove(Edge* edge) { + list_remove(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(next->fPoint.fX) - curr->fPoint.fX; double by = static_cast(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, &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, 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(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; } +bool is_boundary_edge(Edge* edge, SkPath::FillType fillType) { + return apply_fill_type(fillType, edge->fLeftPoly) != + apply_fill_type(fillType, edge->fRightPoly); +} + +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(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. -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; - } +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 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(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((static_cast(end) - static_cast(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(poly->emit(nullptr, pointsEnd)); while (start != pointsEnd) { vertsEnd->fPos = *start; vertsEnd->fWinding = poly->fWinding; diff --git a/src/gpu/GrTessellator.h b/src/gpu/GrTessellator.h index 7cc4311aa5..dd92015cc2 100644 --- a/src/gpu/GrTessellator.h +++ b/src/gpu/GrTessellator.h @@ -8,6 +8,7 @@ #ifndef GrTessellator_DEFINED #define GrTessellator_DEFINED +#include "GrColor.h" #include "SkPoint.h" class SkPath; @@ -23,9 +24,13 @@ namespace GrTessellator { class VertexAllocator { public: + VertexAllocator(size_t stride) : fStride(stride) {} virtual ~VertexAllocator() {} - virtual SkPoint* lock(int vertexCount) = 0; + virtual void* lock(int vertexCount) = 0; virtual void unlock(int actualCount) = 0; + size_t stride() const { return fStride; } +private: + size_t fStride; }; struct WindingVertex { @@ -40,8 +45,9 @@ struct WindingVertex { int PathToVertices(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds, WindingVertex** verts); -int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds, - VertexAllocator*, bool *isLinear); +int PathToTriangles(const SkPath& path, SkScalar tolerance, const SkRect& clipBounds, + VertexAllocator*, bool antialias, const GrColor& color, + bool canTweakAlphaForCoverage, bool *isLinear); } #endif diff --git a/src/gpu/batches/GrTessellatingPathRenderer.cpp b/src/gpu/batches/GrTessellatingPathRenderer.cpp index b2c3138aed..3aa7ce8338 100644 --- a/src/gpu/batches/GrTessellatingPathRenderer.cpp +++ b/src/gpu/batches/GrTessellatingPathRenderer.cpp @@ -12,6 +12,7 @@ #include "GrBatchTest.h" #include "GrClip.h" #include "GrDefaultGeoProcFactory.h" +#include "GrDrawTarget.h" #include "GrMesh.h" #include "GrPathUtils.h" #include "GrPipelineBuilder.h" @@ -24,10 +25,12 @@ #include +#define SK_DISABLE_SCREENSPACE_TESS_AA_PATH_RENDERER + /* - * This path renderer tessellates the path into triangles using GrTessellator, uploads the triangles - * to a vertex buffer, and renders them with a single draw call. It does not currently do - * antialiasing, so it must be used in conjunction with multisampling. + * This path renderer tessellates the path into triangles using GrTessellator, uploads the + * triangles to a vertex buffer, and renders them with a single draw call. It can do screenspace + * antialiasing with a one-pixel coverage ramp. */ namespace { @@ -64,22 +67,23 @@ bool cache_match(GrBuffer* vertexBuffer, SkScalar tol, int* actualCount) { class StaticVertexAllocator : public GrTessellator::VertexAllocator { public: - StaticVertexAllocator(GrResourceProvider* resourceProvider, bool canMapVB) - : fResourceProvider(resourceProvider) + StaticVertexAllocator(size_t stride, GrResourceProvider* resourceProvider, bool canMapVB) + : VertexAllocator(stride) + , fResourceProvider(resourceProvider) , fCanMapVB(canMapVB) , fVertices(nullptr) { } - SkPoint* lock(int vertexCount) override { - size_t size = vertexCount * sizeof(SkPoint); + void* lock(int vertexCount) override { + size_t size = vertexCount * stride(); fVertexBuffer.reset(fResourceProvider->createBuffer( size, kVertex_GrBufferType, kStatic_GrAccessPattern, 0)); if (!fVertexBuffer.get()) { return nullptr; } if (fCanMapVB) { - fVertices = static_cast(fVertexBuffer->map()); + fVertices = fVertexBuffer->map(); } else { - fVertices = new SkPoint[vertexCount]; + fVertices = sk_malloc_throw(vertexCount * stride()); } return fVertices; } @@ -87,8 +91,8 @@ public: if (fCanMapVB) { fVertexBuffer->unmap(); } else { - fVertexBuffer->updateData(fVertices, actualCount * sizeof(SkPoint)); - delete[] fVertices; + fVertexBuffer->updateData(fVertices, actualCount * stride()); + sk_free(fVertices); } fVertices = nullptr; } @@ -97,7 +101,34 @@ private: SkAutoTUnref fVertexBuffer; GrResourceProvider* fResourceProvider; bool fCanMapVB; - SkPoint* fVertices; + void* fVertices; +}; + +class DynamicVertexAllocator : public GrTessellator::VertexAllocator { +public: + DynamicVertexAllocator(size_t stride, GrVertexBatch::Target* target) + : VertexAllocator(stride) + , fTarget(target) + , fVertexBuffer(nullptr) + , fVertices(nullptr) { + } + void* lock(int vertexCount) override { + fVertexCount = vertexCount; + fVertices = fTarget->makeVertexSpace(stride(), vertexCount, &fVertexBuffer, &fFirstVertex); + return fVertices; + } + void unlock(int actualCount) override { + fTarget->putBackVertices(fVertexCount - actualCount, stride()); + fVertices = nullptr; + } + const GrBuffer* vertexBuffer() const { return fVertexBuffer; } + int firstVertex() const { return fFirstVertex; } +private: + GrVertexBatch::Target* fTarget; + const GrBuffer* fVertexBuffer; + int fVertexCount; + int fFirstVertex; + void* fVertices; }; } // namespace @@ -106,13 +137,30 @@ GrTessellatingPathRenderer::GrTessellatingPathRenderer() { } bool GrTessellatingPathRenderer::onCanDrawPath(const CanDrawPathArgs& args) const { - // This path renderer can draw fill styles but does not do antialiasing. It can do convex and - // concave paths, but we'll leave the convex ones to simpler algorithms. We pass on paths that - // have styles, though they may come back around after applying the styling information to the - // geometry to create a filled path. We also skip paths that don't have a key since the real - // advantage of this path renderer comes from caching the tessellated geometry. - return !args.fShape->style().applies() && args.fShape->style().isSimpleFill() && - !args.fAntiAlias && args.fShape->hasUnstyledKey() && !args.fShape->knownToBeConvex(); + // This path renderer can draw fill styles, and can do screenspace antialiasing via a + // one-pixel coverage ramp. It can do convex and concave paths, but we'll leave the convex + // ones to simpler algorithms. We pass on paths that have styles, though they may come back + // around after applying the styling information to the geometry to create a filled path. In + // the non-AA case, We skip paths thta don't have a key since the real advantage of this path + // renderer comes from caching the tessellated geometry. In the AA case, we do not cache, so we + // accept paths without keys. + if (!args.fShape->style().isSimpleFill() || args.fShape->knownToBeConvex()) { + return false; + } + if (args.fAntiAlias) { +#ifdef SK_DISABLE_SCREENSPACE_TESS_AA_PATH_RENDERER + return false; +#else + SkPath path; + args.fShape->asPath(&path); + if (path.countVerbs() > 10) { + return false; + } +#endif + } else if (!args.fShape->hasUnstyledKey()) { + return false; + } + return true; } class TessellatingPathBatch : public GrVertexBatch { @@ -122,8 +170,9 @@ public: static GrDrawBatch* Create(const GrColor& color, const GrShape& shape, const SkMatrix& viewMatrix, - SkRect clipBounds) { - return new TessellatingPathBatch(color, shape, viewMatrix, clipBounds); + SkIRect devClipBounds, + bool antiAlias) { + return new TessellatingPathBatch(color, shape, viewMatrix, devClipBounds, antiAlias); } const char* name() const override { return "TessellatingPathBatch"; } @@ -145,41 +194,53 @@ private: fPipelineInfo = overrides; } - void draw(Target* target, const GrGeometryProcessor* gp) const { - GrResourceProvider* rp = target->resourceProvider(); - SkScalar screenSpaceTol = GrPathUtils::kDefaultTolerance; - SkScalar tol = GrPathUtils::scaleToleranceToSrc(screenSpaceTol, fViewMatrix, - fShape.bounds()); - + SkPath getPath() const { + SkASSERT(!fShape.style().applies()); SkPath path; fShape.asPath(&path); - bool inverseFill = path.isInverseFillType(); + return path; + } + + void draw(Target* target, const GrGeometryProcessor* gp) const { + SkASSERT(!fAntiAlias); + GrResourceProvider* rp = target->resourceProvider(); + bool inverseFill = fShape.inverseFilled(); // construct a cache key from the path's genID and the view matrix static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); GrUniqueKey key; - static constexpr int kClipBoundsCnt = sizeof(fClipBounds) / sizeof(uint32_t); + static constexpr int kClipBoundsCnt = sizeof(fDevClipBounds) / sizeof(uint32_t); int shapeKeyDataCnt = fShape.unstyledKeySize(); SkASSERT(shapeKeyDataCnt >= 0); GrUniqueKey::Builder builder(&key, kDomain, shapeKeyDataCnt + kClipBoundsCnt); fShape.writeUnstyledKey(&builder[0]); // For inverse fills, the tessellation is dependent on clip bounds. if (inverseFill) { - memcpy(&builder[shapeKeyDataCnt], &fClipBounds, sizeof(fClipBounds)); + memcpy(&builder[shapeKeyDataCnt], &fDevClipBounds, sizeof(fDevClipBounds)); } else { - memset(&builder[shapeKeyDataCnt], 0, sizeof(fClipBounds)); + memset(&builder[shapeKeyDataCnt], 0, sizeof(fDevClipBounds)); } builder.finish(); SkAutoTUnref cachedVertexBuffer(rp->findAndRefTByUniqueKey(key)); int actualCount; + SkScalar tol = GrPathUtils::kDefaultTolerance; + tol = GrPathUtils::scaleToleranceToSrc(tol, fViewMatrix, fShape.bounds()); if (cache_match(cachedVertexBuffer.get(), tol, &actualCount)) { this->drawVertices(target, gp, cachedVertexBuffer.get(), 0, actualCount); return; } + SkRect clipBounds = SkRect::Make(fDevClipBounds); + + SkMatrix vmi; + if (!fViewMatrix.invert(&vmi)) { + return; + } + vmi.mapRect(&clipBounds); bool isLinear; bool canMapVB = GrCaps::kNone_MapFlags != target->caps().mapBufferFlags(); - StaticVertexAllocator allocator(rp, canMapVB); - int count = GrTessellator::PathToTriangles(path, tol, fClipBounds, &allocator, &isLinear); + StaticVertexAllocator allocator(gp->getVertexStride(), rp, canMapVB); + int count = GrTessellator::PathToTriangles(getPath(), tol, clipBounds, &allocator, + false, GrColor(), false, &isLinear); if (count == 0) { return; } @@ -191,6 +252,27 @@ private: rp->assignUniqueKeyToResource(key, allocator.vertexBuffer()); } + void drawAA(Target* target, const GrGeometryProcessor* gp) const { + SkASSERT(fAntiAlias); + SkPath path = getPath(); + if (path.isEmpty()) { + return; + } + SkRect clipBounds = SkRect::Make(fDevClipBounds); + path.transform(fViewMatrix); + SkScalar tol = GrPathUtils::kDefaultTolerance; + bool isLinear; + DynamicVertexAllocator allocator(gp->getVertexStride(), target); + bool canTweakAlphaForCoverage = fPipelineInfo.canTweakAlphaForCoverage(); + int count = GrTessellator::PathToTriangles(path, tol, clipBounds, &allocator, + true, fColor, canTweakAlphaForCoverage, + &isLinear); + if (count == 0) { + return; + } + drawVertices(target, gp, allocator.vertexBuffer(), allocator.firstVertex(), count); + } + void onPrepareDraws(Target* target) const override { sk_sp gp; { @@ -201,21 +283,35 @@ private: LocalCoords::kUsePosition_Type : LocalCoords::kUnused_Type); Coverage::Type coverageType; - if (fPipelineInfo.readsCoverage()) { + if (fAntiAlias) { + color = Color(Color::kAttribute_Type); + if (fPipelineInfo.canTweakAlphaForCoverage()) { + coverageType = Coverage::kSolid_Type; + } else { + coverageType = Coverage::kAttribute_Type; + } + } else if (fPipelineInfo.readsCoverage()) { coverageType = Coverage::kSolid_Type; } else { coverageType = Coverage::kNone_Type; } Coverage coverage(coverageType); - gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, fViewMatrix); + if (fAntiAlias) { + gp = GrDefaultGeoProcFactory::MakeForDeviceSpace(color, coverage, localCoords, + fViewMatrix); + } else { + gp = GrDefaultGeoProcFactory::Make(color, coverage, localCoords, fViewMatrix); + } + } + if (fAntiAlias) { + this->drawAA(target, gp.get()); + } else { + this->draw(target, gp.get()); } - this->draw(target, gp.get()); } void drawVertices(Target* target, const GrGeometryProcessor* gp, const GrBuffer* vb, int firstVertex, int count) const { - SkASSERT(gp->getVertexStride() == sizeof(SkPoint)); - GrPrimitiveType primitiveType = TESSELLATOR_WIREFRAME ? kLines_GrPrimitiveType : kTriangles_GrPrimitiveType; GrMesh mesh; @@ -228,24 +324,29 @@ private: TessellatingPathBatch(const GrColor& color, const GrShape& shape, const SkMatrix& viewMatrix, - const SkRect& clipBounds) + const SkIRect& devClipBounds, + bool antiAlias) : INHERITED(ClassID()) , fColor(color) , fShape(shape) - , fViewMatrix(viewMatrix) { - const SkRect& pathBounds = shape.bounds(); - fClipBounds = clipBounds; - // Because the clip bounds are used to add a contour for inverse fills, they must also - // include the path bounds. - fClipBounds.join(pathBounds); - const SkRect& srcBounds = shape.inverseFilled() ? fClipBounds : pathBounds; - this->setTransformedBounds(srcBounds, viewMatrix, HasAABloat::kNo, IsZeroArea::kNo); + , fViewMatrix(viewMatrix) + , fDevClipBounds(devClipBounds) + , fAntiAlias(antiAlias) { + SkRect devBounds; + viewMatrix.mapRect(&devBounds, shape.bounds()); + if (shape.inverseFilled()) { + // Because the clip bounds are used to add a contour for inverse fills, they must also + // include the path bounds. + devBounds.join(SkRect::Make(fDevClipBounds)); + } + this->setBounds(devBounds, HasAABloat::kNo, IsZeroArea::kNo); } GrColor fColor; GrShape fShape; SkMatrix fViewMatrix; - SkRect fClipBounds; // in source space + SkIRect fDevClipBounds; + bool fAntiAlias; GrXPOverridesForBatch fPipelineInfo; typedef GrVertexBatch INHERITED; @@ -254,22 +355,14 @@ private: bool GrTessellatingPathRenderer::onDrawPath(const DrawPathArgs& args) { GR_AUDIT_TRAIL_AUTO_FRAME(args.fDrawContext->auditTrail(), "GrTessellatingPathRenderer::onDrawPath"); - SkASSERT(!args.fAntiAlias); - SkIRect clipBoundsI; args.fClip->getConservativeBounds(args.fDrawContext->width(), args.fDrawContext->height(), &clipBoundsI); - SkRect clipBounds = SkRect::Make(clipBoundsI); - SkMatrix vmi; - if (!args.fViewMatrix->invert(&vmi)) { - return false; - } - vmi.mapRect(&clipBounds); - SkPath path; - args.fShape->asPath(&path); SkAutoTUnref batch(TessellatingPathBatch::Create(args.fPaint->getColor(), *args.fShape, - *args.fViewMatrix, clipBounds)); + *args.fViewMatrix, + clipBoundsI, + args.fAntiAlias)); GrPipelineBuilder pipelineBuilder(*args.fPaint, args.fDrawContext->mustUseHWAA(*args.fPaint)); pipelineBuilder.setUserStencil(args.fUserStencilSettings); @@ -287,19 +380,15 @@ DRAW_BATCH_TEST_DEFINE(TesselatingPathBatch) { GrColor color = GrRandomColor(random); SkMatrix viewMatrix = GrTest::TestMatrixInvertible(random); SkPath path = GrTest::TestPath(random); - SkRect clipBounds = GrTest::TestRect(random); - SkMatrix vmi; - bool result = viewMatrix.invert(&vmi); - if (!result) { - SkFAIL("Cannot invert matrix\n"); - } - vmi.mapRect(&clipBounds); + SkIRect devClipBounds = SkIRect::MakeXYWH( + random->nextU(), random->nextU(), random->nextU(), random->nextU()); + bool antiAlias = random->nextBool(); GrStyle style; do { GrTest::TestStyle(random, &style); } while (style.strokeRec().isHairlineStyle()); GrShape shape(path, style); - return TessellatingPathBatch::Create(color, shape, viewMatrix, clipBounds); + return TessellatingPathBatch::Create(color, shape, viewMatrix, devClipBounds, antiAlias); } #endif