[PathKit] Write more complete docs and clean up API to be consistent

Breaking Changes (should be minor, as it's mostly just things
for testing):
 - PathKit.ApplyPathOp should have returned a new SkPath, but didn't.
It now does and is named "MakeFromOp", which makes the convention of
"Have 'make' in name, needs delete" more consistent.
 - PathKit.FromCmds(arr) now only needs to take the JS Array and
will handle the TypedArrays under the hood. If clients want to deal
with TypedArrays themselves, they can use _FromCmds(ptr, len) directly.
 - PathKit.MakeLTRBRect is now just PathKit.LTRBRect. The thing
returned is a normal JS Object and doesn't need delete().

As per custom with v0 apps, we are updating the minor version
to v0.3.0 to account for breaking changes.


Docs-Preview: https://skia.org/?cl=147960
Bug: skia:8216
Change-Id: Ia3626e69f3e97698fc62a6aee876af005e29ffca
Reviewed-on: https://skia-review.googlesource.com/147960
Reviewed-by: Mike Reed <reed@google.com>
Reviewed-by: Heather Miller <hcm@google.com>
diff --git a/experimental/pathkit/Makefile b/experimental/pathkit/Makefile
index f0e7f45..0547877 100644
--- a/experimental/pathkit/Makefile
+++ b/experimental/pathkit/Makefile
@@ -13,7 +13,7 @@
 
 publish:
 	cd npm-wasm; npm publish
-	cd ../npm-asmjs; npm publish
+	cd npm-asmjs; npm publish
 
 update-major:
 	cd npm-wasm; npm version major
@@ -22,12 +22,12 @@
 
 update-minor:
 	cd npm-wasm; npm version minor
-	cd ../npm-asmjs; npm version minor
+	cd npm-asmjs; npm version minor
 	echo "Don't forget to publish."
 
 update-patch:
 	cd npm-wasm; npm version patch
-	cd ../npm-asmjs; npm version patch
+	cd npm-asmjs; npm version patch
 	echo "Don't forget to publish."
 
 # Build the library and run the tests. If developing locally, test-continuous is better
diff --git a/experimental/pathkit/chaining.js b/experimental/pathkit/chaining.js
index d4902c6..f901d38 100644
--- a/experimental/pathkit/chaining.js
+++ b/experimental/pathkit/chaining.js
@@ -115,13 +115,13 @@
       return null;
     };
 
-    PathKit.SkPath.prototype.quadraticCurveTo = function(x1, y1, x2, y2) {
-      this._quadTo(x1, y1, x2, y2);
+    PathKit.SkPath.prototype.quadraticCurveTo = function(cpx, cpy, x, y) {
+      this._quadTo(cpx, cpy, x, y);
       return this;
     };
 
-    PathKit.SkPath.prototype.quadTo = function(x1, y1, x2, y2) {
-      this._quadTo(x1, y1, x2, y2);
+    PathKit.SkPath.prototype.quadTo = function(cpx, cpy, x, y) {
+      this._quadTo(cpx, cpy, x, y);
       return this;
     };
 
diff --git a/experimental/pathkit/externs.js b/experimental/pathkit/externs.js
index b68a3a2..9116254 100644
--- a/experimental/pathkit/externs.js
+++ b/experimental/pathkit/externs.js
@@ -24,6 +24,9 @@
 	SkBits2FloatUnsigned: function(num) {},
 	_malloc: function(size) {},
 	onRuntimeInitialized: function() {},
+	_FromCmds: function(ptr, size) {},
+	loadCmdsTypedArray: function(arr) {},
+	FromCmds: function(arr) {},
 
 	HEAPF32: {},
 
@@ -40,7 +43,7 @@
 		_lineTo: function(x1, y1) {},
 		_moveTo: function(x1, y1) {},
 		_op: function(otherPath, op) {},
-		_quadTo: function(x1, y1, x2, y2) {},
+		_quadTo: function(cpx, cpy, x, y) {},
 		_rect: function(x, y, w, h) {},
 		_simplify: function() {},
 		_stroke: function(opts) {},
diff --git a/experimental/pathkit/helper.js b/experimental/pathkit/helper.js
index 0d476ab..4f84782 100644
--- a/experimental/pathkit/helper.js
+++ b/experimental/pathkit/helper.js
@@ -7,9 +7,9 @@
   var Float32ArrayCache = {};
 
   // Takes a 2D array of commands and puts them into the WASM heap
-  // as a 1D array.  This allowing them to referenced from the C++ code.
+  // as a 1D array. This allows them to referenced from the C++ code.
   // Returns a 2 element array, with the first item being essentially a
-  // pointer to the array and the second item being the lengh of
+  // pointer to the array and the second item being the length of
   // the new 1D array.
   //
   // Example usage:
@@ -26,7 +26,7 @@
   //
   // If arguments at index 1... in each cmd row are strings, they will be
   // parsed as hex, and then converted to floats using SkBits2FloatUnsigned
-  PathKit['loadCmdsTypedArray'] = function(arr) {
+  PathKit.loadCmdsTypedArray = function(arr) {
     var len = 0;
     for (var r = 0; r < arr.length; r++) {
       len += arr[r].length;
@@ -57,5 +57,13 @@
     PathKit.HEAPF32.set(ta, ptr / ta.BYTES_PER_ELEMENT);
     return [ptr, len];
   }
+
+  // Experimentation has shown that using TypedArrays to pass arrays from
+  // JS to C++ is faster than passing the JS Arrays across.
+  // See above for example of cmds.
+  PathKit.FromCmds = function(cmds) {
+    var ptrLen = PathKit.loadCmdsTypedArray(cmds);
+    return PathKit._FromCmds(ptrLen[0], ptrLen[1]);
+  }
 }(Module)); // When this file is loaded in, the high level object is "Module";
 
diff --git a/experimental/pathkit/npm-asmjs/example.html b/experimental/pathkit/npm-asmjs/example.html
index 3479b83..e5c85e7 100644
--- a/experimental/pathkit/npm-asmjs/example.html
+++ b/experimental/pathkit/npm-asmjs/example.html
@@ -37,6 +37,7 @@
 <canvas class=big id=canvas8></canvas>
 <canvas class=big id=canvas9></canvas>
 <canvas class=big id=canvas10></canvas>
+<canvas class=big id=canvas11></canvas>
 <canvas class=big id=canvasTransform></canvas>
 
 <h2> Supports fill-rules of nonzero and evenodd </h2>
@@ -193,58 +194,74 @@
       // no-op
       (path) => path,
       // dash
-      (path) => path.dash(10, 3, 0),
+      (path, counter) => path.dash(10, 3, counter/5),
       // trim (takes optional 3rd param for returning the trimmed part
       // or the complement)
-      (path) => path.trim(0.25, 0.8, false),
+      (path, counter) => path.trim((counter/100) % 1, 0.8, false),
       // simplify
       (path) => path.simplify(),
       // stroke
-      (path) => path.stroke({
-        width: 15,
+      (path, counter) => path.stroke({
+        width: 10 * (Math.sin(counter/30) + 1),
         join: PathKit.StrokeJoin.BEVEL,
         cap: PathKit.StrokeCap.BUTT,
         miter_limit: 1,
       }),
       // "offset effect", that is, making a border around the shape.
-      (path) => {
+      (path, counter) => {
         let orig = path.copy();
         path.stroke({
-          width: 10,
+          width: 10 + (counter / 4) % 50,
           join: PathKit.StrokeJoin.ROUND,
           cap: PathKit.StrokeCap.SQUARE,
         })
           .op(orig, PathKit.PathOp.DIFFERENCE);
         orig.delete();
+      },
+      (path, counter) => {
+        let simplified = path.simplify().copy();
+        path.stroke({
+          width: 2 + (counter / 2) % 100,
+          join: PathKit.StrokeJoin.BEVEL,
+          cap: PathKit.StrokeCap.BUTT,
+        })
+          .op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
+        simplified.delete();
       }
     ];
 
-    let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Offset"];
+    let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Grow", "Shrink"];
 
-    for (let i = 0; i < effects.length; i++) {
-      let path = PathKit.NewPath();
-      drawStar(path);
+    let counter = 0;
+    function frame() {
+      counter++;
+      for (let i = 0; i < effects.length; i++) {
+        let path = PathKit.NewPath();
+        drawStar(path);
 
-      // The transforms apply directly to the path.
-      effects[i](path);
+        // The transforms apply directly to the path.
+        effects[i](path, counter);
 
-      let ctx = document.getElementById(`canvas${i+5}`).getContext('2d');
-      setCanvasSize(ctx, 300, 300);
-      ctx.strokeStyle = '#3c597a';
-      ctx.fillStyle = '#3c597a';
-      if (i === 4 || i === 5) {
-        ctx.fill(path.toPath2D(), path.getFillTypeString());
-      } else {
-        ctx.stroke(path.toPath2D());
+        let ctx = document.getElementById(`canvas${i+5}`).getContext('2d');
+        setCanvasSize(ctx, 300, 300);
+        ctx.strokeStyle = '#3c597a';
+        ctx.fillStyle = '#3c597a';
+        if (i >=4 ) {
+          ctx.fill(path.toPath2D(), path.getFillTypeString());
+        } else {
+          ctx.stroke(path.toPath2D());
+        }
+
+        ctx.font = '42px monospace';
+
+        let x = 150-ctx.measureText(names[i]).width/2;
+        ctx.strokeText(names[i], x, 290);
+
+        path.delete();
       }
-
-      ctx.font = '42px monospace';
-
-      let x = 150-ctx.measureText(names[i]).width/2;
-      ctx.strokeText(names[i], x, 290);
-
-      path.delete();
+      window.requestAnimationFrame(frame);
     }
+    window.requestAnimationFrame(frame);
   }
 
   function MatrixTransformExample(PathKit) {
diff --git a/experimental/pathkit/npm-asmjs/package.json b/experimental/pathkit/npm-asmjs/package.json
index b938696..792d35a 100644
--- a/experimental/pathkit/npm-asmjs/package.json
+++ b/experimental/pathkit/npm-asmjs/package.json
@@ -1,6 +1,6 @@
 {
   "name": "experimental-pathkit-asmjs",
-  "version": "0.2.0",
+  "version": "0.3.0",
   "description": "A asm.js version of Skia's PathOps toolkit",
   "main": "bin/pathkit.js",
   "homepage": "https://github.com/google/skia/tree/master/experimental/pathkit",
diff --git a/experimental/pathkit/npm-wasm/example.html b/experimental/pathkit/npm-wasm/example.html
index 60d6981..a6c8cca 100644
--- a/experimental/pathkit/npm-wasm/example.html
+++ b/experimental/pathkit/npm-wasm/example.html
@@ -37,6 +37,7 @@
 <canvas class=big id=canvas8></canvas>
 <canvas class=big id=canvas9></canvas>
 <canvas class=big id=canvas10></canvas>
+<canvas class=big id=canvas11></canvas>
 <canvas class=big id=canvasTransform></canvas>
 
 <h2> Supports fill-rules of nonzero and evenodd </h2>
@@ -193,58 +194,74 @@
       // no-op
       (path) => path,
       // dash
-      (path) => path.dash(10, 3, 0),
+      (path, counter) => path.dash(10, 3, counter/5),
       // trim (takes optional 3rd param for returning the trimmed part
       // or the complement)
-      (path) => path.trim(0.25, 0.8, false),
+      (path, counter) => path.trim((counter/100) % 1, 0.8, false),
       // simplify
       (path) => path.simplify(),
       // stroke
-      (path) => path.stroke({
-        width: 15,
+      (path, counter) => path.stroke({
+        width: 10 * (Math.sin(counter/30) + 1),
         join: PathKit.StrokeJoin.BEVEL,
         cap: PathKit.StrokeCap.BUTT,
         miter_limit: 1,
       }),
       // "offset effect", that is, making a border around the shape.
-      (path) => {
+      (path, counter) => {
         let orig = path.copy();
         path.stroke({
-          width: 10,
+          width: 10 + (counter / 4) % 50,
           join: PathKit.StrokeJoin.ROUND,
           cap: PathKit.StrokeCap.SQUARE,
         })
           .op(orig, PathKit.PathOp.DIFFERENCE);
         orig.delete();
+      },
+      (path, counter) => {
+        let simplified = path.simplify().copy();
+        path.stroke({
+          width: 2 + (counter / 2) % 100,
+          join: PathKit.StrokeJoin.BEVEL,
+          cap: PathKit.StrokeCap.BUTT,
+        })
+          .op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
+        simplified.delete();
       }
     ];
 
-    let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Offset"];
+    let names = ["(plain)", "Dash", "Trim", "Simplify", "Stroke", "Grow", "Shrink"];
 
-    for (let i = 0; i < effects.length; i++) {
-      let path = PathKit.NewPath();
-      drawStar(path);
+    let counter = 0;
+    function frame() {
+      counter++;
+      for (let i = 0; i < effects.length; i++) {
+        let path = PathKit.NewPath();
+        drawStar(path);
 
-      // The transforms apply directly to the path.
-      effects[i](path);
+        // The transforms apply directly to the path.
+        effects[i](path, counter);
 
-      let ctx = document.getElementById(`canvas${i+5}`).getContext('2d');
-      setCanvasSize(ctx, 300, 300);
-      ctx.strokeStyle = '#3c597a';
-      ctx.fillStyle = '#3c597a';
-      if (i === 4 || i === 5) {
-        ctx.fill(path.toPath2D(), path.getFillTypeString());
-      } else {
-        ctx.stroke(path.toPath2D());
+        let ctx = document.getElementById(`canvas${i+5}`).getContext('2d');
+        setCanvasSize(ctx, 300, 300);
+        ctx.strokeStyle = '#3c597a';
+        ctx.fillStyle = '#3c597a';
+        if (i >=4 ) {
+          ctx.fill(path.toPath2D(), path.getFillTypeString());
+        } else {
+          ctx.stroke(path.toPath2D());
+        }
+
+        ctx.font = '42px monospace';
+
+        let x = 150-ctx.measureText(names[i]).width/2;
+        ctx.strokeText(names[i], x, 290);
+
+        path.delete();
       }
-
-      ctx.font = '42px monospace';
-
-      let x = 150-ctx.measureText(names[i]).width/2;
-      ctx.strokeText(names[i], x, 290);
-
-      path.delete();
+      window.requestAnimationFrame(frame);
     }
+    window.requestAnimationFrame(frame);
   }
 
   function MatrixTransformExample(PathKit) {
diff --git a/experimental/pathkit/npm-wasm/package.json b/experimental/pathkit/npm-wasm/package.json
index c951625..f83ab63 100644
--- a/experimental/pathkit/npm-wasm/package.json
+++ b/experimental/pathkit/npm-wasm/package.json
@@ -1,6 +1,6 @@
 {
   "name": "experimental-pathkit-wasm",
-  "version": "0.2.0",
+  "version": "0.3.0",
   "description": "A WASM version of Skia's PathOps toolkit",
   "main": "bin/pathkit.js",
   "homepage": "https://github.com/google/skia/tree/master/experimental/pathkit",
diff --git a/experimental/pathkit/pathkit_wasm_bindings.cpp b/experimental/pathkit/pathkit_wasm_bindings.cpp
index c680db1..40772d8 100644
--- a/experimental/pathkit/pathkit_wasm_bindings.cpp
+++ b/experimental/pathkit/pathkit_wasm_bindings.cpp
@@ -134,7 +134,7 @@
                 CHECK_NUM_ARGS(5);
                 x1 = cmds[i++], y1 = cmds[i++];
                 x2 = cmds[i++], y2 = cmds[i++];
-                x3 = cmds[i++]; // width
+                x3 = cmds[i++]; // weight
                 path.conicTo(x1, y1, x2, y2, x3);
                 break;
             case CUBIC:
@@ -249,6 +249,14 @@
     return Op(pathOne, pathTwo, op, &pathOne);
 }
 
+SkPathOrNull EMSCRIPTEN_KEEPALIVE MakeFromOp(const SkPath& pathOne, const SkPath& pathTwo, SkPathOp op) {
+    SkPath out;
+    if (Op(pathOne, pathTwo, op, &out)) {
+        return emscripten::val(out);
+    }
+    return emscripten::val::null();
+}
+
 SkPathOrNull EMSCRIPTEN_KEEPALIVE ResolveBuilder(SkOpBuilder& builder) {
     SkPath path;
     if (builder.resolve(&path)) {
@@ -481,9 +489,9 @@
         .function("_arcTo", &ApplyArcTo)
         //"bezierCurveTo" alias handled in JS bindings
         .function("_close", &ApplyClose)
+        //"closePath" alias handled in JS bindings
         .function("_conicTo", &ApplyConicTo)
         .function("_cubicTo", &ApplyCubicTo)
-        //"closePath" alias handled in JS bindings
 
         .function("_ellipse", &ApplyEllipse)
         .function("_lineTo", &ApplyLineTo)
@@ -530,6 +538,7 @@
         .constructor<>()
 
         .function("add", &SkOpBuilder::add)
+        .function("make", &ResolveBuilder)
         .function("resolve", &ResolveBuilder);
 
     // Without these function() bindings, the function would be exposed but oblivious to
@@ -537,13 +546,14 @@
 
     // Import
     function("FromSVGString", &FromSVGString);
-    function("FromCmds", &FromCmds);
     function("NewPath", &NewPath);
     function("NewPath", &CopyPath);
+    // FromCmds is defined in helper.js to make use of TypedArrays transparent.
+    function("_FromCmds", &FromCmds);
     // Path2D is opaque, so we can't read in from it.
 
     // PathOps
-    function("ApplyPathOp", &ApplyPathOp);
+    function("MakeFromOp", &MakeFromOp);
 
     enum_<SkPathOp>("PathOp")
         .value("DIFFERENCE",         SkPathOp::kDifference_SkPathOp)
@@ -574,7 +584,7 @@
         .field("fRight",  &SkRect::fRight)
         .field("fBottom", &SkRect::fBottom);
 
-    function("MakeLTRBRect", &SkRect::MakeLTRB);
+    function("LTRBRect", &SkRect::MakeLTRB);
 
     // Stroke
     enum_<SkPaint::Join>("StrokeJoin")
diff --git a/experimental/pathkit/tests/path.spec.js b/experimental/pathkit/tests/path.spec.js
index e77be63..852435b 100644
--- a/experimental/pathkit/tests/path.spec.js
+++ b/experimental/pathkit/tests/path.spec.js
@@ -112,13 +112,13 @@
             LoadPathKit.then(() => {
                 // Based on test_bounds_crbug_513799
                 let path = PathKit.NewPath();
-                expect(path.getBounds()).toEqual(PathKit.MakeLTRBRect(0, 0, 0, 0));
+                expect(path.getBounds()).toEqual(PathKit.LTRBRect(0, 0, 0, 0));
                 path.moveTo(-5, -8);
-                expect(path.getBounds()).toEqual(PathKit.MakeLTRBRect(-5, -8, -5, -8));
+                expect(path.getBounds()).toEqual(PathKit.LTRBRect(-5, -8, -5, -8));
                 path.rect(1, 2, 2, 2);
-                expect(path.getBounds()).toEqual(PathKit.MakeLTRBRect(-5, -8, 3, 4));
+                expect(path.getBounds()).toEqual(PathKit.LTRBRect(-5, -8, 3, 4));
                 path.moveTo(1, 2);
-                expect(path.getBounds()).toEqual(PathKit.MakeLTRBRect(-5, -8, 3, 4));
+                expect(path.getBounds()).toEqual(PathKit.LTRBRect(-5, -8, 3, 4));
                 path.delete();
                 done();
             });
@@ -130,9 +130,9 @@
                 let path = PathKit.NewPath();
                 path.moveTo(1, 1);
                 path.quadraticCurveTo(4, 3, 2, 2);
-                expect(path.getBounds()).toEqual(PathKit.MakeLTRBRect(1, 1, 4, 3));
+                expect(path.getBounds()).toEqual(PathKit.LTRBRect(1, 1, 4, 3));
                 ExpectRectsToBeEqual(path.computeTightBounds(),
-                                     PathKit.MakeLTRBRect(1, 1,
+                                     PathKit.LTRBRect(1, 1,
                                         bits2float("0x40333334"),  // 2.8
                                         bits2float("0x40155556"))); // 2.3333333
                 path.delete();
diff --git a/experimental/pathkit/tests/pathops.spec.js b/experimental/pathkit/tests/pathops.spec.js
index 39d879c..273a6a4 100644
--- a/experimental/pathkit/tests/pathops.spec.js
+++ b/experimental/pathkit/tests/pathops.spec.js
@@ -117,11 +117,6 @@
         return e;
     }
 
-    function fromCmds(cmds) {
-        let [ptr, len] = PathKit.loadCmdsTypedArray(cmds);
-        return PathKit.FromCmds(ptr, len);
-    }
-
     it('combines two paths with .op() and matches what we see from C++', function(done) {
         LoadPathKit.then(() => {
             // Test JSON created with:
@@ -135,11 +130,11 @@
                     for (testName of testNames) {
                         let test = json[testName];
 
-                        let path1 = fromCmds(test.p1);
+                        let path1 = PathKit.FromCmds(test.p1);
                         expect(path1).not.toBeNull(`path1 error when loading cmds '${test.p1}'`);
                         path1.setFillType(getFillType(test.fillType1));
 
-                        let path2 = fromCmds(test.p2);
+                        let path2 = PathKit.FromCmds(test.p2);
                         expect(path2).not.toBeNull(`path2 error when loading cmds '${test.p2}'`);
                         path2.setFillType(getFillType(test.fillType2));
 
@@ -149,7 +144,7 @@
                             expect(combined).toBeNull(`Test ${testName} should have not created output, but did`);
                         } else {
                             expect(combined).not.toBeNull();
-                            let expected = fromCmds(test.out);
+                            let expected = PathKit.FromCmds(test.out);
                             // Do a tolerant match.
                             let diff = diffPaths(expected, combined);
                             if (test.expectMatch === 'yes'){
@@ -192,7 +187,7 @@
                     for (testName of testNames) {
                         let test = json[testName];
 
-                        let path = fromCmds(test.path);
+                        let path = PathKit.FromCmds(test.path);
                         expect(path).not.toBeNull(`path1 error when loading cmds '${test.path}'`);
                         path.setFillType(getFillType(test.fillType));
 
@@ -202,7 +197,7 @@
                             expect(simplified).toBeNull(`Test ${testName} should have not created output, but did`);
                         } else {
                             expect(simplified).not.toBeNull();
-                            let expected = fromCmds(test.out);
+                            let expected = PathKit.FromCmds(test.out);
                             // Do a tolerant match.
                             let diff = diffPaths(expected, simplified);
                             if (test.expectMatch === 'yes'){
diff --git a/experimental/pathkit/tests/svg.spec.js b/experimental/pathkit/tests/svg.spec.js
index c28618b..ca3afe2 100644
--- a/experimental/pathkit/tests/svg.spec.js
+++ b/experimental/pathkit/tests/svg.spec.js
@@ -45,8 +45,7 @@
                        [PathKit.LINE_VERB, 5, 295],
                        [PathKit.LINE_VERB, 205, 5],
                        [PathKit.CLOSE_VERB]];
-            let [ptr, len] = PathKit.loadCmdsTypedArray(cmds);
-            let path = PathKit.FromCmds(ptr, len);
+            let path = PathKit.FromCmds(cmds);
 
             let svgStr = path.toSVGString();
             // We output it in terse form, which is different than Wikipedia's version
@@ -64,8 +63,7 @@
                        [PathKit.LINE_VERB, 5, 295],
                        [PathKit.LINE_VERB, "0x15e80300", "0x400004dc"], // 9.37088e-26f, 2.0003f
                        [PathKit.CLOSE_VERB]];
-            let [ptr, len] = PathKit.loadCmdsTypedArray(cmds);
-            let path = PathKit.FromCmds(ptr, len);
+            let path = PathKit.FromCmds(cmds);
 
             let svgStr = path.toSVGString();
             expect(svgStr).toEqual('M9.37088e-26 2.0003L795 5L595 295L5 295L9.37088e-26 2.0003Z');