[skottie/cleanup] Refactor parser state handling
* introduce AnimationBuilder to hold mostly immutable (modulo caching)
state
* split the scoped animator state into AnimatorScope
This will facilitate splitting the monolithic Skottie.cpp in follow-up
CLs.
Refactoring only, no functional changes.
TBR=
Change-Id: I0a8295e60be4559586fc4a9fea3dee4a7f5714d4
Reviewed-on: https://skia-review.googlesource.com/148390
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp
index ccc2fed..1c070f4 100644
--- a/modules/skottie/src/Skottie.cpp
+++ b/modules/skottie/src/Skottie.cpp
@@ -53,8 +53,6 @@
namespace skottie {
-using internal::AttachContext;
-
namespace internal {
void LogJSON(const skjson::Value& json, const char msg[]) {
@@ -62,8 +60,6 @@
LOG("%s: %s\n", msg, dump.c_str());
}
-} // namespace internal
-
namespace {
// DEPRECATED: replace w/ LogJSON.
@@ -73,7 +69,7 @@
return false;
}
-sk_sp<sksg::Matrix> AttachMatrix(const skjson::ObjectValue& t, AttachContext* ctx,
+sk_sp<sksg::Matrix> AttachMatrix(const skjson::ObjectValue& t, AnimatorScope* ascope,
sk_sp<sksg::Matrix> parentMatrix) {
static const VectorValue g_default_vec_0 = { 0, 0},
g_default_vec_100 = {100, 100};
@@ -81,15 +77,15 @@
auto matrix = sksg::Matrix::Make(SkMatrix::I(), parentMatrix);
auto adapter = sk_make_sp<TransformAdapter>(matrix);
- auto bound = BindProperty<VectorValue>(t["a"], &ctx->fAnimators,
+ auto bound = BindProperty<VectorValue>(t["a"], ascope,
[adapter](const VectorValue& a) {
adapter->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
}, g_default_vec_0);
- bound |= BindProperty<VectorValue>(t["p"], &ctx->fAnimators,
+ bound |= BindProperty<VectorValue>(t["p"], ascope,
[adapter](const VectorValue& p) {
adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
}, g_default_vec_0);
- bound |= BindProperty<VectorValue>(t["s"], &ctx->fAnimators,
+ bound |= BindProperty<VectorValue>(t["s"], ascope,
[adapter](const VectorValue& s) {
adapter->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
}, g_default_vec_100);
@@ -100,15 +96,15 @@
// we can still make use of rz.
jrotation = &t["rz"];
}
- bound |= BindProperty<ScalarValue>(*jrotation, &ctx->fAnimators,
+ bound |= BindProperty<ScalarValue>(*jrotation, ascope,
[adapter](const ScalarValue& r) {
adapter->setRotation(r);
}, 0.0f);
- bound |= BindProperty<ScalarValue>(t["sk"], &ctx->fAnimators,
+ bound |= BindProperty<ScalarValue>(t["sk"], ascope,
[adapter](const ScalarValue& sk) {
adapter->setSkew(sk);
}, 0.0f);
- bound |= BindProperty<ScalarValue>(t["sa"], &ctx->fAnimators,
+ bound |= BindProperty<ScalarValue>(t["sa"], ascope,
[adapter](const ScalarValue& sa) {
adapter->setSkewAxis(sa);
}, 0.0f);
@@ -116,14 +112,14 @@
return bound ? matrix : parentMatrix;
}
-sk_sp<sksg::RenderNode> AttachOpacity(const skjson::ObjectValue& jtransform, AttachContext* ctx,
+sk_sp<sksg::RenderNode> AttachOpacity(const skjson::ObjectValue& jtransform, AnimatorScope* ascope,
sk_sp<sksg::RenderNode> childNode) {
if (!childNode)
return nullptr;
auto opacityNode = sksg::OpacityEffect::Make(childNode);
- if (!BindProperty<ScalarValue>(jtransform["o"], &ctx->fAnimators,
+ if (!BindProperty<ScalarValue>(jtransform["o"], ascope,
[opacityNode](const ScalarValue& o) {
// BM opacity is [0..100]
opacityNode->setOpacity(o * 0.01f);
@@ -135,11 +131,9 @@
return std::move(opacityNode);
}
-sk_sp<sksg::RenderNode> AttachComposition(const skjson::ObjectValue&, AttachContext* ctx);
-
-sk_sp<sksg::Path> AttachPath(const skjson::Value& jpath, AttachContext* ctx) {
+sk_sp<sksg::Path> AttachPath(const skjson::Value& jpath, AnimatorScope* ascope) {
auto path_node = sksg::Path::Make();
- return BindProperty<ShapeValue>(jpath, &ctx->fAnimators,
+ return BindProperty<ShapeValue>(jpath, ascope,
[path_node](const ShapeValue& p) {
// FillType is tracked in the SG node, not in keyframes -- make sure we preserve it.
auto path = ValueTraits<ShapeValue>::As<SkPath>(p);
@@ -150,24 +144,25 @@
: nullptr;
}
-sk_sp<sksg::GeometryNode> AttachPathGeometry(const skjson::ObjectValue& jpath, AttachContext* ctx) {
- return AttachPath(jpath["ks"], ctx);
+sk_sp<sksg::GeometryNode> AttachPathGeometry(const skjson::ObjectValue& jpath,
+ AnimatorScope* ascope) {
+ return AttachPath(jpath["ks"], ascope);
}
sk_sp<sksg::GeometryNode> AttachRRectGeometry(const skjson::ObjectValue& jrect,
- AttachContext* ctx) {
+ AnimatorScope* ascope) {
auto rect_node = sksg::RRect::Make();
auto adapter = sk_make_sp<RRectAdapter>(rect_node);
- auto p_attached = BindProperty<VectorValue>(jrect["p"], &ctx->fAnimators,
+ auto p_attached = BindProperty<VectorValue>(jrect["p"], ascope,
[adapter](const VectorValue& p) {
adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
});
- auto s_attached = BindProperty<VectorValue>(jrect["s"], &ctx->fAnimators,
+ auto s_attached = BindProperty<VectorValue>(jrect["s"], ascope,
[adapter](const VectorValue& s) {
adapter->setSize(ValueTraits<VectorValue>::As<SkSize>(s));
});
- auto r_attached = BindProperty<ScalarValue>(jrect["r"], &ctx->fAnimators,
+ auto r_attached = BindProperty<ScalarValue>(jrect["r"], ascope,
[adapter](const ScalarValue& r) {
adapter->setRadius(SkSize::Make(r, r));
});
@@ -180,15 +175,15 @@
}
sk_sp<sksg::GeometryNode> AttachEllipseGeometry(const skjson::ObjectValue& jellipse,
- AttachContext* ctx) {
+ AnimatorScope* ascope) {
auto rect_node = sksg::RRect::Make();
auto adapter = sk_make_sp<RRectAdapter>(rect_node);
- auto p_attached = BindProperty<VectorValue>(jellipse["p"], &ctx->fAnimators,
+ auto p_attached = BindProperty<VectorValue>(jellipse["p"], ascope,
[adapter](const VectorValue& p) {
adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
});
- auto s_attached = BindProperty<VectorValue>(jellipse["s"], &ctx->fAnimators,
+ auto s_attached = BindProperty<VectorValue>(jellipse["s"], ascope,
[adapter](const VectorValue& s) {
const auto sz = ValueTraits<VectorValue>::As<SkSize>(s);
adapter->setSize(sz);
@@ -203,7 +198,7 @@
}
sk_sp<sksg::GeometryNode> AttachPolystarGeometry(const skjson::ObjectValue& jstar,
- AttachContext* ctx) {
+ AnimatorScope* ascope) {
static constexpr PolyStarAdapter::Type gTypes[] = {
PolyStarAdapter::Type::kStar, // "sy": 1
PolyStarAdapter::Type::kPoly, // "sy": 2
@@ -218,31 +213,31 @@
auto path_node = sksg::Path::Make();
auto adapter = sk_make_sp<PolyStarAdapter>(path_node, gTypes[type]);
- BindProperty<VectorValue>(jstar["p"], &ctx->fAnimators,
+ BindProperty<VectorValue>(jstar["p"], ascope,
[adapter](const VectorValue& p) {
adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
});
- BindProperty<ScalarValue>(jstar["pt"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jstar["pt"], ascope,
[adapter](const ScalarValue& pt) {
adapter->setPointCount(pt);
});
- BindProperty<ScalarValue>(jstar["ir"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jstar["ir"], ascope,
[adapter](const ScalarValue& ir) {
adapter->setInnerRadius(ir);
});
- BindProperty<ScalarValue>(jstar["or"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jstar["or"], ascope,
[adapter](const ScalarValue& otr) {
adapter->setOuterRadius(otr);
});
- BindProperty<ScalarValue>(jstar["is"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jstar["is"], ascope,
[adapter](const ScalarValue& is) {
adapter->setInnerRoundness(is);
});
- BindProperty<ScalarValue>(jstar["os"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jstar["os"], ascope,
[adapter](const ScalarValue& os) {
adapter->setOuterRoundness(os);
});
- BindProperty<ScalarValue>(jstar["r"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jstar["r"], ascope,
[adapter](const ScalarValue& r) {
adapter->setRotation(r);
});
@@ -250,10 +245,10 @@
return std::move(path_node);
}
-sk_sp<sksg::Color> AttachColor(const skjson::ObjectValue& jcolor, AttachContext* ctx,
+sk_sp<sksg::Color> AttachColor(const skjson::ObjectValue& jcolor, AnimatorScope* ascope,
const char prop_name[]) {
auto color_node = sksg::Color::Make(SK_ColorBLACK);
- BindProperty<VectorValue>(jcolor[prop_name], &ctx->fAnimators,
+ BindProperty<VectorValue>(jcolor[prop_name], ascope,
[color_node](const VectorValue& c) {
color_node->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
});
@@ -261,7 +256,7 @@
return color_node;
}
-sk_sp<sksg::Gradient> AttachGradient(const skjson::ObjectValue& jgrad, AttachContext* ctx) {
+sk_sp<sksg::Gradient> AttachGradient(const skjson::ObjectValue& jgrad, AnimatorScope* ascope) {
const skjson::ObjectValue* stops = jgrad["g"];
if (!stops)
return nullptr;
@@ -285,15 +280,15 @@
gradient_node = std::move(radial_node);
}
- BindProperty<VectorValue>((*stops)["k"], &ctx->fAnimators,
+ BindProperty<VectorValue>((*stops)["k"], ascope,
[adapter](const VectorValue& stops) {
adapter->setColorStops(stops);
});
- BindProperty<VectorValue>(jgrad["s"], &ctx->fAnimators,
+ BindProperty<VectorValue>(jgrad["s"], ascope,
[adapter](const VectorValue& s) {
adapter->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(s));
});
- BindProperty<VectorValue>(jgrad["e"], &ctx->fAnimators,
+ BindProperty<VectorValue>(jgrad["e"], ascope,
[adapter](const VectorValue& e) {
adapter->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(e));
});
@@ -301,12 +296,12 @@
return gradient_node;
}
-sk_sp<sksg::PaintNode> AttachPaint(const skjson::ObjectValue& jpaint, AttachContext* ctx,
+sk_sp<sksg::PaintNode> AttachPaint(const skjson::ObjectValue& jpaint, AnimatorScope* ascope,
sk_sp<sksg::PaintNode> paint_node) {
if (paint_node) {
paint_node->setAntiAlias(true);
- BindProperty<ScalarValue>(jpaint["o"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jpaint["o"], ascope,
[paint_node](const ScalarValue& o) {
// BM opacity is [0..100]
paint_node->setOpacity(o * 0.01f);
@@ -316,14 +311,14 @@
return paint_node;
}
-sk_sp<sksg::PaintNode> AttachStroke(const skjson::ObjectValue& jstroke, AttachContext* ctx,
+sk_sp<sksg::PaintNode> AttachStroke(const skjson::ObjectValue& jstroke, AnimatorScope* ascope,
sk_sp<sksg::PaintNode> stroke_node) {
if (!stroke_node)
return nullptr;
stroke_node->setStyle(SkPaint::kStroke_Style);
- BindProperty<ScalarValue>(jstroke["w"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jstroke["w"], ascope,
[stroke_node](const ScalarValue& w) {
stroke_node->setStrokeWidth(w);
});
@@ -349,21 +344,24 @@
return stroke_node;
}
-sk_sp<sksg::PaintNode> AttachColorFill(const skjson::ObjectValue& jfill, AttachContext* ctx) {
- return AttachPaint(jfill, ctx, AttachColor(jfill, ctx, "c"));
+sk_sp<sksg::PaintNode> AttachColorFill(const skjson::ObjectValue& jfill, AnimatorScope* ascope) {
+ return AttachPaint(jfill, ascope, AttachColor(jfill, ascope, "c"));
}
-sk_sp<sksg::PaintNode> AttachGradientFill(const skjson::ObjectValue& jfill, AttachContext* ctx) {
- return AttachPaint(jfill, ctx, AttachGradient(jfill, ctx));
+sk_sp<sksg::PaintNode> AttachGradientFill(const skjson::ObjectValue& jfill, AnimatorScope* ascope) {
+ return AttachPaint(jfill, ascope, AttachGradient(jfill, ascope));
}
-sk_sp<sksg::PaintNode> AttachColorStroke(const skjson::ObjectValue& jstroke, AttachContext* ctx) {
- return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachColor(jstroke, ctx, "c")));
+sk_sp<sksg::PaintNode> AttachColorStroke(const skjson::ObjectValue& jstroke,
+ AnimatorScope* ascope) {
+ return AttachStroke(jstroke, ascope, AttachPaint(jstroke, ascope,
+ AttachColor(jstroke, ascope, "c")));
}
sk_sp<sksg::PaintNode> AttachGradientStroke(const skjson::ObjectValue& jstroke,
- AttachContext* ctx) {
- return AttachStroke(jstroke, ctx, AttachPaint(jstroke, ctx, AttachGradient(jstroke, ctx)));
+ AnimatorScope* ascope) {
+ return AttachStroke(jstroke, ascope, AttachPaint(jstroke, ascope,
+ AttachGradient(jstroke, ascope)));
}
sk_sp<sksg::Merge> Merge(std::vector<sk_sp<sksg::GeometryNode>>&& geos, sksg::Merge::Mode mode) {
@@ -379,7 +377,7 @@
}
std::vector<sk_sp<sksg::GeometryNode>> AttachMergeGeometryEffect(
- const skjson::ObjectValue& jmerge, AttachContext*,
+ const skjson::ObjectValue& jmerge, AnimatorScope*,
std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
static constexpr sksg::Merge::Mode gModes[] = {
sksg::Merge::Mode::kMerge, // "mm": 1
@@ -399,7 +397,7 @@
}
std::vector<sk_sp<sksg::GeometryNode>> AttachTrimGeometryEffect(
- const skjson::ObjectValue& jtrim, AttachContext* ctx,
+ const skjson::ObjectValue& jtrim, AnimatorScope* ascope,
std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
enum class Mode {
@@ -424,15 +422,15 @@
trimmed.push_back(trimEffect);
const auto adapter = sk_make_sp<TrimEffectAdapter>(std::move(trimEffect));
- BindProperty<ScalarValue>(jtrim["s"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jtrim["s"], ascope,
[adapter](const ScalarValue& s) {
adapter->setStart(s);
});
- BindProperty<ScalarValue>(jtrim["e"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jtrim["e"], ascope,
[adapter](const ScalarValue& e) {
adapter->setEnd(e);
});
- BindProperty<ScalarValue>(jtrim["o"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jtrim["o"], ascope,
[adapter](const ScalarValue& o) {
adapter->setOffset(o);
});
@@ -442,7 +440,7 @@
}
std::vector<sk_sp<sksg::GeometryNode>> AttachRoundGeometryEffect(
- const skjson::ObjectValue& jtrim, AttachContext* ctx,
+ const skjson::ObjectValue& jtrim, AnimatorScope* ascope,
std::vector<sk_sp<sksg::GeometryNode>>&& geos) {
std::vector<sk_sp<sksg::GeometryNode>> rounded;
@@ -452,7 +450,7 @@
const auto roundEffect = sksg::RoundEffect::Make(std::move(g));
rounded.push_back(roundEffect);
- BindProperty<ScalarValue>(jtrim["r"], &ctx->fAnimators,
+ BindProperty<ScalarValue>(jtrim["r"], ascope,
[roundEffect](const ScalarValue& r) {
roundEffect->setRadius(r);
});
@@ -461,7 +459,7 @@
return rounded;
}
-using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const skjson::ObjectValue&, AttachContext*);
+using GeometryAttacherT = sk_sp<sksg::GeometryNode> (*)(const skjson::ObjectValue&, AnimatorScope*);
static constexpr GeometryAttacherT gGeometryAttachers[] = {
AttachPathGeometry,
AttachRRectGeometry,
@@ -469,7 +467,7 @@
AttachPolystarGeometry,
};
-using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const skjson::ObjectValue&, AttachContext*);
+using PaintAttacherT = sk_sp<sksg::PaintNode> (*)(const skjson::ObjectValue&, AnimatorScope*);
static constexpr PaintAttacherT gPaintAttachers[] = {
AttachColorFill,
AttachColorStroke,
@@ -479,7 +477,7 @@
using GeometryEffectAttacherT =
std::vector<sk_sp<sksg::GeometryNode>> (*)(const skjson::ObjectValue&,
- AttachContext*,
+ AnimatorScope*,
std::vector<sk_sp<sksg::GeometryNode>>&&);
static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
AttachMergeGeometryEffect,
@@ -541,22 +539,23 @@
};
struct AttachShapeContext {
- AttachShapeContext(AttachContext* ctx,
+ AttachShapeContext(AnimatorScope* ascope,
std::vector<sk_sp<sksg::GeometryNode>>* geos,
std::vector<GeometryEffectRec>* effects,
size_t committedAnimators)
- : fCtx(ctx)
+ : fScope(ascope)
, fGeometryStack(geos)
, fGeometryEffectStack(effects)
, fCommittedAnimators(committedAnimators) {}
- AttachContext* fCtx;
+ AnimatorScope* fScope;
std::vector<sk_sp<sksg::GeometryNode>>* fGeometryStack;
std::vector<GeometryEffectRec>* fGeometryEffectStack;
size_t fCommittedAnimators;
};
-sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape, AttachShapeContext* shapeCtx) {
+sk_sp<sksg::RenderNode> AttachShape(const skjson::ArrayValue* jshape,
+ AttachShapeContext* shapeCtx) {
if (!jshape)
return nullptr;
@@ -592,10 +591,10 @@
switch (info->fShapeType) {
case ShapeType::kTransform:
- if ((shape_matrix = AttachMatrix(*shape, shapeCtx->fCtx, nullptr))) {
+ if ((shape_matrix = AttachMatrix(*shape, shapeCtx->fScope, nullptr))) {
shape_wrapper = sksg::Transform::Make(std::move(shape_wrapper), shape_matrix);
}
- shape_wrapper = AttachOpacity(*shape, shapeCtx->fCtx, std::move(shape_wrapper));
+ shape_wrapper = AttachOpacity(*shape, shapeCtx->fScope, std::move(shape_wrapper));
break;
case ShapeType::kGeometryEffect:
SkASSERT(info->fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
@@ -619,7 +618,7 @@
case ShapeType::kGeometry: {
SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryAttachers));
if (auto geo = gGeometryAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
- shapeCtx->fCtx)) {
+ shapeCtx->fScope)) {
geos.push_back(std::move(geo));
}
} break;
@@ -628,7 +627,7 @@
SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gGeometryEffectAttachers));
if (!geos.empty()) {
geos = gGeometryEffectAttachers[rec->fInfo.fAttacherIndex](rec->fJson,
- shapeCtx->fCtx,
+ shapeCtx->fScope,
std::move(geos));
}
@@ -638,7 +637,7 @@
shapeCtx->fGeometryEffectStack->pop_back();
} break;
case ShapeType::kGroup: {
- AttachShapeContext groupShapeCtx(shapeCtx->fCtx,
+ AttachShapeContext groupShapeCtx(shapeCtx->fScope,
&geos,
shapeCtx->fGeometryEffectStack,
shapeCtx->fCommittedAnimators);
@@ -650,7 +649,7 @@
} break;
case ShapeType::kPaint: {
SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gPaintAttachers));
- auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson, shapeCtx->fCtx);
+ auto paint = gPaintAttachers[rec->fInfo.fAttacherIndex](rec->fJson, shapeCtx->fScope);
if (!paint || geos.empty())
break;
@@ -659,7 +658,7 @@
// Apply all pending effects from the stack.
for (auto it = shapeCtx->fGeometryEffectStack->rbegin();
it != shapeCtx->fGeometryEffectStack->rend(); ++it) {
- drawGeos = it->fAttach(it->fJson, shapeCtx->fCtx, std::move(drawGeos));
+ drawGeos = it->fAttach(it->fJson, shapeCtx->fScope, std::move(drawGeos));
}
// If we still have multiple geos, reduce using 'merge'.
@@ -669,7 +668,7 @@
SkASSERT(geo);
draws.push_back(sksg::Draw::Make(std::move(geo), std::move(paint)));
- shapeCtx->fCommittedAnimators = shapeCtx->fCtx->fAnimators.size();
+ shapeCtx->fCommittedAnimators = shapeCtx->fScope->size();
} break;
default:
break;
@@ -694,7 +693,10 @@
return draws.empty() ? nullptr : shape_wrapper;
}
-sk_sp<sksg::RenderNode> AttachNestedAnimation(const char* name, AttachContext* ctx) {
+} // namespace
+
+sk_sp<sksg::RenderNode> AnimationBuilder::attachNestedAnimation(const char* name,
+ AnimatorScope* ascope) {
class SkottieSGAdapter final : public sksg::RenderNode {
public:
explicit SkottieSGAdapter(sk_sp<Animation> animation)
@@ -736,29 +738,30 @@
const float fTimeScale;
};
- const auto data = ctx->fResources.load("", name);
+ const auto data = fResourceProvider.load("", name);
if (!data) {
LOG("!! Could not load: %s\n", name);
return nullptr;
}
auto animation = Animation::Make(static_cast<const char*>(data->data()), data->size(),
- &ctx->fResources);
+ &fResourceProvider);
if (!animation) {
LOG("!! Could not parse nested animation: %s\n", name);
return nullptr;
}
- ctx->fAnimators.push_back(
- skstd::make_unique<SkottieAnimatorAdapter>(animation,
- animation->duration() / ctx->fDuration));
+ ascope->push_back(
+ skstd::make_unique<SkottieAnimatorAdapter>(animation, animation->duration() / fDuration));
return sk_make_sp<SkottieSGAdapter>(std::move(animation));
}
-sk_sp<sksg::RenderNode> AttachAssetRef(const skjson::ObjectValue& jlayer, AttachContext* ctx,
- sk_sp<sksg::RenderNode>(*attach_proc)(const skjson::ObjectValue& comp, AttachContext* ctx)) {
+sk_sp<sksg::RenderNode> AnimationBuilder::attachAssetRef(
+ const skjson::ObjectValue& jlayer, AnimatorScope* ascope,
+ sk_sp<sksg::RenderNode>(AnimationBuilder::* attach_proc)(const skjson::ObjectValue& comp,
+ AnimatorScope* ascope)) {
const auto refId = ParseDefault<SkString>(jlayer["refId"], SkString());
if (refId.isEmpty()) {
@@ -767,10 +770,10 @@
}
if (refId.startsWith("$")) {
- return AttachNestedAnimation(refId.c_str() + 1, ctx);
+ return this->attachNestedAnimation(refId.c_str() + 1, ascope);
}
- const auto* asset_info = ctx->fAssets.find(refId);
+ const auto* asset_info = fAssets.find(refId);
if (!asset_info) {
LOG("!! Asset not found: '%s'\n", refId.c_str());
return nullptr;
@@ -782,13 +785,14 @@
}
asset_info->fIsAttaching = true;
- auto asset = attach_proc(*asset_info->fAsset, ctx);
+ auto asset = (this->*attach_proc)(*asset_info->fAsset, ascope);
asset_info->fIsAttaching = false;
return asset;
}
-sk_sp<sksg::RenderNode> AttachCompLayer(const skjson::ObjectValue& jlayer, AttachContext* ctx) {
+sk_sp<sksg::RenderNode> AnimationBuilder::attachPrecompLayer(const skjson::ObjectValue& jlayer,
+ AnimatorScope* ascope) {
const skjson::ObjectValue* time_remap = jlayer["tm"];
const auto start_time = ParseDefault<float>(jlayer["st"], 0.0f),
stretch_time = ParseDefault<float>(jlayer["sr"], 1.0f);
@@ -796,10 +800,10 @@
!SkScalarNearlyEqual(stretch_time, 1) ||
time_remap;
- sksg::AnimatorList local_animators;
- AttachContext local_ctx = ctx->makeScoped(requires_time_mapping ? local_animators
- : ctx->fAnimators);
- auto comp_layer = AttachAssetRef(jlayer, &local_ctx, AttachComposition);
+ AnimatorScope local_animators;
+ auto comp_layer = this->attachAssetRef(jlayer,
+ requires_time_mapping ? &local_animators : ascope,
+ &AnimationBuilder::attachComposition);
// Applies a bias/scale/remap t-adjustment to child animators.
class CompTimeMapper final : public sksg::GroupAnimator {
@@ -837,19 +841,20 @@
// The lambda below captures a raw pointer to the mapper object. That should be safe,
// because both the lambda and the mapper are scoped/owned by ctx->fAnimators.
auto* raw_mapper = time_mapper.get();
- auto frame_rate = ctx->fFrameRate;
- BindProperty<ScalarValue>(*time_remap, &ctx->fAnimators,
+ auto frame_rate = fFrameRate;
+ BindProperty<ScalarValue>(*time_remap, ascope,
[raw_mapper, frame_rate](const ScalarValue& t) {
raw_mapper->remapTime(t * frame_rate);
});
}
- ctx->fAnimators.push_back(std::move(time_mapper));
+ ascope->push_back(std::move(time_mapper));
}
return comp_layer;
}
-sk_sp<sksg::RenderNode> AttachSolidLayer(const skjson::ObjectValue& jlayer, AttachContext*) {
+sk_sp<sksg::RenderNode> AnimationBuilder::attachSolidLayer(const skjson::ObjectValue& jlayer,
+ AnimatorScope*) {
const auto size = SkSize::Make(ParseDefault<float>(jlayer["sw"], 0.0f),
ParseDefault<float>(jlayer["sh"], 0.0f));
const skjson::StringValue* hex_str = jlayer["sc"];
@@ -867,7 +872,8 @@
sksg::Color::Make(color));
}
-sk_sp<sksg::RenderNode> AttachImageAsset(const skjson::ObjectValue& jimage, AttachContext* ctx) {
+sk_sp<sksg::RenderNode> AnimationBuilder::attachImageAsset(const skjson::ObjectValue& jimage,
+ AnimatorScope*) {
const skjson::StringValue* name = jimage["p"];
const skjson::StringValue* path = jimage["u"];
if (!name) {
@@ -877,52 +883,55 @@
const auto name_cstr = name->begin(),
path_cstr = path ? path->begin() : "";
const auto res_id = SkStringPrintf("%s|%s", path_cstr, name_cstr);
- if (auto* attached_image = ctx->fAssetCache.find(res_id)) {
+ if (auto* attached_image = fAssetCache.find(res_id)) {
return *attached_image;
}
- const auto data = ctx->fResources.load(path_cstr, name_cstr);
+ const auto data = fResourceProvider.load(path_cstr, name_cstr);
if (!data) {
LOG("!! Could not load image resource: %s/%s\n", path_cstr, name_cstr);
return nullptr;
}
// TODO: non-intrisic image sizing
- return *ctx->fAssetCache.set(res_id, sksg::Image::Make(SkImage::MakeFromEncoded(data)));
+ return *fAssetCache.set(res_id, sksg::Image::Make(SkImage::MakeFromEncoded(data)));
}
-sk_sp<sksg::RenderNode> AttachImageLayer(const skjson::ObjectValue& jlayer, AttachContext* ctx) {
- return AttachAssetRef(jlayer, ctx, AttachImageAsset);
+sk_sp<sksg::RenderNode> AnimationBuilder::attachImageLayer(const skjson::ObjectValue& jlayer,
+ AnimatorScope* ascope) {
+ return this->attachAssetRef(jlayer, ascope, &AnimationBuilder::attachImageAsset);
}
-sk_sp<sksg::RenderNode> AttachNullLayer(const skjson::ObjectValue& layer, AttachContext*) {
+sk_sp<sksg::RenderNode> AnimationBuilder::attachNullLayer(const skjson::ObjectValue& layer,
+ AnimatorScope*) {
// Null layers are used solely to drive dependent transforms,
// but we use free-floating sksg::Matrices for that purpose.
return nullptr;
}
-sk_sp<sksg::RenderNode> AttachShapeLayer(const skjson::ObjectValue& layer, AttachContext* ctx) {
+sk_sp<sksg::RenderNode> AnimationBuilder::attachShapeLayer(const skjson::ObjectValue& layer,
+ AnimatorScope* ascope) {
std::vector<sk_sp<sksg::GeometryNode>> geometryStack;
std::vector<GeometryEffectRec> geometryEffectStack;
- AttachShapeContext shapeCtx(ctx, &geometryStack, &geometryEffectStack, ctx->fAnimators.size());
+ AttachShapeContext shapeCtx(ascope, &geometryStack, &geometryEffectStack, ascope->size());
auto shapeNode = AttachShape(layer["shapes"], &shapeCtx);
// Trim uncommitted animators: AttachShape consumes effects on the fly, and greedily attaches
// geometries => at the end, we can end up with unused geometries, which are nevertheless alive
// due to attached animators. To avoid this, we track committed animators and discard the
// orphans here.
- SkASSERT(shapeCtx.fCommittedAnimators <= ctx->fAnimators.size());
- ctx->fAnimators.resize(shapeCtx.fCommittedAnimators);
+ SkASSERT(shapeCtx.fCommittedAnimators <= ascope->size());
+ ascope->resize(shapeCtx.fCommittedAnimators);
return shapeNode;
}
-struct AttachLayerContext {
- AttachLayerContext(const skjson::ArrayValue& jlayers, AttachContext* ctx)
- : fLayerList(jlayers), fCtx(ctx) {}
+struct AnimationBuilder::AttachLayerContext {
+ AttachLayerContext(const skjson::ArrayValue& jlayers, AnimatorScope* scope)
+ : fLayerList(jlayers), fScope(scope) {}
const skjson::ArrayValue& fLayerList;
- AttachContext* fCtx;
+ AnimatorScope* fScope;
SkTHashMap<int, sk_sp<sksg::Matrix>> fLayerMatrixMap;
sk_sp<sksg::RenderNode> fCurrentMatte;
@@ -967,7 +976,7 @@
auto parent_matrix = this->AttachParentLayerMatrix(jlayer, layer_index);
if (const skjson::ObjectValue* jtransform = jlayer["ks"]) {
- return *fLayerMatrixMap.set(layer_index, AttachMatrix(*jtransform, fCtx,
+ return *fLayerMatrixMap.set(layer_index, AttachMatrix(*jtransform, fScope,
std::move(parent_matrix)));
}
@@ -975,6 +984,8 @@
}
};
+namespace {
+
struct MaskInfo {
SkBlendMode fBlendMode; // used when masking with layers/blending
sksg::Merge::Mode fMergeMode; // used when clipping
@@ -1005,7 +1016,7 @@
}
sk_sp<sksg::RenderNode> AttachMask(const skjson::ArrayValue* jmask,
- AttachContext* ctx,
+ AnimatorScope* ascope,
sk_sp<sksg::RenderNode> childNode) {
if (!jmask) return childNode;
@@ -1040,7 +1051,7 @@
continue;
}
- auto mask_path = AttachPath((*m)["pt"], ctx);
+ auto mask_path = AttachPath((*m)["pt"], ascope);
if (!mask_path) {
LogFail(*m, "Could not parse mask path");
continue;
@@ -1058,7 +1069,7 @@
mask_paint->setBlendMode(mask_stack.empty() ? SkBlendMode::kSrc
: mask_info->fBlendMode);
- has_opacity |= BindProperty<ScalarValue>((*m)["o"], &ctx->fAnimators,
+ has_opacity |= BindProperty<ScalarValue>((*m)["o"], ascope,
[mask_paint](const ScalarValue& o) {
mask_paint->setOpacity(o * 0.01f);
}, 100.0f);
@@ -1103,7 +1114,7 @@
sk_sp<sksg::RenderNode> AttachFillLayerEffect(const skjson::ArrayValue* jeffect_props,
- AttachContext* ctx,
+ AnimatorScope* ascope,
sk_sp<sksg::RenderNode> layer) {
if (!jeffect_props) return layer;
@@ -1114,7 +1125,7 @@
switch (const auto ty = ParseDefault<int>((*jprop)["ty"], -1)) {
case 2: // color
- color_node = AttachColor(*jprop, ctx, "v");
+ color_node = AttachColor(*jprop, ascope, "v");
break;
default:
LOG("?? Ignoring unsupported fill effect poperty type: %d\n", ty);
@@ -1127,15 +1138,17 @@
: nullptr;
}
-sk_sp<sksg::RenderNode> AttachLayerEffects(const skjson::ArrayValue& jeffects,
- AttachContext* ctx,
- sk_sp<sksg::RenderNode> layer) {
+} // namespace
+
+sk_sp<sksg::RenderNode> AnimationBuilder::attachLayerEffects(const skjson::ArrayValue& jeffects,
+ AnimatorScope* ascope,
+ sk_sp<sksg::RenderNode> layer) {
for (const skjson::ObjectValue* jeffect : jeffects) {
if (!jeffect) continue;
switch (const auto ty = ParseDefault<int>((*jeffect)["ty"], -1)) {
case 21: // Fill
- layer = AttachFillLayerEffect((*jeffect)["ef"], ctx, std::move(layer));
+ layer = AttachFillLayerEffect((*jeffect)["ef"], ascope, std::move(layer));
break;
default:
LOG("?? Unsupported layer effect type: %d\n", ty);
@@ -1146,19 +1159,19 @@
return layer;
}
-sk_sp<sksg::RenderNode> AttachLayer(const skjson::ObjectValue* jlayer,
- AttachLayerContext* layerCtx) {
+sk_sp<sksg::RenderNode> AnimationBuilder::attachLayer(const skjson::ObjectValue* jlayer,
+ AttachLayerContext* layerCtx) {
if (!jlayer) return nullptr;
- using internal::AttachTextLayer;
- using LayerAttacher = sk_sp<sksg::RenderNode> (*)(const skjson::ObjectValue&, AttachContext*);
+ using LayerAttacher = sk_sp<sksg::RenderNode> (AnimationBuilder::*)(const skjson::ObjectValue&,
+ AnimatorScope*);
static constexpr LayerAttacher gLayerAttachers[] = {
- AttachCompLayer, // 'ty': 0
- AttachSolidLayer, // 'ty': 1
- AttachImageLayer, // 'ty': 2
- AttachNullLayer, // 'ty': 3
- AttachShapeLayer, // 'ty': 4
- AttachTextLayer, // 'ty': 5
+ &AnimationBuilder::attachPrecompLayer, // 'ty': 0
+ &AnimationBuilder::attachSolidLayer, // 'ty': 1
+ &AnimationBuilder::attachImageLayer, // 'ty': 2
+ &AnimationBuilder::attachNullLayer, // 'ty': 3
+ &AnimationBuilder::attachShapeLayer, // 'ty': 4
+ &AnimationBuilder::attachTextLayer, // 'ty': 5
};
int type = ParseDefault<int>((*jlayer)["ty"], -1);
@@ -1166,11 +1179,10 @@
return nullptr;
}
- sksg::AnimatorList layer_animators;
- AttachContext local_ctx = layerCtx->fCtx->makeScoped(layer_animators);
+ AnimatorScope layer_animators;
// Layer content.
- auto layer = gLayerAttachers[type](*jlayer, &local_ctx);
+ auto layer = (this->*(gLayerAttachers[type]))(*jlayer, &layer_animators);
// Clip layers with explicit dimensions.
float w = 0, h = 0;
@@ -1181,7 +1193,7 @@
}
// Optional layer mask.
- layer = AttachMask((*jlayer)["masksProperties"], &local_ctx, std::move(layer));
+ layer = AttachMask((*jlayer)["masksProperties"], &layer_animators, std::move(layer));
// Optional layer transform.
if (auto layerMatrix = layerCtx->AttachLayerMatrix(*jlayer)) {
@@ -1191,12 +1203,12 @@
// Optional layer opacity.
// TODO: de-dupe this "ks" lookup with matrix above.
if (const skjson::ObjectValue* jtransform = (*jlayer)["ks"]) {
- layer = AttachOpacity(*jtransform, &local_ctx, std::move(layer));
+ layer = AttachOpacity(*jtransform, &layer_animators, std::move(layer));
}
// Optional layer effects.
if (const skjson::ArrayValue* jeffects = (*jlayer)["ef"]) {
- layer = AttachLayerEffects(*jeffects, &local_ctx, std::move(layer));
+ layer = this->attachLayerEffects(*jeffects, &layer_animators, std::move(layer));
}
class LayerController final : public sksg::GroupAnimator {
@@ -1235,7 +1247,7 @@
if (in >= out || !controller_node)
return nullptr;
- layerCtx->fCtx->fAnimators.push_back(
+ layerCtx->fScope->push_back(
skstd::make_unique<LayerController>(std::move(layer_animators), controller_node, in, out));
if (ParseDefault<bool>((*jlayer)["td"], false)) {
@@ -1263,15 +1275,16 @@
return std::move(controller_node);
}
-sk_sp<sksg::RenderNode> AttachComposition(const skjson::ObjectValue& comp, AttachContext* ctx) {
+sk_sp<sksg::RenderNode> AnimationBuilder::attachComposition(const skjson::ObjectValue& comp,
+ AnimatorScope* scope) {
const skjson::ArrayValue* jlayers = comp["layers"];
if (!jlayers) return nullptr;
SkSTArray<16, sk_sp<sksg::RenderNode>, true> layers;
- AttachLayerContext layerCtx(*jlayers, ctx);
+ AttachLayerContext layerCtx(*jlayers, scope);
for (const auto& l : *jlayers) {
- if (auto layer_fragment = AttachLayer(l, &layerCtx)) {
+ if (auto layer_fragment = this->attachLayer(l, &layerCtx)) {
layers.push_back(std::move(layer_fragment));
}
}
@@ -1289,7 +1302,39 @@
return std::move(comp_group);
}
-} // namespace
+AnimationBuilder::AnimationBuilder(const ResourceProvider& rp, sk_sp<SkFontMgr> fontmgr,
+ Animation::Stats* stats, float duration, float framerate)
+ : fResourceProvider(rp)
+ , fFontMgr(std::move(fontmgr))
+ , fStats(stats)
+ , fDuration(duration)
+ , fFrameRate(framerate) {}
+
+std::unique_ptr<sksg::Scene> AnimationBuilder::parse(const skjson::ObjectValue& jroot) {
+ this->parseAssets(jroot["assets"]);
+ this->parseFonts(jroot["fonts"], jroot["chars"]);
+
+ AnimatorScope animators;
+ auto root = this->attachComposition(jroot, &animators);
+
+ fStats->fAnimatorCount = animators.size();
+
+ return sksg::Scene::Make(std::move(root), std::move(animators));
+}
+
+void AnimationBuilder::parseAssets(const skjson::ArrayValue* jassets) {
+ if (!jassets) {
+ return;
+ }
+
+ for (const skjson::ObjectValue* asset : *jassets) {
+ if (asset) {
+ fAssets.set(ParseDefault<SkString>((*asset)["id"], SkString()), { asset, false });
+ }
+ }
+}
+
+} // namespace internal
sk_sp<Animation> Animation::Make(SkStream* stream, const ResourceProvider* provider, Stats* stats) {
if (!stream->hasLength()) {
@@ -1397,33 +1442,10 @@
, fInPoint(in)
, fOutPoint(out) {
- internal::AssetMap assets;
- if (const skjson::ArrayValue* jassets = json["assets"]) {
- for (const skjson::ObjectValue* asset : *jassets) {
- if (asset) {
- assets.set(ParseDefault<SkString>((*asset)["id"], SkString()), { asset, false });
- }
- }
- }
+ internal::AnimationBuilder builder(resources, SkFontMgr::RefDefault(), stats,
+ this->duration(), fps);
- // TODO: plumb external font mgr.
- const auto fontmgr = SkFontMgr::RefDefault();
- const auto fonts = internal::ParseFonts(json["fonts"], json["chars"], fontmgr.get());
-
- internal::AssetCache asset_cache;
- sksg::AnimatorList animators;
- AttachContext ctx = { resources,
- assets,
- fonts,
- this->duration(),
- fFrameRate,
- asset_cache,
- animators };
- auto root = AttachComposition(json, &ctx);
-
- stats->fAnimatorCount = animators.size();
-
- fScene = sksg::Scene::Make(std::move(root), std::move(animators));
+ fScene = builder.parse(json);
// In case the client calls render before the first tick.
this->seek(0);
diff --git a/modules/skottie/src/SkottieAnimator.cpp b/modules/skottie/src/SkottieAnimator.cpp
index d1db812..c9c43b4 100644
--- a/modules/skottie/src/SkottieAnimator.cpp
+++ b/modules/skottie/src/SkottieAnimator.cpp
@@ -16,6 +16,7 @@
#include <memory>
namespace skottie {
+namespace internal {
namespace {
@@ -354,31 +355,32 @@
template <>
bool BindProperty(const skjson::Value& jv,
- sksg::AnimatorList* animators,
+ AnimatorScope* ascope,
std::function<void(const ScalarValue&)>&& apply,
const ScalarValue* noop) {
- return BindPropertyImpl(jv, animators, std::move(apply), noop);
+ return BindPropertyImpl(jv, ascope, std::move(apply), noop);
}
template <>
bool BindProperty(const skjson::Value& jv,
- sksg::AnimatorList* animators,
+ AnimatorScope* ascope,
std::function<void(const VectorValue&)>&& apply,
const VectorValue* noop) {
if (!jv.is<skjson::ObjectValue>())
return false;
return ParseDefault<bool>(jv.as<skjson::ObjectValue>()["s"], false)
- ? BindSplitPositionProperty(jv, animators, std::move(apply), noop)
- : BindPropertyImpl(jv, animators, std::move(apply), noop);
+ ? BindSplitPositionProperty(jv, ascope, std::move(apply), noop)
+ : BindPropertyImpl(jv, ascope, std::move(apply), noop);
}
template <>
bool BindProperty(const skjson::Value& jv,
- sksg::AnimatorList* animators,
+ AnimatorScope* ascope,
std::function<void(const ShapeValue&)>&& apply,
const ShapeValue* noop) {
- return BindPropertyImpl(jv, animators, std::move(apply), noop);
+ return BindPropertyImpl(jv, ascope, std::move(apply), noop);
}
+} // namespace internal
} // namespace skottie
diff --git a/modules/skottie/src/SkottieAnimator.h b/modules/skottie/src/SkottieAnimator.h
index 23bd9aa..33abf1c 100644
--- a/modules/skottie/src/SkottieAnimator.h
+++ b/modules/skottie/src/SkottieAnimator.h
@@ -8,30 +8,32 @@
#ifndef SkottieAnimator_DEFINED
#define SkottieAnimator_DEFINED
-#include "SkSGScene.h"
+#include "SkottiePriv.h"
#include <functional>
namespace skjson { class Value; }
namespace skottie {
+namespace internal {
// 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 <typename T>
bool BindProperty(const skjson::Value&,
- sksg::AnimatorList*,
+ AnimatorScope*,
std::function<void(const T&)>&&,
const T* default_igore = nullptr);
template <typename T>
bool BindProperty(const skjson::Value& jv,
- sksg::AnimatorList* animators,
+ AnimatorScope* animators,
std::function<void(const T&)>&& apply,
const T& default_ignore) {
return BindProperty(jv, animators, std::move(apply), &default_ignore);
}
+} // namespace internal
} // namespace skottie
#endif // SkottieAnimator_DEFINED
diff --git a/modules/skottie/src/SkottiePriv.h b/modules/skottie/src/SkottiePriv.h
index 097ccc5..fd9be2b 100644
--- a/modules/skottie/src/SkottiePriv.h
+++ b/modules/skottie/src/SkottiePriv.h
@@ -8,6 +8,8 @@
#ifndef SkottiePriv_DEFINED
#define SkottiePriv_DEFINED
+#include "Skottie.h"
+
#include "SkFontStyle.h"
#include "SkSGScene.h"
#include "SkString.h"
@@ -26,53 +28,82 @@
} // namespace skjson
namespace sksg {
+class Matrix;
class RenderNode;
} // namespace sksg
namespace skottie {
-class ResourceProvider;
-
namespace internal {
-struct AssetInfo {
- const skjson::ObjectValue* fAsset;
- mutable bool fIsAttaching; // Used for cycle detection
-};
-using AssetMap = SkTHashMap<SkString, AssetInfo>;
-using AssetCache = SkTHashMap<SkString, sk_sp<sksg::RenderNode>>;
-
-struct FontInfo {
- SkString fFamily,
- fStyle;
- SkScalar fAscent;
- sk_sp<SkTypeface> fTypeface;
-
- bool matches(const char family[], const char style[]) const;
-};
-using FontMap = SkTHashMap<SkString, FontInfo>;
-
-struct AttachContext {
- AttachContext makeScoped(sksg::AnimatorList& animators) const {
- return { fResources, fAssets, fFonts, fDuration, fFrameRate, fAssetCache, animators };
- }
-
- const ResourceProvider& fResources;
- const AssetMap& fAssets;
- const FontMap& fFonts;
- const float fDuration,
- fFrameRate;
- AssetCache& fAssetCache;
- sksg::AnimatorList& fAnimators;
-};
-
void LogJSON(const skjson::Value&, const char[]);
-FontMap ParseFonts(const skjson::ObjectValue* jfonts,
- const skjson::ArrayValue* jchars,
- const SkFontMgr*);
+using AnimatorScope = sksg::AnimatorList;
-sk_sp<sksg::RenderNode> AttachTextLayer(const skjson::ObjectValue&, AttachContext*);
+class AnimationBuilder final : public SkNoncopyable {
+public:
+ AnimationBuilder(const ResourceProvider&, sk_sp<SkFontMgr>, Animation::Stats*,
+ float duration, float framerate);
+
+ std::unique_ptr<sksg::Scene> parse(const skjson::ObjectValue&);
+
+private:
+ struct AttachLayerContext;
+
+ void parseAssets(const skjson::ArrayValue*);
+ void parseFonts (const skjson::ObjectValue* jfonts,
+ const skjson::ArrayValue* jchars);
+
+ sk_sp<sksg::RenderNode> attachComposition(const skjson::ObjectValue&, AnimatorScope*);
+ sk_sp<sksg::RenderNode> attachLayer(const skjson::ObjectValue*, AttachLayerContext*);
+ sk_sp<sksg::RenderNode> attachLayerEffects(const skjson::ArrayValue& jeffects, AnimatorScope*,
+ sk_sp<sksg::RenderNode>);
+
+ sk_sp<sksg::RenderNode> attachAssetRef(const skjson::ObjectValue&, AnimatorScope*,
+ sk_sp<sksg::RenderNode>(AnimationBuilder::*)(const skjson::ObjectValue&,
+ AnimatorScope* ctx));
+ sk_sp<sksg::RenderNode> attachImageAsset(const skjson::ObjectValue&, AnimatorScope*);
+
+ sk_sp<sksg::RenderNode> attachNestedAnimation(const char* name, AnimatorScope* ascope);
+
+ sk_sp<sksg::RenderNode> attachImageLayer (const skjson::ObjectValue&, AnimatorScope*);
+ sk_sp<sksg::RenderNode> attachNullLayer (const skjson::ObjectValue&, AnimatorScope*);
+ sk_sp<sksg::RenderNode> attachPrecompLayer(const skjson::ObjectValue&, AnimatorScope*);
+ sk_sp<sksg::RenderNode> attachShapeLayer (const skjson::ObjectValue&, AnimatorScope*);
+ sk_sp<sksg::RenderNode> attachSolidLayer (const skjson::ObjectValue&, AnimatorScope*);
+ sk_sp<sksg::RenderNode> attachTextLayer (const skjson::ObjectValue&, AnimatorScope*);
+
+ const ResourceProvider& fResourceProvider;
+ const sk_sp<SkFontMgr> fFontMgr;
+ Animation::Stats* fStats;
+ const float fDuration,
+ fFrameRate;
+
+ struct AssetInfo {
+ const skjson::ObjectValue* fAsset;
+ mutable bool fIsAttaching; // Used for cycle detection
+ };
+
+ struct FontInfo {
+ SkString fFamily,
+ fStyle;
+ SkScalar fAscent;
+ sk_sp<SkTypeface> fTypeface;
+
+ bool matches(const char family[], const char style[]) const;
+ };
+
+ // TODO: consolidate these two?
+ using AssetMap = SkTHashMap<SkString, AssetInfo>;
+ using AssetCache = SkTHashMap<SkString, sk_sp<sksg::RenderNode>>;
+ using FontMap = SkTHashMap<SkString, FontInfo>;
+
+ AssetMap fAssets;
+ AssetCache fAssetCache;
+ FontMap fFonts;
+
+ using INHERITED = SkNoncopyable;
+};
} // namespace internal
} // namespace skottie
diff --git a/modules/skottie/src/SkottieTextLayer.cpp b/modules/skottie/src/SkottieTextLayer.cpp
index 2d35caa..622fabf 100644
--- a/modules/skottie/src/SkottieTextLayer.cpp
+++ b/modules/skottie/src/SkottieTextLayer.cpp
@@ -24,12 +24,6 @@
namespace {
-bool ParseGlyph(const skjson::ObjectValue* jglyph, FontInfo* finfo) {
- // TODO: add glyphs support.
-
- return true;
-}
-
SkFontStyle FontStyle(const char* style) {
static constexpr struct {
const char* fName;
@@ -82,15 +76,13 @@
} // namespace
-bool FontInfo::matches(const char family[], const char style[]) const {
+bool AnimationBuilder::FontInfo::matches(const char family[], const char style[]) const {
return 0 == strcmp(fFamily.c_str(), family)
&& 0 == strcmp(fStyle.c_str(), style);
}
-FontMap ParseFonts(const skjson::ObjectValue* jfonts, const skjson::ArrayValue* jchars,
- const SkFontMgr* fontmgr) {
- FontMap fonts;
-
+void AnimationBuilder::parseFonts(const skjson::ObjectValue* jfonts,
+ const skjson::ArrayValue* jchars) {
// Optional array of font entries, referenced (by name) from text layer document nodes. E.g.
// "fonts": {
// "list": [
@@ -124,19 +116,19 @@
continue;
}
- sk_sp<SkTypeface> tf(fontmgr->matchFamilyStyle(jfamily->begin(),
- FontStyle(jstyle->begin())));
+ sk_sp<SkTypeface> tf(fFontMgr->matchFamilyStyle(jfamily->begin(),
+ FontStyle(jstyle->begin())));
if (!tf) {
LOG("!! Could not create typeface for %s|%s\n",
jfamily->begin(), jstyle->begin());
// Last resort.
- tf.reset(fontmgr->matchFamilyStyle("Arial", SkFontStyle::Normal()));
+ tf.reset(fFontMgr->matchFamilyStyle("Arial", SkFontStyle::Normal()));
if (!tf) {
continue;
}
}
- fonts.set(SkString(jname->begin(), jname->size()),
+ fFonts.set(SkString(jname->begin(), jname->size()),
{
SkString(jfamily->begin(), jfamily->size()),
SkString(jstyle->begin(), jstyle->size()),
@@ -196,7 +188,7 @@
// If problematic, we can refactor as a two-level hashmap.
if (!current_font || !current_font->matches(family, style)) {
current_font = nullptr;
- fonts.foreach([&](const SkString& name, FontInfo* finfo) {
+ fFonts.foreach([&](const SkString& name, FontInfo* finfo) {
if (finfo->matches(family, style)) {
current_font = finfo;
// TODO: would be nice to break early here...
@@ -208,16 +200,13 @@
}
}
- if (!ParseGlyph(*jchar, current_font)) {
- LogJSON(*jchar, "!! Invalid glyph");
- }
+ // TODO: parse glyphs
}
}
-
- return fonts;
}
-sk_sp<sksg::RenderNode> AttachTextLayer(const skjson::ObjectValue& layer, AttachContext* ctx) {
+sk_sp<sksg::RenderNode> AnimationBuilder::attachTextLayer(const skjson::ObjectValue& layer,
+ AnimatorScope* ascope) {
// General text node format:
// "t": {
// "a": [], // animators (TODO)
@@ -278,7 +267,7 @@
return nullptr;
}
- const auto* font = ctx->fFonts.find(SkString(font_name->begin(), font_name->size()));
+ const auto* font = fFonts.find(SkString(font_name->begin(), font_name->size()));
if (!font) {
LOG("!! Unknown font: \"%s\"\n", font_name->begin());
return nullptr;