Add a fast codepath for SkPath.addPath(kAppend)

Change-Id: I49469f29cc10032d687b938ded379ef7e2f52da2
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/269190
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/include/private/SkPathRef.h b/include/private/SkPathRef.h
index 4775ad0..ab476cc 100644
--- a/include/private/SkPathRef.h
+++ b/include/private/SkPathRef.h
@@ -84,6 +84,17 @@
         }
 
         /**
+         * Concatenates all verbs from 'path' onto the pathRef's verbs array. Increases the point
+         * count by the number of points in 'path', and the conic weight count by the number of
+         * conics in 'path'.
+         *
+         * Returns pointers to the uninitialized points and conic weights data.
+         */
+        std::tuple<SkPoint*, SkScalar*> growForVerbsInPath(const SkPathRef& path) {
+            return fPathRef->growForVerbsInPath(path);
+        }
+
+        /**
          * Resets the path ref to a new verb and point count. The new verbs and points are
          * uninitialized.
          */
@@ -417,6 +428,14 @@
     SkPoint* growForVerb(int /*SkPath::Verb*/ verb, SkScalar weight);
 
     /**
+     * Concatenates all verbs from 'path' onto our own verbs array. Increases the point count by the
+     * number of points in 'path', and the conic weight count by the number of conics in 'path'.
+     *
+     * Returns pointers to the uninitialized points and conic weights data.
+     */
+    std::tuple<SkPoint*, SkScalar*> growForVerbsInPath(const SkPathRef& path);
+
+    /**
      * Private, non-const-ptr version of the public function verbsMemBegin().
      */
     uint8_t* verbsBeginWritable() { return fVerbs.begin(); }
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index a6b39c2..4074d6a 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -1343,6 +1343,10 @@
 }
 
 SkPath& SkPath::addPath(const SkPath& srcPath, const SkMatrix& matrix, AddPathMode mode) {
+    if (srcPath.isEmpty()) {
+        return *this;
+    }
+
     // Detect if we're trying to add ourself
     const SkPath* src = &srcPath;
     SkTLazy<SkPath> tmp;
@@ -1350,6 +1354,19 @@
         src = tmp.set(srcPath);
     }
 
+    if (kAppend_AddPathMode == mode && !matrix.hasPerspective()) {
+        if (src->fLastMoveToIndex >= 0) {
+            fLastMoveToIndex = this->countPoints() + src->fLastMoveToIndex;
+        }
+        SkPathRef::Editor ed(&fPathRef);
+        auto [newPts, newWeights] = ed.growForVerbsInPath(*src->fPathRef);
+        matrix.mapPoints(newPts, src->fPathRef->points(), src->countPoints());
+        if (int numWeights = src->fPathRef->countWeights()) {
+            memcpy(newWeights, src->fPathRef->conicWeights(), numWeights * sizeof(newWeights[0]));
+        }
+        return this->dirtyAfterEdit();
+    }
+
     SkPathRef::Editor(&fPathRef, src->countVerbs(), src->countPoints());
 
     RawIter iter(*src);
@@ -1362,7 +1379,8 @@
         switch (verb) {
             case kMove_Verb:
                 proc(matrix, &pts[0], &pts[0], 1);
-                if (firstVerb && mode == kExtend_AddPathMode && !isEmpty()) {
+                if (firstVerb && !isEmpty()) {
+                    SkASSERT(mode == kExtend_AddPathMode);
                     injectMoveToIfNeeded(); // In case last contour is closed
                     SkPoint lastPt;
                     // don't add lineTo if it is degenerate
diff --git a/src/core/SkPathRef.cpp b/src/core/SkPathRef.cpp
index 75086a0..f665625 100644
--- a/src/core/SkPathRef.cpp
+++ b/src/core/SkPathRef.cpp
@@ -332,6 +332,32 @@
     out->fIsRRect = false;
 }
 
+std::tuple<SkPoint*, SkScalar*> SkPathRef::growForVerbsInPath(const SkPathRef& path) {
+    SkDEBUGCODE(this->validate();)
+
+    fSegmentMask |= path.fSegmentMask;
+    fBoundsIsDirty = true;  // this also invalidates fIsFinite
+    fIsOval = false;
+    fIsRRect = false;
+
+    if (int numVerbs = path.countVerbs()) {
+        memcpy(fVerbs.append(numVerbs), path.fVerbs.begin(), numVerbs * sizeof(fVerbs[0]));
+    }
+
+    SkPoint* pts = nullptr;
+    if (int numPts = path.countPoints()) {
+        pts = fPoints.append(numPts);
+    }
+
+    SkScalar* weights = nullptr;
+    if (int numConics = path.countWeights()) {
+        weights = fConicWeights.append(numConics);
+    }
+
+    SkDEBUGCODE(this->validate();)
+    return {pts, weights};
+}
+
 SkPoint* SkPathRef::growForRepeatedVerb(int /*SkPath::Verb*/ verb,
                                         int numVbs,
                                         SkScalar** weights) {