addPoly() entry-point, to quickly add MoveTo+N*LineTo (useful in dashing)
Review URL: https://codereview.appspot.com/6256063

git-svn-id: http://skia.googlecode.com/svn/trunk@4061 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/include/core/SkPath.h b/include/core/SkPath.h
index 03108df..e509346 100644
--- a/include/core/SkPath.h
+++ b/include/core/SkPath.h
@@ -559,6 +559,19 @@
     void addRoundRect(const SkRect& rect, const SkScalar radii[],
                       Direction dir = kCW_Direction);
 
+    /**
+     *  Add a new contour made of just lines. This is just a fast version of
+     *  the following:
+     *      this->moveTo(pts[0]);
+     *      for (int i = 1; i < count; ++i) {
+     *          this->lineTo(pts[i]);
+     *      }
+     *      if (close) {
+     *          this->close();
+     *      }
+     */
+    void addPoly(const SkPoint pts[], int count, bool close);
+
     /** Add a copy of src to the path, offset by (dx,dy)
         @param src  The path to add as a new contour
         @param dx   The amount to translate the path in X as it is added
diff --git a/src/core/SkPath.cpp b/src/core/SkPath.cpp
index ee6a171..5f63651 100644
--- a/src/core/SkPath.cpp
+++ b/src/core/SkPath.cpp
@@ -12,6 +12,11 @@
 #include "SkWriter32.h"
 #include "SkMath.h"
 
+// This value is just made-up for now. When count is 4, calling memset was much
+// slower than just writing the loop. This seems odd, and hopefully in the
+// future this we appear to have been a fluke...
+#define MIN_COUNT_FOR_MEMSET_TO_BE_FAST 16
+
 ////////////////////////////////////////////////////////////////////////////
 
 /**
@@ -627,6 +632,39 @@
     this->close();
 }
 
+void SkPath::addPoly(const SkPoint pts[], int count, bool close) {
+    SkDEBUGCODE(this->validate();)
+    if (count <= 0) {
+        return;
+    }
+
+    fLastMoveToIndex = fPts.count();
+    fPts.append(count, pts);
+
+    // +close makes room for the extra kClose_Verb
+    uint8_t* vb = fVerbs.append(count + close);
+    vb[0] = kMove_Verb;
+
+    if (count > 1) {
+        // cast to unsigned, so if MIN_COUNT_FOR_MEMSET_TO_BE_FAST is defined to
+        // be 0, the compiler will remove the test/branch entirely.
+        if ((unsigned)count >= MIN_COUNT_FOR_MEMSET_TO_BE_FAST) {
+            memset(&vb[1], kLine_Verb, count - 1);
+        } else {
+            for (int i = 1; i < count; ++i) {
+                vb[i] = kLine_Verb;
+            }
+        }
+        fSegmentMask |= kLine_SegmentMask;
+    }
+    if (close) {
+        vb[count] = kClose_Verb;
+    }
+
+    GEN_ID_INC;
+    DIRTY_AFTER_EDIT;
+}
+
 #define CUBIC_ARC_FACTOR    ((SK_ScalarSqrt2 - SK_Scalar1) * 4 / 3)
 
 void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry,
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
index fae2d4b..7b8f869 100644
--- a/tests/PathTest.cpp
+++ b/tests/PathTest.cpp
@@ -16,6 +16,72 @@
 #include "SkSize.h"
 #include "SkWriter32.h"
 
+// assert that we always
+//  start with a moveTo
+//  only have 1 moveTo
+//  only have Lines after that
+//  end with a single close
+//  only have (at most) 1 close
+//
+static void test_poly(skiatest::Reporter* reporter, const SkPath& path,
+                      const SkPoint srcPts[], int count, bool expectClose) {
+    SkPath::RawIter iter(path);
+    SkPoint         pts[4];
+    SkPath::Verb    verb;
+
+    bool firstTime = true;
+    bool foundClose = false;
+    for (;;) {
+        switch (iter.next(pts)) {
+            case SkPath::kMove_Verb:
+                REPORTER_ASSERT(reporter, firstTime);
+                REPORTER_ASSERT(reporter, pts[0] == srcPts[0]);
+                srcPts++;
+                firstTime = false;
+                break;
+            case SkPath::kLine_Verb:
+                REPORTER_ASSERT(reporter, !firstTime);
+                REPORTER_ASSERT(reporter, pts[1] == srcPts[0]);
+                srcPts++;
+                break;
+            case SkPath::kQuad_Verb:
+                REPORTER_ASSERT(reporter, !"unexpected quad verb");
+                break;
+            case SkPath::kCubic_Verb:
+                REPORTER_ASSERT(reporter, !"unexpected cubic verb");
+                break;
+            case SkPath::kClose_Verb:
+                REPORTER_ASSERT(reporter, !firstTime);
+                REPORTER_ASSERT(reporter, !foundClose);
+                REPORTER_ASSERT(reporter, expectClose);
+                foundClose = true;
+                break;
+            case SkPath::kDone_Verb:
+                goto DONE;
+        }
+    }
+DONE:
+    REPORTER_ASSERT(reporter, foundClose == expectClose);
+}
+
+static void test_addPoly(skiatest::Reporter* reporter) {
+    SkPoint pts[32];
+    SkRandom rand;
+    
+    for (size_t i = 0; i < SK_ARRAY_COUNT(pts); ++i) {
+        pts[i].fX = rand.nextSScalar1();
+        pts[i].fY = rand.nextSScalar1();
+    }
+
+    for (int doClose = 0; doClose <= 1; ++doClose) {
+        for (size_t count = 1; count <= SK_ARRAY_COUNT(pts); ++count) {
+            SkPath path;
+            path.addPoly(pts, count, SkToBool(doClose));
+            test_poly(reporter, path, pts, count, SkToBool(doClose));
+        }
+    }
+}
+
 static void test_strokerec(skiatest::Reporter* reporter) {
     SkStrokeRec rec(SkStrokeRec::kFill_InitStyle);
     REPORTER_ASSERT(reporter, rec.isFillStyle());
@@ -1398,8 +1464,8 @@
     test_raw_iter(reporter);
     test_circle(reporter);
     test_oval(reporter);
-    
     test_strokerec(reporter);
+    test_addPoly(reporter);
 }
 
 #include "TestClassDef.h"