From fab4a9b9882bfd1c2d8c3fd5eeaf691caeba0f31 Mon Sep 17 00:00:00 2001 From: ethannicholas Date: Fri, 26 Aug 2016 11:03:32 -0700 Subject: fixed 'corners' of paths in GrAAConvexTessellator BUG=skia:5671 GOLD_TRYBOT_URL= https://gold.skia.org/search?issue=2280943003 Review-Url: https://codereview.chromium.org/2280943003 --- src/gpu/batches/GrAAConvexTessellator.cpp | 57 ++++++++++++++++++++----------- src/gpu/batches/GrAAConvexTessellator.h | 50 ++++++++++++++++----------- 2 files changed, 69 insertions(+), 38 deletions(-) (limited to 'src/gpu') 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 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 fPts; - SkTDArray fCoverages; + SkTDArray fPts; + SkTDArray fCoverages; // movable points are those that can be slid further along their bisector - SkTDArray fMovable; + SkTDArray fMovable; // The outward facing normals for the original polygon - SkTDArray fNorms; + SkTDArray 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 fBisectors; + SkTDArray fBisectors; // Tracks whether a given point is interior to a curve. Such points are // assumed to have shallow curvature. - SkTDArray fIsCurve; + SkTDArray fCurveState; - SkPoint::Side fSide; // winding of the original polygon + SkPoint::Side fSide; // winding of the original polygon // The triangulation of the points - SkTDArray fIndices; + SkTDArray fIndices; - Ring fInitialRing; + Ring fInitialRing; #if GR_AA_CONVEX_TESSELLATOR_VIZ // When visualizing save all the rings - SkTDArray fRings; + SkTDArray 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 fPointBuffer; + SkTDArray fPointBuffer; }; -- cgit v1.2.3