[PathKit] Add PathOps Op and Simplify tests imported from the C++ tests.

Because of https://skia-review.googlesource.com/c/skia/+/146165 and
https://skia-review.googlesource.com/c/skia/+/146100 we have a way
to turn PathOps tests into JSON, which has input paths (as Cmd arrays),
combination verb and expected output.

In this CL, we make tests from the JSON, compare them to the expected
output and, optionally, create SVGs to visualize the difference if any.

API changes (nothing breaking on release builds):
 - Exposes SkRect as a JS Object.  No need to call delete() on this.
 - expose path.getBounds() and path.computeTightBounds()
 - Remove SkRegion exposure (debug/test only), which was going to be
used for this purpose, but the approach in this CL works fine.
 - Add loadCmdsTypedArray(cmd) helper function to JS [see helper.js].
This was previously known as `floatTypedArrayFrom2D` in the
old shell.html, and is now exposed to avoid clients having to
implement this boilerplate by themselves.
 - Add set/getFillType - mostly for testing the difference between
a Winding and an EvenOdd path.

Bug: skia:8216
Change-Id: I2cd25ce2e1e7f285c79c596678678e62135963f0
Reviewed-on: https://skia-review.googlesource.com/146524
Reviewed-by: Cary Clark <caryclark@google.com>
diff --git a/experimental/pathkit/pathkit_wasm_bindings.cpp b/experimental/pathkit/pathkit_wasm_bindings.cpp
index 9f129b8..5587cad 100644
--- a/experimental/pathkit/pathkit_wasm_bindings.cpp
+++ b/experimental/pathkit/pathkit_wasm_bindings.cpp
@@ -12,7 +12,6 @@
 #include "SkPath.h"
 #include "SkPathOps.h"
 #include "SkRect.h"
-#include "SkRegion.h"
 #include "SkString.h"
 
 #include <emscripten/emscripten.h>
@@ -328,18 +327,17 @@
 }
 
 //========================================================================================
-// Region things
+// Testing things
 //========================================================================================
 
-#ifdef PATHKIT_TESTING
-SkPathOrVal GetBoundaryPathFromRegion(SkRegion& region) {
-    SkPath p;
-    if (region.getBoundaryPath(&p)) {
-        return emscripten::val(p);
-    }
-    return emscripten::val::null();
+// The use case for this is on the JS side is something like:
+//     PathKit.SkBits2FloatUnsigned(parseInt("0xc0a00000"))
+// to have precise float values for tests. In the C++ tests, we can use SkBits2Float because
+// it takes int32_t, but the JS parseInt basically returns an unsigned int. So, we add in
+// this helper which casts for us on the way to SkBits2Float.
+float SkBits2FloatUnsigned(uint32_t floatAsBits) {
+    return SkBits2Float((int32_t) floatAsBits);
 }
-#endif
 
 // Binds the classes to the JS
 //
@@ -391,6 +389,12 @@
         .function("quadTo",
             select_overload<void(SkScalar, SkScalar, SkScalar, SkScalar)>(&SkPath::quadTo))
 
+        // Extra features
+        .function("setFillType", &SkPath::setFillType)
+        .function("getFillType", &SkPath::getFillType)
+        .function("getBounds", &SkPath::getBounds)
+        .function("computeTightBounds", &SkPath::computeTightBounds)
+
         // PathOps
         .function("simplify", &SimplifyPath)
         .function("op", &ApplyPathOp)
@@ -431,42 +435,35 @@
         .value("XOR",                SkPathOp::kXOR_SkPathOp)
         .value("REVERSE_DIFFERENCE", SkPathOp::kReverseDifference_SkPathOp);
 
+    enum_<SkPath::FillType>("FillType")
+        .value("WINDING",            SkPath::FillType::kWinding_FillType)
+        .value("EVENODD",            SkPath::FillType::kEvenOdd_FillType)
+        .value("INVERSE_WINDING",    SkPath::FillType::kInverseWinding_FillType)
+        .value("INVERSE_EVENODD",    SkPath::FillType::kInverseEvenOdd_FillType);
+
     constant("MOVE_VERB",  MOVE);
     constant("LINE_VERB",  LINE);
     constant("QUAD_VERB",  QUAD);
     constant("CUBIC_VERB", CUBIC);
     constant("CLOSE_VERB", CLOSE);
 
+    // A value object is much simpler than a class - it is returned as a JS
+    // object and does not require delete().
+    // https://kripken.github.io/emscripten-site/docs/porting/connecting_cpp_and_javascript/embind.html#value-types
+    value_object<SkRect>("SkRect")
+        .field("fLeft",   &SkRect::fLeft)
+        .field("fTop",    &SkRect::fTop)
+        .field("fRight",  &SkRect::fRight)
+        .field("fBottom", &SkRect::fBottom);
+
+    function("MakeLTRBRect", &SkRect::MakeLTRB);
+
     // coming soon - Stroke
 
     // coming soon - Matrix
 
-    // coming soon - Bounds/Trim
+    // coming soon - Trim
 
-#ifdef PATHKIT_TESTING
-    function("SkBits2Float", &SkBits2Float);
-
-    enum_<SkRegion::Op>("RegionOp")
-        .value("DIFFERENCE",         SkRegion::Op::kDifference_Op)
-        .value("INTERSECT",          SkRegion::Op::kIntersect_Op)
-        .value("UNION",              SkRegion::Op::kUnion_Op)
-        .value("XOR",                SkRegion::Op::kXOR_Op)
-        .value("REVERSE_DIFFERENCE", SkRegion::Op::kReverseDifference_Op)
-        .value("REPLACE",            SkRegion::Op::kReplace_Op);
-
-    class_<SkRegion>("SkRegion")
-        .constructor<>()
-
-        .function("setRect",
-            select_overload<bool(int32_t, int32_t, int32_t, int32_t)>(&SkRegion::setRect))
-        .function("setPath", &SkRegion::setPath)
-        .function("opLTRB",
-            select_overload<bool(int32_t, int32_t, int32_t, int32_t, SkRegion::Op)>(&SkRegion::op))
-        .function("opRegion",
-            select_overload<bool(const SkRegion&, SkRegion::Op)>(&SkRegion::op))
-        .function("opRegionAB",
-            select_overload<bool(const SkRegion&, const SkRegion&, SkRegion::Op)>(&SkRegion::op))
-
-        .function("getBoundaryPath", &GetBoundaryPathFromRegion);
-#endif
+    // Test Utils
+    function("SkBits2FloatUnsigned", &SkBits2FloatUnsigned);
 }