[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');
diff --git a/site/user/modules/pathkit.md b/site/user/modules/pathkit.md
index 8cf1b10..9dacee4 100644
--- a/site/user/modules/pathkit.md
+++ b/site/user/modules/pathkit.md
@@ -7,23 +7,774 @@
 Download the library
 --------------------
 
-See [the npm page](https://www.npmjs.com/package/experimental-pathkit-wasm) for details on downloading
-and getting started.
+See the the npm page for either the [WebAssembly](https://www.npmjs.com/package/experimental-pathkit-wasm) version
+or the [asm.js](https://www.npmjs.com/package/experimental-pathkit-asmjs) version
+for details on downloading and getting started.
+
+WebAssembly has faster load times and better overall performance but is
+currently supported by only Chrome and Firefox (with a flag).
+The asm.js version should run anywhere JavaScript does.
 
 Features
 --------
 
-PathKit is still under rapid development, so the exact API is still changing.
+PathKit is still under rapid development, so the exact API is subject to change.
 
 The primary features are:
 
   - API compatibility (e.g. drop-in replacement) with [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D)
   - Can output to SVG / Canvas / Path2D
-  - Exposes a variety of path effects: <img width=800 src="./PathKit_effects.png"/>
+  - Exposes a variety of path effects:
+
+<style>
+  canvas.patheffect {
+    border: 1px dashed #AAA;
+    width: 200px;
+    height: 200px;
+  }
+</style>
+
+<div id=effects>
+  <canvas class=patheffect id=canvas1 title="Plain: A drawn star with overlapping solid lines"></canvas>
+  <canvas class=patheffect id=canvas2 title="Dash: A drawn star with overlapping dashed lines"></canvas>
+  <canvas class=patheffect id=canvas3 title="Trim: A portion of a drawn star with overlapping solid lines"></canvas>
+  <canvas class=patheffect id=canvas4 title="Simplify: A drawn star with non-overlapping solid lines."></canvas>
+  <canvas class=patheffect id=canvas5 title="Stroke: A drawn star with non-overlapping solid lines stroked at various thicknesses and with square edges"></canvas>
+  <canvas class=patheffect id=canvas6 title="Grow: A drawn star's expanding outline"></canvas>
+  <canvas class=patheffect id=canvas7 title="Shrink: A solid drawn star shrunk down"></canvas>
+  <canvas class=patheffect id=canvasTransform title="Transform: A drawn star moved and rotated by an Affine Matrix"></canvas>
+</div>
+
+<script src="https://unpkg.com/experimental-pathkit-asmjs@0.3.0/bin/pathkit.js"></script>
+<script>
+  try {
+    PathKitInit({
+      locateFile: (file) => 'https://unpkg.com/experimental-pathkit-asmjs@0.3.0/bin/'+file,
+    }).then((PathKit) => {
+      // Code goes here using PathKit
+      PathEffectsExample(PathKit);
+      MatrixTransformExample(PathKit);
+    });
+
+    }
+    catch(error) {
+      console.warn(error, 'falling back to image');
+      docment.getElementById('effects').innerHTML = '<img width=800 src="./PathKit_effects.png"/>'
+  }
+
+  function setCanvasSize(ctx, width, height) {
+    ctx.canvas.width = width;
+    ctx.canvas.height = height;
+  }
+
+  function drawStar(path) {
+    let R = 115.2, C = 128.0;
+    path.moveTo(C + R + 22, C);
+    for (let i = 1; i < 8; i++) {
+      let a = 2.6927937 * i;
+      path.lineTo(C + R * Math.cos(a) + 22, C + R * Math.sin(a));
+    }
+    path.closePath();
+    return path;
+  }
+
+  function PathEffectsExample(PathKit) {
+    let effects = [
+      // no-op
+      (path) => path,
+      // dash
+      (path, counter) => path.dash(10, 3, counter/5),
+      // trim (takes optional 3rd param for returning the trimmed part
+      // or the complement)
+      (path, counter) => path.trim((counter/100) % 1, 0.8, false),
+      // simplify
+      (path) => path.simplify(),
+      // stroke
+      (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, counter) => {
+        let orig = path.copy();
+        path.stroke({
+          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", "Grow", "Shrink"];
+
+    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, counter);
+
+        let ctx = document.getElementById(`canvas${i+1}`).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();
+      }
+      window.requestAnimationFrame(frame);
+    }
+    window.requestAnimationFrame(frame);
+  }
+
+  function MatrixTransformExample(PathKit) {
+    // Creates an animated star that twists and moves.
+    let ctx = document.getElementById('canvasTransform').getContext('2d');
+    setCanvasSize(ctx, 300, 300);
+    ctx.strokeStyle = '#3c597a';
+
+    let path = drawStar(PathKit.NewPath());
+    // TODO(kjlubick): Perhaps expose some matrix helper functions to allow
+    // clients to build their own matrices like this?
+    // These matrices represent a 2 degree rotation and a 1% scale factor.
+    let scaleUp = [1.0094, -0.0352,  3.1041,
+                   0.0352,  1.0094, -6.4885,
+                   0     ,  0      , 1];
+
+    let scaleDown = [ 0.9895, 0.0346, -2.8473,
+                     -0.0346, 0.9895,  6.5276,
+                      0     , 0     ,  1];
+
+    let i = 0;
+    function frame(){
+      i++;
+      if (Math.round(i/100) % 2) {
+        path.transform(scaleDown);
+      } else {
+        path.transform(scaleUp);
+      }
+
+      ctx.clearRect(0, 0, 300, 300);
+      ctx.stroke(path.toPath2D());
+
+      ctx.font = '42px monospace';
+      let x = 150-ctx.measureText('Transform').width/2;
+      ctx.strokeText('Transform', x, 290);
+
+      window.requestAnimationFrame(frame);
+    }
+    window.requestAnimationFrame(frame);
+  }
+</script>
 
 
-Example code
+Example Code
 ------------
 The best place to look for examples on how to use PathKit would be in the
-[example.html](https://github.com/google/skia/blob/master/experimental/pathkit/npm-wasm/example.html#L45)
-which comes in the npm package.
\ No newline at end of file
+[example.html](https://github.com/google/skia/blob/master/experimental/pathkit/npm-wasm/example.html#L45),
+which comes in the npm package.
+
+
+API
+----
+
+The primary feature of the library is the `SkPath` object. It can be created:
+
+ - From the SVG string of a path `PathKit.FromSVGString(str)`
+ - From a 2D array of verbs and arguments `PathKit.FromCmds(cmds)`
+ - From `PathKit.NewPath()` (It will be blank)
+ - As a copy of an existing `SkPath` with `path.copy()` or `PathKit.NewPath(path)`
+
+It can be exported as:
+
+ - An SVG string `path.toSVGString()`
+ - A [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D) object `path.toPath2D()`
+ - Directly to a canvas 2D context `path.toCanvas(ctx)`
+ - A 2D array of verbs and arguments `path.toCmds()`
+
+Once an SkPath object has been made, it can be interacted with in the following ways:
+
+ - expanded by any of the Path2D operations (`moveTo`, `lineTo`, `rect`, `arc`, etc)
+ - combined with other paths using `op` or `PathKit.MakeFromOp(p1, p2, op)`.  For example, `path1.op(path2, PathKit.PathOp.INTERSECT)` will set path1 to be the area represented by where path1 and path2 overlap (intersect). `PathKit.MakeFromOp(path1, path2, PathKit.PathOp.INTERSECT)` will do the same but returned as a new `SkPath` object.
+ - adjusted with some of the effects (`trim`, `dash`, `stroke`, etc)
+
+
+**Important**: Any objects (`SkPath`, `SkOpBuilder`, etc) that are created must be cleaned up with `path.delete()` when they
+leave the scope to avoid leaking the memory in the WASM heap. This includes any of the constructors, `copy()`,
+or any function prefixed with "make".
+
+
+### PathKit ###
+
+#### `FromSVGString(str)` ####
+**str** - `String` representing an [SVGPath](https://www.w3schools.com/graphics/svg_path.asp)
+
+Returns an `SkPath` with the same verbs and arguments as the SVG string, or `null` on a failure.
+
+Example:
+
+    let path = PathKit.FromSVGString('M150 0 L75 200 L225 200 Z');
+    // path represents a triangle
+    // don't forget to do path.delete() when it goes out of scope.
+
+#### `FromCmds(cmds)` ####
+**cmds** - `Array<Array<Number>>`, a 2D array of commands, where a command is a verb
+           followed by its arguments.
+
+Returns an `SkPath` with the verbs and arguments from the list or `null` on a failure.
+
+This can be faster than calling `.moveTo()`, `.lineTo()`, etc many times.
+
+Example:
+
+    let cmds = [
+        [PathKit.MOVE_VERB, 0, 10],
+        [PathKit.LINE_VERB, 30, 40],
+        [PathKit.QUAD_VERB, 20, 50, 45, 60],
+    ];
+    let path = PathKit.FromCmds(cmds);
+    // path is the same as if a user had done
+    // let path = PathKit.NewPath().moveTo(0, 10).lineTo(30, 40).quadTo(20, 50, 45, 60);
+    // don't forget to do path.delete() when it goes out of scope.
+
+#### `NewPath()` ####
+
+Returns an empty `SkPath` object.
+
+Example:
+
+    let path = PathKit.NewPath();
+    path.moveTo(0, 10)
+        .lineTo(30, 40)
+        .quadTo(20, 50, 45, 60);
+    // don't forget to do path.delete() when it goes out of scope.
+    // Users can also do let path = new PathKit.SkPath();
+
+#### `NewPath(pathToCopy)` ####
+**pathToCopy** - SkPath, a path to make a copy of.
+
+Returns a `SkPath` that is a copy of the passed in `SkPath`.
+
+Example:
+
+    let otherPath = ...;
+    let clone = PathKit.NewPath(otherPath);
+    clone.simplify();
+    // don't forget to do clone.delete() when it goes out of scope.
+    // Users can also do let clone = new PathKit.SkPath(otherPath);
+    // or let clone = otherPath.copy();
+
+#### `MakeFromOp(pathOne, pathTwo, op)` ####
+**pathOne** - `SkPath`, a path. <br>
+**pathTwo** - `SkPath`, a path. <br>
+**op** - `PathOp`, an op to apply
+
+Returns a new `SkPath` that is the result of applying the given PathOp to the first and second
+path (order matters).
+
+Example:
+
+    let pathOne = PathKit.NewPath().moveTo(0, 20).lineTo(10, 10).lineTo(20, 20).close();
+    let pathTwo = PathKit.NewPath().moveTo(10, 20).lineTo(20, 10).lineTo(30, 20).close();
+    let mountains = PathKit.MakeFromOp(pathOne, pathTwo, PathKit.PathOp.UNION);
+    // don't forget to do mountains.delete() when it goes out of scope.
+    // Users can also do pathOne.op(pathTwo, PathKit.PathOp.UNION);
+    // to have the resulting path be stored to pathOne and avoid allocating another object.
+
+### SkPath (object) ###
+
+#### `addPath(otherPath)` ####
+**otherPath** - `SkPath`, a path to append to this path
+
+Adds the given path to `this` and then returns `this` for chaining purposes.
+
+#### `addPath(otherPath, transform)` ####
+**otherPath** - `SkPath`, a path to append to this path. <br>
+**transform** - [SVGMatrix](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix),
+                a transform to apply to otherPath before appending it.
+
+Adds the given path to `this` after applying the transform and then returns `this` for
+chaining purposes. See [Path2D.addPath()](https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath)
+for more details.
+
+#### `addPath(otherPath, a, b, c, d, e, f)` ####
+**otherPath** - `SkPath`, a path to append to this path. <br>
+**a, b, c, d, e, f** - `Number`, the six components of an
+                       [SVGMatrix](https://developer.mozilla.org/en-US/docs/Web/API/SVGMatrix),
+                       which define the transform to apply to otherPath before appending it.
+
+Adds the given path to `this` after applying the transform and then returns `this` for
+chaining purposes. See [Path2D.addPath()](https://developer.mozilla.org/en-US/docs/Web/API/Path2D/addPath)
+for more details.
+
+Example:
+
+    let box = PathKit.NewPath().rect(0, 0, 100, 100);
+    let moreBoxes = PathKit.NewPath();
+    // add box un-transformed (i.e. at 0, 0)
+    moreBoxes.addPath(box)
+    // the params fill out a 2d matrix like:
+    //     a c e
+    //     b d f
+    //     0 0 1
+    // add box 300 points to the right
+             .addPath(box, 1, 0, 0, 1, 300, 0)
+    // add a box shrunk by 50% in both directions
+             .addPath(box, 0.5, 0, 0, 0.5, 0, 0);
+    // moreBoxes now has 3 paths appended to it
+
+#### `addPath(otherPath, scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2)` ####
+**otherPath** - `SkPath`, a path to append to this path. <br>
+**scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2** -
+                       `Number`, the nine components of an
+                       [Affine Matrix](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations),
+                       which define the transform to apply to otherPath before appending it.
+
+Adds the given path to `this` after applying the transform and then returns `this` for
+chaining purposes.
+
+Example:
+
+    let box = PathKit.NewPath().rect(0, 0, 100, 100);
+    let moreBoxes = PathKit.NewPath();
+    // add box un-transformed (i.e. at 0, 0)
+    moreBoxes.addPath(box)
+    // add box 300 points to the right
+             .addPath(box, 1, 0, 0,
+                           0, 1, 300,
+                           0, 0 ,1)
+    // add a box shrunk by 50% in both directions
+             .addPath(box, 0.5, 0,   0,
+                           0,   0.5, 0,
+                           0,   0,   1)
+    // moreBoxes now has 3 paths appended to it
+
+#### `arc(x, y, radius, startAngle, endAngle, ccw=false)` ####
+**x, y** - `Number`, The coordinates of the arc's center. <br>
+**radius** - `Number`, The radius of the arc. <br>
+**startAngle, endAngle** - `Number`, the start and end of the angle, measured
+                           clockwise from the positive x axis and in radians. <br>
+**ccw** - `Boolean`, optional argument specifying if the arc should be drawn
+          counter-clockwise between **startAngle** and **endAngle** instead of
+          clockwise, the default.
+
+Adds the described arc to `this` then returns `this` for
+chaining purposes.  See [Path2D.arc()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arc)
+for more details.
+
+Example:
+
+    let path = PathKit.NewPath();
+    path.moveTo(20, 120);
+        .arc(20, 120, 18, 0, 1.75 * Math.PI);
+        .lineTo(20, 120);
+    // path looks like a pie with a 1/8th slice removed.
+
+#### `arcTo(x1, y1, x2, y2, radius)` ####
+**x1, y1, x2, y2** - `Number`, The coordinates defining the control points. <br>
+**radius** - `Number`, The radius of the arc.
+
+Adds the described arc to `this` (appending a line, if needed) then returns `this` for
+chaining purposes.  See [Path2D.arcTo()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/arcTo)
+for more details.
+
+#### `close()` or `closePath()` ####
+Returns the pen to the start of the current sub-path, then returns `this` for
+chaining purposes.  See [Path2D.closePath()](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/closePath)
+for more details.
+
+#### `computeTightBounds()` ####
+
+Returns an `SkRect` that represents the minimum and maximum area of
+`this` path. See [SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_computeTightBounds)
+for more details.
+
+#### `conicTo(x1, y1, x2, y2, w)` ####
+**x1, y1, x2, y2** - `Number`, The coordinates defining the control point and the end point. <br>
+**w** - `Number`, The weight of the conic.
+
+Adds the described conic line to `this` (appending a line, if needed) then returns `this` for
+chaining purposes. See [SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_conicTo)
+for more details.
+
+#### `copy()` ####
+
+Return a copy of `this` path.
+
+#### `cubicTo(cp1x, cp1y, cp2x, cp2y, x, y)` or `bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y)` ####
+**cp1x, cp1y, cp2x, cp2y** - `Number`, The coordinates defining the control points. <br>
+**x,y** - `Number`, The coordinates defining the end point
+
+Adds the described cubic line to `this` (appending a line, if needed) then returns `this` for
+chaining purposes. See [Path2D.bezierCurveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/bezierCurveTo)
+for more details.
+
+#### `dash(on, off, phase)` ####
+**on, off** - `Number`, The number of pixels the dash should be on (drawn) and off (blank). <br>
+**phase** - `Number`, The number of pixels the on/off should be offset (mod **on** + **off**)
+
+Applies a dashed path effect to `this` then returns `this` for chaining purposes.
+See the "Dash" effect above for a visual example.
+
+Example:
+
+    let box = PathKit.NewPath().rect(0, 0, 100, 100);
+    box.dash(20, 10, 3);
+    // box is now a dashed rectangle that will draw for 20 pixels, then
+    // stop for 10 pixels.  Since phase is 3, the first line won't start
+    // at (0, 0), but 3 pixels around the path (3, 0)
+
+#### `ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, ccw=false)` ####
+**x, y** - `Number`, The coordinates of the center of the ellipse. <br>
+**radiusX, radiusY** - `Number`, The radii in the X and Y directions. <br>
+**rotation** - `Number`, The rotation in radians of this ellipse. <br>
+**startAngle, endAngle** - `Number`, the starting and ending angles of which to draw,
+                            measured in radians from the positive x axis. <br>
+**ccw** - `Boolean`, optional argument specifying if the ellipse should be drawn
+          counter-clockwise between **startAngle** and **endAngle** instead of
+          clockwise, the default.
+
+Adds the described ellipse to `this` then returns `this` for chaining purposes.
+See [Path2D.ellipse](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/ellipse)
+for more details.
+
+#### `equals(otherPath)` ####
+**otherPath** - `SkPath`, the path to compare to.
+
+Returns a `Boolean` value based on if `this` path is equal
+to **otherPath**.
+
+#### `getBounds()` ####
+
+Returns an `SkRect` that represents the minimum and maximum area of
+`this` path. See [SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_getBounds)
+for more details.
+
+#### `getFillType()` ####
+
+Returns a `FillType` based on what this path is. This defaults to
+`PathKit.FillType.WINDING`, but may change with `op()` or `simplify()`.
+
+Clients will typically want `getFillTypeString()` because that value
+can be passed directly to an SVG or Canvas.
+
+#### `getFillTypeString()` ####
+
+Returns a `String` representing the fillType of `this` path.
+The values are either "nonzero" or "evenodd".
+
+Example:
+
+    let path = ...;
+    let ctx = document.getElementById('canvas1').getContext('2d');
+    ctx.strokeStyle = 'green';
+    ctx.fill(path.toPath2D(), path.getFillTypeString());
+
+#### `moveTo(x, y)` ####
+**x, y** - `Number`, The coordinates of where the pen should be moved to.
+
+Moves the pen (without drawing) to the given coordinates then returns `this` for chaining purposes.
+See [Path2D.moveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/moveTo)
+for more details.
+
+#### `lineTo(x, y)` ####
+**x, y** - `Number`, The coordinates of where the pen should be moved to.
+
+Draws a straight line to the given coordinates then returns `this` for chaining purposes.
+See [Path2D.lineTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineTo)
+for more details.
+
+#### `op(otherPath, operation)` ####
+**otherPath** - `SkPath`, The other path to be combined with `this`. <br>
+**operation** - `PathOp`, The operation to apply to the two paths.
+
+Combines otherPath into `this` path with the given operation and returns `this`
+for chaining purposes.
+
+Example:
+
+    let pathOne = PathKit.NewPath().moveTo(0, 20).lineTo(10, 10).lineTo(20, 20).close();
+    let pathTwo = PathKit.NewPath().moveTo(10, 20).lineTo(20, 10).lineTo(30, 20).close();
+    // Combine the two triangles to look like two mountains
+    let mountains = pathOne.copy().op(pathOne, pathTwo, PathKit.PathOp.UNION);
+    // set pathOne to be the small triangle where pathOne and pathTwo overlap
+    pathOne.op(pathOne, pathTwo, PathKit.PathOp.INTERSECT);
+    // since copy() was called, don't forget to call delete() on mountains.
+
+#### `quadTo(cpx, cpy, x, y)` or `quadraticCurveTo(cpx, cpy, x, y)` ####
+**cpx, cpy** - `Number`, The coordinates for the control point. <br>
+**x, y** - `Number`, The coordinates for the end point.
+
+Draws a quadratic Bézier curve with the given coordinates then returns `this` for chaining purposes.
+See [Path2D.quadraticCurveTo](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/quadraticCurveTo)
+for more details.
+
+#### `rect(x, y, w, h)` ####
+**x, y** - `Number`, The coordinates of the upper-left corner of the rectangle. <br>
+**w, h** - `Number`, The width and height of the rectangle
+
+Draws a rectangle on `this`, then returns `this` for chaining purposes.
+See [Path2D.rect](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/rect)
+for more details.
+
+#### `setFillType(fillType)` ####
+**fillType** - `FillType`, the new fillType.
+
+Set the fillType of the path. See [SkPath reference](https://skia.org/user/api/SkPath_Reference#SkPath_FillType)
+for more details.
+
+#### `simplify()` ####
+Set `this` path to a set of *non-overlapping* contours that describe the same area
+as the original path. See the "Simplify" effect above for a visual example.
+
+#### `stroke(opts)` ####
+**opts** - `StrokeOpts`, contains the options for stroking.
+
+
+Strokes `this` path out with the given options. This can be used for a variety of
+effects.  See the "Stroke", "Grow", and "Shrink" effects above for visual examples.
+
+Example:
+
+    let box = PathKit.NewPath().rect(0, 0, 100, 100);
+    // Stroke the path with width 10 and rounded corners
+    let rounded = box.copy().stroke({width: 10, join: PathKit.StrokeJoin.ROUND});
+    // Grow effect, that is, a 20 pixel expansion around the box.
+    let grow = box.copy().stroke({width: 20}).op(box, PathKit.PathOp.DIFFERENCE);
+    // Shrink effect, in which we subtract away from the original
+    let simplified = box.copy().simplify(); // sometimes required for complicated paths
+    let shrink = box.copy().stroke({width: 15, cap: PathKit.StrokeCap.BUTT})
+                           .op(simplified, PathKit.PathOp.REVERSE_DIFFERENCE);
+    // Don't forget to call delete() on each of the copies!
+
+#### `toCanvas(ctx)` ####
+**ctx** - `Canvas2DContext`, Canvas on which to draw the path.
+
+Draws `this` path on the passed in
+[Canvas Context](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D).
+
+Example:
+
+    let box = PathKit.NewPath().rect(0, 0, 100, 100);
+    let ctx = document.getElementById('canvas1').getContext('2d');
+    ctx.strokeStyle = 'green';
+    ctx.beginPath();
+    box.toCanvas(ctx);
+    ctx.stroke();  // could also ctx.fill()
+
+#### `toCmds()` ####
+
+Returns a 2D Array of verbs and args. See `PathKit.FromCmds()` for
+more details.
+
+#### `toPath2D()` ####
+
+Returns a [Path2D](https://developer.mozilla.org/en-US/docs/Web/API/Path2D) object
+that has the same operations as `this` path.
+
+Example:
+
+    let box = PathKit.NewPath().rect(0, 0, 100, 100);
+    let ctx = document.getElementById('canvas1').getContext('2d');
+    ctx.strokeStyle = 'green';
+    ctx.stroke(box.toPath2D());
+
+#### `toSVGString()` ####
+
+Returns a `String` representing an [SVGPath](https://www.w3schools.com/graphics/svg_path.asp) based on `this` path.
+
+Example:
+
+    let box = PathKit.NewPath().rect(0, 0, 100, 100);
+    let svg = document.getElementById('svg1');
+    let newPath = document.createElementNS('http://www.w3.org/2000/svg', 'path');
+    newPath.setAttribute('stroke', 'green');
+    newPath.setAttribute('fill', 'white');
+    newPath.setAttribute('d', box.toSVGString());
+    svg.appendChild(newPath);
+
+#### `transform(matr)` ####
+**matr** - `SkMatrix`, i.e. an `Array<Number>` of the nine numbers of an Affine Transform Matrix.
+
+Applies the specified [transform](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations)
+to `this` and then returns `this` for chaining purposes.
+
+#### `transform(scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2)` ####
+**scaleX, skewX, transX, skewY, scaleY, transY, pers0, pers1, pers2** -
+        `Number`, the nine numbers of an Affine Transform Matrix.
+
+Applies the specified [transform](https://en.wikipedia.org/wiki/Transformation_matrix#Affine_transformations)
+to `this` and then returns `this` for chaining purposes.
+
+Example:
+
+    let path = PathKit.NewPath().rect(0, 0, 100, 100);
+    // scale up the path by 5x
+    path.transform([5, 0, 0,
+                    0, 5, 0,
+                    0, 0, 1]);
+    // move the path 75 px to the right.
+    path.transform(1, 0, 75,
+                   0, 1, 0,
+                   0, 0, 1);
+
+#### `trim(startT, stopT, isComplement=false)` ####
+**startT, stopT** - `Number`, values in [0, 1] that indicate the start and stop
+                    "percentages" of the path to draw <br>
+**isComplement** - `Boolean`, If the complement of the trimmed section should
+                    be drawn instead of the areas between **startT** and **stopT**.
+
+Sets `this` path to be a subset of the original path, then returns `this` for chaining purposes.
+See the "Trim" effect above for a visual example.
+
+Example:
+
+    let box = PathKit.NewPath().rect(0, 0, 100, 100);
+    box.trim(0.25, 1.0);
+    // box is now the 3 segments that look like a U
+    // (the top segment has been removed).
+
+
+### SkOpBuilder (object)  ###
+This object enables chaining multiple PathOps together.
+Create one with `let builder = new PathKit.SkOpBuilder();`
+When created, the internal state is "empty path".
+Don't forget to call `delete()` on both the builder and the result
+of `resolve()`
+
+#### `add(path, operation)` ####
+**path** - `SkPath`, The path to be combined with the given rule. <br>
+**operation** - `PathOp`, The operation to apply to the two paths.
+
+Adds a path and the operand to the builder.
+
+#### `make()` or `resolve()` ####
+
+Creates and returns a new `SkPath` based on all the given paths
+and operands.
+
+Don't forget to call `.delete()` on the returned path when it goes out of scope.
+
+
+### SkMatrix (struct) ###
+`SkMatrix` translates between a C++ struct and a JS Array.
+It basically takes a nine element 1D Array and turns it into a
+3x3 2D Affine Matrix.
+
+### SkRect (struct) ###
+
+`SkRect` translates between a C++ struct and a JS Object with
+the following keys (all values are `Number`:
+
+  - **fLeft**: x coordinate of top-left corner
+  - **fTop**: y coordinate of top-left corner
+  - **fRight**: x coordinate of bottom-right corner
+  - **fBottom**: y coordinate of bottom-rightcorner
+
+### StrokeOpts (struct) ###
+`StrokeOpts` translates between a C++ struct and a JS Object with
+the following keys:
+
+  - **width**, `Number` the width of the lines of the path. Default 1.
+  - **miter_limit**, `Number`, the miter limit. Defautl 4. See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#Miter_Limit) for more details.
+  - **join**, `StrokeJoin`, the join to use. Default `PathKit.StrokeJoin.MITER`.
+See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#SkPaint_Join) for more details.
+  - **cap**, `StrokeCap`, the cap to use. Default `PathKit.StrokeCap.BUTT`.
+See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#Stroke_Cap) for more details.
+
+### PathOp (enum) ###
+The following enum values are exposed. They are essentially constant
+objects, differentiated by thier `.value` property.
+
+  - `PathKit.PathOp.DIFFERENCE`
+  - `PathKit.PathOp.INTERSECT`
+  - `PathKit.PathOp.REVERSE_DIFFERENCE`
+  - `PathKit.PathOp.UNION`
+  - `PathKit.PathOp.XOR`
+
+These are used in `PathKit.MakeFromOp()` and `SkPath.op()`.
+
+### FillType (enum) ###
+The following enum values are exposed. They are essentially constant
+objects, differentiated by thier `.value` property.
+
+  - `PathKit.FillType.WINDING` (also known as nonzero)
+  - `PathKit.FillType.EVENODD`
+  - `PathKit.FillType.INVERSE_WINDING`
+  - `PathKit.FillType.INVERSE_EVENODD`
+
+These are used by `SkPath.getFillType()` and `SkPath.setFillType()`, but
+generally clients will want `SkPath.getFillTypeString()`.
+
+### StrokeJoin (enum) ###
+The following enum values are exposed. They are essentially constant
+objects, differentiated by thier `.value` property.
+
+  - `PathKit.StrokeJoin.MITER`
+  - `PathKit.StrokeJoin.ROUND`
+  - `PathKit.StrokeJoin.BEVEL`
+
+See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#SkPaint_Join) for more details.
+
+### StrokeCap (enum) ###
+The following enum values are exposed. They are essentially constant
+objects, differentiated by thier `.value` property.
+
+  - `PathKit.StrokeCap.BUTT`
+  - `PathKit.StrokeCap.ROUND`
+  - `PathKit.StrokeCap.SQUARE`
+
+See [SkPaint reference](https://skia.org/user/api/SkPaint_Reference#Stroke_Cap) for more details.
+
+### Constants ###
+The following constants are exposed:
+
+  - `PathKit.MOVE_VERB` = 0
+  - `PathKit.LINE_VERB` = 1
+  - `PathKit.QUAD_VERB` = 2
+  - `PathKit.CONIC_VERB` = 3
+  - `PathKit.CUBIC_VERB` = 4
+  - `PathKit.CLOSE_VERB` = 5
+
+These are only needed for `PathKit.FromCmds()`.
+
+### Functions for testing only ###
+
+#### `PathKit.LTRBRect(left, top, right, bottom)` ####
+**left** - `Number`, x coordinate of top-left corner of the `SkRect`. <br>
+**top** - `Number`, y coordinate of top-left corner of the `SkRect`. <br>
+**right** - `Number`, x coordinate of bottom-right corner of the `SkRect`. <br>
+**bottom** - `Number`, y coordinate of bottom-right corner of the `SkRect`.
+
+Returns an `SkRect` object with the given params.
+
+#### `SkPath.dump()` ####
+
+Prints all the verbs and arguments to the console.
+Only available on Debug and Test builds.