aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/gpu
diff options
context:
space:
mode:
authorGravatar ethannicholas <ethannicholas@google.com>2016-08-26 11:03:32 -0700
committerGravatar Commit bot <commit-bot@chromium.org>2016-08-26 11:03:32 -0700
commitfab4a9b9882bfd1c2d8c3fd5eeaf691caeba0f31 (patch)
treebfd78d791b56dc9188ebf3fe2bac693848a4b7fe /src/gpu
parent5580c6913500ccd80fb03a72e6992ee0d5b42d88 (diff)
fixed 'corners' of paths in GrAAConvexTessellator
Diffstat (limited to 'src/gpu')
-rw-r--r--src/gpu/batches/GrAAConvexTessellator.cpp57
-rw-r--r--src/gpu/batches/GrAAConvexTessellator.h50
2 files changed, 69 insertions, 38 deletions
diff --git a/src/gpu/batches/GrAAConvexTessellator.cpp b/src/gpu/batches/GrAAConvexTessellator.cpp
index 7e28d24e93..2c069f6342 100644
--- a/src/gpu/batches/GrAAConvexTessellator.cpp
+++ b/src/gpu/batches/GrAAConvexTessellator.cpp
@@ -29,6 +29,9 @@ static const SkScalar kConicTolerance = 0.5f;
// dot product below which we use a round cap between curve segments
static const SkScalar kRoundCapThreshold = 0.8f;
+// dot product above which we consider two adjacent curves to be part of the "same" curve
+static const SkScalar kCurveConnectionThreshold = 0.95f;
+
static SkScalar intersect(const SkPoint& p0, const SkPoint& n0,
const SkPoint& p1, const SkPoint& n1) {
const SkPoint v = p1 - p0;
@@ -60,14 +63,14 @@ int GrAAConvexTessellator::addPt(const SkPoint& pt,
SkScalar depth,
SkScalar coverage,
bool movable,
- bool isCurve) {
+ CurveState curve) {
this->validate();
int index = fPts.count();
*fPts.push() = pt;
*fCoverages.push() = coverage;
*fMovable.push() = movable;
- *fIsCurve.push() = isCurve;
+ *fCurveState.push() = curve;
this->validate();
return index;
@@ -146,6 +149,19 @@ void GrAAConvexTessellator::computeBisectors() {
} else {
fBisectors[cur].negate(); // make the bisector face in
}
+ if (fCurveState[prev] == kIndeterminate_CurveState) {
+ if (fCurveState[cur] == kSharp_CurveState) {
+ fCurveState[prev] = kSharp_CurveState;
+ } else {
+ if (SkScalarAbs(fNorms[cur].dot(fNorms[prev])) > kCurveConnectionThreshold) {
+ fCurveState[prev] = kCurve_CurveState;
+ fCurveState[cur] = kCurve_CurveState;
+ } else {
+ fCurveState[prev] = kSharp_CurveState;
+ fCurveState[cur] = kSharp_CurveState;
+ }
+ }
+ }
SkASSERT(SkScalarNearlyEqual(1.0f, fBisectors[cur].length()));
}
@@ -304,7 +320,7 @@ bool GrAAConvexTessellator::extractFromPath(const SkMatrix& m, const SkPath& pat
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
switch (verb) {
case SkPath::kLine_Verb:
- this->lineTo(m, pts[1], false);
+ this->lineTo(m, pts[1], kSharp_CurveState);
break;
case SkPath::kQuad_Verb:
this->quadTo(m, pts);
@@ -461,11 +477,11 @@ void GrAAConvexTessellator::createOuterRing(const Ring& previousRing, SkScalar o
perp2.scale(outset);
perp2 += fPts[originalIdx];
- bool isCurve = fIsCurve[originalIdx];
+ CurveState curve = fCurveState[originalIdx];
// 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)
- int perp1Idx = this->addPt(perp1, -outset, coverage, false, isCurve);
+ int perp1Idx = this->addPt(perp1, -outset, coverage, false, curve);
nextRing->addIdx(perp1Idx, originalIdx);
int perp2Idx;
@@ -473,11 +489,11 @@ void GrAAConvexTessellator::createOuterRing(const Ring& previousRing, SkScalar o
if (duplicate_pt(perp2, this->point(perp1Idx))) {
perp2Idx = perp1Idx;
} else {
- perp2Idx = this->addPt(perp2, -outset, coverage, false, isCurve);
+ perp2Idx = this->addPt(perp2, -outset, coverage, false, curve);
}
if (perp2Idx != perp1Idx) {
- if (isCurve) {
+ if (curve == kCurve_CurveState) {
// bevel or round depending upon curvature
SkScalar dotProd = normal1.dot(normal2);
if (dotProd < kRoundCapThreshold) {
@@ -492,7 +508,7 @@ void GrAAConvexTessellator::createOuterRing(const Ring& previousRing, SkScalar o
// For very shallow angles all the corner points could fuse
if (!duplicate_pt(miter, this->point(perp1Idx))) {
int miterIdx;
- miterIdx = this->addPt(miter, -outset, coverage, false, false);
+ miterIdx = this->addPt(miter, -outset, coverage, false, kSharp_CurveState);
nextRing->addIdx(miterIdx, originalIdx);
// The two triangles for the corner
this->addTri(originalIdx, perp1Idx, miterIdx);
@@ -520,7 +536,8 @@ void GrAAConvexTessellator::createOuterRing(const Ring& previousRing, SkScalar o
// For very shallow angles all the corner points could fuse
if (!duplicate_pt(miter, this->point(perp1Idx))) {
int miterIdx;
- miterIdx = this->addPt(miter, -outset, coverage, false, false);
+ miterIdx = this->addPt(miter, -outset, coverage, false,
+ kSharp_CurveState);
nextRing->addIdx(miterIdx, originalIdx);
// The two triangles for the corner
this->addTri(originalIdx, perp1Idx, miterIdx);
@@ -704,7 +721,7 @@ bool GrAAConvexTessellator::createInsetRing(const Ring& lastRing, Ring* nextRing
SkScalar coverage = compute_coverage(depth, initialDepth, initialCoverage,
targetDepth, targetCoverage);
newIdx = this->addPt(fCandidateVerts.point(i), depth, coverage,
- fCandidateVerts.originatingIdx(i) != -1, false);
+ fCandidateVerts.originatingIdx(i) != -1, kSharp_CurveState);
} else {
SkASSERT(fCandidateVerts.originatingIdx(i) != -1);
this->updatePt(fCandidateVerts.originatingIdx(i), fCandidateVerts.point(i), depth,
@@ -823,7 +840,7 @@ bool GrAAConvexTessellator::Ring::isConvex(const GrAAConvexTessellator& tess) co
#endif
-void GrAAConvexTessellator::lineTo(SkPoint p, bool isCurve) {
+void GrAAConvexTessellator::lineTo(SkPoint p, CurveState curve) {
if (this->numPts() > 0 && duplicate_pt(p, this->lastPoint())) {
return;
}
@@ -834,7 +851,7 @@ void GrAAConvexTessellator::lineTo(SkPoint p, bool isCurve) {
// The old last point is on the line from the second to last to the new point
this->popLastPt();
fNorms.pop();
- fIsCurve.pop();
+ fCurveState.pop();
// double-check that the new last point is not a duplicate of the new point. In an ideal
// world this wouldn't be necessary (since it's only possible for non-convex paths), but
// floating point precision issues mean it can actually happen on paths that were determined
@@ -844,7 +861,7 @@ void GrAAConvexTessellator::lineTo(SkPoint p, bool isCurve) {
}
}
SkScalar initialRingCoverage = fStrokeWidth < 0.0f ? 0.5f : 1.0f;
- this->addPt(p, 0.0f, initialRingCoverage, false, isCurve);
+ this->addPt(p, 0.0f, initialRingCoverage, false, curve);
if (this->numPts() > 1) {
*fNorms.push() = fPts.top() - fPts[fPts.count()-2];
SkDEBUGCODE(SkScalar len =) SkPoint::Normalize(&fNorms.top());
@@ -853,9 +870,9 @@ void GrAAConvexTessellator::lineTo(SkPoint p, bool isCurve) {
}
}
-void GrAAConvexTessellator::lineTo(const SkMatrix& m, SkPoint p, bool isCurve) {
+void GrAAConvexTessellator::lineTo(const SkMatrix& m, SkPoint p, CurveState curve) {
m.mapPoints(&p, 1);
- this->lineTo(p, isCurve);
+ this->lineTo(p, curve);
}
void GrAAConvexTessellator::quadTo(SkPoint pts[3]) {
@@ -865,9 +882,10 @@ void GrAAConvexTessellator::quadTo(SkPoint pts[3]) {
int count = GrPathUtils::generateQuadraticPoints(pts[0], pts[1], pts[2],
kQuadTolerance, &target, maxCount);
fPointBuffer.setCount(count);
- for (int i = 0; i < count; i++) {
- lineTo(fPointBuffer[i], true);
+ for (int i = 0; i < count - 1; i++) {
+ lineTo(fPointBuffer[i], kCurve_CurveState);
}
+ lineTo(fPointBuffer[count - 1], kIndeterminate_CurveState);
}
void GrAAConvexTessellator::quadTo(const SkMatrix& m, SkPoint pts[3]) {
@@ -887,9 +905,10 @@ void GrAAConvexTessellator::cubicTo(const SkMatrix& m, SkPoint pts[4]) {
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(fPointBuffer[i], true);
+ for (int i = 0; i < count - 1; i++) {
+ lineTo(fPointBuffer[i], kCurve_CurveState);
}
+ lineTo(fPointBuffer[count - 1], kIndeterminate_CurveState);
}
// include down here to avoid compilation errors caused by "-" overload in SkGeometry.h
diff --git a/src/gpu/batches/GrAAConvexTessellator.h b/src/gpu/batches/GrAAConvexTessellator.h
index faa251e2a8..2683147912 100644
--- a/src/gpu/batches/GrAAConvexTessellator.h
+++ b/src/gpu/batches/GrAAConvexTessellator.h
@@ -166,12 +166,24 @@ private:
SkTDArray<PointData> fPts;
};
+ // Represents whether a given point is within a curve. A point is inside a curve only if it is
+ // an interior point within a quad, cubic, or conic, or if it is the endpoint of a quad, cubic,
+ // or conic with another curve meeting it at (more or less) the same angle.
+ enum CurveState {
+ // point is a sharp vertex
+ kSharp_CurveState,
+ // endpoint of a curve with the other side's curvature not yet determined
+ kIndeterminate_CurveState,
+ // point is in the interior of a curve
+ kCurve_CurveState
+ };
+
bool movable(int index) const { return fMovable[index]; }
// 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, SkScalar coverage, bool movable, bool isCurve);
+ int addPt(const SkPoint& pt, SkScalar depth, SkScalar coverage, bool movable, CurveState curve);
void popLastPt();
void popFirstPtShuffle();
@@ -191,9 +203,9 @@ private:
int edgeIdx, SkScalar desiredDepth,
SkPoint* result) const;
- void lineTo(SkPoint p, bool isCurve);
+ void lineTo(SkPoint p, CurveState curve);
- void lineTo(const SkMatrix& m, SkPoint p, bool isCurve);
+ void lineTo(const SkMatrix& m, SkPoint p, CurveState curve);
void quadTo(SkPoint pts[3]);
@@ -226,43 +238,43 @@ private:
void validate() const;
// fPts, fCoverages & fMovable should always have the same # of elements
- SkTDArray<SkPoint> fPts;
- SkTDArray<SkScalar> fCoverages;
+ SkTDArray<SkPoint> fPts;
+ SkTDArray<SkScalar> fCoverages;
// movable points are those that can be slid further along their bisector
- SkTDArray<bool> fMovable;
+ SkTDArray<bool> fMovable;
// The outward facing normals for the original polygon
- SkTDArray<SkVector> fNorms;
+ SkTDArray<SkVector> fNorms;
// 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;
+ SkTDArray<SkVector> fBisectors;
// Tracks whether a given point is interior to a curve. Such points are
// assumed to have shallow curvature.
- SkTDArray<bool> fIsCurve;
+ SkTDArray<CurveState> fCurveState;
- SkPoint::Side fSide; // winding of the original polygon
+ SkPoint::Side fSide; // winding of the original polygon
// The triangulation of the points
- SkTDArray<int> fIndices;
+ SkTDArray<int> fIndices;
- Ring fInitialRing;
+ Ring fInitialRing;
#if GR_AA_CONVEX_TESSELLATOR_VIZ
// When visualizing save all the rings
- SkTDArray<Ring*> fRings;
+ SkTDArray<Ring*> fRings;
#else
- Ring fRings[2];
+ Ring fRings[2];
#endif
- CandidateVerts fCandidateVerts;
+ CandidateVerts fCandidateVerts;
// < 0 means filling rather than stroking
- SkScalar fStrokeWidth;
+ SkScalar fStrokeWidth;
- SkPaint::Join fJoin;
+ SkPaint::Join fJoin;
- SkScalar fMiterLimit;
+ SkScalar fMiterLimit;
- SkTDArray<SkPoint> fPointBuffer;
+ SkTDArray<SkPoint> fPointBuffer;
};