[skottie] Cache attached assets

This avoids redundant instantiations for assets referenced multiple
times.

TBR=

Change-Id: I8f61f73e695f0d567e55ef077c7d3fb344399f12
Reviewed-on: https://skia-review.googlesource.com/101002
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/experimental/skottie/Skottie.cpp b/experimental/skottie/Skottie.cpp
index 8545de8..8b7a3cc 100644
--- a/experimental/skottie/Skottie.cpp
+++ b/experimental/skottie/Skottie.cpp
@@ -50,7 +50,12 @@
 
 namespace {
 
-using AssetMap = SkTHashMap<SkString, const Json::Value*>;
+struct AssetRec {
+    const Json::Value*      fJson;
+    sk_sp<sksg::RenderNode> fNode;
+};
+
+using AssetMap = SkTHashMap<SkString, AssetRec>;
 
 struct AttachContext {
     const ResourceProvider&    fResources;
@@ -682,23 +687,33 @@
     return draws.empty() ? nullptr : shape_wrapper;
 }
 
-sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& layer, AttachContext* ctx) {
-    SkASSERT(layer.isObject());
+sk_sp<sksg::RenderNode> AttachAsset(const Json::Value& jobject, AttachContext* ctx,
+                                    sk_sp<sksg::RenderNode>(proc)(const Json::Value&,
+                                                                  AttachContext*)) {
+    SkASSERT(jobject.isObject());
 
     SkString refId;
-    if (!Parse(layer["refId"], &refId) || refId.isEmpty()) {
-        LOG("!! Comp layer missing refId\n");
+    if (!Parse(jobject["refId"], &refId) || refId.isEmpty()) {
+        LOG("!! Missing asset refId\n");
         return nullptr;
     }
 
-    const auto* comp = ctx->fAssets.find(refId);
-    if (!comp) {
-        LOG("!! Pre-comp not found: '%s'\n", refId.c_str());
+    auto* rec = ctx->fAssets.find(refId);
+    if (!rec) {
+        LOG("!! Asset not found: '%s'\n", refId.c_str());
         return nullptr;
     }
 
-    // TODO: cycle detection
-    return AttachComposition(**comp, ctx);
+    if (!rec->fNode) {
+        // TODO: cycle detection
+        rec->fNode = proc(*rec->fJson, ctx);
+    }
+
+    return rec->fNode;
+}
+
+sk_sp<sksg::RenderNode> AttachCompLayer(const Json::Value& jlayer, AttachContext* ctx) {
+    return AttachAsset(jlayer, ctx, AttachComposition);
 }
 
 sk_sp<sksg::RenderNode> AttachSolidLayer(const Json::Value& jlayer, AttachContext*) {
@@ -742,22 +757,8 @@
         SkImage::MakeFromEncoded(SkData::MakeFromStream(resStream.get(), resStream->getLength())));
 }
 
-sk_sp<sksg::RenderNode> AttachImageLayer(const Json::Value& layer, AttachContext* ctx) {
-    SkASSERT(layer.isObject());
-
-    SkString refId;
-    if (!Parse(layer["refId"], &refId) || refId.isEmpty()) {
-        LOG("!! Image layer missing refId\n");
-        return nullptr;
-    }
-
-    const auto* jimage = ctx->fAssets.find(refId);
-    if (!jimage) {
-        LOG("!! Image asset not found: '%s'\n", refId.c_str());
-        return nullptr;
-    }
-
-    return AttachImageAsset(**jimage, ctx);
+sk_sp<sksg::RenderNode> AttachImageLayer(const Json::Value& jlayer, AttachContext* ctx) {
+    return AttachAsset(jlayer, ctx, AttachImageAsset);
 }
 
 sk_sp<sksg::RenderNode> AttachNullLayer(const Json::Value& layer, AttachContext*) {
@@ -1094,7 +1095,7 @@
             continue;
         }
 
-        assets.set(ParseDefault(asset["id"], SkString()), &asset);
+        assets.set(ParseDefault(asset["id"], SkString()), {&asset, nullptr});
     }
 
     sksg::Scene::AnimatorList animators;