From fa7e9a813e44f283b14f686eeb857d0d00735811 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Fri, 4 May 2018 15:10:54 -0400 Subject: [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 Reviewed-by: Mike Reed --- experimental/skottie/Skottie.cpp | 236 +++++++++++++--------------- experimental/skottie/Skottie.h | 11 +- experimental/skottie/SkottieAnimator.cpp | 61 ++++---- experimental/skottie/SkottieAnimator.h | 6 +- experimental/skottie/SkottieJson.cpp | 255 +++++++++++++++++++++++++++++++ experimental/skottie/SkottieJson.h | 76 +++++++++ experimental/skottie/SkottieParser.cpp | 148 ------------------ experimental/skottie/SkottieParser.h | 28 ---- 8 files changed, 476 insertions(+), 345 deletions(-) create mode 100644 experimental/skottie/SkottieJson.cpp create mode 100644 experimental/skottie/SkottieJson.h delete mode 100644 experimental/skottie/SkottieParser.cpp delete mode 100644 experimental/skottie/SkottieParser.h (limited to 'experimental') diff --git a/experimental/skottie/Skottie.cpp b/experimental/skottie/Skottie.cpp index c4e964d4ee..99549bfccd 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 skottie { namespace { -using AssetMap = SkTHashMap; +using AssetMap = SkTHashMap; struct AttachContext { const ResourceProvider& fResources; @@ -62,13 +61,13 @@ struct AttachContext { 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 AttachMatrix(const Json::Value& t, AttachContext* ctx, +sk_sp AttachMatrix(const json::ValueRef& t, AttachContext* ctx, sk_sp parentMatrix) { if (!t.isObject()) return nullptr; @@ -88,13 +87,13 @@ sk_sp AttachMatrix(const Json::Value& t, AttachContext* ctx, adapter->setScale(ValueTraits::As(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(*jrotation, &ctx->fAnimators, + auto rotation_attached = BindProperty(jrotation, &ctx->fAnimators, [adapter](const ScalarValue& r) { adapter->setRotation(r); }); @@ -120,7 +119,7 @@ sk_sp AttachMatrix(const Json::Value& t, AttachContext* ctx, return matrix; } -sk_sp AttachOpacity(const Json::Value& jtransform, AttachContext* ctx, +sk_sp AttachOpacity(const json::ValueRef& jtransform, AttachContext* ctx, sk_sp childNode) { if (!jtransform.isObject() || !childNode) return childNode; @@ -129,8 +128,8 @@ sk_sp AttachOpacity(const Json::Value& jtransform, AttachConte // 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(-1) == 100) { // Ignoring static full opacity. return childNode; } @@ -145,9 +144,9 @@ sk_sp AttachOpacity(const Json::Value& jtransform, AttachConte return std::move(opacityNode); } -sk_sp AttachComposition(const Json::Value&, AttachContext* ctx); +sk_sp AttachComposition(const json::ValueRef&, AttachContext* ctx); -sk_sp AttachPath(const Json::Value& jpath, AttachContext* ctx) { +sk_sp AttachPath(const json::ValueRef& jpath, AttachContext* ctx) { auto path_node = sksg::Path::Make(); return BindProperty(jpath, &ctx->fAnimators, [path_node](const ShapeValue& p) { @@ -157,13 +156,13 @@ sk_sp AttachPath(const Json::Value& jpath, AttachContext* ctx) { : nullptr; } -sk_sp AttachPathGeometry(const Json::Value& jpath, AttachContext* ctx) { +sk_sp AttachPathGeometry(const json::ValueRef& jpath, AttachContext* ctx) { SkASSERT(jpath.isObject()); return AttachPath(jpath["ks"], ctx); } -sk_sp AttachRRectGeometry(const Json::Value& jrect, AttachContext* ctx) { +sk_sp AttachRRectGeometry(const json::ValueRef& jrect, AttachContext* ctx) { SkASSERT(jrect.isObject()); auto rect_node = sksg::RRect::Make(); @@ -189,7 +188,7 @@ sk_sp AttachRRectGeometry(const Json::Value& jrect, AttachCo return std::move(rect_node); } -sk_sp AttachEllipseGeometry(const Json::Value& jellipse, AttachContext* ctx) { +sk_sp AttachEllipseGeometry(const json::ValueRef& jellipse, AttachContext* ctx) { SkASSERT(jellipse.isObject()); auto rect_node = sksg::RRect::Make(); @@ -213,7 +212,7 @@ sk_sp AttachEllipseGeometry(const Json::Value& jellipse, Att return std::move(rect_node); } -sk_sp AttachPolystarGeometry(const Json::Value& jstar, AttachContext* ctx) { +sk_sp AttachPolystarGeometry(const json::ValueRef& jstar, AttachContext* ctx) { SkASSERT(jstar.isObject()); static constexpr PolyStarAdapter::Type gTypes[] = { @@ -221,7 +220,7 @@ sk_sp AttachPolystarGeometry(const Json::Value& jstar, Attac PolyStarAdapter::Type::kPoly, // "sy": 2 }; - const auto type = ParseDefault(jstar["sy"], 0) - 1; + const auto type = jstar["sy"].toDefault(0) - 1; if (type < 0 || type >= SkTo(SK_ARRAY_COUNT(gTypes))) { LogFail(jstar, "Unknown polystar type"); return nullptr; @@ -262,7 +261,7 @@ sk_sp AttachPolystarGeometry(const Json::Value& jstar, Attac return std::move(path_node); } -sk_sp AttachColor(const Json::Value& obj, AttachContext* ctx) { +sk_sp AttachColor(const json::ValueRef& obj, AttachContext* ctx) { SkASSERT(obj.isObject()); auto color_node = sksg::Color::Make(SK_ColorBLACK); @@ -274,21 +273,21 @@ sk_sp AttachColor(const Json::Value& obj, AttachContext* ctx) { return color_attached ? color_node : nullptr; } -sk_sp AttachGradient(const Json::Value& obj, AttachContext* ctx) { +sk_sp 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(-1); if (stopCount < 0) return nullptr; sk_sp gradient_node; sk_sp adapter; - if (ParseDefault(obj["t"], 1) == 1) { + if (obj["t"].toDefault(1) == 1) { auto linear_node = sksg::LinearGradient::Make(); adapter = sk_make_sp(linear_node, stopCount); gradient_node = std::move(linear_node); @@ -316,7 +315,7 @@ sk_sp AttachGradient(const Json::Value& obj, AttachContext* ctx) return gradient_node; } -sk_sp AttachPaint(const Json::Value& jpaint, AttachContext* ctx, +sk_sp AttachPaint(const json::ValueRef& jpaint, AttachContext* ctx, sk_sp paint_node) { if (paint_node) { paint_node->setAntiAlias(true); @@ -331,7 +330,7 @@ sk_sp AttachPaint(const Json::Value& jpaint, AttachContext* ctx return paint_node; } -sk_sp AttachStroke(const Json::Value& jstroke, AttachContext* ctx, +sk_sp AttachStroke(const json::ValueRef& jstroke, AttachContext* ctx, sk_sp stroke_node) { SkASSERT(jstroke.isObject()); @@ -347,14 +346,14 @@ sk_sp AttachStroke(const Json::Value& jstroke, AttachContext* c 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(ParseDefault(jstroke["lj"], 1) - 1, + stroke_node->setStrokeJoin(gJoins[SkTPin(jstroke["lj"].toDefault(1) - 1, 0, SK_ARRAY_COUNT(gJoins) - 1)]); static constexpr SkPaint::Cap gCaps[] = { @@ -362,38 +361,38 @@ sk_sp AttachStroke(const Json::Value& jstroke, AttachContext* c SkPaint::kRound_Cap, SkPaint::kSquare_Cap, }; - stroke_node->setStrokeCap(gCaps[SkTPin(ParseDefault(jstroke["lc"], 1) - 1, + stroke_node->setStrokeCap(gCaps[SkTPin(jstroke["lc"].toDefault(1) - 1, 0, SK_ARRAY_COUNT(gCaps) - 1)]); return stroke_node; } -sk_sp AttachColorFill(const Json::Value& jfill, AttachContext* ctx) { +sk_sp AttachColorFill(const json::ValueRef& jfill, AttachContext* ctx) { SkASSERT(jfill.isObject()); return AttachPaint(jfill, ctx, AttachColor(jfill, ctx)); } -sk_sp AttachGradientFill(const Json::Value& jfill, AttachContext* ctx) { +sk_sp AttachGradientFill(const json::ValueRef& jfill, AttachContext* ctx) { SkASSERT(jfill.isObject()); return AttachPaint(jfill, ctx, AttachGradient(jfill, ctx)); } -sk_sp AttachColorStroke(const Json::Value& jstroke, AttachContext* ctx) { +sk_sp AttachColorStroke(const json::ValueRef& jstroke, AttachContext* ctx) { SkASSERT(jstroke.isObject()); return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx))); } -sk_sp AttachGradientStroke(const Json::Value& jstroke, AttachContext* ctx) { +sk_sp AttachGradientStroke(const json::ValueRef& jstroke, AttachContext* ctx) { SkASSERT(jstroke.isObject()); return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx))); } std::vector> AttachMergeGeometryEffect( - const Json::Value& jmerge, AttachContext* ctx, std::vector>&& geos) { + const json::ValueRef& jmerge, AttachContext* ctx, std::vector>&& geos) { std::vector> merged; static constexpr sksg::Merge::Mode gModes[] = { @@ -404,7 +403,7 @@ std::vector> AttachMergeGeometryEffect( sksg::Merge::Mode::kXOR , // "mm": 5 }; - const auto mode = gModes[SkTPin(ParseDefault(jmerge["mm"], 1) - 1, + const auto mode = gModes[SkTPin(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> AttachMergeGeometryEffect( } std::vector> AttachTrimGeometryEffect( - const Json::Value& jtrim, AttachContext* ctx, std::vector>&& geos) { + const json::ValueRef& jtrim, AttachContext* ctx, std::vector>&& geos) { enum class Mode { kMerged, // "m": 1 kSeparate, // "m": 2 } gModes[] = { Mode::kMerged, Mode::kSeparate }; - const auto mode = gModes[SkTPin(ParseDefault(jtrim["m"], 1) - 1, + const auto mode = gModes[SkTPin(jtrim["m"].toDefault(1) - 1, 0, SK_ARRAY_COUNT(gModes) - 1)]; std::vector> inputs; @@ -454,7 +453,7 @@ std::vector> AttachTrimGeometryEffect( } std::vector> AttachRoundGeometryEffect( - const Json::Value& jtrim, AttachContext* ctx, std::vector>&& geos) { + const json::ValueRef& jtrim, AttachContext* ctx, std::vector>&& geos) { std::vector> rounded; rounded.reserve(geos.size()); @@ -472,7 +471,7 @@ std::vector> AttachRoundGeometryEffect( return rounded; } -using GeometryAttacherT = sk_sp (*)(const Json::Value&, AttachContext*); +using GeometryAttacherT = sk_sp (*)(const json::ValueRef&, AttachContext*); static constexpr GeometryAttacherT gGeometryAttachers[] = { AttachPathGeometry, AttachRRectGeometry, @@ -480,7 +479,7 @@ static constexpr GeometryAttacherT gGeometryAttachers[] = { AttachPolystarGeometry, }; -using PaintAttacherT = sk_sp (*)(const Json::Value&, AttachContext*); +using PaintAttacherT = sk_sp (*)(const json::ValueRef&, AttachContext*); static constexpr PaintAttacherT gPaintAttachers[] = { AttachColorFill, AttachColorStroke, @@ -489,7 +488,7 @@ static constexpr PaintAttacherT gPaintAttachers[] = { }; using GeometryEffectAttacherT = - std::vector> (*)(const Json::Value&, + std::vector> (*)(const json::ValueRef&, AttachContext*, std::vector>&&); static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = { @@ -512,7 +511,7 @@ struct ShapeInfo { 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 @@ const ShapeInfo* FindShapeInfo(const Json::Value& shape) { 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 @@ const ShapeInfo* FindShapeInfo(const Json::Value& shape) { } struct GeometryEffectRec { - const Json::Value& fJson; + const json::ValueRef fJson; GeometryEffectAttacherT fAttach; }; @@ -569,7 +568,7 @@ struct AttachShapeContext { size_t fCommittedAnimators; }; -sk_sp AttachShape(const Json::Value& jshape, AttachShapeContext* shapeCtx) { +sk_sp AttachShape(const json::ValueRef& jshape, AttachShapeContext* shapeCtx) { if (!jshape.isArray()) return nullptr; @@ -580,7 +579,7 @@ sk_sp AttachShape(const Json::Value& jshape, AttachShapeContex sk_sp shape_matrix; struct ShapeRec { - const Json::Value& fJson; + const json::ValueRef fJson; const ShapeInfo& fInfo; }; @@ -591,8 +590,8 @@ sk_sp AttachShape(const Json::Value& jshape, AttachShapeContex // * store recs for next pass // std::vector 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 @@ sk_sp AttachNestedAnimation(const char* path, AttachContext* c return sk_make_sp(std::move(animation)); } -sk_sp AttachCompLayer(const Json::Value& jlayer, AttachContext* ctx, +sk_sp 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 @@ sk_sp AttachCompLayer(const Json::Value& jlayer, AttachContext } // TODO: cycle detection - return AttachComposition(**comp, ctx); + return AttachComposition(*comp, ctx); } -sk_sp AttachSolidLayer(const Json::Value& jlayer, AttachContext*, +sk_sp 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 @@ sk_sp AttachSolidLayer(const Json::Value& jlayer, AttachContex sksg::Color::Make(color)); } -sk_sp AttachImageAsset(const Json::Value& jimage, AttachContext* ctx) { +sk_sp 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 @@ sk_sp AttachImageAsset(const Json::Value& jimage, AttachContex SkImage::MakeFromEncoded(SkData::MakeFromStream(resStream.get(), resStream->getLength()))); } -sk_sp AttachImageLayer(const Json::Value& layer, AttachContext* ctx, +sk_sp 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 @@ sk_sp AttachImageLayer(const Json::Value& layer, AttachContext return nullptr; } - return AttachImageAsset(**jimage, ctx); + return AttachImageAsset(*jimage, ctx); } -sk_sp AttachNullLayer(const Json::Value& layer, AttachContext*, float*, float*) { +sk_sp AttachNullLayer(const json::ValueRef& layer, AttachContext*, float*, float*) { SkASSERT(layer.isObject()); // Null layers are used solely to drive dependent transforms, @@ -867,7 +866,7 @@ sk_sp AttachNullLayer(const Json::Value& layer, AttachContext* return nullptr; } -sk_sp AttachShapeLayer(const Json::Value& layer, AttachContext* ctx, +sk_sp AttachShapeLayer(const json::ValueRef& layer, AttachContext* ctx, float*, float*) { SkASSERT(layer.isObject()); @@ -886,7 +885,7 @@ sk_sp AttachShapeLayer(const Json::Value& layer, AttachContext return shapeNode; } -sk_sp AttachTextLayer(const Json::Value& layer, AttachContext*, float*, float*) { +sk_sp AttachTextLayer(const json::ValueRef& layer, AttachContext*, float*, float*) { SkASSERT(layer.isObject()); LOG("?? Text layer stub\n"); @@ -894,42 +893,40 @@ sk_sp AttachTextLayer(const Json::Value& layer, AttachContext* } 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> fLayerMatrixMap; sk_sp fCurrentMatte; - sk_sp AttachParentLayerMatrix(const Json::Value& jlayer) { + sk_sp 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(-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 matrix; + for (const json::ValueRef l : fLayerList) { + if (l["ind"].toDefault(-1) == parent_index) { + matrix = this->AttachLayerMatrix(l); + break; } } return nullptr; } - sk_sp AttachLayerMatrix(const Json::Value& jlayer) { + sk_sp AttachLayerMatrix(const json::ValueRef& jlayer) { SkASSERT(jlayer.isObject()); - const auto layer_index = ParseDefault(jlayer["ind"], -1); + const auto layer_index = jlayer["ind"].toDefault(-1); if (layer_index < 0) return nullptr; @@ -962,7 +959,7 @@ SkBlendMode MaskBlendMode(char mode) { return SkBlendMode::kSrcOver; } -sk_sp AttachMask(const Json::Value& jmask, +sk_sp AttachMask(const json::ValueRef& jmask, AttachContext* ctx, sk_sp childNode) { if (!jmask.isArray()) @@ -977,7 +974,7 @@ sk_sp AttachMask(const Json::Value& jmask, bool opaque_mask = true; - for (const auto& m : jmask) { + for (const json::ValueRef m : jmask) { if (!m.isObject()) continue; @@ -987,12 +984,12 @@ sk_sp AttachMask(const Json::Value& jmask, 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 @@ sk_sp AttachMask(const Json::Value& jmask, return sksg::MaskEffect::Make(std::move(childNode), std::move(mask_group)); } -sk_sp AttachLayer(const Json::Value& jlayer, - AttachLayerContext* layerCtx) { +sk_sp AttachLayer(const json::ValueRef& jlayer, AttachLayerContext* layerCtx) { if (!jlayer.isObject()) return nullptr; - using LayerAttacher = sk_sp (*)(const Json::Value&, AttachContext*, + using LayerAttacher = sk_sp (*)(const json::ValueRef&, AttachContext*, float* time_bias, float* time_scale); static constexpr LayerAttacher gLayerAttachers[] = { AttachCompLayer, // 'ty': 0 @@ -1047,7 +1043,7 @@ sk_sp AttachLayer(const Json::Value& jlayer, AttachTextLayer, // 'ty': 5 }; - int type = ParseDefault(jlayer["ty"], -1); + int type = jlayer["ty"].toDefault(-1); if (type < 0 || type >= SkTo(SK_ARRAY_COUNT(gLayerAttachers))) { return nullptr; } @@ -1066,8 +1062,8 @@ sk_sp AttachLayer(const Json::Value& jlayer, 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 @@ sk_sp AttachLayer(const Json::Value& jlayer, }; 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 @@ sk_sp AttachLayer(const Json::Value& jlayer, 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 @@ sk_sp AttachLayer(const Json::Value& jlayer, 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(1) - 1; if (matteType >= 0 && matteType < SkTo(SK_ARRAY_COUNT(gMaskModes))) { return sksg::MaskEffect::Make(std::move(controller_node), @@ -1163,18 +1159,18 @@ sk_sp AttachLayer(const Json::Value& jlayer, return std::move(controller_node); } -sk_sp AttachComposition(const Json::Value& comp, AttachContext* ctx) { +sk_sp 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, 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 @@ sk_sp Animation::Make(SkStream* stream, const ResourceProvider& res, 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(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 @@ sk_sp Animation::MakeFromFile(const char path[], const ResourceProvid } 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; diff --git a/experimental/skottie/Skottie.h b/experimental/skottie/Skottie.h index 44541fd81b..275127aa4e 100644 --- a/experimental/skottie/Skottie.h +++ b/experimental/skottie/Skottie.h @@ -11,8 +11,6 @@ #include "SkRefCnt.h" #include "SkSize.h" #include "SkString.h" -#include "SkTArray.h" -#include "SkTHash.h" #include "SkTypes.h" #include @@ -21,12 +19,12 @@ class SkCanvas; struct SkRect; class SkStream; -namespace Json { class Value; } - namespace sksg { class Scene; } namespace skottie { +namespace json { class ValueRef; } + class ResourceProvider : public SkNoncopyable { public: virtual ~ResourceProvider() = default; @@ -63,9 +61,8 @@ public: void setShowInval(bool show); private: - Animation(const ResourceProvider&, - SkString ver, const SkSize& size, SkScalar fps, - const Json::Value&, Stats*); + Animation(const ResourceProvider&, SkString ver, const SkSize& size, SkScalar fps, + const json::ValueRef&, Stats*); SkString fVersion; SkSize fSize; diff --git a/experimental/skottie/SkottieAnimator.cpp b/experimental/skottie/SkottieAnimator.cpp index 34803e05b5..d8cc6e7769 100644 --- a/experimental/skottie/SkottieAnimator.cpp +++ b/experimental/skottie/SkottieAnimator.cpp @@ -8,9 +8,9 @@ #include "SkottieAnimator.h" #include "SkCubicMap.h" -#include "SkJSONCPP.h" +#include "SkottieJson.h" #include "SkottieValue.h" -#include "SkottieParser.h" +#include "SkString.h" #include "SkTArray.h" #include @@ -21,9 +21,9 @@ namespace { #define LOG SkDebugf -bool LogFail(const Json::Value& json, const char* msg) { - const auto dump = json.toStyledString(); - LOG("!! %s: %s", msg, dump.c_str()); +bool LogFail(const json::ValueRef& json, const char* msg) { + const auto dump = json.toString(); + LOG("!! %s: %s\n", msg, dump.c_str()); return false; } @@ -67,20 +67,19 @@ protected: : SkTPin(fCubicMaps[rec.cmidx].computeYFromX(lt), 0.0f, 1.0f); } - virtual int parseValue(const Json::Value&) = 0; + virtual int parseValue(const json::ValueRef&) = 0; - void parseKeyFrames(const Json::Value& jframes) { + void parseKeyFrames(const json::ValueRef& jframes) { if (!jframes.isArray()) return; - for (const auto& jframe : jframes) { + for (const json::ValueRef jframe : jframes) { if (!jframe.isObject()) continue; float t0; - if (!Parse(jframe["t"], &t0)) { + if (!jframe["t"].to(&t0)) continue; - } if (!fRecs.empty()) { if (fRecs.back().t1 >= t0) { @@ -93,25 +92,23 @@ protected: } const auto vidx0 = this->parseValue(jframe["s"]); - if (vidx0 < 0) { + if (vidx0 < 0) continue; - } // Defaults for constant frames. int vidx1 = vidx0, cmidx = -1; - if (!ParseDefault(jframe["h"], false)) { + if (!jframe["h"].toDefault(false)) { // Regular frame, requires an end value. vidx1 = this->parseValue(jframe["e"]); - if (vidx1 < 0) { + if (vidx1 < 0) continue; - } // default is linear lerp static constexpr SkPoint kDefaultC0 = { 0, 0 }, kDefaultC1 = { 1, 1 }; - const auto c0 = ParseDefault(jframe["i"], kDefaultC0), - c1 = ParseDefault(jframe["o"], kDefaultC1); + const auto c0 = jframe["i"].toDefault(kDefaultC0), + c1 = jframe["o"].toDefault(kDefaultC1); if (c0 != kDefaultC0 || c1 != kDefaultC1) { // TODO: is it worth de-duping these? @@ -181,7 +178,7 @@ private: template class KeyframeAnimator final : public KeyframeAnimatorBase { public: - static std::unique_ptr Make(const Json::Value& jframes, + static std::unique_ptr Make(const json::ValueRef& jframes, std::function&& apply) { std::unique_ptr animator(new KeyframeAnimator(jframes, std::move(apply))); if (!animator->count()) @@ -199,15 +196,15 @@ protected: } private: - KeyframeAnimator(const Json::Value& jframes, + KeyframeAnimator(const json::ValueRef& jframes, std::function&& apply) : fApplyFunc(std::move(apply)) { this->parseKeyFrames(jframes); } - int parseValue(const Json::Value& jv) override { + int parseValue(const json::ValueRef& jv) override { T val; - if (!Parse(jv, &val) || (!fVs.empty() && + if (!jv.to(&val) || (!fVs.empty() && ValueTraits::Cardinality(val) != ValueTraits::Cardinality(fVs.back()))) { return -1; } @@ -241,21 +238,21 @@ private: }; template -static inline bool BindPropertyImpl(const Json::Value& jprop, +static inline bool BindPropertyImpl(const json::ValueRef& jprop, sksg::AnimatorList* animators, std::function&& apply, const T* noop = nullptr) { if (!jprop.isObject()) return false; - const auto& jpropA = jprop["a"]; - const auto& jpropK = jprop["k"]; + const auto jpropA = jprop["a"]; + const auto jpropK = jprop["k"]; // Older Json versions don't have an "a" animation marker. // For those, we attempt to parse both ways. - if (!ParseDefault(jpropA, false)) { + if (!jpropA.toDefault(false)) { T val; - if (Parse(jpropK, &val)) { + if (jpropK.to(&val)) { // Static property. if (noop && val == *noop) return false; @@ -283,7 +280,7 @@ static inline bool BindPropertyImpl(const Json::Value& jprop, class SplitPointAnimator final : public sksg::Animator { public: - static std::unique_ptr Make(const Json::Value& jprop, + static std::unique_ptr Make(const json::ValueRef& jprop, std::function&& apply, const VectorValue*) { if (!jprop.isObject()) @@ -337,7 +334,7 @@ private: using INHERITED = sksg::Animator; }; -bool BindSplitPositionProperty(const Json::Value& jprop, +bool BindSplitPositionProperty(const json::ValueRef& jprop, sksg::AnimatorList* animators, std::function&& apply, const VectorValue* noop) { @@ -352,7 +349,7 @@ bool BindSplitPositionProperty(const Json::Value& jprop, } // namespace template <> -bool BindProperty(const Json::Value& jprop, +bool BindProperty(const json::ValueRef& jprop, sksg::AnimatorList* animators, std::function&& apply, const ScalarValue* noop) { @@ -360,17 +357,17 @@ bool BindProperty(const Json::Value& jprop, } template <> -bool BindProperty(const Json::Value& jprop, +bool BindProperty(const json::ValueRef& jprop, sksg::AnimatorList* animators, std::function&& apply, const VectorValue* noop) { - return ParseDefault(jprop["s"], false) + return jprop["s"].toDefault(false) ? BindSplitPositionProperty(jprop, animators, std::move(apply), noop) : BindPropertyImpl(jprop, animators, std::move(apply), noop); } template <> -bool BindProperty(const Json::Value& jprop, +bool BindProperty(const json::ValueRef& jprop, sksg::AnimatorList* animators, std::function&& apply, const ShapeValue* noop) { diff --git a/experimental/skottie/SkottieAnimator.h b/experimental/skottie/SkottieAnimator.h index 77be06b809..6dc8f6c759 100644 --- a/experimental/skottie/SkottieAnimator.h +++ b/experimental/skottie/SkottieAnimator.h @@ -12,14 +12,14 @@ #include -namespace Json { class Value; } - namespace skottie { +namespace json { class ValueRef; } + // This is the workhorse for property binding: depending on whether the property is animated, // it will either apply immediately or instantiate and attach a keyframe animator. template -bool BindProperty(const Json::Value&, +bool BindProperty(const json::ValueRef&, sksg::AnimatorList*, std::function&&, const T* noop = nullptr); diff --git a/experimental/skottie/SkottieJson.cpp b/experimental/skottie/SkottieJson.cpp new file mode 100644 index 0000000000..7b2c6ae985 --- /dev/null +++ b/experimental/skottie/SkottieJson.cpp @@ -0,0 +1,255 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "SkottieJson.h" + +#include "SkData.h" +#include "SkScalar.h" +#include "SkPath.h" +#include "SkPoint.h" +#include "SkStream.h" +#include "SkString.h" +#include "SkottieValue.h" + +#include "rapidjson/error/en.h" +#include "rapidjson/prettywriter.h" +#include "rapidjson/stringbuffer.h" + +#include + +namespace skottie { + +namespace json { + +template <> +bool json::ValueRef::to(SkScalar* v) const { + if (!fValue) return false; + + // Some versions wrap values as single-element arrays. + if (fValue->IsArray() && fValue->Size() == 1) { + return json::ValueRef(fValue->operator[](0)).to(v); + } + + if (!fValue->IsNumber()) + return false; + + *v = static_cast(fValue->GetDouble()); + + return true; +} + +template <> +bool json::ValueRef::to(bool* v) const { + if (!fValue) return false; + + switch(fValue->GetType()) { + case rapidjson::kNumberType: + *v = SkToBool(fValue->GetDouble()); + return true; + case rapidjson::kFalseType: + case rapidjson::kTrueType: + *v = fValue->GetBool(); + return true; + default: + break; + } + + return false; +} + +template <> +bool json::ValueRef::to(int* v) const { + if (!fValue || !fValue->IsInt()) + return false; + + *v = fValue->GetInt(); + + return true; +} + +template <> +bool json::ValueRef::to(SkString* v) const { + if (!fValue || !fValue->IsString()) + return false; + + v->set(fValue->GetString()); + + return true; +} + +template <> +bool json::ValueRef::to(SkPoint* v) const { + if (!fValue || !fValue->IsObject()) + return false; + + const auto jvx = ValueRef(fValue->operator[]("x")), + jvy = ValueRef(fValue->operator[]("y")); + + // Some BM versions seem to store x/y as single-element arrays. + return json::ValueRef(jvx.isArray() ? jvx.operator[](size_t(0)) : jvx).to(&v->fX) + && json::ValueRef(jvy.isArray() ? jvy.operator[](size_t(0)) : jvy).to(&v->fY); +} + +template <> +bool json::ValueRef::to>(std::vector* v) const { + if (!fValue || !fValue->IsArray()) + return false; + + v->resize(fValue->Size()); + for (size_t i = 0; i < fValue->Size(); ++i) { + if (!ValueRef(fValue->operator[](i)).to(v->data() + i)) { + return false; + } + } + + return true; +} + +namespace { + +bool ParsePointVec(const rapidjson::Value& jv, std::vector* pts) { + if (!jv.IsArray()) + return false; + + pts->clear(); + pts->reserve(jv.Size()); + + std::vector vec; + for (size_t i = 0; i < jv.Size(); ++i) { + if (!ValueRef(jv[i]).to(&vec) || vec.size() != 2) + return false; + pts->push_back(SkPoint::Make(vec[0], vec[1])); + } + + return true; +} + +} // namespace + +template <> +bool json::ValueRef::to(ShapeValue* v) const { + SkASSERT(v->fVertices.empty()); + + if (!fValue) + return false; + + // Some versions wrap values as single-element arrays. + if (fValue->IsArray() && fValue->Size() == 1) { + return json::ValueRef(fValue->operator[](0)).to(v); + } + + std::vector inPts, // Cubic Bezier "in" control points, relative to vertices. + outPts, // Cubic Bezier "out" control points, relative to vertices. + verts; // Cubic Bezier vertices. + + if (!fValue->IsObject() || + !ParsePointVec(fValue->operator[]("i"), &inPts) || + !ParsePointVec(fValue->operator[]("o"), &outPts) || + !ParsePointVec(fValue->operator[]("v"), &verts) || + inPts.size() != outPts.size() || + inPts.size() != verts.size()) { + + return false; + } + + v->fVertices.reserve(inPts.size()); + for (size_t i = 0; i < inPts.size(); ++i) { + v->fVertices.push_back(BezierVertex({inPts[i], outPts[i], verts[i]})); + } + v->fClosed = json::ValueRef(fValue->operator[]("c")).toDefault(false); + + return true; +} + +size_t ValueRef::size() const { + return this->isArray() ? fValue->Size() : 0; +} + +ValueRef ValueRef::operator[](size_t i) const { + return this->isArray() ? ValueRef(fValue->operator[](i)) : ValueRef(); +} + +ValueRef ValueRef::operator[](const char* key) const { + if (!this->isObject()) + return ValueRef(); + + const auto m = fValue->FindMember(key); + return m == fValue->MemberEnd() ? ValueRef() : ValueRef(m->value); +} + +const rapidjson::Value* ValueRef::begin() const { + if (this->isArray()) { + return fValue->Begin(); + } + if (this->isObject()) { + return &fValue->MemberBegin()->value; + } + + return nullptr; +} + +const rapidjson::Value* ValueRef::end() const { + if (this->isArray()) { + return fValue->End(); + } + if (this->isObject()) { + return &fValue->MemberEnd()->value; + } + + return nullptr; +} + +SkString json::ValueRef::toString() const { +#ifdef SK_DEBUG + rapidjson::StringBuffer buf; + rapidjson::PrettyWriter writer(buf); + fValue->Accept(writer); + + return SkString(buf.GetString()); +#else + return SkString(); +#endif // SK_DEBUG +} + +Document::Document(SkStream* stream) { + if (!stream->hasLength()) { + SkDebugf("!! unsupported unseekable json stream\n"); + return; + } + + // RapidJSON provides three DOM-builder approaches: + // + // 1) in-place : all data buffered, constructs the DOM in-place -- this is the fastest + // 2) from buffer: all data buffered, copies to DOM -- this is slightly slower + // 3) from stream: streamed data, reads/copies to DOM -- this is *significantly* slower + // + // We like fast, so #1 it is. + + // The buffer needs to be C-string. + const auto size = stream->getLength(); + fData = SkData::MakeUninitialized(size + 1); + if (stream->read(fData->writable_data(), size) < size) { + SkDebugf("!! could not read JSON stream\n"); + return; + } + + auto data = static_cast(fData->writable_data()); + data[size] = '\0'; + + fDocument.ParseInsitu(data); + +#ifdef SK_DEBUG + if (fDocument.HasParseError()) { + SkDebugf("!! failed to parse json: %s\n", + rapidjson::GetParseError_En(fDocument.GetParseError())); + } +#endif +} + +} // namespace json + +} // namespace skottie diff --git a/experimental/skottie/SkottieJson.h b/experimental/skottie/SkottieJson.h new file mode 100644 index 0000000000..76e17c610e --- /dev/null +++ b/experimental/skottie/SkottieJson.h @@ -0,0 +1,76 @@ +/* + * Copyright 2018 Google Inc. + * + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#ifndef SkottieJson_DEFINED +#define SkottieJson_DEFINED + +#include "SkRefCnt.h" + +#include "rapidjson/document.h" + +class SkData; +class SkStream; +class SkString; + +namespace skottie { + +namespace json { + +class ValueRef { +public: + ValueRef() : fValue(nullptr) {} + ValueRef(const rapidjson::Value& v) : fValue(v.IsNull() ? nullptr : &v) {} + + bool isNull() const { return !fValue; } + bool isObject() const { return fValue && fValue->IsObject(); } + bool isArray() const { return fValue && fValue->IsArray(); } + + template + bool to(T*) const; + + template + T toDefault(const T& defaultValue) const { + T v; + if (!this->to(&v)) { + v = defaultValue; + } + return v; + } + + size_t size() const; + ValueRef operator[](size_t i) const; + ValueRef operator[](const char* key) const; + + bool operator==(const ValueRef& other) const { return fValue == other.fValue; } + bool operator!=(const ValueRef& other) const { return !(*this == other); } + + const rapidjson::Value* begin() const; + const rapidjson::Value* end() const; + + SkString toString() const; + +private: + const rapidjson::Value* fValue; +}; + +// Container for the json DOM +class Document { +public: + explicit Document(SkStream*); + + ValueRef root() const { return fDocument; } + +private: + sk_sp fData; // raw data + rapidjson::Document fDocument; // in-place json DOM +}; + +} // namespace json + +} // namespace skottie + +#endif // SkottieJson_DEFINED diff --git a/experimental/skottie/SkottieParser.cpp b/experimental/skottie/SkottieParser.cpp deleted file mode 100644 index 36690036d2..0000000000 --- a/experimental/skottie/SkottieParser.cpp +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2018 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#include "SkottieParser.h" - -#include "SkJSONCPP.h" -#include "SkScalar.h" -#include "SkPath.h" -#include "SkPoint.h" -#include "SkString.h" -#include "SkottieValue.h" - -#include - -namespace skottie { - -template <> -bool Parse(const Json::Value& jv, SkScalar* v) { - // Some versions wrap values as single-element arrays. - if (jv.isArray() && jv.size() == 1) { - return Parse(jv[0], v); - } - - if (jv.isNull() || !jv.isConvertibleTo(Json::realValue)) - return false; - - *v = jv.asFloat(); - - return true; -} - -template <> -bool Parse(const Json::Value& jv, bool* v) { - if (jv.isNull() || !jv.isConvertibleTo(Json::booleanValue)) - return false; - - *v = jv.asBool(); - - return true; -} - -template <> -bool Parse(const Json::Value& jv, int* v) { - if (jv.isNull() || !jv.isConvertibleTo(Json::intValue)) - return false; - - *v = jv.asInt(); - - return true; -} - -template <> -bool Parse(const Json::Value& jv, SkString* v) { - if (jv.isNull() || !jv.isConvertibleTo(Json::stringValue)) - return false; - - v->set(jv.isString() ? jv.asCString() : jv.asString().c_str()); - - return true; -} - -template <> -bool Parse(const Json::Value& jv, SkPoint* v) { - if (!jv.isObject()) - return false; - - const auto& jvx = jv["x"], - jvy = jv["y"]; - - // Some BM versions seem to store x/y as single-element arrays. - return Parse(jvx.isArray() ? jvx[0] : jvx, &v->fX) - && Parse(jvy.isArray() ? jvy[0] : jvy, &v->fY); -} - -template <> -bool Parse>(const Json::Value& jv, std::vector* v) { - if (!jv.isArray()) - return false; - - v->resize(jv.size()); - - for (Json::ArrayIndex i = 0; i < jv.size(); ++i) { - if (!Parse(jv[i], v->data() + i)) { - return false; - } - } - - return true; -} - -namespace { - -bool ParsePointVec(const Json::Value& jv, std::vector* pts) { - if (!jv.isArray()) - return false; - - pts->clear(); - pts->reserve(jv.size()); - - std::vector vec; - for (Json::ArrayIndex i = 0; i < jv.size(); ++i) { - if (!Parse(jv[i], &vec) || vec.size() != 2) - return false; - pts->push_back(SkPoint::Make(vec[0], vec[1])); - } - - return true; -} - -} // namespace - -template <> -bool Parse(const Json::Value& jv, ShapeValue* v) { - SkASSERT(v->fVertices.empty()); - - // Some versions wrap values as single-element arrays. - if (jv.isArray() && jv.size() == 1) { - return Parse(jv[0], v); - } - - std::vector inPts, // Cubic Bezier "in" control points, relative to vertices. - outPts, // Cubic Bezier "out" control points, relative to vertices. - verts; // Cubic Bezier vertices. - - if (!jv.isObject() || - !ParsePointVec(jv["i"], &inPts) || - !ParsePointVec(jv["o"], &outPts) || - !ParsePointVec(jv["v"], &verts) || - inPts.size() != outPts.size() || - inPts.size() != verts.size()) { - - return false; - } - - v->fVertices.reserve(inPts.size()); - for (size_t i = 0; i < inPts.size(); ++i) { - v->fVertices.push_back(BezierVertex({inPts[i], outPts[i], verts[i]})); - } - v->fClosed = ParseDefault(jv["c"], false); - - return true; -} - -} // nasmespace skottie diff --git a/experimental/skottie/SkottieParser.h b/experimental/skottie/SkottieParser.h deleted file mode 100644 index b805484cd0..0000000000 --- a/experimental/skottie/SkottieParser.h +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018 Google Inc. - * - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -#ifndef SkottieParser_DEFINED -#define SkottieParser_DEFINED - -namespace Json { class Value; } - -namespace skottie { - -template -bool Parse(const Json::Value&, T*); - -template -static inline T ParseDefault(const Json::Value& jv, const T& defaultValue) { - T v; - if (!Parse(jv, &v)) - v = defaultValue; - return v; -} - -} // nasmespace skottie - -#endif // SkottieParser_DEFINED -- cgit v1.2.3