• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "modules/skottie/src/Composition.h"
9 
10 #include "include/core/SkString.h"
11 #include "include/private/base/SkTo.h"
12 #include "modules/jsonreader/SkJSONReader.h"
13 #include "modules/skottie/include/Skottie.h"
14 #include "modules/skottie/src/Camera.h"
15 #include "modules/skottie/src/SkottieJson.h"
16 #include "modules/skottie/src/SkottiePriv.h"
17 #include "modules/sksg/include/SkSGGroup.h"
18 #include "modules/sksg/include/SkSGRenderNode.h"
19 #include "modules/sksg/include/SkSGTransform.h"
20 
21 #include <algorithm>
22 #include <utility>
23 
24 namespace skottie {
25 namespace internal {
26 
ScopedAssetRef(const AnimationBuilder * abuilder,const skjson::ObjectValue & jlayer)27 AnimationBuilder::ScopedAssetRef::ScopedAssetRef(const AnimationBuilder* abuilder,
28                                                  const skjson::ObjectValue& jlayer) {
29     const auto refId = ParseDefault<SkString>(jlayer["refId"], SkString());
30     if (refId.isEmpty()) {
31         abuilder->log(Logger::Level::kError, nullptr, "Layer missing refId.");
32         return;
33     }
34 
35     const auto* asset_info = abuilder->fAssets.find(refId);
36     if (!asset_info) {
37         abuilder->log(Logger::Level::kError, nullptr, "Asset not found: '%s'.", refId.c_str());
38         return;
39     }
40 
41     if (asset_info->fIsAttaching) {
42         abuilder->log(Logger::Level::kError, nullptr,
43                   "Asset cycle detected for: '%s'", refId.c_str());
44         return;
45     }
46 
47     asset_info->fIsAttaching = true;
48 
49     fInfo = asset_info;
50 }
51 
CompositionBuilder(const AnimationBuilder & abuilder,const SkSize & size,const skjson::ObjectValue & jcomp)52 CompositionBuilder::CompositionBuilder(const AnimationBuilder& abuilder,
53                                        const SkSize& size,
54                                        const skjson::ObjectValue& jcomp)
55     : fSize(size)
56 {
57     int camera_builder_index = -1;
58 
59     // Prepare layer builders.
60     if (const skjson::ArrayValue* jlayers = jcomp["layers"]) {
61         fLayerBuilders.reserve(SkToInt(jlayers->size()));
62         for (const skjson::ObjectValue* jlayer : *jlayers) {
63             if (!jlayer) continue;
64 
65             const auto  lbuilder_index = fLayerBuilders.size();
66             fLayerBuilders.emplace_back(*jlayer, fSize);
67             const auto& lbuilder = fLayerBuilders.back();
68 
69             fLayerIndexMap.set(lbuilder.index(), lbuilder_index);
70 
71             // Keep track of the camera builder.
72             if (lbuilder.isCamera()) {
73                 // We only support one (first) camera for now.
74                 if (camera_builder_index < 0) {
75                     camera_builder_index = SkToInt(lbuilder_index);
76                 } else {
77                     abuilder.log(Logger::Level::kWarning, jlayer,
78                                  "Ignoring duplicate camera layer.");
79                 }
80             }
81         }
82     }
83 
84     // Attach a camera transform upfront, if needed (required to build
85     // all other 3D transform chains).
86     if (camera_builder_index >= 0) {
87         // Explicit camera.
88         fCameraTransform = fLayerBuilders[camera_builder_index].buildTransform(abuilder, this);
89     } else if (ParseDefault<int>(jcomp["ddd"], 0) && !fSize.isEmpty()) {
90         // Default/implicit camera when 3D layers are present.
91         fCameraTransform = CameraAdaper::DefaultCameraTransform(fSize);
92     }
93 }
94 
95 CompositionBuilder::~CompositionBuilder() = default;
96 
layerBuilder(int layer_index)97 LayerBuilder* CompositionBuilder::layerBuilder(int layer_index) {
98     if (layer_index < 0) {
99         return nullptr;
100     }
101 
102     if (const auto* idx = fLayerIndexMap.find(layer_index)) {
103         return &fLayerBuilders[SkToInt(*idx)];
104     }
105 
106     return nullptr;
107 }
108 
layerContent(const AnimationBuilder & abuilder,int layer_index)109 sk_sp<sksg::RenderNode> CompositionBuilder::layerContent(const AnimationBuilder& abuilder,
110                                                          int layer_index) {
111     if (auto* lbuilder = this->layerBuilder(layer_index)) {
112         return lbuilder->getContentTree(abuilder, this);
113     }
114 
115     return nullptr;
116 }
117 
build(const AnimationBuilder & abuilder)118 sk_sp<sksg::RenderNode> CompositionBuilder::build(const AnimationBuilder& abuilder) {
119     // First pass - transitively attach layer transform chains.
120     for (auto& lbuilder : fLayerBuilders) {
121         lbuilder.buildTransform(abuilder, this);
122     }
123 
124     // Second pass - attach actual layer contents and finalize the layer render tree.
125     std::vector<sk_sp<sksg::RenderNode>> layers;
126     layers.reserve(fLayerBuilders.size());
127 
128     int prev_layer_index = -1;
129     for (auto& lbuilder : fLayerBuilders) {
130         if (auto layer = lbuilder.buildRenderTree(abuilder, this, prev_layer_index)) {
131             layers.push_back(std::move(layer));
132         }
133         prev_layer_index = lbuilder.index();
134     }
135 
136     if (layers.empty()) {
137         return nullptr;
138     }
139 
140     if (layers.size() == 1) {
141         return std::move(layers[0]);
142     }
143 
144     // Layers are painted in bottom->top order.
145     std::reverse(layers.begin(), layers.end());
146     layers.shrink_to_fit();
147 
148     return sksg::Group::Make(std::move(layers));
149 }
150 
151 } // namespace internal
152 } // namespace skottie
153