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