[skottie] Switch to RapidJSON

- pull latest RapidJSON under third_party/externals/rapidjson
  (note: and older RS version is already pulled as part of angle2,
  and it is also checked in G3)

- add a thin Json porting layer (SkottieJson) to isolate RS
  idiosyncrasies

- convert Skottie to use the new helpers

- parse the DOM in-place (based on local experiments this is the
  fastest method)

Ta-da: Skottie now parses JSON ~10x faster!


Change-Id: Ida9099638f88ed025fee83055c8cd8680ee27176
Reviewed-on: https://skia-review.googlesource.com/125744
Commit-Queue: Florin Malita <fmalita@chromium.org>
Reviewed-by: Mike Reed <reed@google.com>
diff --git a/experimental/skottie/Skottie.cpp b/experimental/skottie/Skottie.cpp
index c4e964d..99549bf 100644
--- a/experimental/skottie/Skottie.cpp
+++ b/experimental/skottie/Skottie.cpp
@@ -8,10 +8,9 @@
 #include "Skottie.h"
 
 #include "SkCanvas.h"
-#include "SkJSONCPP.h"
 #include "SkottieAdapter.h"
 #include "SkottieAnimator.h"
-#include "SkottieParser.h"
+#include "SkottieJson.h"
 #include "SkottieValue.h"
 #include "SkData.h"
 #include "SkImage.h"
@@ -53,7 +52,7 @@
 
 namespace {
 
-using AssetMap = SkTHashMap<SkString, const Json::Value*>;
+using AssetMap = SkTHashMap<SkString, json::ValueRef>;
 
 struct AttachContext {
     const ResourceProvider& fResources;
@@ -62,13 +61,13 @@
     sksg::AnimatorList&     fAnimators;
 };
 
-bool LogFail(const Json::Value& json, const char* msg) {
-    const auto dump = json.toStyledString();
+bool LogFail(const json::ValueRef& json, const char* msg) {
+    const auto dump = json.toString();
     LOG("!! %s: %s", msg, dump.c_str());
     return false;
 }
 
-sk_sp<sksg::Matrix> AttachMatrix(const Json::Value& t, AttachContext* ctx,
+sk_sp<sksg::Matrix> AttachMatrix(const json::ValueRef& t, AttachContext* ctx,
                                         sk_sp<sksg::Matrix> parentMatrix) {
     if (!t.isObject())
         return nullptr;
@@ -88,13 +87,13 @@
                 adapter->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
             });
 
-    auto* jrotation = &t["r"];
-    if (jrotation->isNull()) {
+    auto jrotation = t["r"];
+    if (jrotation.isNull()) {
         // 3d rotations have separate rx,ry,rz components.  While we don't fully support them,
         // we can still make use of rz.
-        jrotation = &t["rz"];
+        jrotation = t["rz"];
     }
-    auto rotation_attached = BindProperty<ScalarValue>(*jrotation, &ctx->fAnimators,
+    auto rotation_attached = BindProperty<ScalarValue>(jrotation, &ctx->fAnimators,
             [adapter](const ScalarValue& r) {
                 adapter->setRotation(r);
             });
@@ -120,7 +119,7 @@
     return matrix;
 }
 
-sk_sp<sksg::RenderNode> AttachOpacity(const Json::Value& jtransform, AttachContext* ctx,
+sk_sp<sksg::RenderNode> AttachOpacity(const json::ValueRef& jtransform, AttachContext* ctx,
                                       sk_sp<sksg::RenderNode> childNode) {
     if (!jtransform.isObject() || !childNode)
         return childNode;
@@ -129,8 +128,8 @@
     // nodes for the extremely common case of static opaciy == 100.
     const auto& opacity = jtransform["o"];
     if (opacity.isObject() &&
-        !ParseDefault(opacity["a"], true) &&
-        ParseDefault(opacity["k"], -1) == 100) {
+        !opacity["a"].toDefault(true) &&
+        opacity["k"].toDefault<int>(-1) == 100) {
         // Ignoring static full opacity.
         return childNode;
     }
@@ -145,9 +144,9 @@
     return std::move(opacityNode);
 }
 
-sk_sp<sksg::RenderNode> AttachComposition(const Json::Value&, AttachContext* ctx);
+sk_sp<sksg::RenderNode> AttachComposition(const json::ValueRef&, AttachContext* ctx);
 
-sk_sp<sksg::Path> AttachPath(const Json::Value& jpath, AttachContext* ctx) {
+sk_sp<sksg::Path> AttachPath(const json::ValueRef& jpath, AttachContext* ctx) {
     auto path_node = sksg::Path::Make();
     return BindProperty<ShapeValue>(jpath, &ctx->fAnimators,
         [path_node](const ShapeValue& p) {
@@ -157,13 +156,13 @@
         : nullptr;
 }
 
-sk_sp<sksg::GeometryNode> AttachPathGeometry(const Json::Value& jpath, AttachContext* ctx) {
+sk_sp<sksg::GeometryNode> AttachPathGeometry(const json::ValueRef& jpath, AttachContext* ctx) {
     SkASSERT(jpath.isObject());
 
     return AttachPath(jpath["ks"], ctx);
 }
 
-sk_sp<sksg::GeometryNode> AttachRRectGeometry(const Json::Value& jrect, AttachContext* ctx) {
+sk_sp<sksg::GeometryNode> AttachRRectGeometry(const json::ValueRef& jrect, AttachContext* ctx) {
     SkASSERT(jrect.isObject());
 
     auto rect_node = sksg::RRect::Make();
@@ -189,7 +188,7 @@
     return std::move(rect_node);
 }
 
-sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const Json::Value& jellipse, AttachContext* ctx) {
+sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const json::ValueRef& jellipse, AttachContext* ctx) {
     SkASSERT(jellipse.isObject());
 
     auto rect_node = sksg::RRect::Make();
@@ -213,7 +212,7 @@
     return std::move(rect_node);
 }
 
-sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const Json::Value& jstar, AttachContext* ctx) {
+sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const json::ValueRef& jstar, AttachContext* ctx) {
     SkASSERT(jstar.isObject());
 
     static constexpr PolyStarAdapter::Type gTypes[] = {
@@ -221,7 +220,7 @@
         PolyStarAdapter::Type::kPoly, // "sy": 2
     };
 
-    const auto type = ParseDefault(jstar["sy"], 0) - 1;
+    const auto type = jstar["sy"].toDefault<int>(0) - 1;
     if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gTypes))) {
         LogFail(jstar, "Unknown polystar type");
         return nullptr;
@@ -262,7 +261,7 @@
     return std::move(path_node);
 }
 
-sk_sp<sksg::Color> AttachColor(const Json::Value& obj, AttachContext* ctx) {
+sk_sp<sksg::Color> AttachColor(const json::ValueRef& obj, AttachContext* ctx) {
     SkASSERT(obj.isObject());
 
     auto color_node = sksg::Color::Make(SK_ColorBLACK);
@@ -274,21 +273,21 @@
     return color_attached ? color_node : nullptr;
 }
 
-sk_sp<sksg::Gradient> AttachGradient(const Json::Value& obj, AttachContext* ctx) {
+sk_sp<sksg::Gradient> AttachGradient(const json::ValueRef& obj, AttachContext* ctx) {
     SkASSERT(obj.isObject());
 
     const auto& stops = obj["g"];
     if (!stops.isObject())
         return nullptr;
 
-    const auto stopCount = ParseDefault(stops["p"], -1);
+    const auto stopCount = stops["p"].toDefault<int>(-1);
     if (stopCount < 0)
         return nullptr;
 
     sk_sp<sksg::Gradient> gradient_node;
     sk_sp<GradientAdapter> adapter;
 
-    if (ParseDefault(obj["t"], 1) == 1) {
+    if (obj["t"].toDefault<int>(1) == 1) {
         auto linear_node = sksg::LinearGradient::Make();
         adapter = sk_make_sp<LinearGradientAdapter>(linear_node, stopCount);
         gradient_node = std::move(linear_node);
@@ -316,7 +315,7 @@
     return gradient_node;
 }
 
-sk_sp<sksg::PaintNode> AttachPaint(const Json::Value& jpaint, AttachContext* ctx,
+sk_sp<sksg::PaintNode> AttachPaint(const json::ValueRef& jpaint, AttachContext* ctx,
                                    sk_sp<sksg::PaintNode> paint_node) {
     if (paint_node) {
         paint_node->setAntiAlias(true);
@@ -331,7 +330,7 @@
     return paint_node;
 }
 
-sk_sp<sksg::PaintNode> AttachStroke(const Json::Value& jstroke, AttachContext* ctx,
+sk_sp<sksg::PaintNode> AttachStroke(const json::ValueRef& jstroke, AttachContext* ctx,
                                     sk_sp<sksg::PaintNode> stroke_node) {
     SkASSERT(jstroke.isObject());
 
@@ -347,14 +346,14 @@
     if (!width_attached)
         return nullptr;
 
-    stroke_node->setStrokeMiter(ParseDefault(jstroke["ml"], 4.0f));
+    stroke_node->setStrokeMiter(jstroke["ml"].toDefault(4.0f));
 
     static constexpr SkPaint::Join gJoins[] = {
         SkPaint::kMiter_Join,
         SkPaint::kRound_Join,
         SkPaint::kBevel_Join,
     };
-    stroke_node->setStrokeJoin(gJoins[SkTPin<int>(ParseDefault(jstroke["lj"], 1) - 1,
+    stroke_node->setStrokeJoin(gJoins[SkTPin<int>(jstroke["lj"].toDefault<int>(1) - 1,
                                                   0, SK_ARRAY_COUNT(gJoins) - 1)]);
 
     static constexpr SkPaint::Cap gCaps[] = {
@@ -362,38 +361,38 @@
         SkPaint::kRound_Cap,
         SkPaint::kSquare_Cap,
     };
-    stroke_node->setStrokeCap(gCaps[SkTPin<int>(ParseDefault(jstroke["lc"], 1) - 1,
+    stroke_node->setStrokeCap(gCaps[SkTPin<int>(jstroke["lc"].toDefault<int>(1) - 1,
                                                 0, SK_ARRAY_COUNT(gCaps) - 1)]);
 
     return stroke_node;
 }
 
-sk_sp<sksg::PaintNode> AttachColorFill(const Json::Value& jfill, AttachContext* ctx) {
+sk_sp<sksg::PaintNode> AttachColorFill(const json::ValueRef& jfill, AttachContext* ctx) {
     SkASSERT(jfill.isObject());
 
     return AttachPaint(jfill, ctx, AttachColor(jfill, ctx));
 }
 
-sk_sp<sksg::PaintNode> AttachGradientFill(const Json::Value& jfill, AttachContext* ctx) {
+sk_sp<sksg::PaintNode> AttachGradientFill(const json::ValueRef& jfill, AttachContext* ctx) {
     SkASSERT(jfill.isObject());
 
     return AttachPaint(jfill, ctx, AttachGradient(jfill, ctx));
 }
 
-sk_sp<sksg::PaintNode> AttachColorStroke(const Json::Value& jstroke, AttachContext* ctx) {
+sk_sp<sksg::PaintNode> AttachColorStroke(const json::ValueRef& jstroke, AttachContext* ctx) {
     SkASSERT(jstroke.isObject());
 
     return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx)));
 }
 
-sk_sp<sksg::PaintNode> AttachGradientStroke(const Json::Value& jstroke, AttachContext* ctx) {
+sk_sp<sksg::PaintNode> AttachGradientStroke(const json::ValueRef& jstroke, AttachContext* ctx) {
     SkASSERT(jstroke.isObject());
 
     return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx)));
 }
 
 std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
-    const Json::Value& jmerge, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
+    const json::ValueRef& jmerge, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
     std::vector<sk_sp<sksg::GeometryNode>> merged;
 
     static constexpr sksg::Merge::Mode gModes[] = {
@@ -404,7 +403,7 @@
         sksg::Merge::Mode::kXOR      ,  // "mm": 5
     };
 
-    const auto mode = gModes[SkTPin<int>(ParseDefault(jmerge["mm"], 1) - 1,
+    const auto mode = gModes[SkTPin<int>(jmerge["mm"].toDefault(1) - 1,
                                          0, SK_ARRAY_COUNT(gModes) - 1)];
     merged.push_back(sksg::Merge::Make(std::move(geos), mode));
 
@@ -412,14 +411,14 @@
 }
 
 std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
-    const Json::Value& jtrim, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
+    const json::ValueRef& jtrim, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
 
     enum class Mode {
         kMerged,   // "m": 1
         kSeparate, // "m": 2
     } gModes[] = { Mode::kMerged, Mode::kSeparate };
 
-    const auto mode = gModes[SkTPin<int>(ParseDefault(jtrim["m"], 1) - 1,
+    const auto mode = gModes[SkTPin<int>(jtrim["m"].toDefault(1) - 1,
                                          0, SK_ARRAY_COUNT(gModes) - 1)];
 
     std::vector<sk_sp<sksg::GeometryNode>> inputs;
@@ -454,7 +453,7 @@
 }
 
 std::vector<sk_sp<sksg::GeometryNode>> AttachRoundGeometryEffect(
-    const Json::Value& jtrim, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
+    const json::ValueRef& jtrim, AttachContext* ctx, std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
 
     std::vector<sk_sp<sksg::GeometryNode>> rounded;
     rounded.reserve(geos.size());
@@ -472,7 +471,7 @@
     return rounded;
 }
 
-using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const Json::Value&, AttachContext*);
+using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const json::ValueRef&, AttachContext*);
 static constexpr GeometryAttacherT gGeometryAttachers[] = {
     AttachPathGeometry,
     AttachRRectGeometry,
@@ -480,7 +479,7 @@
     AttachPolystarGeometry,
 };
 
-using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const Json::Value&, AttachContext*);
+using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const json::ValueRef&, AttachContext*);
 static constexpr PaintAttacherT gPaintAttachers[] = {
     AttachColorFill,
     AttachColorStroke,
@@ -489,7 +488,7 @@
 };
 
 using GeometryEffectAttacherT =
-    std::vector<sk_sp<sksg::GeometryNode>> (*)(const Json::Value&,
+    std::vector<sk_sp<sksg::GeometryNode>> (*)(const json::ValueRef&,
                                                AttachContext*,
                                                std::vector<sk_sp<sksg::GeometryNode>>&&);
 static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
@@ -512,7 +511,7 @@
     uint32_t    fAttacherIndex; // index into respective attacher tables
 };
 
-const ShapeInfo* FindShapeInfo(const Json::Value& shape) {
+const ShapeInfo* FindShapeInfo(const json::ValueRef& shape) {
     static constexpr ShapeInfo gShapeInfo[] = {
         { "el", ShapeType::kGeometry      , 2 }, // ellipse   -> AttachEllipseGeometry
         { "fl", ShapeType::kPaint         , 0 }, // fill      -> AttachColorFill
@@ -532,11 +531,11 @@
     if (!shape.isObject())
         return nullptr;
 
-    const auto& type = shape["ty"];
-    if (!type.isString())
+    const auto type = shape["ty"].toDefault(SkString());
+    if (type.isEmpty())
         return nullptr;
 
-    const auto* info = bsearch(type.asCString(),
+    const auto* info = bsearch(type.c_str(),
                                gShapeInfo,
                                SK_ARRAY_COUNT(gShapeInfo),
                                sizeof(ShapeInfo),
@@ -549,7 +548,7 @@
 }
 
 struct GeometryEffectRec {
-    const Json::Value&      fJson;
+    const json::ValueRef      fJson;
     GeometryEffectAttacherT fAttach;
 };
 
@@ -569,7 +568,7 @@
     size_t                                  fCommittedAnimators;
 };
 
-sk_sp<sksg::RenderNode> AttachShape(const Json::Value& jshape, AttachShapeContext* shapeCtx) {
+sk_sp<sksg::RenderNode> AttachShape(const json::ValueRef& jshape, AttachShapeContext* shapeCtx) {
     if (!jshape.isArray())
         return nullptr;
 
@@ -580,7 +579,7 @@
     sk_sp<sksg::Matrix> shape_matrix;
 
     struct ShapeRec {
-        const Json::Value& fJson;
+        const json::ValueRef fJson;
         const ShapeInfo&   fInfo;
     };
 
@@ -591,8 +590,8 @@
     //   * store recs for next pass
     //
     std::vector<ShapeRec> recs;
-    for (Json::ArrayIndex i = 0; i < jshape.size(); ++i) {
-        const auto& s = jshape[jshape.size() - 1 - i];
+    for (size_t i = 0; i < jshape.size(); ++i) {
+        const auto s = jshape[jshape.size() - 1 - i];
         const auto* info = FindShapeInfo(s);
         if (!info) {
             LogFail(s.isObject() ? s["ty"] : s, "Unknown shape");
@@ -765,18 +764,18 @@
     return sk_make_sp<SkottieSGAdapter>(std::move(animation));
 }
 
-sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& jlayer, AttachContext* ctx,
+sk_sp<sksg::RenderNode> AttachCompLayer(const json::ValueRef& jlayer, AttachContext* ctx,
                                         float* time_bias, float* time_scale) {
     SkASSERT(jlayer.isObject());
 
     SkString refId;
-    if (!Parse(jlayer["refId"], &refId) || refId.isEmpty()) {
+    if (!jlayer["refId"].to(&refId) || refId.isEmpty()) {
         LOG("!! Comp layer missing refId\n");
         return nullptr;
     }
 
-    const auto start_time = ParseDefault(jlayer["st"], 0.0f),
-             stretch_time = ParseDefault(jlayer["sr"], 1.0f);
+    const auto start_time = jlayer["st"].toDefault(0.0f),
+             stretch_time = jlayer["sr"].toDefault(1.0f);
 
     *time_bias = -start_time;
     *time_scale = 1 / stretch_time;
@@ -795,16 +794,16 @@
     }
 
     // TODO: cycle detection
-    return AttachComposition(**comp, ctx);
+    return AttachComposition(*comp, ctx);
 }
 
-sk_sp<sksg::RenderNode> AttachSolidLayer(const Json::Value& jlayer, AttachContext*,
+sk_sp<sksg::RenderNode> AttachSolidLayer(const json::ValueRef& jlayer, AttachContext*,
                                          float*, float*) {
     SkASSERT(jlayer.isObject());
 
-    const auto size = SkSize::Make(ParseDefault(jlayer["sw"], 0.0f),
-                                   ParseDefault(jlayer["sh"], 0.0f));
-    const auto hex = ParseDefault(jlayer["sc"], SkString());
+    const auto size = SkSize::Make(jlayer["sw"].toDefault(0.0f),
+                                   jlayer["sh"].toDefault(0.0f));
+    const auto hex = jlayer["sc"].toDefault(SkString());
     uint32_t c;
     if (size.isEmpty() ||
         !hex.startsWith("#") ||
@@ -819,11 +818,11 @@
                             sksg::Color::Make(color));
 }
 
-sk_sp<sksg::RenderNode> AttachImageAsset(const Json::Value& jimage, AttachContext* ctx) {
+sk_sp<sksg::RenderNode> AttachImageAsset(const json::ValueRef& jimage, AttachContext* ctx) {
     SkASSERT(jimage.isObject());
 
-    const auto name = ParseDefault(jimage["p"], SkString()),
-               path = ParseDefault(jimage["u"], SkString());
+    const auto name = jimage["p"].toDefault(SkString()),
+               path = jimage["u"].toDefault(SkString());
     if (name.isEmpty())
         return nullptr;
 
@@ -840,12 +839,12 @@
         SkImage::MakeFromEncoded(SkData::MakeFromStream(resStream.get(), resStream->getLength())));
 }
 
-sk_sp<sksg::RenderNode> AttachImageLayer(const Json::Value& layer, AttachContext* ctx,
+sk_sp<sksg::RenderNode> AttachImageLayer(const json::ValueRef& layer, AttachContext* ctx,
                                          float*, float*) {
     SkASSERT(layer.isObject());
 
     SkString refId;
-    if (!Parse(layer["refId"], &refId) || refId.isEmpty()) {
+    if (!layer["refId"].to(&refId) || refId.isEmpty()) {
         LOG("!! Image layer missing refId\n");
         return nullptr;
     }
@@ -856,10 +855,10 @@
         return nullptr;
     }
 
-    return AttachImageAsset(**jimage, ctx);
+    return AttachImageAsset(*jimage, ctx);
 }
 
-sk_sp<sksg::RenderNode> AttachNullLayer(const Json::Value& layer, AttachContext*, float*, float*) {
+sk_sp<sksg::RenderNode> AttachNullLayer(const json::ValueRef& layer, AttachContext*, float*, float*) {
     SkASSERT(layer.isObject());
 
     // Null layers are used solely to drive dependent transforms,
@@ -867,7 +866,7 @@
     return nullptr;
 }
 
-sk_sp<sksg::RenderNode> AttachShapeLayer(const Json::Value& layer, AttachContext* ctx,
+sk_sp<sksg::RenderNode> AttachShapeLayer(const json::ValueRef& layer, AttachContext* ctx,
                                          float*, float*) {
     SkASSERT(layer.isObject());
 
@@ -886,7 +885,7 @@
     return shapeNode;
 }
 
-sk_sp<sksg::RenderNode> AttachTextLayer(const Json::Value& layer, AttachContext*, float*, float*) {
+sk_sp<sksg::RenderNode> AttachTextLayer(const json::ValueRef& layer, AttachContext*, float*, float*) {
     SkASSERT(layer.isObject());
 
     LOG("?? Text layer stub\n");
@@ -894,42 +893,40 @@
 }
 
 struct AttachLayerContext {
-    AttachLayerContext(const Json::Value& jlayers, AttachContext* ctx)
+    AttachLayerContext(const json::ValueRef& jlayers, AttachContext* ctx)
         : fLayerList(jlayers), fCtx(ctx) {}
 
-    const Json::Value&                   fLayerList;
+    const json::ValueRef                   fLayerList;
     AttachContext*                       fCtx;
     SkTHashMap<int, sk_sp<sksg::Matrix>> fLayerMatrixMap;
     sk_sp<sksg::RenderNode>              fCurrentMatte;
 
-    sk_sp<sksg::Matrix> AttachParentLayerMatrix(const Json::Value& jlayer) {
+    sk_sp<sksg::Matrix> AttachParentLayerMatrix(const json::ValueRef& jlayer) {
         SkASSERT(jlayer.isObject());
         SkASSERT(fLayerList.isArray());
 
-        const auto parent_index = ParseDefault(jlayer["parent"], -1);
+        const auto parent_index = jlayer["parent"].toDefault<int>(-1);
         if (parent_index < 0)
             return nullptr;
 
         if (auto* m = fLayerMatrixMap.find(parent_index))
             return *m;
 
-        for (const auto& l : fLayerList) {
-            if (!l.isObject()) {
-                continue;
-            }
-
-            if (ParseDefault(l["ind"], -1) == parent_index) {
-                return this->AttachLayerMatrix(l);
+        sk_sp<sksg::Matrix> matrix;
+        for (const json::ValueRef l : fLayerList) {
+            if (l["ind"].toDefault<int>(-1) == parent_index) {
+                matrix = this->AttachLayerMatrix(l);
+                break;
             }
         }
 
         return nullptr;
     }
 
-    sk_sp<sksg::Matrix> AttachLayerMatrix(const Json::Value& jlayer) {
+    sk_sp<sksg::Matrix> AttachLayerMatrix(const json::ValueRef& jlayer) {
         SkASSERT(jlayer.isObject());
 
-        const auto layer_index = ParseDefault(jlayer["ind"], -1);
+        const auto layer_index = jlayer["ind"].toDefault<int>(-1);
         if (layer_index < 0)
             return nullptr;
 
@@ -962,7 +959,7 @@
     return SkBlendMode::kSrcOver;
 }
 
-sk_sp<sksg::RenderNode> AttachMask(const Json::Value& jmask,
+sk_sp<sksg::RenderNode> AttachMask(const json::ValueRef& jmask,
                                    AttachContext* ctx,
                                    sk_sp<sksg::RenderNode> childNode) {
     if (!jmask.isArray())
@@ -977,7 +974,7 @@
 
     bool opaque_mask = true;
 
-    for (const auto& m : jmask) {
+    for (const json::ValueRef m : jmask) {
         if (!m.isObject())
             continue;
 
@@ -987,12 +984,12 @@
             continue;
         }
 
-        mask_path->setFillType(ParseDefault(m["inv"], false)
+        mask_path->setFillType(m["inv"].toDefault(false)
             ? SkPath::kInverseWinding_FillType
             : SkPath::kWinding_FillType);
 
         SkString mode;
-        if (!Parse(m["mode"], &mode) ||
+        if (!m["mode"].to(&mode) ||
             mode.size() != 1 ||
             !strcmp(mode.c_str(), "n")) { // "None" masks have no effect.
             continue;
@@ -1031,12 +1028,11 @@
     return sksg::MaskEffect::Make(std::move(childNode), std::move(mask_group));
 }
 
-sk_sp<sksg::RenderNode> AttachLayer(const Json::Value& jlayer,
-                                    AttachLayerContext* layerCtx) {
+sk_sp<sksg::RenderNode> AttachLayer(const json::ValueRef& jlayer, AttachLayerContext* layerCtx) {
     if (!jlayer.isObject())
         return nullptr;
 
-    using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const Json::Value&, AttachContext*,
+    using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const json::ValueRef&, AttachContext*,
                                                       float* time_bias, float* time_scale);
     static constexpr LayerAttacher gLayerAttachers[] = {
         AttachCompLayer,  // 'ty': 0
@@ -1047,7 +1043,7 @@
         AttachTextLayer,  // 'ty': 5
     };
 
-    int type = ParseDefault(jlayer["ty"], -1);
+    int type = jlayer["ty"].toDefault<int>(-1);
     if (type < 0 || type >= SkTo<int>(SK_ARRAY_COUNT(gLayerAttachers))) {
         return nullptr;
     }
@@ -1066,8 +1062,8 @@
     auto layer = gLayerAttachers[type](jlayer, &local_ctx, &time_bias, &time_scale);
 
     // Clip layers with explicit dimensions.
-    float w, h;
-    if (Parse(jlayer["w"], &w) && Parse(jlayer["h"], &h)) {
+    float w = 0, h = 0;
+    if (jlayer["w"].to(&w) && jlayer["h"].to(&h)) {
         layer = sksg::ClipEffect::Make(std::move(layer),
                                        sksg::Rect::Make(SkRect::MakeWH(w, h)),
                                        true);
@@ -1120,8 +1116,8 @@
     };
 
     auto controller_node = sksg::OpacityEffect::Make(std::move(layer));
-    const auto        in = ParseDefault(jlayer["ip"], 0.0f),
-                     out = ParseDefault(jlayer["op"], in);
+    const auto        in = jlayer["ip"].toDefault(0.0f),
+                     out = jlayer["op"].toDefault(in);
 
     if (!jlayer["tm"].isNull()) {
         LogFail(jlayer["tm"], "Unsupported time remapping");
@@ -1138,7 +1134,7 @@
                                             time_bias,
                                             time_scale));
 
-    if (ParseDefault(jlayer["td"], false)) {
+    if (jlayer["td"].toDefault(false)) {
         // This layer is a matte.  We apply it as a mask to the next layer.
         layerCtx->fCurrentMatte = std::move(controller_node);
         return nullptr;
@@ -1150,7 +1146,7 @@
             sksg::MaskEffect::Mode::kNormal, // tt: 1
             sksg::MaskEffect::Mode::kInvert, // tt: 2
         };
-        const auto matteType = ParseDefault(jlayer["tt"], 1) - 1;
+        const auto matteType = jlayer["tt"].toDefault<int>(1) - 1;
 
         if (matteType >= 0 && matteType < SkTo<int>(SK_ARRAY_COUNT(gMaskModes))) {
             return sksg::MaskEffect::Make(std::move(controller_node),
@@ -1163,18 +1159,18 @@
     return std::move(controller_node);
 }
 
-sk_sp<sksg::RenderNode> AttachComposition(const Json::Value& comp, AttachContext* ctx) {
+sk_sp<sksg::RenderNode> AttachComposition(const json::ValueRef& comp, AttachContext* ctx) {
     if (!comp.isObject())
         return nullptr;
 
-    const auto& jlayers = comp["layers"];
+    const auto jlayers = comp["layers"];
     if (!jlayers.isArray())
         return nullptr;
 
     SkSTArray<16, sk_sp<sksg::RenderNode>, true> layers;
     AttachLayerContext                           layerCtx(jlayers, ctx);
 
-    for (const auto& l : jlayers) {
+    for (const json::ValueRef l : jlayers) {
         if (auto layer_fragment = AttachLayer(l, &layerCtx)) {
             layers.push_back(std::move(layer_fragment));
         }
@@ -1207,33 +1203,21 @@
         return nullptr;
     }
 
+    stats->fJsonSize = stream->getLength();
     const auto t0 = SkTime::GetMSecs();
 
-    Json::Value json;
-    {
-        auto data = SkData::MakeFromStream(stream, stream->getLength());
-        if (!data) {
-            LOG("!! could not read stream\n");
-            return nullptr;
-        }
-        stats->fJsonSize = data->size();
-
-        Json::Reader reader;
-
-        auto dataStart = static_cast<const char*>(data->data());
-        if (!reader.parse(dataStart, dataStart + data->size(), json, false) || !json.isObject()) {
-            LOG("!! failed to parse json: %s\n", reader.getFormattedErrorMessages().c_str());
-            return nullptr;
-        }
-    }
+    const json::Document doc(stream);
+    const auto json = doc.root();
+    if (!json.isObject())
+        return nullptr;
 
     const auto t1 = SkTime::GetMSecs();
     stats->fJsonParseTimeMS = t1 - t0;
 
-    const auto version = ParseDefault(json["v"], SkString());
-    const auto size    = SkSize::Make(ParseDefault(json["w"], 0.0f),
-                                      ParseDefault(json["h"], 0.0f));
-    const auto fps     = ParseDefault(json["fr"], -1.0f);
+    const auto version = json["v"].toDefault(SkString());
+    const auto size    = SkSize::Make(json["w"].toDefault(0.0f),
+                                      json["h"].toDefault(0.0f));
+    const auto fps     = json["fr"].toDefault(-1.0f);
 
     if (size.isEmpty() || version.isEmpty() || fps <= 0) {
         LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)",
@@ -1278,21 +1262,19 @@
 }
 
 Animation::Animation(const ResourceProvider& resources,
-                     SkString version, const SkSize& size, SkScalar fps, const Json::Value& json,
+                     SkString version, const SkSize& size, SkScalar fps, const json::ValueRef& json,
                      Stats* stats)
     : fVersion(std::move(version))
     , fSize(size)
     , fFrameRate(fps)
-    , fInPoint(ParseDefault(json["ip"], 0.0f))
-    , fOutPoint(SkTMax(ParseDefault(json["op"], SK_ScalarMax), fInPoint)) {
+    , fInPoint(json["ip"].toDefault(0.0f))
+    , fOutPoint(SkTMax(json["op"].toDefault(SK_ScalarMax), fInPoint)) {
 
     AssetMap assets;
-    for (const auto& asset : json["assets"]) {
-        if (!asset.isObject()) {
-            continue;
+    for (const json::ValueRef asset : json["assets"]) {
+        if (asset.isObject()) {
+            assets.set(asset["id"].toDefault(SkString()), asset);
         }
-
-        assets.set(ParseDefault(asset["id"], SkString()), &asset);
     }
 
     sksg::AnimatorList animators;