aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar ethannicholas <ethannicholas@google.com>2015-06-10 12:11:17 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2015-06-10 12:11:17 -0700
commit1a1b3ac0d4feecb0fefa8a07c7abf3471c96f545 (patch)
tree4c03c8b596f18b5367e57624cfa0194d1e0107b0
parent523beb7fd91e6a171a69f44cf6cc8e8d88a22dd0 (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.gypi2
-rw-r--r--src/gpu/GrAAConvexTessellator.cpp287
-rw-r--r--src/gpu/GrAAConvexTessellator.h21
-rw-r--r--src/gpu/GrAALinearizingConvexPathRenderer.cpp320
-rw-r--r--src/gpu/GrAALinearizingConvexPathRenderer.h35
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