diff options
author | ethannicholas <ethannicholas@google.com> | 2015-06-10 12:11:17 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-10 12:11:17 -0700 |
commit | 1a1b3ac0d4feecb0fefa8a07c7abf3471c96f545 (patch) | |
tree | 4c03c8b596f18b5367e57624cfa0194d1e0107b0 | |
parent | 523beb7fd91e6a171a69f44cf6cc8e8d88a22dd0 (diff) |
Added GrAAFlatteningConvexPathRenderer.
This is an alternate version of GrAAConvexPathRenderer which handles
curves by first flattening them to straight lines.
BUG=skia:
Review URL: https://codereview.chromium.org/1158803002
-rw-r--r-- | gyp/gpu.gypi | 2 | ||||
-rw-r--r-- | src/gpu/GrAAConvexTessellator.cpp | 287 | ||||
-rw-r--r-- | src/gpu/GrAAConvexTessellator.h | 21 | ||||
-rw-r--r-- | src/gpu/GrAALinearizingConvexPathRenderer.cpp | 320 | ||||
-rw-r--r-- | src/gpu/GrAALinearizingConvexPathRenderer.h | 35 |
5 files changed, 569 insertions, 96 deletions
diff --git a/gyp/gpu.gypi b/gyp/gpu.gypi index f47e6fa60d..0396f196af 100644 --- a/gyp/gpu.gypi +++ b/gyp/gpu.gypi @@ -57,6 +57,8 @@ '<(skia_src_path)/gpu/GrAAHairLinePathRenderer.h', '<(skia_src_path)/gpu/GrAAConvexPathRenderer.cpp', '<(skia_src_path)/gpu/GrAAConvexPathRenderer.h', + '<(skia_src_path)/gpu/GrAALinearizingConvexPathRenderer.cpp', + '<(skia_src_path)/gpu/GrAALinearizingConvexPathRenderer.h', '<(skia_src_path)/gpu/GrAAConvexTessellator.cpp', '<(skia_src_path)/gpu/GrAAConvexTessellator.h', '<(skia_src_path)/gpu/GrAADistanceFieldPathRenderer.cpp', diff --git a/src/gpu/GrAAConvexTessellator.cpp b/src/gpu/GrAAConvexTessellator.cpp index 5a1e4c2dfe..56a408d644 100644 --- a/src/gpu/GrAAConvexTessellator.cpp +++ b/src/gpu/GrAAConvexTessellator.cpp @@ -10,6 +10,7 @@ #include "SkPath.h" #include "SkPoint.h" #include "SkString.h" +#include "GrPathUtils.h" // Next steps: // use in AAConvexPathRenderer @@ -51,13 +52,15 @@ static SkScalar abs_dist_from_line(const SkPoint& p0, const SkVector& v, const S int GrAAConvexTessellator::addPt(const SkPoint& pt, SkScalar depth, - bool movable) { + bool movable, + bool isCurve) { this->validate(); int index = fPts.count(); *fPts.push() = pt; *fDepths.push() = depth; *fMovable.push() = movable; + *fIsCurve.push() = isCurve; this->validate(); return index; @@ -236,7 +239,6 @@ bool GrAAConvexTessellator::computePtAlongBisector(int startIdx, } bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& path) { - SkASSERT(SkPath::kLine_SegmentMask == path.getSegmentMasks()); SkASSERT(SkPath::kConvex_Convexity == path.getConvexity()); // Outer ring: 3*numPts @@ -250,7 +252,8 @@ bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& pat fNorms.setReserve(path.countPoints()); - SkScalar minCross = SK_ScalarMax, maxCross = -SK_ScalarMax; + SkDEBUGCODE(fMinCross = SK_ScalarMax;) + SkDEBUGCODE(fMaxCross = -SK_ScalarMax;) // TODO: is there a faster way to extract the points from the path? Perhaps // get all the points via a new entry point, transform them all in bulk @@ -261,38 +264,16 @@ bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& pat while ((verb = iter.next(pts)) != SkPath::kDone_Verb) { switch (verb) { case SkPath::kLine_Verb: - m.mapPoints(&pts[1], 1); - if (this->numPts() > 0 && duplicate_pt(pts[1], this->lastPoint())) { - continue; - } - - SkASSERT(fPts.count() <= 1 || fPts.count() == fNorms.count()+1); - if (this->numPts() >= 2 && - abs_dist_from_line(fPts.top(), fNorms.top(), pts[1]) < kClose) { - // The old last point is on the line from the second to last to the new point - this->popLastPt(); - fNorms.pop(); - } - - this->addPt(pts[1], 0.0f, false); - if (this->numPts() > 1) { - *fNorms.push() = fPts.top() - fPts[fPts.count()-2]; - SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms.top()); - SkASSERT(len > 0.0f); - SkASSERT(SkScalarNearlyEqual(1.0f, fNorms.top().length())); - } - - if (this->numPts() >= 3) { - int cur = this->numPts()-1; - SkScalar cross = SkPoint::CrossProduct(fNorms[cur-1], fNorms[cur-2]); - maxCross = SkTMax(maxCross, cross); - minCross = SkTMin(minCross, cross); - } + this->lineTo(m, pts[1], false); break; case SkPath::kQuad_Verb: - case SkPath::kConic_Verb: + this->quadTo(m, pts); + break; case SkPath::kCubic_Verb: - SkASSERT(false); + this->cubicTo(m, pts); + break; + case SkPath::kConic_Verb: + this->conicTo(m, pts, iter.conicWeight()); break; case SkPath::kMove_Verb: case SkPath::kClose_Verb: @@ -342,16 +323,14 @@ bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& pat return false; } - // Check the cross produce of the final trio + // Check the cross product of the final trio SkScalar cross = SkPoint::CrossProduct(fNorms[0], fNorms.top()); - maxCross = SkTMax(maxCross, cross); - minCross = SkTMin(minCross, cross); - - if (maxCross > 0.0f) { - SkASSERT(minCross >= 0.0f); + SkDEBUGCODE(fMaxCross = SkTMax(fMaxCross, cross)); + SkDEBUGCODE(fMinCross = SkTMin(fMinCross, cross)); + SkASSERT((fMaxCross >= 0.0f) == (fMinCross >= 0.0f)); + if (cross > 0.0f) { fSide = SkPoint::kRight_Side; } else { - SkASSERT(minCross <= 0.0f); fSide = SkPoint::kLeft_Side; } @@ -404,69 +383,109 @@ void GrAAConvexTessellator::createOuterRing() { const int numPts = fPts.count(); - // For each vertex of the original polygon we add three points to the - // outset polygon - one extending perpendicular to each impinging edge - // and one along the bisector. Two triangles are added for each corner - // and two are added along each edge. int prev = numPts - 1; int lastPerpIdx = -1, firstPerpIdx = -1, newIdx0, newIdx1, newIdx2; for (int cur = 0; cur < numPts; ++cur) { - // The perpendicular point for the last edge - SkPoint temp = fNorms[prev]; - temp.scale(fTargetDepth); - temp += fPts[cur]; - - // We know it isn't a duplicate of the prior point (since it and this - // one are just perpendicular offsets from the non-merged polygon points) - newIdx0 = this->addPt(temp, -fTargetDepth, false); - - // The bisector outset point - temp = fBisectors[cur]; - temp.scale(-fTargetDepth); // the bisectors point in - temp += fPts[cur]; - - // For very shallow angles all the corner points could fuse - if (duplicate_pt(temp, this->point(newIdx0))) { - newIdx1 = newIdx0; - } else { - newIdx1 = this->addPt(temp, -fTargetDepth, false); - } - - // The perpendicular point for the next edge. - temp = fNorms[cur]; - temp.scale(fTargetDepth); - temp += fPts[cur]; - - // For very shallow angles all the corner points could fuse. - if (duplicate_pt(temp, this->point(newIdx1))) { - newIdx2 = newIdx1; - } else { - newIdx2 = this->addPt(temp, -fTargetDepth, false); + if (fIsCurve[cur]) { + // Inside a curve, we assume that the curvature is shallow enough (due to tesselation) + // that we only need one corner point. Mathematically, the distance the corner point + // gets shifted out should depend on the angle between the two line segments (as in + // mitering), but again due to tesselation we assume that this angle is small and + // therefore the correction factor is negligible and we do not bother with it. + + // The bisector outset point + SkPoint temp = fBisectors[cur]; + temp.scale(-fTargetDepth); // the bisectors point in + temp += fPts[cur]; + + // double-check our "sufficiently flat" assumption; we want the bisector point to be + // close to the normal point. + #define kFlatnessTolerance 1.0f + SkDEBUGCODE(SkPoint prevNormal = fNorms[prev];) + SkDEBUGCODE(prevNormal.scale(fTargetDepth);) + SkDEBUGCODE(prevNormal += fPts[cur];) + SkASSERT((temp - prevNormal).length() < kFlatnessTolerance); + + newIdx1 = this->addPt(temp, -fTargetDepth, false, true); + + if (0 == cur) { + // Store the index of the first perpendicular point to finish up + firstPerpIdx = newIdx1; + SkASSERT(-1 == lastPerpIdx); + } else { + // The triangles for the previous edge + this->addTri(prev, newIdx1, cur); + this->addTri(prev, lastPerpIdx, newIdx1); + } + + prev = cur; + // Track the last perpendicular outset point so we can construct the + // trailing edge triangles. + lastPerpIdx = newIdx1; } - - if (0 == cur) { - // Store the index of the first perpendicular point to finish up - firstPerpIdx = newIdx0; - SkASSERT(-1 == lastPerpIdx); - } else { - // The triangles for the previous edge - this->addTri(prev, newIdx0, cur); - this->addTri(prev, lastPerpIdx, newIdx0); + else { + // For each vertex of the original polygon we add three points to the + // outset polygon - one extending perpendicular to each impinging edge + // and one along the bisector. Two triangles are added for each corner + // and two are added along each edge. + + // The perpendicular point for the last edge + SkPoint temp = fNorms[prev]; + temp.scale(fTargetDepth); + temp += fPts[cur]; + + // We know it isn't a duplicate of the prior point (since it and this + // one are just perpendicular offsets from the non-merged polygon points) + newIdx0 = this->addPt(temp, -fTargetDepth, false, false); + + // The bisector outset point + temp = fBisectors[cur]; + temp.scale(-fTargetDepth); // the bisectors point in + temp += fPts[cur]; + + // For very shallow angles all the corner points could fuse + if (duplicate_pt(temp, this->point(newIdx0))) { + newIdx1 = newIdx0; + } else { + newIdx1 = this->addPt(temp, -fTargetDepth, false, false); + } + + // The perpendicular point for the next edge. + temp = fNorms[cur]; + temp.scale(fTargetDepth); + temp += fPts[cur]; + + // For very shallow angles all the corner points could fuse. + if (duplicate_pt(temp, this->point(newIdx1))) { + newIdx2 = newIdx1; + } else { + newIdx2 = this->addPt(temp, -fTargetDepth, false, false); + } + + if (0 == cur) { + // Store the index of the first perpendicular point to finish up + firstPerpIdx = newIdx0; + SkASSERT(-1 == lastPerpIdx); + } else { + // The triangles for the previous edge + this->addTri(prev, newIdx0, cur); + this->addTri(prev, lastPerpIdx, newIdx0); + } + + // The two triangles for the corner + this->addTri(cur, newIdx0, newIdx1); + this->addTri(cur, newIdx1, newIdx2); + + prev = cur; + // Track the last perpendicular outset point so we can construct the + // trailing edge triangles. + lastPerpIdx = newIdx2; } - - // The two triangles for the corner - this->addTri(cur, newIdx0, newIdx1); - this->addTri(cur, newIdx1, newIdx2); - - prev = cur; - // Track the last perpendicular outset point so we can construct the - // trailing edge triangles. - lastPerpIdx = newIdx2; } // pick up the final edge rect - this->addTri(numPts-1, firstPerpIdx, 0); - this->addTri(numPts-1, lastPerpIdx, firstPerpIdx); + this->addTri(numPts - 1, firstPerpIdx, 0); + this->addTri(numPts - 1, lastPerpIdx, firstPerpIdx); this->validate(); } @@ -592,7 +611,7 @@ bool GrAAConvexTessellator::createInsetRing(const Ring& lastRing, Ring* nextRing // if the originating index is still valid then this point wasn't // fused (and is thus movable) newIdx = this->addPt(fCandidateVerts.point(i), depth, - fCandidateVerts.originatingIdx(i) != -1); + fCandidateVerts.originatingIdx(i) != -1, false); } else { SkASSERT(fCandidateVerts.originatingIdx(i) != -1); this->updatePt(fCandidateVerts.originatingIdx(i), fCandidateVerts.point(i), depth); @@ -768,6 +787,84 @@ void GrAAConvexTessellator::checkAllDepths() const { } #endif +#define kQuadTolerance 0.2f +#define kCubicTolerance 0.2f +#define kConicTolerance 0.5f + +void GrAAConvexTessellator::lineTo(const SkMatrix& m, SkPoint p, bool isCurve) { + m.mapPoints(&p, 1); + if (this->numPts() > 0 && duplicate_pt(p, this->lastPoint())) { + return; + } + + SkASSERT(fPts.count() <= 1 || fPts.count() == fNorms.count()+1); + if (this->numPts() >= 2 && + abs_dist_from_line(fPts.top(), fNorms.top(), p) < kClose) { + // The old last point is on the line from the second to last to the new point + this->popLastPt(); + fNorms.pop(); + fIsCurve.pop(); + } + this->addPt(p, 0.0f, false, isCurve); + if (this->numPts() > 1) { + *fNorms.push() = fPts.top() - fPts[fPts.count()-2]; + SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms.top()); + SkASSERT(len > 0.0f); + SkASSERT(SkScalarNearlyEqual(1.0f, fNorms.top().length())); + } + SkDEBUGCODE( + if (this->numPts() >= 3) { + int cur = this->numPts()-1; + SkScalar cross = SkPoint::CrossProduct(fNorms[cur-1], fNorms[cur-2]); + fMaxCross = SkTMax(fMaxCross, cross); + fMinCross = SkTMin(fMinCross, cross); + } + ) +} + +void GrAAConvexTessellator::quadTo(const SkMatrix& m, SkPoint pts[3]) { + int maxCount = GrPathUtils::quadraticPointCount(pts, kQuadTolerance); + fPointBuffer.setReserve(maxCount); + SkPoint* target = fPointBuffer.begin(); + int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2], + kQuadTolerance, &target, maxCount); + fPointBuffer.setCount(count); + for (int i = 0; i < count; i++) { + lineTo(m, fPointBuffer[i], true); + } +} + +void GrAAConvexTessellator::cubicTo(const SkMatrix& m, SkPoint pts[4]) { + int maxCount = GrPathUtils::cubicPointCount(pts, kCubicTolerance); + fPointBuffer.setReserve(maxCount); + SkPoint* target = fPointBuffer.begin(); + int count = GrPathUtils::generateCubicPoints(pts[0], pts[1], pts[2], pts[3], + kCubicTolerance, &target, maxCount); + fPointBuffer.setCount(count); + for (int i = 0; i < count; i++) { + lineTo(m, fPointBuffer[i], true); + } +} + +// include down here to avoid compilation errors caused by "-" overload in SkGeometry.h +#include "SkGeometry.h" + +void GrAAConvexTessellator::conicTo(const SkMatrix& m, SkPoint* pts, SkScalar w) { + SkAutoConicToQuads quadder; + const SkPoint* quads = quadder.computeQuads(pts, w, kConicTolerance); + SkPoint lastPoint = *(quads++); + int count = quadder.countQuads(); + for (int i = 0; i < count; ++i) { + SkPoint quadPts[3]; + quadPts[0] = lastPoint; + quadPts[1] = quads[0]; + quadPts[2] = i == count - 1 ? pts[2] : quads[1]; + quadTo(m, quadPts); + lastPoint = quadPts[2]; + quads += 2; + } +} + ////////////////////////////////////////////////////////////////////////////// #if GR_AA_CONVEX_TESSELLATOR_VIZ static const SkScalar kPointRadius = 0.02f; diff --git a/src/gpu/GrAAConvexTessellator.h b/src/gpu/GrAAConvexTessellator.h index 707995fadd..93e8d4ba61 100644 --- a/src/gpu/GrAAConvexTessellator.h +++ b/src/gpu/GrAAConvexTessellator.h @@ -165,7 +165,7 @@ private: // Movable points are those that can be slid along their bisector. // Basically, a point is immovable if it is part of the original // polygon or it results from the fusing of two bisectors. - int addPt(const SkPoint& pt, SkScalar depth, bool movable); + int addPt(const SkPoint& pt, SkScalar depth, bool movable, bool isCurve); void popLastPt(); void popFirstPtShuffle(); @@ -185,6 +185,14 @@ private: int edgeIdx, SkScalar desiredDepth, SkPoint* result) const; + void lineTo(const SkMatrix& m, SkPoint p, bool isCurve); + + void quadTo(const SkMatrix& m, SkPoint pts[3]); + + void cubicTo(const SkMatrix& m, SkPoint pts[4]); + + void conicTo(const SkMatrix& m, SkPoint pts[3], SkScalar w); + void terminate(const Ring& lastRing); // return false on failure/degenerate path @@ -217,6 +225,11 @@ private: // The inward facing bisector at each point in the original polygon. Only // needed for exterior ring creation and then handed off to the initial ring. SkTDArray<SkVector> fBisectors; + + // Tracks whether a given point is interior to a curve. Such points are + // assumed to have shallow curvature. + SkTDArray<bool> fIsCurve; + SkPoint::Side fSide; // winding of the original polygon // The triangulation of the points @@ -233,10 +246,16 @@ private: SkScalar fTargetDepth; + SkTDArray<SkPoint> fPointBuffer; + // If some goes wrong with the inset computation the tessellator will // truncate the creation of the inset polygon. In this case the depth // check will complain. SkDEBUGCODE(bool fShouldCheckDepths;) + + SkDEBUGCODE(SkScalar fMinCross;) + + SkDEBUGCODE(SkScalar fMaxCross;) }; diff --git a/src/gpu/GrAALinearizingConvexPathRenderer.cpp b/src/gpu/GrAALinearizingConvexPathRenderer.cpp new file mode 100644 index 0000000000..e03df81def --- /dev/null +++ b/src/gpu/GrAALinearizingConvexPathRenderer.cpp @@ -0,0 +1,320 @@ + +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "GrAALinearizingConvexPathRenderer.h" + +#include "GrAAConvexTessellator.h" +#include "GrBatch.h" +#include "GrBatchTarget.h" +#include "GrBatchTest.h" +#include "GrContext.h" +#include "GrDefaultGeoProcFactory.h" +#include "GrGeometryProcessor.h" +#include "GrInvariantOutput.h" +#include "GrPathUtils.h" +#include "GrProcessor.h" +#include "GrPipelineBuilder.h" +#include "GrStrokeInfo.h" +#include "SkGeometry.h" +#include "SkString.h" +#include "SkTraceEvent.h" +#include "gl/GrGLProcessor.h" +#include "gl/GrGLSL.h" +#include "gl/GrGLGeometryProcessor.h" +#include "gl/builders/GrGLProgramBuilder.h" + +#define DEFAULT_BUFFER_SIZE 100 + +GrAALinearizingConvexPathRenderer::GrAALinearizingConvexPathRenderer() { +} + +/////////////////////////////////////////////////////////////////////////////// + +bool GrAALinearizingConvexPathRenderer::canDrawPath(const GrDrawTarget* target, + const GrPipelineBuilder*, + const SkMatrix& viewMatrix, + const SkPath& path, + const GrStrokeInfo& stroke, + bool antiAlias) const { + return (antiAlias && stroke.isFillStyle() && !path.isInverseFillType() && path.isConvex()); +} + +// extract the result vertices and indices from the GrAAConvexTessellator +static void extract_verts(const GrAAConvexTessellator& tess, + void* vertices, + size_t vertexStride, + GrColor color, + uint16_t firstIndex, + uint16_t* idxs, + bool tweakAlphaForCoverage) { + intptr_t verts = reinterpret_cast<intptr_t>(vertices); + + for (int i = 0; i < tess.numPts(); ++i) { + *((SkPoint*)((intptr_t)verts + i * vertexStride)) = tess.point(i); + } + + // Make 'verts' point to the colors + verts += sizeof(SkPoint); + for (int i = 0; i < tess.numPts(); ++i) { + SkASSERT(tess.depth(i) >= -0.5f && tess.depth(i) <= 0.5f); + if (tweakAlphaForCoverage) { + SkASSERT(SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f)) <= 255); + unsigned scale = SkScalarRoundToInt(255.0f * (tess.depth(i) + 0.5f)); + GrColor scaledColor = (0xff == scale) ? color : SkAlphaMulQ(color, scale); + *reinterpret_cast<GrColor*>(verts + i * vertexStride) = scaledColor; + } else { + *reinterpret_cast<GrColor*>(verts + i * vertexStride) = color; + *reinterpret_cast<float*>(verts + i * vertexStride + sizeof(GrColor)) = + tess.depth(i) + 0.5f; + } + } + + for (int i = 0; i < tess.numIndices(); ++i) { + idxs[i] = tess.index(i) + firstIndex; + } +} + +static const GrGeometryProcessor* create_fill_gp(bool tweakAlphaForCoverage, + const SkMatrix& localMatrix, + bool usesLocalCoords, + bool coverageIgnored) { + uint32_t flags = GrDefaultGeoProcFactory::kColor_GPType; + if (!tweakAlphaForCoverage) { + flags |= GrDefaultGeoProcFactory::kCoverage_GPType; + } + + return GrDefaultGeoProcFactory::Create(flags, GrColor_WHITE, usesLocalCoords, coverageIgnored, + SkMatrix::I(), localMatrix); +} + +class AAFlatteningConvexPathBatch : public GrBatch { +public: + struct Geometry { + GrColor fColor; + SkMatrix fViewMatrix; + SkPath fPath; + }; + + static GrBatch* Create(const Geometry& geometry) { + return SkNEW_ARGS(AAFlatteningConvexPathBatch, (geometry)); + } + + const char* name() const override { return "AAConvexBatch"; } + + void getInvariantOutputColor(GrInitInvariantOutput* out) const override { + // When this is called on a batch, there is only one geometry bundle + out->setKnownFourComponents(fGeoData[0].fColor); + } + void getInvariantOutputCoverage(GrInitInvariantOutput* out) const override { + out->setUnknownSingleComponent(); + } + + void initBatchTracker(const GrPipelineInfo& init) override { + // Handle any color overrides + if (init.fColorIgnored) { + fGeoData[0].fColor = GrColor_ILLEGAL; + } else if (GrColor_ILLEGAL != init.fOverrideColor) { + fGeoData[0].fColor = init.fOverrideColor; + } + + // setup batch properties + fBatch.fColorIgnored = init.fColorIgnored; + fBatch.fColor = fGeoData[0].fColor; + fBatch.fUsesLocalCoords = init.fUsesLocalCoords; + fBatch.fCoverageIgnored = init.fCoverageIgnored; + fBatch.fLinesOnly = SkPath::kLine_SegmentMask == fGeoData[0].fPath.getSegmentMasks(); + fBatch.fCanTweakAlphaForCoverage = init.fCanTweakAlphaForCoverage; + } + + void draw(GrBatchTarget* batchTarget, const GrPipeline* pipeline, int vertexCount, + size_t vertexStride, void* vertices, int indexCount, uint16_t* indices) { + if (vertexCount == 0 || indexCount == 0) { + return; + } + const GrVertexBuffer* vertexBuffer; + GrVertices info; + int firstVertex; + void* verts = batchTarget->makeVertSpace(vertexStride, vertexCount, &vertexBuffer, + &firstVertex); + if (!verts) { + SkDebugf("Could not allocate vertices\n"); + return; + } + memcpy(verts, vertices, vertexCount * vertexStride); + + const GrIndexBuffer* indexBuffer; + int firstIndex; + uint16_t* idxs = batchTarget->makeIndexSpace(indexCount, &indexBuffer, &firstIndex); + if (!idxs) { + SkDebugf("Could not allocate indices\n"); + return; + } + memcpy(idxs, indices, indexCount * sizeof(uint16_t)); + info.initIndexed(kTriangles_GrPrimitiveType, vertexBuffer, indexBuffer, firstVertex, + firstIndex, vertexCount, indexCount); + batchTarget->draw(info); + } + + void generateGeometry(GrBatchTarget* batchTarget, const GrPipeline* pipeline) override { + bool canTweakAlphaForCoverage = this->canTweakAlphaForCoverage(); + + SkMatrix invert; + if (this->usesLocalCoords() && !this->viewMatrix().invert(&invert)) { + SkDebugf("Could not invert viewmatrix\n"); + return; + } + + // Setup GrGeometryProcessor + SkAutoTUnref<const GrGeometryProcessor> gp( + create_fill_gp(canTweakAlphaForCoverage, invert, + this->usesLocalCoords(), + this->coverageIgnored())); + + batchTarget->initDraw(gp, pipeline); + + size_t vertexStride = gp->getVertexStride(); + + SkASSERT(canTweakAlphaForCoverage ? + vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorAttr) : + vertexStride == sizeof(GrDefaultGeoProcFactory::PositionColorCoverageAttr)); + + GrAAConvexTessellator tess; + + int instanceCount = fGeoData.count(); + + int vertexCount = 0; + int indexCount = 0; + int maxVertices = DEFAULT_BUFFER_SIZE; + int maxIndices = DEFAULT_BUFFER_SIZE; + uint8_t* vertices = (uint8_t*) malloc(maxVertices * vertexStride); + uint16_t* indices = (uint16_t*) malloc(maxIndices * sizeof(uint16_t)); + for (int i = 0; i < instanceCount; i++) { + tess.rewind(); + + Geometry& args = fGeoData[i]; + + if (!tess.tessellate(args.fViewMatrix, args.fPath)) { + continue; + } + + int currentIndices = tess.numIndices(); + SkASSERT(currentIndices <= UINT16_MAX); + if (indexCount + currentIndices > UINT16_MAX) { + // if we added the current instance, we would overflow the indices we can store in a + // uint16_t. Draw what we've got so far and reset. + draw(batchTarget, pipeline, vertexCount, vertexStride, vertices, indexCount, + indices); + vertexCount = 0; + indexCount = 0; + } + int currentVertices = tess.numPts(); + if (vertexCount + currentVertices > maxVertices) { + maxVertices = SkTMax(vertexCount + currentVertices, maxVertices * 2); + vertices = (uint8_t*) realloc(vertices, maxVertices * vertexStride); + } + if (indexCount + currentIndices > maxIndices) { + maxIndices = SkTMax(indexCount + currentIndices, maxIndices * 2); + indices = (uint16_t*) realloc(indices, maxIndices * sizeof(uint16_t)); + } + + extract_verts(tess, vertices + vertexStride * vertexCount, vertexStride, args.fColor, + vertexCount, indices + indexCount, canTweakAlphaForCoverage); + vertexCount += currentVertices; + indexCount += currentIndices; + } + draw(batchTarget, pipeline, vertexCount, vertexStride, vertices, indexCount, indices); + free(vertices); + free(indices); + } + + SkSTArray<1, Geometry, true>* geoData() { return &fGeoData; } + +private: + AAFlatteningConvexPathBatch(const Geometry& geometry) { + this->initClassID<AAFlatteningConvexPathBatch>(); + fGeoData.push_back(geometry); + + // compute bounds + fBounds = geometry.fPath.getBounds(); + geometry.fViewMatrix.mapRect(&fBounds); + } + + bool onCombineIfPossible(GrBatch* t) override { + AAFlatteningConvexPathBatch* that = t->cast<AAFlatteningConvexPathBatch>(); + + SkASSERT(this->usesLocalCoords() == that->usesLocalCoords()); + if (this->usesLocalCoords() && !this->viewMatrix().cheapEqualTo(that->viewMatrix())) { + return false; + } + + // In the event of two batches, one who can tweak, one who cannot, we just fall back to + // not tweaking + if (this->canTweakAlphaForCoverage() != that->canTweakAlphaForCoverage()) { + fBatch.fCanTweakAlphaForCoverage = false; + } + + fGeoData.push_back_n(that->geoData()->count(), that->geoData()->begin()); + this->joinBounds(that->bounds()); + return true; + } + + GrColor color() const { return fBatch.fColor; } + bool linesOnly() const { return fBatch.fLinesOnly; } + bool usesLocalCoords() const { return fBatch.fUsesLocalCoords; } + bool canTweakAlphaForCoverage() const { return fBatch.fCanTweakAlphaForCoverage; } + const SkMatrix& viewMatrix() const { return fGeoData[0].fViewMatrix; } + bool coverageIgnored() const { return fBatch.fCoverageIgnored; } + + struct BatchTracker { + GrColor fColor; + bool fUsesLocalCoords; + bool fColorIgnored; + bool fCoverageIgnored; + bool fLinesOnly; + bool fCanTweakAlphaForCoverage; + }; + + BatchTracker fBatch; + SkSTArray<1, Geometry, true> fGeoData; +}; + +bool GrAALinearizingConvexPathRenderer::onDrawPath(GrDrawTarget* target, + GrPipelineBuilder* pipelineBuilder, + GrColor color, + const SkMatrix& vm, + const SkPath& path, + const GrStrokeInfo&, + bool antiAlias) { + if (path.isEmpty()) { + return true; + } + AAFlatteningConvexPathBatch::Geometry geometry; + geometry.fColor = color; + geometry.fViewMatrix = vm; + geometry.fPath = path; + + SkAutoTUnref<GrBatch> batch(AAFlatteningConvexPathBatch::Create(geometry)); + target->drawBatch(pipelineBuilder, batch); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// + +#ifdef GR_TEST_UTILS + +BATCH_TEST_DEFINE(AAFlatteningConvexPathBatch) { + AAFlatteningConvexPathBatch::Geometry geometry; + geometry.fColor = GrRandomColor(random); + geometry.fViewMatrix = GrTest::TestMatrixInvertible(random); + geometry.fPath = GrTest::TestPathConvex(random); + + return AAFlatteningConvexPathBatch::Create(geometry); +} + +#endif diff --git a/src/gpu/GrAALinearizingConvexPathRenderer.h b/src/gpu/GrAALinearizingConvexPathRenderer.h new file mode 100644 index 0000000000..8776332f49 --- /dev/null +++ b/src/gpu/GrAALinearizingConvexPathRenderer.h @@ -0,0 +1,35 @@ + +/* + * Copyright 2015 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef GrAALinearizingConvexPathRenderer_DEFINED +#define GrAALinearizingConvexPathRenderer_DEFINED + +#include "GrPathRenderer.h" + +class GrAALinearizingConvexPathRenderer : public GrPathRenderer { +public: + GrAALinearizingConvexPathRenderer(); + + virtual bool canDrawPath(const GrDrawTarget*, + const GrPipelineBuilder*, + const SkMatrix& viewMatrix, + const SkPath&, + const GrStrokeInfo&, + bool antiAlias) const override; + +protected: + virtual bool onDrawPath(GrDrawTarget*, + GrPipelineBuilder*, + GrColor, + const SkMatrix& viewMatrix, + const SkPath&, + const GrStrokeInfo&, + bool antiAlias) override; +}; + +#endif |