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/SkCanvas.h"
11#include "modules/skottie/src/Camera.h"
12#include "modules/skottie/src/SkottieJson.h"
13#include "modules/skottie/src/SkottiePriv.h"
14#include "modules/sksg/include/SkSGGroup.h"
15
16#include <algorithm>
17
18namespace skottie {
19namespace internal {
20
21AnimationBuilder::ScopedAssetRef::ScopedAssetRef(const AnimationBuilder* abuilder,
22 const skjson::ObjectValue& jlayer) {
23 const auto refId = ParseDefault<SkString>(jlayer["refId"], SkString());
24 if (refId.isEmpty()) {
25 abuilder->log(Logger::Level::kError, nullptr, "Layer missing refId.");
26 return;
27 }
28
29 const auto* asset_info = abuilder->fAssets.find(refId);
30 if (!asset_info) {
31 abuilder->log(Logger::Level::kError, nullptr, "Asset not found: '%s'.", refId.c_str());
32 return;
33 }
34
35 if (asset_info->fIsAttaching) {
36 abuilder->log(Logger::Level::kError, nullptr,
37 "Asset cycle detected for: '%s'", refId.c_str());
38 return;
39 }
40
41 asset_info->fIsAttaching = true;
42
43 fInfo = asset_info;
44}
45
46CompositionBuilder::CompositionBuilder(const AnimationBuilder& abuilder,
47 const SkSize& size,
48 const skjson::ObjectValue& jcomp)
49 : fSize(size) {
50
51 // Optional motion blur params.
52 if (const skjson::ObjectValue* jmb = jcomp["mb"]) {
53 static constexpr size_t kMaxSamplesPerFrame = 64;
54 fMotionBlurSamples = std::min(ParseDefault<size_t>((*jmb)["spf"], 1ul),
55 kMaxSamplesPerFrame);
56 fMotionBlurAngle = SkTPin(ParseDefault((*jmb)["sa"], 0.0f), 0.0f, 720.0f);
57 fMotionBlurPhase = SkTPin(ParseDefault((*jmb)["sp"], 0.0f), -360.0f, 360.0f);
58 }
59
60 int camera_builder_index = -1;
61
62 // Prepare layer builders.
63 if (const skjson::ArrayValue* jlayers = jcomp["layers"]) {
64 fLayerBuilders.reserve(SkToInt(jlayers->size()));
65 for (const skjson::ObjectValue* jlayer : *jlayers) {
66 if (!jlayer) continue;
67
68 const auto lbuilder_index = fLayerBuilders.size();
69 const auto& lbuilder = fLayerBuilders.emplace_back(*jlayer);
70
71 fLayerIndexMap.set(lbuilder.index(), lbuilder_index);
72
73 // Keep track of the camera builder.
74 if (lbuilder.isCamera()) {
75 // We only support one (first) camera for now.
76 if (camera_builder_index < 0) {
77 camera_builder_index = SkToInt(lbuilder_index);
78 } else {
79 abuilder.log(Logger::Level::kWarning, jlayer,
80 "Ignoring duplicate camera layer.");
81 }
82 }
83 }
84 }
85
86 // Attach a camera transform upfront, if needed (required to build
87 // all other 3D transform chains).
88 if (camera_builder_index >= 0) {
89 // Explicit camera.
90 fCameraTransform = fLayerBuilders[camera_builder_index].buildTransform(abuilder, this);
91 } else if (ParseDefault<int>(jcomp["ddd"], 0)) {
92 // Default/implicit camera when 3D layers are present.
93 fCameraTransform = CameraAdaper::DefaultCameraTransform(fSize);
94 }
95}
96
97CompositionBuilder::~CompositionBuilder() = default;
98
99LayerBuilder* CompositionBuilder::layerBuilder(int layer_index) {
100 if (layer_index < 0) {
101 return nullptr;
102 }
103
104 if (const auto* idx = fLayerIndexMap.find(layer_index)) {
105 return &fLayerBuilders[SkToInt(*idx)];
106 }
107
108 return nullptr;
109}
110
111sk_sp<sksg::RenderNode> CompositionBuilder::build(const AnimationBuilder& abuilder) {
112 // First pass - transitively attach layer transform chains.
113 for (auto& lbuilder : fLayerBuilders) {
114 lbuilder.buildTransform(abuilder, this);
115 }
116
117 // Second pass - attach actual layer contents and finalize the layer render tree.
118 std::vector<sk_sp<sksg::RenderNode>> layers;
119 layers.reserve(fLayerBuilders.size());
120
121 LayerBuilder* prev_layer = nullptr;
122 for (auto& lbuilder : fLayerBuilders) {
123 if (auto layer = lbuilder.buildRenderTree(abuilder, this, prev_layer)) {
124 layers.push_back(std::move(layer));
125 }
126 prev_layer = &lbuilder;
127 }
128
129 if (layers.empty()) {
130 return nullptr;
131 }
132
133 if (layers.size() == 1) {
134 return std::move(layers[0]);
135 }
136
137 // Layers are painted in bottom->top order.
138 std::reverse(layers.begin(), layers.end());
139 layers.shrink_to_fit();
140
141 return sksg::Group::Make(std::move(layers));
142}
143
144} // namespace internal
145} // namespace skottie
146