Revert "Reland "Revert "Add arcs as a specialized geometry to GrShape."""

This reverts commit 580aee2fa4a57bf8208498fbc23acea04e16e092.

Bug: skia:7794
Change-Id: I9c2b923859c826dff58c22c529dc4e2ab4d0f186
Reviewed-on: https://skia-review.googlesource.com/124042
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index 3e4f990..497037a 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -1304,6 +1304,25 @@
 
     SkPoint singlePt;
 
+    // Adds a move-to to 'pt' if forceMoveTo is true. Otherwise a lineTo unless we're sufficiently
+    // close to 'pt' currently. This prevents spurious lineTos when adding a series of contiguous
+    // arcs from the same oval.
+    auto addPt = [&forceMoveTo, this](const SkPoint& pt) {
+        SkPoint lastPt;
+#ifdef SK_DISABLE_ARC_TO_LINE_TO_CHECK
+        static constexpr bool kSkipLineToCheck = true;
+#else
+        static constexpr bool kSkipLineToCheck = false;
+#endif
+        if (forceMoveTo) {
+            this->moveTo(pt);
+        } else if (kSkipLineToCheck || !this->getLastPt(&lastPt) ||
+                   !SkScalarNearlyEqual(lastPt.fX, pt.fX) ||
+                   !SkScalarNearlyEqual(lastPt.fY, pt.fY)) {
+            this->lineTo(pt);
+        }
+    };
+
     // At this point, we know that the arc is not a lone point, but startV == stopV
     // indicates that the sweepAngle is too small such that angles_to_unit_vectors
     // cannot handle it.
@@ -1318,7 +1337,7 @@
         // make sin(endAngle) to be 0 which will then draw a dot.
         singlePt.set(oval.centerX() + radiusX * sk_float_cos(endAngle),
             oval.centerY() + radiusY * sk_float_sin(endAngle));
-        forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
+        addPt(singlePt);
         return;
     }
 
@@ -1327,12 +1346,12 @@
     if (count) {
         this->incReserve(count * 2 + 1);
         const SkPoint& pt = conics[0].fPts[0];
-        forceMoveTo ? this->moveTo(pt) : this->lineTo(pt);
+        addPt(pt);
         for (int i = 0; i < count; ++i) {
             this->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
         }
     } else {
-        forceMoveTo ? this->moveTo(singlePt) : this->lineTo(singlePt);
+        addPt(singlePt);
     }
 }
 
@@ -3343,6 +3362,20 @@
     return true;
 }
 
+bool SkPathPriv::DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
+    if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
+        // This gets converted to an oval.
+        return true;
+    }
+    if (useCenter) {
+        // This is a pie wedge. It's convex if the angle is <= 180.
+        return SkScalarAbs(sweepAngle) <= 180.f;
+    }
+    // When the angle exceeds 360 this wraps back on top of itself. Otherwise it is a circle clipped
+    // to a secant, i.e. convex.
+    return SkScalarAbs(sweepAngle) <= 360.f;
+}
+
 void SkPathPriv::CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle,
                                    SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect) {
     SkASSERT(!oval.isEmpty());
@@ -3353,11 +3386,15 @@
     path->setFillType(SkPath::kWinding_FillType);
     if (isFillNoPathEffect && SkScalarAbs(sweepAngle) >= 360.f) {
         path->addOval(oval);
+        SkASSERT(path->isConvex() && DrawArcIsConvex(sweepAngle, false, isFillNoPathEffect));
         return;
     }
     if (useCenter) {
         path->moveTo(oval.centerX(), oval.centerY());
     }
+    auto firstDir =
+            sweepAngle > 0 ? SkPathPriv::kCW_FirstDirection : SkPathPriv::kCCW_FirstDirection;
+    bool convex = DrawArcIsConvex(sweepAngle, useCenter, isFillNoPathEffect);
     // Arc to mods at 360 and drawArc is not supposed to.
     bool forceMoveTo = !useCenter;
     while (sweepAngle <= -360.f) {
@@ -3380,6 +3417,8 @@
     if (useCenter) {
         path->close();
     }
+    path->setConvexity(convex ? SkPath::kConvex_Convexity : SkPath::kConcave_Convexity);
+    path->fFirstDirection.store(firstDir);
 }
 
 ///////////////////////////////////////////////////////////////////////////////////////////////////