From 7d42c44cddee7b7a12fa7c59099e989995eabf3a Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Thu, 14 Jun 2018 16:16:01 -0400 Subject: [skottie] Switch to SkJSON Convert to SkJSON APIs and remove RapidJSON dependency. TBR= Change-Id: I5b4fd78821a443326e3a5b370748d840b80ef279 Reviewed-on: https://skia-review.googlesource.com/134612 Reviewed-by: Mike Klein Commit-Queue: Florin Malita --- modules/skjson/BUILD.gn | 1 - modules/skjson/src/SkJSONBench.cpp | 2 +- modules/skottie/BUILD.gn | 4 +- modules/skottie/include/Skottie.h | 5 +- modules/skottie/src/Skottie.cpp | 358 +++++++++++++++----------------- modules/skottie/src/SkottieAnimator.cpp | 88 ++++---- modules/skottie/src/SkottieAnimator.h | 6 +- modules/skottie/src/SkottieJson.cpp | 207 ++++++------------ modules/skottie/src/SkottieJson.h | 62 +----- 9 files changed, 298 insertions(+), 435 deletions(-) (limited to 'modules') diff --git a/modules/skjson/BUILD.gn b/modules/skjson/BUILD.gn index 531f0e0443..dd8ca65973 100644 --- a/modules/skjson/BUILD.gn +++ b/modules/skjson/BUILD.gn @@ -65,7 +65,6 @@ source_set("bench") { deps = [ ":skjson", "../..:skia", - "../../third_party/rapidjson", ] } } diff --git a/modules/skjson/src/SkJSONBench.cpp b/modules/skjson/src/SkJSONBench.cpp index b9c3664306..4ef1d6c01b 100644 --- a/modules/skjson/src/SkJSONBench.cpp +++ b/modules/skjson/src/SkJSONBench.cpp @@ -55,7 +55,7 @@ private: DEF_BENCH( return new JsonBench; ) -#if !defined(SK_BUILD_FOR_ANDROID_FRAMEWORK) +#if (0) #include "rapidjson/document.h" diff --git a/modules/skottie/BUILD.gn b/modules/skottie/BUILD.gn index 26b48bc59d..dff355cf82 100644 --- a/modules/skottie/BUILD.gn +++ b/modules/skottie/BUILD.gn @@ -34,8 +34,8 @@ source_set("skottie") { configs += [ "../../:skia_private" ] deps = [ "../..:skia", - "../../third_party/rapidjson", - "../sksg:sksg", + "../skjson", + "../sksg", ] } } diff --git a/modules/skottie/include/Skottie.h b/modules/skottie/include/Skottie.h index a1993832f6..54f293c849 100644 --- a/modules/skottie/include/Skottie.h +++ b/modules/skottie/include/Skottie.h @@ -19,12 +19,11 @@ class SkCanvas; struct SkRect; class SkStream; +namespace skjson { class ObjectValue; } namespace sksg { class Scene; } namespace skottie { -namespace json { class ValueRef; } - class SK_API ResourceProvider : public SkNoncopyable { public: virtual ~ResourceProvider() = default; @@ -78,7 +77,7 @@ public: private: Animation(const ResourceProvider&, SkString ver, const SkSize& size, SkScalar fps, - const json::ValueRef&, Stats*); + const skjson::ObjectValue&, Stats*); SkString fVersion; SkSize fSize; diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp index 229b5c600a..71521c6691 100644 --- a/modules/skottie/src/Skottie.cpp +++ b/modules/skottie/src/Skottie.cpp @@ -54,8 +54,8 @@ namespace skottie { namespace { struct AssetInfo { - json::ValueRef fAsset; - mutable bool fIsAttaching; // Used for cycle detection + const skjson::ObjectValue* fAsset; + mutable bool fIsAttaching; // Used for cycle detection }; using AssetMap = SkTHashMap; @@ -67,17 +67,14 @@ struct AttachContext { sksg::AnimatorList& fAnimators; }; -bool LogFail(const json::ValueRef& json, const char* msg) { +bool LogFail(const skjson::Value& json, const char* msg) { const auto dump = json.toString(); LOG("!! %s: %s\n", msg, dump.c_str()); return false; } -sk_sp AttachMatrix(const json::ValueRef& t, AttachContext* ctx, - sk_sp parentMatrix) { - if (!t.isObject()) - return nullptr; - +sk_sp AttachMatrix(const skjson::ObjectValue& t, AttachContext* ctx, + sk_sp parentMatrix) { auto matrix = sksg::Matrix::Make(SkMatrix::I(), std::move(parentMatrix)); auto adapter = sk_make_sp(matrix); auto anchor_attached = BindProperty(t["a"], &ctx->fAnimators, @@ -93,13 +90,13 @@ sk_sp AttachMatrix(const json::ValueRef& t, AttachContext* ctx, adapter->setScale(ValueTraits::As(s)); }); - auto jrotation = t["r"]; - if (jrotation.isNull()) { + const auto* jrotation = &t["r"]; + if (jrotation->is()) { // 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); }); @@ -125,10 +122,10 @@ sk_sp AttachMatrix(const json::ValueRef& t, AttachContext* ctx, return matrix; } -sk_sp AttachOpacity(const json::ValueRef& jtransform, AttachContext* ctx, +sk_sp AttachOpacity(const skjson::ObjectValue& jtransform, AttachContext* ctx, sk_sp childNode) { - if (!jtransform.isObject() || !childNode) - return childNode; + if (!childNode) + return nullptr; static constexpr ScalarValue kNoopOpacity = 100; auto opacityNode = sksg::OpacityEffect::Make(childNode); @@ -145,9 +142,9 @@ sk_sp AttachOpacity(const json::ValueRef& jtransform, AttachCo return std::move(opacityNode); } -sk_sp AttachComposition(const json::ValueRef&, AttachContext* ctx); +sk_sp AttachComposition(const skjson::ObjectValue&, AttachContext* ctx); -sk_sp AttachPath(const json::ValueRef& jpath, AttachContext* ctx) { +sk_sp AttachPath(const skjson::Value& jpath, AttachContext* ctx) { auto path_node = sksg::Path::Make(); return BindProperty(jpath, &ctx->fAnimators, [path_node](const ShapeValue& p) { @@ -157,15 +154,12 @@ sk_sp AttachPath(const json::ValueRef& jpath, AttachContext* ctx) { : nullptr; } -sk_sp AttachPathGeometry(const json::ValueRef& jpath, AttachContext* ctx) { - SkASSERT(jpath.isObject()); - +sk_sp AttachPathGeometry(const skjson::ObjectValue& jpath, AttachContext* ctx) { return AttachPath(jpath["ks"], ctx); } -sk_sp AttachRRectGeometry(const json::ValueRef& jrect, AttachContext* ctx) { - SkASSERT(jrect.isObject()); - +sk_sp AttachRRectGeometry(const skjson::ObjectValue& jrect, + AttachContext* ctx) { auto rect_node = sksg::RRect::Make(); auto adapter = sk_make_sp(rect_node); @@ -189,9 +183,8 @@ sk_sp AttachRRectGeometry(const json::ValueRef& jrect, Attac return std::move(rect_node); } -sk_sp AttachEllipseGeometry(const json::ValueRef& jellipse, AttachContext* ctx) { - SkASSERT(jellipse.isObject()); - +sk_sp AttachEllipseGeometry(const skjson::ObjectValue& jellipse, + AttachContext* ctx) { auto rect_node = sksg::RRect::Make(); auto adapter = sk_make_sp(rect_node); @@ -213,15 +206,14 @@ sk_sp AttachEllipseGeometry(const json::ValueRef& jellipse, return std::move(rect_node); } -sk_sp AttachPolystarGeometry(const json::ValueRef& jstar, AttachContext* ctx) { - SkASSERT(jstar.isObject()); - +sk_sp AttachPolystarGeometry(const skjson::ObjectValue& jstar, + AttachContext* ctx) { static constexpr PolyStarAdapter::Type gTypes[] = { PolyStarAdapter::Type::kStar, // "sy": 1 PolyStarAdapter::Type::kPoly, // "sy": 2 }; - const auto type = jstar["sy"].toDefault(0) - 1; + const auto type = ParseDefault(jstar["sy"], 0) - 1; if (type < 0 || type >= SkTo(SK_ARRAY_COUNT(gTypes))) { LogFail(jstar, "Unknown polystar type"); return nullptr; @@ -262,11 +254,9 @@ sk_sp AttachPolystarGeometry(const json::ValueRef& jstar, At return std::move(path_node); } -sk_sp AttachColor(const json::ValueRef& obj, AttachContext* ctx) { - SkASSERT(obj.isObject()); - +sk_sp AttachColor(const skjson::ObjectValue& jcolor, AttachContext* ctx) { auto color_node = sksg::Color::Make(SK_ColorBLACK); - auto color_attached = BindProperty(obj["c"], &ctx->fAnimators, + auto color_attached = BindProperty(jcolor["c"], &ctx->fAnimators, [color_node](const VectorValue& c) { color_node->setColor(ValueTraits::As(c)); }); @@ -274,21 +264,19 @@ sk_sp AttachColor(const json::ValueRef& obj, AttachContext* ctx) { return color_attached ? color_node : nullptr; } -sk_sp AttachGradient(const json::ValueRef& obj, AttachContext* ctx) { - SkASSERT(obj.isObject()); - - const auto stops = obj["g"]; - if (!stops.isObject()) +sk_sp AttachGradient(const skjson::ObjectValue& jgrad, AttachContext* ctx) { + const skjson::ObjectValue* stops = jgrad["g"]; + if (!stops) return nullptr; - const auto stopCount = stops["p"].toDefault(-1); + const auto stopCount = ParseDefault((*stops)["p"], -1); if (stopCount < 0) return nullptr; sk_sp gradient_node; sk_sp adapter; - if (obj["t"].toDefault(1) == 1) { + if (ParseDefault(jgrad["t"], 1) == 1) { auto linear_node = sksg::LinearGradient::Make(); adapter = sk_make_sp(linear_node, stopCount); gradient_node = std::move(linear_node); @@ -300,15 +288,15 @@ sk_sp AttachGradient(const json::ValueRef& obj, AttachContext* c gradient_node = std::move(radial_node); } - BindProperty(stops["k"], &ctx->fAnimators, + BindProperty((*stops)["k"], &ctx->fAnimators, [adapter](const VectorValue& stops) { adapter->setColorStops(stops); }); - BindProperty(obj["s"], &ctx->fAnimators, + BindProperty(jgrad["s"], &ctx->fAnimators, [adapter](const VectorValue& s) { adapter->setStartPoint(ValueTraits::As(s)); }); - BindProperty(obj["e"], &ctx->fAnimators, + BindProperty(jgrad["e"], &ctx->fAnimators, [adapter](const VectorValue& e) { adapter->setEndPoint(ValueTraits::As(e)); }); @@ -316,7 +304,7 @@ sk_sp AttachGradient(const json::ValueRef& obj, AttachContext* c return gradient_node; } -sk_sp AttachPaint(const json::ValueRef& jpaint, AttachContext* ctx, +sk_sp AttachPaint(const skjson::ObjectValue& jpaint, AttachContext* ctx, sk_sp paint_node) { if (paint_node) { paint_node->setAntiAlias(true); @@ -331,10 +319,8 @@ sk_sp AttachPaint(const json::ValueRef& jpaint, AttachContext* return paint_node; } -sk_sp AttachStroke(const json::ValueRef& jstroke, AttachContext* ctx, +sk_sp AttachStroke(const skjson::ObjectValue& jstroke, AttachContext* ctx, sk_sp stroke_node) { - SkASSERT(jstroke.isObject()); - if (!stroke_node) return nullptr; @@ -347,14 +333,14 @@ sk_sp AttachStroke(const json::ValueRef& jstroke, AttachContext if (!width_attached) return nullptr; - stroke_node->setStrokeMiter(jstroke["ml"].toDefault(4.0f)); + stroke_node->setStrokeMiter(ParseDefault(jstroke["ml"], 4.0f)); static constexpr SkPaint::Join gJoins[] = { SkPaint::kMiter_Join, SkPaint::kRound_Join, SkPaint::kBevel_Join, }; - stroke_node->setStrokeJoin(gJoins[SkTPin(jstroke["lj"].toDefault(1) - 1, + stroke_node->setStrokeJoin(gJoins[SkTPin(ParseDefault(jstroke["lj"], 1) - 1, 0, SK_ARRAY_COUNT(gJoins) - 1)]); static constexpr SkPaint::Cap gCaps[] = { @@ -362,38 +348,32 @@ sk_sp AttachStroke(const json::ValueRef& jstroke, AttachContext SkPaint::kRound_Cap, SkPaint::kSquare_Cap, }; - stroke_node->setStrokeCap(gCaps[SkTPin(jstroke["lc"].toDefault(1) - 1, + stroke_node->setStrokeCap(gCaps[SkTPin(ParseDefault(jstroke["lc"], 1) - 1, 0, SK_ARRAY_COUNT(gCaps) - 1)]); return stroke_node; } -sk_sp AttachColorFill(const json::ValueRef& jfill, AttachContext* ctx) { - SkASSERT(jfill.isObject()); - +sk_sp AttachColorFill(const skjson::ObjectValue& jfill, AttachContext* ctx) { return AttachPaint(jfill, ctx, AttachColor(jfill, ctx)); } -sk_sp AttachGradientFill(const json::ValueRef& jfill, AttachContext* ctx) { - SkASSERT(jfill.isObject()); - +sk_sp AttachGradientFill(const skjson::ObjectValue& jfill, AttachContext* ctx) { return AttachPaint(jfill, ctx, AttachGradient(jfill, ctx)); } -sk_sp AttachColorStroke(const json::ValueRef& jstroke, AttachContext* ctx) { - SkASSERT(jstroke.isObject()); - +sk_sp AttachColorStroke(const skjson::ObjectValue& jstroke, AttachContext* ctx) { return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx))); } -sk_sp AttachGradientStroke(const json::ValueRef& jstroke, AttachContext* ctx) { - SkASSERT(jstroke.isObject()); - +sk_sp AttachGradientStroke(const skjson::ObjectValue& jstroke, + AttachContext* ctx) { return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx))); } std::vector> AttachMergeGeometryEffect( - const json::ValueRef& jmerge, AttachContext* ctx, std::vector>&& geos) { + const skjson::ObjectValue& jmerge, AttachContext*, + std::vector>&& geos) { std::vector> merged; static constexpr sksg::Merge::Mode gModes[] = { @@ -404,7 +384,7 @@ std::vector> AttachMergeGeometryEffect( sksg::Merge::Mode::kXOR , // "mm": 5 }; - const auto mode = gModes[SkTPin(jmerge["mm"].toDefault(1) - 1, + const auto mode = gModes[SkTPin(ParseDefault(jmerge["mm"], 1) - 1, 0, SK_ARRAY_COUNT(gModes) - 1)]; merged.push_back(sksg::Merge::Make(std::move(geos), mode)); @@ -412,14 +392,15 @@ std::vector> AttachMergeGeometryEffect( } std::vector> AttachTrimGeometryEffect( - const json::ValueRef& jtrim, AttachContext* ctx, std::vector>&& geos) { + const skjson::ObjectValue& 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(jtrim["m"].toDefault(1) - 1, + const auto mode = gModes[SkTPin(ParseDefault(jtrim["m"], 1) - 1, 0, SK_ARRAY_COUNT(gModes) - 1)]; std::vector> inputs; @@ -454,7 +435,8 @@ std::vector> AttachTrimGeometryEffect( } std::vector> AttachRoundGeometryEffect( - const json::ValueRef& jtrim, AttachContext* ctx, std::vector>&& geos) { + const skjson::ObjectValue& jtrim, AttachContext* ctx, + std::vector>&& geos) { std::vector> rounded; rounded.reserve(geos.size()); @@ -472,7 +454,7 @@ std::vector> AttachRoundGeometryEffect( return rounded; } -using GeometryAttacherT = sk_sp (*)(const json::ValueRef&, AttachContext*); +using GeometryAttacherT = sk_sp (*)(const skjson::ObjectValue&, AttachContext*); static constexpr GeometryAttacherT gGeometryAttachers[] = { AttachPathGeometry, AttachRRectGeometry, @@ -480,7 +462,7 @@ static constexpr GeometryAttacherT gGeometryAttachers[] = { AttachPolystarGeometry, }; -using PaintAttacherT = sk_sp (*)(const json::ValueRef&, AttachContext*); +using PaintAttacherT = sk_sp (*)(const skjson::ObjectValue&, AttachContext*); static constexpr PaintAttacherT gPaintAttachers[] = { AttachColorFill, AttachColorStroke, @@ -489,7 +471,7 @@ static constexpr PaintAttacherT gPaintAttachers[] = { }; using GeometryEffectAttacherT = - std::vector> (*)(const json::ValueRef&, + std::vector> (*)(const skjson::ObjectValue&, AttachContext*, std::vector>&&); static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = { @@ -512,7 +494,7 @@ struct ShapeInfo { uint32_t fAttacherIndex; // index into respective attacher tables }; -const ShapeInfo* FindShapeInfo(const json::ValueRef& shape) { +const ShapeInfo* FindShapeInfo(const skjson::ObjectValue& jshape) { static constexpr ShapeInfo gShapeInfo[] = { { "el", ShapeType::kGeometry , 2 }, // ellipse -> AttachEllipseGeometry { "fl", ShapeType::kPaint , 0 }, // fill -> AttachColorFill @@ -530,7 +512,7 @@ const ShapeInfo* FindShapeInfo(const json::ValueRef& shape) { }; SkString type; - if (!shape["ty"].to(&type) || type.isEmpty()) + if (!Parse(jshape["ty"], &type) || type.isEmpty()) return nullptr; const auto* info = bsearch(type.c_str(), @@ -546,8 +528,8 @@ const ShapeInfo* FindShapeInfo(const json::ValueRef& shape) { } struct GeometryEffectRec { - const json::ValueRef fJson; - GeometryEffectAttacherT fAttach; + const skjson::ObjectValue& fJson; + GeometryEffectAttacherT fAttach; }; struct AttachShapeContext { @@ -566,8 +548,8 @@ struct AttachShapeContext { size_t fCommittedAnimators; }; -sk_sp AttachShape(const json::ValueRef& jshape, AttachShapeContext* shapeCtx) { - if (!jshape.isArray()) +sk_sp AttachShape(const skjson::ArrayValue* jshape, AttachShapeContext* shapeCtx) { + if (!jshape) return nullptr; SkDEBUGCODE(const auto initialGeometryEffects = shapeCtx->fGeometryEffectStack->size();) @@ -577,8 +559,8 @@ sk_sp AttachShape(const json::ValueRef& jshape, AttachShapeCon sk_sp shape_matrix; struct ShapeRec { - const json::ValueRef fJson; - const ShapeInfo& fInfo; + const skjson::ObjectValue& fJson; + const ShapeInfo& fInfo; }; // First pass (bottom->top): @@ -588,27 +570,29 @@ sk_sp AttachShape(const json::ValueRef& jshape, AttachShapeCon // * store recs for next pass // std::vector recs; - for (size_t i = 0; i < jshape.size(); ++i) { - const auto s = jshape[jshape.size() - 1 - i]; - const auto* info = FindShapeInfo(s); + for (size_t i = 0; i < jshape->size(); ++i) { + const skjson::ObjectValue* shape = (*jshape)[jshape->size() - 1 - i]; + if (!shape) continue; + + const auto* info = FindShapeInfo(*shape); if (!info) { - LogFail(s["ty"], "Unknown shape"); + LogFail((*shape)["ty"], "Unknown shape"); continue; } - recs.push_back({ s, *info }); + recs.push_back({ *shape, *info }); switch (info->fShapeType) { case ShapeType::kTransform: - if ((shape_matrix = AttachMatrix(s, shapeCtx->fCtx, nullptr))) { + if ((shape_matrix = AttachMatrix(*shape, shapeCtx->fCtx, nullptr))) { shape_wrapper = sksg::Transform::Make(std::move(shape_wrapper), shape_matrix); } - shape_wrapper = AttachOpacity(s, shapeCtx->fCtx, std::move(shape_wrapper)); + shape_wrapper = AttachOpacity(*shape, shapeCtx->fCtx, std::move(shape_wrapper)); break; case ShapeType::kGeometryEffect: SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers)); shapeCtx->fGeometryEffectStack->push_back( - { s, gGeometryEffectAttachers[info->fAttacherIndex] }); + { *shape, gGeometryEffectAttachers[info->fAttacherIndex] }); break; default: break; @@ -640,7 +624,7 @@ sk_sp AttachShape(const json::ValueRef& jshape, AttachShapeCon std::move(geos)); } - SkASSERT(shapeCtx->fGeometryEffectStack->back().fJson == rec->fJson); + SkASSERT(&shapeCtx->fGeometryEffectStack->back().fJson == &rec->fJson); SkASSERT(shapeCtx->fGeometryEffectStack->back().fAttach == gGeometryEffectAttachers[rec->fInfo.fAttacherIndex]); shapeCtx->fGeometryEffectStack->pop_back(); @@ -762,10 +746,10 @@ sk_sp AttachNestedAnimation(const char* path, AttachContext* c return sk_make_sp(std::move(animation)); } -sk_sp AttachAssetRef(const json::ValueRef& jlayer, AttachContext* ctx, - sk_sp(*attach_proc)(const json::ValueRef& comp, AttachContext* ctx)) { +sk_sp AttachAssetRef(const skjson::ObjectValue& jlayer, AttachContext* ctx, + sk_sp(*attach_proc)(const skjson::ObjectValue& comp, AttachContext* ctx)) { - const auto refId = jlayer["refId"].toDefault(SkString()); + const auto refId = ParseDefault(jlayer["refId"], SkString()); if (refId.isEmpty()) { LOG("!! Layer missing refId\n"); return nullptr; @@ -787,18 +771,16 @@ sk_sp AttachAssetRef(const json::ValueRef& jlayer, AttachConte } asset_info->fIsAttaching = true; - auto asset = attach_proc(asset_info->fAsset, ctx); + auto asset = attach_proc(*asset_info->fAsset, ctx); asset_info->fIsAttaching = false; return asset; } -sk_sp AttachCompLayer(const json::ValueRef& jlayer, AttachContext* ctx, +sk_sp AttachCompLayer(const skjson::ObjectValue& jlayer, AttachContext* ctx, float* time_bias, float* time_scale) { - SkASSERT(jlayer.isObject()); - - const auto start_time = jlayer["st"].toDefault(0.0f), - stretch_time = jlayer["sr"].toDefault(1.0f); + const auto start_time = ParseDefault(jlayer["st"], 0.0f), + stretch_time = ParseDefault(jlayer["sr"], 1.0f); *time_bias = -start_time; *time_scale = sk_ieee_float_divide(1, stretch_time); @@ -809,13 +791,11 @@ sk_sp AttachCompLayer(const json::ValueRef& jlayer, AttachCont return AttachAssetRef(jlayer, ctx, AttachComposition); } -sk_sp AttachSolidLayer(const json::ValueRef& jlayer, AttachContext*, +sk_sp AttachSolidLayer(const skjson::ObjectValue& jlayer, AttachContext*, float*, float*) { - SkASSERT(jlayer.isObject()); - - const auto size = SkSize::Make(jlayer["sw"].toDefault(0.0f), - jlayer["sh"].toDefault(0.0f)); - const auto hex = jlayer["sc"].toDefault(SkString()); + const auto size = SkSize::Make(ParseDefault(jlayer["sw"], 0.0f), + ParseDefault(jlayer["sh"], 0.0f)); + const auto hex = ParseDefault(jlayer["sc"], SkString()); uint32_t c; if (size.isEmpty() || !hex.startsWith("#") || @@ -830,11 +810,9 @@ sk_sp AttachSolidLayer(const json::ValueRef& jlayer, AttachCon sksg::Color::Make(color)); } -sk_sp AttachImageAsset(const json::ValueRef& jimage, AttachContext* ctx) { - SkASSERT(jimage.isObject()); - - const auto name = jimage["p"].toDefault(SkString()), - path = jimage["u"].toDefault(SkString()); +sk_sp AttachImageAsset(const skjson::ObjectValue& jimage, AttachContext* ctx) { + const auto name = ParseDefault(jimage["p"], SkString()), + path = ParseDefault(jimage["u"], SkString()); if (name.isEmpty()) return nullptr; @@ -851,25 +829,20 @@ sk_sp AttachImageAsset(const json::ValueRef& jimage, AttachCon SkImage::MakeFromEncoded(SkData::MakeFromStream(resStream.get(), resStream->getLength()))); } -sk_sp AttachImageLayer(const json::ValueRef& jlayer, AttachContext* ctx, +sk_sp AttachImageLayer(const skjson::ObjectValue& jlayer, AttachContext* ctx, float*, float*) { - SkASSERT(jlayer.isObject()); - return AttachAssetRef(jlayer, ctx, AttachImageAsset); } -sk_sp AttachNullLayer(const json::ValueRef& layer, AttachContext*, float*, float*) { - SkASSERT(layer.isObject()); - +sk_sp AttachNullLayer(const skjson::ObjectValue& layer, AttachContext*, + float*, float*) { // Null layers are used solely to drive dependent transforms, // but we use free-floating sksg::Matrices for that purpose. return nullptr; } -sk_sp AttachShapeLayer(const json::ValueRef& layer, AttachContext* ctx, +sk_sp AttachShapeLayer(const skjson::ObjectValue& layer, AttachContext* ctx, float*, float*) { - SkASSERT(layer.isObject()); - std::vector> geometryStack; std::vector geometryEffectStack; AttachShapeContext shapeCtx(ctx, &geometryStack, &geometryEffectStack, ctx->fAnimators.size()); @@ -885,28 +858,23 @@ sk_sp AttachShapeLayer(const json::ValueRef& layer, AttachCont return shapeNode; } -sk_sp AttachTextLayer(const json::ValueRef& layer, AttachContext*, float*, float*) { - SkASSERT(layer.isObject()); - +sk_sp AttachTextLayer(const skjson::ObjectValue& layer, AttachContext*, + float*, float*) { LOG("?? Text layer stub\n"); return nullptr; } struct AttachLayerContext { - AttachLayerContext(const json::ValueRef& jlayers, AttachContext* ctx) - : fLayerList(jlayers), fCtx(ctx) { - SkASSERT(fLayerList.isArray()); - } + AttachLayerContext(const skjson::ArrayValue& jlayers, AttachContext* ctx) + : fLayerList(jlayers), fCtx(ctx) {} - const json::ValueRef fLayerList; + const skjson::ArrayValue& fLayerList; AttachContext* fCtx; SkTHashMap> fLayerMatrixMap; sk_sp fCurrentMatte; - sk_sp AttachLayerMatrix(const json::ValueRef& jlayer) { - SkASSERT(jlayer.isObject()); - - const auto layer_index = jlayer["ind"].toDefault(-1); + sk_sp AttachLayerMatrix(const skjson::ObjectValue& jlayer) { + const auto layer_index = ParseDefault(jlayer["ind"], -1); if (layer_index < 0) return nullptr; @@ -917,26 +885,27 @@ struct AttachLayerContext { } private: - sk_sp AttachParentLayerMatrix(const json::ValueRef& jlayer, int layer_index) { - SkASSERT(jlayer.isObject()); - - const auto parent_index = jlayer["parent"].toDefault(-1); + sk_sp AttachParentLayerMatrix(const skjson::ObjectValue& jlayer, + int layer_index) { + const auto parent_index = ParseDefault(jlayer["parent"], -1); if (parent_index < 0 || parent_index == layer_index) return nullptr; if (auto* m = fLayerMatrixMap.find(parent_index)) return *m; - for (const json::ValueRef l : fLayerList) { - if (l["ind"].toDefault(-1) == parent_index) { - return this->AttachLayerMatrixImpl(l, parent_index); + for (const skjson::ObjectValue* l : fLayerList) { + if (!l) continue; + + if (ParseDefault((*l)["ind"], -1) == parent_index) { + return this->AttachLayerMatrixImpl(*l, parent_index); } } return nullptr; } - sk_sp AttachLayerMatrixImpl(const json::ValueRef& jlayer, int layer_index) { + sk_sp AttachLayerMatrixImpl(const skjson::ObjectValue& jlayer, int layer_index) { SkASSERT(!fLayerMatrixMap.find(layer_index)); // Add a stub entry to break recursion cycles. @@ -944,7 +913,12 @@ private: auto parent_matrix = this->AttachParentLayerMatrix(jlayer, layer_index); - return *fLayerMatrixMap.set(layer_index, AttachMatrix(jlayer["ks"], fCtx, parent_matrix)); + if (const skjson::ObjectValue* jtransform = jlayer["ks"]) { + return *fLayerMatrixMap.set(layer_index, AttachMatrix(*jtransform, fCtx, + std::move(parent_matrix))); + + } + return nullptr; } }; @@ -962,11 +936,10 @@ SkBlendMode MaskBlendMode(char mode) { return SkBlendMode::kSrcOver; } -sk_sp AttachMask(const json::ValueRef& jmask, +sk_sp AttachMask(const skjson::ArrayValue* jmask, AttachContext* ctx, sk_sp childNode) { - if (!jmask.isArray()) - return childNode; + if (!jmask) return childNode; struct MaskRecord { sk_sp mask_path; @@ -977,22 +950,21 @@ sk_sp AttachMask(const json::ValueRef& jmask, bool opaque_mask = true; - for (const json::ValueRef m : jmask) { - if (!m.isObject()) - continue; + for (const skjson::ObjectValue* m : *jmask) { + if (!m) continue; - auto mask_path = AttachPath(m["pt"], ctx); + auto mask_path = AttachPath((*m)["pt"], ctx); if (!mask_path) { - LogFail(m, "Could not parse mask path"); + LogFail(*m, "Could not parse mask path"); continue; } - mask_path->setFillType(m["inv"].toDefault(false) + mask_path->setFillType(ParseDefault((*m)["inv"], false) ? SkPath::kInverseWinding_FillType : SkPath::kWinding_FillType); SkString mode; - if (!m["mode"].to(&mode) || + if (!Parse((*m)["mode"], &mode) || mode.size() != 1 || !strcmp(mode.c_str(), "n")) { // "None" masks have no effect. continue; @@ -1003,7 +975,7 @@ sk_sp AttachMask(const json::ValueRef& jmask, mask_paint->setBlendMode(MaskBlendMode(mode.c_str()[0])); const auto animator_count = ctx->fAnimators.size(); - BindProperty(m["o"], &ctx->fAnimators, + BindProperty((*m)["o"], &ctx->fAnimators, [mask_paint](const ScalarValue& o) { mask_paint->setOpacity(o * 0.01f); }); opaque_mask &= (animator_count == ctx->fAnimators.size() && mask_paint->getOpacity() >= 1); @@ -1031,11 +1003,11 @@ sk_sp AttachMask(const json::ValueRef& jmask, return sksg::MaskEffect::Make(std::move(childNode), std::move(mask_group)); } -sk_sp AttachLayer(const json::ValueRef& jlayer, AttachLayerContext* layerCtx) { - if (!jlayer.isObject()) - return nullptr; +sk_sp AttachLayer(const skjson::ObjectValue* jlayer, + AttachLayerContext* layerCtx) { + if (!jlayer) return nullptr; - using LayerAttacher = sk_sp (*)(const json::ValueRef&, AttachContext*, + using LayerAttacher = sk_sp (*)(const skjson::ObjectValue&, AttachContext*, float* time_bias, float* time_scale); static constexpr LayerAttacher gLayerAttachers[] = { AttachCompLayer, // 'ty': 0 @@ -1046,7 +1018,7 @@ sk_sp AttachLayer(const json::ValueRef& jlayer, AttachLayerCon AttachTextLayer, // 'ty': 5 }; - int type = jlayer["ty"].toDefault(-1); + int type = ParseDefault((*jlayer)["ty"], -1); if (type < 0 || type >= SkTo(SK_ARRAY_COUNT(gLayerAttachers))) { return nullptr; } @@ -1062,26 +1034,29 @@ sk_sp AttachLayer(const json::ValueRef& jlayer, AttachLayerCon time_scale = 1; // Layer content. - auto layer = gLayerAttachers[type](jlayer, &local_ctx, &time_bias, &time_scale); + auto layer = gLayerAttachers[type](*jlayer, &local_ctx, &time_bias, &time_scale); // Clip layers with explicit dimensions. float w = 0, h = 0; - if (jlayer["w"].to(&w) && jlayer["h"].to(&h)) { + if (Parse((*jlayer)["w"], &w) && Parse((*jlayer)["h"], &h)) { layer = sksg::ClipEffect::Make(std::move(layer), sksg::Rect::Make(SkRect::MakeWH(w, h)), true); } // Optional layer mask. - layer = AttachMask(jlayer["masksProperties"], &local_ctx, std::move(layer)); + layer = AttachMask((*jlayer)["masksProperties"], &local_ctx, std::move(layer)); // Optional layer transform. - if (auto layerMatrix = layerCtx->AttachLayerMatrix(jlayer)) { + if (auto layerMatrix = layerCtx->AttachLayerMatrix(*jlayer)) { layer = sksg::Transform::Make(std::move(layer), std::move(layerMatrix)); } // Optional layer opacity. - layer = AttachOpacity(jlayer["ks"], &local_ctx, std::move(layer)); + // TODO: de-dupe this "ks" lookup with matrix above. + if (const skjson::ObjectValue* jtransform = (*jlayer)["ks"]) { + layer = AttachOpacity(*jtransform, &local_ctx, std::move(layer)); + } class LayerController final : public sksg::GroupAnimator { public: @@ -1119,11 +1094,11 @@ sk_sp AttachLayer(const json::ValueRef& jlayer, AttachLayerCon }; auto controller_node = sksg::OpacityEffect::Make(std::move(layer)); - const auto in = jlayer["ip"].toDefault(0.0f), - out = jlayer["op"].toDefault(in); + const auto in = ParseDefault((*jlayer)["ip"], 0.0f), + out = ParseDefault((*jlayer)["op"], in); - if (!jlayer["tm"].isNull()) { - LogFail(jlayer["tm"], "Unsupported time remapping"); + if (!(*jlayer)["tm"].is()) { + LogFail((*jlayer)["tm"], "Unsupported time remapping"); } if (in >= out || !controller_node) @@ -1137,7 +1112,7 @@ sk_sp AttachLayer(const json::ValueRef& jlayer, AttachLayerCon time_bias, time_scale)); - if (jlayer["td"].toDefault(false)) { + if (ParseDefault((*jlayer)["td"], false)) { // This layer is a matte. We apply it as a mask to the next layer. layerCtx->fCurrentMatte = std::move(controller_node); return nullptr; @@ -1149,7 +1124,7 @@ sk_sp AttachLayer(const json::ValueRef& jlayer, AttachLayerCon sksg::MaskEffect::Mode::kNormal, // tt: 1 sksg::MaskEffect::Mode::kInvert, // tt: 2 }; - const auto matteType = jlayer["tt"].toDefault(1) - 1; + const auto matteType = ParseDefault((*jlayer)["tt"], 1) - 1; if (matteType >= 0 && matteType < SkTo(SK_ARRAY_COUNT(gMaskModes))) { return sksg::MaskEffect::Make(std::move(controller_node), @@ -1162,18 +1137,14 @@ sk_sp AttachLayer(const json::ValueRef& jlayer, AttachLayerCon return std::move(controller_node); } -sk_sp AttachComposition(const json::ValueRef& comp, AttachContext* ctx) { - if (!comp.isObject()) - return nullptr; - - const auto jlayers = comp["layers"]; - if (!jlayers.isArray()) - return nullptr; +sk_sp AttachComposition(const skjson::ObjectValue& comp, AttachContext* ctx) { + const skjson::ArrayValue* jlayers = comp["layers"]; + if (!jlayers) return nullptr; SkSTArray<16, sk_sp, true> layers; - AttachLayerContext layerCtx(jlayers, ctx); + AttachLayerContext layerCtx(*jlayers, ctx); - for (const json::ValueRef l : jlayers) { + for (const auto& l : *jlayers) { if (auto layer_fragment = AttachLayer(l, &layerCtx)) { layers.push_back(std::move(layer_fragment)); } @@ -1209,18 +1180,27 @@ sk_sp Animation::Make(SkStream* stream, const ResourceProvider* provi stats->fJsonSize = stream->getLength(); const auto t0 = SkTime::GetMSecs(); - const json::Document doc(stream); - const auto json = doc.root(); - if (!json.isObject()) + auto data = SkData::MakeFromStream(stream, stream->getLength()); + if (!data) { + SkDebugf("!! Failed to read the input stream.\n"); return nullptr; + } + + const skjson::DOM dom(static_cast(data->data()), data->size()); + if (!dom.root().is()) { + // TODO: more error info. + SkDebugf("!! Failed to parse JSON input.\n"); + return nullptr; + } + const auto& json = dom.root().as(); const auto t1 = SkTime::GetMSecs(); stats->fJsonParseTimeMS = t1 - t0; - 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); + 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); if (size.isEmpty() || version.isEmpty() || fps <= 0) { LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f)", @@ -1270,18 +1250,20 @@ sk_sp Animation::MakeFromFile(const char path[], const ResourceProvid } Animation::Animation(const ResourceProvider& resources, - SkString version, const SkSize& size, SkScalar fps, const json::ValueRef& json, - Stats* stats) + SkString version, const SkSize& size, SkScalar fps, + const skjson::ObjectValue& json, Stats* stats) : fVersion(std::move(version)) , fSize(size) , fFrameRate(fps) - , fInPoint(json["ip"].toDefault(0.0f)) - , fOutPoint(SkTMax(json["op"].toDefault(SK_ScalarMax), fInPoint)) { + , fInPoint(ParseDefault(json["ip"], 0.0f)) + , fOutPoint(SkTMax(ParseDefault(json["op"], SK_ScalarMax), fInPoint)) { AssetMap assets; - for (const json::ValueRef asset : json["assets"]) { - if (asset.isObject()) { - assets.set(asset["id"].toDefault(SkString()), { asset, false }); + if (const skjson::ArrayValue* jassets = json["assets"]) { + for (const skjson::ObjectValue* asset : *jassets) { + if (asset) { + assets.set(ParseDefault((*asset)["id"], SkString()), { asset, false }); + } } } diff --git a/modules/skottie/src/SkottieAnimator.cpp b/modules/skottie/src/SkottieAnimator.cpp index 4554409761..a023963906 100644 --- a/modules/skottie/src/SkottieAnimator.cpp +++ b/modules/skottie/src/SkottieAnimator.cpp @@ -21,7 +21,7 @@ namespace { #define LOG SkDebugf -bool LogFail(const json::ValueRef& json, const char* msg) { +bool LogFail(const skjson::Value& json, const char* msg) { const auto dump = json.toString(); LOG("!! %s: %s\n", msg, dump.c_str()); return false; @@ -67,15 +67,14 @@ protected: : SkTPin(fCubicMaps[rec.cmidx].computeYFromX(lt), 0.0f, 1.0f); } - virtual int parseValue(const json::ValueRef&) = 0; + virtual int parseValue(const skjson::Value&) = 0; - void parseKeyFrames(const json::ValueRef& jframes) { - if (!jframes.isArray()) - return; + void parseKeyFrames(const skjson::ArrayValue& jframes) { + for (const skjson::ObjectValue* jframe : jframes) { + if (!jframe) continue; - for (const json::ValueRef jframe : jframes) { float t0; - if (!jframe["t"].to(&t0)) + if (!Parse((*jframe)["t"], &t0)) continue; if (!fRecs.empty()) { @@ -88,24 +87,24 @@ protected: fRecs.back().t1 = t0; } - const auto vidx0 = this->parseValue(jframe["s"]); + const auto vidx0 = this->parseValue((*jframe)["s"]); if (vidx0 < 0) continue; // Defaults for constant frames. int vidx1 = vidx0, cmidx = -1; - if (!jframe["h"].toDefault(false)) { + if (!ParseDefault((*jframe)["h"], false)) { // Regular frame, requires an end value. - vidx1 = this->parseValue(jframe["e"]); + vidx1 = this->parseValue((*jframe)["e"]); if (vidx1 < 0) continue; // default is linear lerp static constexpr SkPoint kDefaultC0 = { 0, 0 }, kDefaultC1 = { 1, 1 }; - const auto c0 = jframe["i"].toDefault(kDefaultC0), - c1 = jframe["o"].toDefault(kDefaultC1); + const auto c0 = ParseDefault((*jframe)["i"], kDefaultC0), + c1 = ParseDefault((*jframe)["o"], kDefaultC1); if (c0 != kDefaultC0 || c1 != kDefaultC1) { // TODO: is it worth de-duping these? @@ -175,9 +174,11 @@ private: template class KeyframeAnimator final : public KeyframeAnimatorBase { public: - static std::unique_ptr Make(const json::ValueRef& jframes, + static std::unique_ptr Make(const skjson::ArrayValue* jv, std::function&& apply) { - std::unique_ptr animator(new KeyframeAnimator(jframes, std::move(apply))); + if (!jv) return nullptr; + + std::unique_ptr animator(new KeyframeAnimator(*jv, std::move(apply))); if (!animator->count()) return nullptr; @@ -193,15 +194,15 @@ protected: } private: - KeyframeAnimator(const json::ValueRef& jframes, + KeyframeAnimator(const skjson::ArrayValue& jframes, std::function&& apply) : fApplyFunc(std::move(apply)) { this->parseKeyFrames(jframes); } - int parseValue(const json::ValueRef& jv) override { + int parseValue(const skjson::Value& jv) override { T val; - if (!jv.to(&val) || (!fVs.empty() && + if (!Parse(jv, &val) || (!fVs.empty() && ValueTraits::Cardinality(val) != ValueTraits::Cardinality(fVs.back()))) { return -1; } @@ -235,21 +236,20 @@ private: }; template -static inline bool BindPropertyImpl(const json::ValueRef& jprop, +static inline bool BindPropertyImpl(const skjson::ObjectValue* jprop, sksg::AnimatorList* animators, std::function&& apply, const T* noop = nullptr) { - if (!jprop.isObject()) - return false; + if (!jprop) 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 (!jpropA.toDefault(false)) { + if (!ParseDefault(jpropA, false)) { T val; - if (jpropK.to(&val)) { + if (Parse(jpropK, &val)) { // Static property. if (noop && val == *noop) return false; @@ -258,8 +258,8 @@ static inline bool BindPropertyImpl(const json::ValueRef& jprop, return true; } - if (!jpropA.isNull()) { - return LogFail(jprop, "Could not parse (explicit) static property"); + if (!jpropA.is()) { + return LogFail(*jprop, "Could not parse (explicit) static property"); } } @@ -267,7 +267,7 @@ static inline bool BindPropertyImpl(const json::ValueRef& jprop, auto animator = KeyframeAnimator::Make(jpropK, std::move(apply)); if (!animator) { - return LogFail(jprop, "Could not parse keyframed property"); + return LogFail(*jprop, "Could not parse keyframed property"); } animators->push_back(std::move(animator)); @@ -277,11 +277,10 @@ static inline bool BindPropertyImpl(const json::ValueRef& jprop, class SplitPointAnimator final : public sksg::Animator { public: - static std::unique_ptr Make(const json::ValueRef& jprop, + static std::unique_ptr Make(const skjson::ObjectValue* jprop, std::function&& apply, const VectorValue*) { - if (!jprop.isObject()) - return nullptr; + if (!jprop) return nullptr; std::unique_ptr split_animator( new SplitPointAnimator(std::move(apply))); @@ -290,11 +289,11 @@ public: // the object itself, so the scope is bound to the life time of the object. auto* split_animator_ptr = split_animator.get(); - if (!BindPropertyImpl(jprop["x"], &split_animator->fAnimators, + if (!BindPropertyImpl((*jprop)["x"], &split_animator->fAnimators, [split_animator_ptr](const ScalarValue& x) { split_animator_ptr->setX(x); }) || - !BindPropertyImpl(jprop["y"], &split_animator->fAnimators, + !BindPropertyImpl((*jprop)["y"], &split_animator->fAnimators, [split_animator_ptr](const ScalarValue& y) { split_animator_ptr->setY(y); })) { - LogFail(jprop, "Could not parse split property"); + LogFail(*jprop, "Could not parse split property"); return nullptr; } @@ -331,11 +330,11 @@ private: using INHERITED = sksg::Animator; }; -bool BindSplitPositionProperty(const json::ValueRef& jprop, +bool BindSplitPositionProperty(const skjson::Value& jv, sksg::AnimatorList* animators, std::function&& apply, const VectorValue* noop) { - if (auto split_animator = SplitPointAnimator::Make(jprop, std::move(apply), noop)) { + if (auto split_animator = SplitPointAnimator::Make(jv, std::move(apply), noop)) { animators->push_back(std::unique_ptr(split_animator.release())); return true; } @@ -346,29 +345,32 @@ bool BindSplitPositionProperty(const json::ValueRef& jprop, } // namespace template <> -bool BindProperty(const json::ValueRef& jprop, +bool BindProperty(const skjson::Value& jv, sksg::AnimatorList* animators, std::function&& apply, const ScalarValue* noop) { - return BindPropertyImpl(jprop, animators, std::move(apply), noop); + return BindPropertyImpl(jv, animators, std::move(apply), noop); } template <> -bool BindProperty(const json::ValueRef& jprop, +bool BindProperty(const skjson::Value& jv, sksg::AnimatorList* animators, std::function&& apply, const VectorValue* noop) { - return jprop["s"].toDefault(false) - ? BindSplitPositionProperty(jprop, animators, std::move(apply), noop) - : BindPropertyImpl(jprop, animators, std::move(apply), noop); + if (!jv.is()) + return false; + + return ParseDefault(jv.as()["s"], false) + ? BindSplitPositionProperty(jv, animators, std::move(apply), noop) + : BindPropertyImpl(jv, animators, std::move(apply), noop); } template <> -bool BindProperty(const json::ValueRef& jprop, +bool BindProperty(const skjson::Value& jv, sksg::AnimatorList* animators, std::function&& apply, const ShapeValue* noop) { - return BindPropertyImpl(jprop, animators, std::move(apply), noop); + return BindPropertyImpl(jv, animators, std::move(apply), noop); } } // namespace skottie diff --git a/modules/skottie/src/SkottieAnimator.h b/modules/skottie/src/SkottieAnimator.h index 6dc8f6c759..c4464e3138 100644 --- a/modules/skottie/src/SkottieAnimator.h +++ b/modules/skottie/src/SkottieAnimator.h @@ -12,14 +12,14 @@ #include -namespace skottie { +namespace skjson { class Value; } -namespace json { class ValueRef; } +namespace skottie { // 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::ValueRef&, +bool BindProperty(const skjson::Value&, sksg::AnimatorList*, std::function&&, const T* noop = nullptr); diff --git a/modules/skottie/src/SkottieJson.cpp b/modules/skottie/src/SkottieJson.cpp index 23e616d1ea..4f23939d15 100644 --- a/modules/skottie/src/SkottieJson.cpp +++ b/modules/skottie/src/SkottieJson.cpp @@ -14,45 +14,37 @@ #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 { +using namespace skjson; template <> -bool ValueRef::to(SkScalar* v) const { - if (!fValue) return false; - +bool Parse(const Value& v, SkScalar* s) { // Some versions wrap values as single-element arrays. - if (fValue->IsArray() && fValue->Size() == 1) { - return ValueRef(fValue->operator[](0)).to(v); + if (const skjson::ArrayValue* array = v) { + if (array->size() == 1) { + return Parse((*array)[0], s); + } } - if (!fValue->IsNumber()) - return false; - - *v = static_cast(fValue->GetDouble()); + if (const skjson::NumberValue* num = v) { + *s = static_cast(**num); + return true; + } - return true; + return false; } template <> -bool ValueRef::to(bool* v) const { - if (!fValue) return false; - - switch(fValue->GetType()) { - case rapidjson::kNumberType: - *v = SkToBool(fValue->GetDouble()); +bool Parse(const Value& v, bool* b) { + switch(v.getType()) { + case Value::Type::kNumber: + *b = SkToBool(*v.as()); return true; - case rapidjson::kFalseType: - case rapidjson::kTrueType: - *v = fValue->GetBool(); + case Value::Type::kBool: + *b = *v.as(); return true; default: break; @@ -62,46 +54,49 @@ bool ValueRef::to(bool* v) const { } template <> -bool ValueRef::to(int* v) const { - if (!fValue || !fValue->IsInt()) - return false; - - *v = fValue->GetInt(); +bool Parse(const Value& v, int* i) { + if (const skjson::NumberValue* num = v) { + const auto dbl = **num; + *i = dbl; + return *i == dbl; + } - return true; + return false; } template <> -bool ValueRef::to(SkString* v) const { - if (!fValue || !fValue->IsString()) - return false; - - v->set(fValue->GetString()); +bool Parse(const Value& v, SkString* s) { + if (const skjson::StringValue* sv = v) { + s->set(sv->begin(), sv->size()); + return true; + } - return true; + return false; } template <> -bool ValueRef::to(SkPoint* v) const { - if (!fValue || !fValue->IsObject()) +bool Parse(const Value& v, SkPoint* pt) { + if (!v.is()) return false; + const auto& ov = v.as(); - const auto jvx = ValueRef(this->operator[]("x")), - jvy = ValueRef(this->operator[]("y")); + const auto& jvx = ov["x"]; + const auto& jvy = ov["y"]; // Some BM versions seem to store x/y as single-element arrays. - return ValueRef(jvx.isArray() ? jvx.operator[](size_t(0)) : jvx).to(&v->fX) - && ValueRef(jvy.isArray() ? jvy.operator[](size_t(0)) : jvy).to(&v->fY); + return Parse(jvx.is() ? jvx.as()[0] : jvx, &pt->fX) + && Parse(jvy.is() ? jvy.as()[0] : jvy, &pt->fY); } template <> -bool ValueRef::to>(std::vector* v) const { - if (!fValue || !fValue->IsArray()) +bool Parse>(const Value& v, std::vector* vec) { + if (!v.is()) return false; + const auto& av = v.as(); - v->resize(fValue->Size()); - for (size_t i = 0; i < fValue->Size(); ++i) { - if (!ValueRef(fValue->operator[](i)).to(v->data() + i)) { + vec->resize(av.size()); + for (size_t i = 0; i < av.size(); ++i) { + if (!Parse(av[i], vec->data() + i)) { return false; } } @@ -111,16 +106,17 @@ bool ValueRef::to>(std::vector* v) const { namespace { -bool ParsePointVec(const ValueRef& jv, std::vector* pts) { - if (!jv.isArray()) +bool ParsePointVec(const Value& v, std::vector* pts) { + if (!v.is()) return false; + const auto& av = v.as(); pts->clear(); - pts->reserve(jv.size()); + pts->reserve(av.size()); std::vector vec; - for (size_t i = 0; i < jv.size(); ++i) { - if (!jv[i].to(&vec) || vec.size() != 2) + for (size_t i = 0; i < av.size(); ++i) { + if (!Parse(av[i], &vec) || vec.size() != 2) return false; pts->push_back(SkPoint::Make(vec[0], vec[1])); } @@ -131,113 +127,40 @@ bool ParsePointVec(const ValueRef& jv, std::vector* pts) { } // namespace template <> -bool ValueRef::to(ShapeValue* v) const { - SkASSERT(v->fVertices.empty()); - - if (!fValue) - return false; +bool Parse(const Value& v, ShapeValue* sh) { + SkASSERT(sh->fVertices.empty()); // Some versions wrap values as single-element arrays. - if (fValue->IsArray() && fValue->Size() == 1) { - return ValueRef(fValue->operator[](0)).to(v); + if (const skjson::ArrayValue* av = v) { + if (av->size() == 1) { + return Parse((*av)[0], sh); + } } + if (!v.is()) + return false; + const auto& ov = v.as(); + 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(this->operator[]("i"), &inPts) || - !ParsePointVec(this->operator[]("o"), &outPts) || - !ParsePointVec(this->operator[]("v"), &verts) || + if (!ParsePointVec(ov["i"], &inPts) || + !ParsePointVec(ov["o"], &outPts) || + !ParsePointVec(ov["v"], &verts) || inPts.size() != outPts.size() || inPts.size() != verts.size()) { return false; } - v->fVertices.reserve(inPts.size()); + sh->fVertices.reserve(inPts.size()); for (size_t i = 0; i < inPts.size(); ++i) { - v->fVertices.push_back(BezierVertex({inPts[i], outPts[i], verts[i]})); + sh->fVertices.push_back(BezierVertex({inPts[i], outPts[i], verts[i]})); } - v->fClosed = this->operator[]("c").toDefault(false); + sh->fClosed = ParseDefault(ov["c"], false); return true; } -size_t ValueRef::size() const { - return this->isArray() ? fValue->Size() : 0; -} - -ValueRef ValueRef::operator[](size_t i) const { - return i < this->size() ? 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 { - return this->isArray() ? fValue->Begin() : nullptr; -} - -const rapidjson::Value* ValueRef::end() const { - return this->isArray() ? fValue->End() : nullptr; -} - -SkString ValueRef::toString() const { -#ifdef SK_DEBUG - rapidjson::StringBuffer buf; - if (fValue) { - 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/modules/skottie/src/SkottieJson.h b/modules/skottie/src/SkottieJson.h index 76e17c610e..98ee25a3ce 100644 --- a/modules/skottie/src/SkottieJson.h +++ b/modules/skottie/src/SkottieJson.h @@ -8,68 +8,26 @@ #ifndef SkottieJson_DEFINED #define SkottieJson_DEFINED +#include "SkJSON.h" #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 +bool Parse(const skjson::Value&, T*); - template - T toDefault(const T& defaultValue) const { - T v; - if (!this->to(&v)) { - v = defaultValue; - } - return v; +template +T ParseDefault(const skjson::Value& v, const T& defaultValue) { + T res; + if (!Parse(v, &res)) { + res = defaultValue; } - - 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 + return res; +} } // namespace skottie -- cgit v1.2.3