fixed 'corners' of paths in GrAAConvexTessellator
authorethannicholas <ethannicholas@google.com>
Fri, 26 Aug 2016 18:03:32 +0000 (11:03 -0700)
committerCommit bot <commit-bot@chromium.org>
Fri, 26 Aug 2016 18:03:32 +0000 (11:03 -0700)
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
src/gpu/batches/GrAAConvexTessellator.h

index 7e28d24..2c069f6 100644 (file)
@@ -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
index faa251e..2683147 100644 (file)
@@ -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;
 };