1 /*
2 * Copyright 2021 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 "include/core/SkBlendMode.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkData.h"
11 #include "include/core/SkImage.h"
12 #include "include/core/SkMatrix.h"
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkPicture.h"
15 #include "include/core/SkPictureRecorder.h"
16 #include "include/core/SkRect.h"
17 #include "include/core/SkRefCnt.h"
18 #include "include/core/SkSamplingOptions.h"
19 #include "include/core/SkShader.h"
20 #include "include/core/SkSize.h"
21 #include "include/core/SkString.h"
22 #include "include/core/SkTileMode.h"
23 #include "include/effects/SkRuntimeEffect.h"
24 #include "include/private/base/SkAssert.h"
25 #include "include/private/base/SkDebug.h"
26 #include "include/private/base/SkSpan_impl.h"
27 #include "modules/skottie/include/Skottie.h"
28 #include "modules/skottie/src/Adapter.h"
29 #include "modules/skottie/src/SkottieJson.h"
30 #include "modules/skottie/src/SkottiePriv.h"
31 #include "modules/skottie/src/SkottieValue.h"
32 #include "modules/skottie/src/animator/Animator.h"
33 #include "modules/skottie/src/effects/Effects.h"
34 #include "modules/skresources/include/SkResources.h"
35 #include "modules/sksg/include/SkSGColorFilter.h"
36 #include "modules/sksg/include/SkSGNode.h"
37 #include "modules/sksg/include/SkSGRenderNode.h"
38 #include "src/utils/SkJSON.h"
39
40 #include <cstdint>
41 #include <cstring>
42 #include <memory>
43 #include <tuple>
44 #include <utility>
45 #include <vector>
46
47 struct SkPoint;
48
49 namespace sksg {
50 class InvalidationController;
51 }
52
53 namespace skottie::internal {
54
55 // https://g-issues.chromium.org/issues/40064011
56 #if defined(SK_ENABLE_SKOTTIE_SKSLEFFECT)
57
58 class SkSLShaderNode final : public sksg::CustomRenderNode {
59 public:
SkSLShaderNode(sk_sp<RenderNode> child,const SkSize & content_size)60 explicit SkSLShaderNode(sk_sp<RenderNode> child, const SkSize& content_size)
61 : INHERITED({std::move(child)})
62 , fContentSize(content_size) {}
63
contentShader()64 sk_sp<SkShader> contentShader() {
65 if (!fContentShader || this->hasChildrenInval()) {
66 const auto& child = this->children()[0];
67 child->revalidate(nullptr, SkMatrix::I());
68
69 SkPictureRecorder recorder;
70 child->render(recorder.beginRecording(SkRect::MakeSize(fContentSize)));
71
72 fContentShader = recorder.finishRecordingAsPicture()
73 ->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat, SkFilterMode::kLinear,
74 nullptr, nullptr);
75 }
76
77 return fContentShader;
78 }
79
80 SG_ATTRIBUTE(Shader, sk_sp<SkShader>, fEffectShader)
81 private:
onRevalidate(sksg::InvalidationController * ic,const SkMatrix & ctm)82 SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
83 const auto& child = this->children()[0];
84 return child->revalidate(ic, ctm);
85 }
86
onRender(SkCanvas * canvas,const RenderContext * ctx) const87 void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
88 const auto& bounds = this->bounds();
89 const auto local_ctx = ScopedRenderContext(canvas, ctx)
90 .setIsolation(bounds, canvas->getTotalMatrix(), true);
91
92 canvas->saveLayer(&bounds, nullptr);
93 this->children()[0]->render(canvas, local_ctx);
94
95 SkPaint effect_paint;
96 effect_paint.setShader(fEffectShader);
97 effect_paint.setBlendMode(SkBlendMode::kSrcIn);
98
99 canvas->drawPaint(effect_paint);
100 }
101
onNodeAt(const SkPoint &) const102 const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
103
104 sk_sp<SkShader> fEffectShader;
105 sk_sp<SkShader> fContentShader;
106 const SkSize fContentSize;
107
108 using INHERITED = sksg::CustomRenderNode;
109 };
110
111 class SkSLEffectBase {
112 public:
SkSLEffectBase(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder)113 SkSLEffectBase(const skjson::ArrayValue& jprops,
114 const AnimationBuilder& abuilder)
115 {
116 if (jprops.size() < 1) {
117 return;
118 }
119 const skjson::ObjectValue* jSkSL = jprops[kSkSL_index];
120 if (!jSkSL) {
121 return;
122 }
123 const skjson::StringValue* jShader = (*jSkSL)["sh"];
124 if (!jShader) {
125 return;
126 }
127 SkString shader = SkString(jShader->begin(), jShader->size());
128 auto result = SkRuntimeEffect::MakeForShader(shader, {});
129 if (!result.effect) {
130 abuilder.log(Logger::Level::kError, nullptr, "Failed to parse SkSL shader: %s",
131 result.errorText.c_str());
132 return;
133 }
134 fEffect = std::move(result.effect);
135 }
136 protected:
137 enum : size_t {
138 kSkSL_index = 0,
139 kFirstUniform_index = 1,
140 };
141
142 enum : size_t {
143 kSkSLProp_uniform = 0, // Maps to the integer value 1
144 kSkSLProp_image = 98, // Maps to the integer value 98
145 kSkSLProp_layer = 99 // Maps to the integer value 99
146 };
147
148 struct ChildData {
149 int type;
150 SkString name;
151 SkRuntimeEffect::ChildPtr child;
152 };
153
bindUniforms(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder,AnimatablePropertyContainer * const & container)154 void bindUniforms(const skjson::ArrayValue& jprops,
155 const AnimationBuilder& abuilder,
156 AnimatablePropertyContainer * const &container) {
157 // construct dynamic uniform list from jprops, skip SkSL property
158 for (size_t i = kFirstUniform_index; i < jprops.size(); i++) {
159 const skjson::ObjectValue* jprop = jprops[i];
160 if (!jprop) { continue; }
161 const skjson::StringValue* uniformName = (*jprop)["nm"];
162 if (!uniformName) { continue; }
163 int type = ParseDefault<int>((*jprop)["ty"], kSkSLProp_uniform);
164 if (type == kSkSLProp_uniform) {
165 auto uniformTuple = std::make_tuple(SkString(uniformName->begin(),
166 uniformName->size()),
167 std::make_unique<VectorValue>());
168 fUniforms.push_back(std::move(uniformTuple));
169 container->bind(abuilder, (*jprop)["v"], std::get<1>(fUniforms.back()).get());
170 } else if (type == kSkSLProp_image) {
171 const skjson::ObjectValue* jimageRef = (*jprop)["v"];
172 const AnimationBuilder::ScopedAssetRef footageAsset(&abuilder, *jimageRef);
173 const auto* asset_info = abuilder.loadFootageAsset(*footageAsset);
174 if (asset_info && asset_info->fAsset) {
175 // TODO: instead of resolving shaders here, save a collection of footage assets
176 // onSync, grab the correct frameData and create a shader then
177 auto frameData = asset_info->fAsset->getFrameData(0);
178 SkSamplingOptions sampling(SkFilterMode::kLinear);
179 fChildren.push_back({type, SkString(uniformName->begin(), uniformName->size()),
180 frameData.image->makeShader(sampling)});
181 } else {
182 SkDebugf("cannot find asset for custom shader effect");
183 }
184 } else if (type == kSkSLProp_layer) { /* layer content */
185 fChildren.push_back({type, SkString(uniformName->begin(), uniformName->size()),
186 SkRuntimeEffect::ChildPtr()});
187 }
188 }
189 }
190
buildUniformData() const191 sk_sp<SkData> buildUniformData() const {
192 auto uniformData = SkData::MakeZeroInitialized(fEffect->uniformSize());
193 SkASSERT(uniformData);
194 for (const auto& uniform : fUniforms) {
195 const auto& name = std::get<0>(uniform);
196 const auto& data = std::get<1>(uniform);
197 auto metadata = fEffect->findUniform(name.c_str());
198 if (metadata && metadata->count == static_cast<int>(data->size())) {
199 auto dst = reinterpret_cast<uint8_t*>(uniformData->writable_data())
200 + metadata->offset;
201 memcpy(reinterpret_cast<void*>(dst), data->data(), data->size() * sizeof(float));
202 } else {
203 SkDebugf("cannot set malformed uniform: %s\n", name.c_str());
204 }
205 }
206 return uniformData;
207 }
208
buildChildrenData(sk_sp<SkSLShaderNode> node) const209 std::vector<SkRuntimeEffect::ChildPtr> buildChildrenData(sk_sp<SkSLShaderNode> node) const {
210 std::vector<SkRuntimeEffect::ChildPtr> childrenData(fEffect->children().size());
211 for (const auto& childData : fChildren) {
212 auto metadata = fEffect->findChild(childData.name.c_str());
213 if (childData.type == kSkSLProp_layer) {
214 childrenData[metadata->index] = (node->contentShader());
215 } else if (childData.type == kSkSLProp_image) {
216 childrenData[metadata->index] = childData.child;
217 }
218 }
219 return childrenData;
220 }
221 sk_sp<SkRuntimeEffect> fEffect;
222 std::vector<std::tuple<SkString, std::unique_ptr<VectorValue>>> fUniforms;
223 std::vector<ChildData> fChildren;
224 };
225
226 class SkSLShaderAdapter final : public DiscardableAdapterBase<SkSLShaderAdapter,
227 SkSLShaderNode>,
228 public SkSLEffectBase {
229 public:
SkSLShaderAdapter(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder,sk_sp<SkSLShaderNode> node)230 SkSLShaderAdapter(const skjson::ArrayValue& jprops,
231 const AnimationBuilder& abuilder,
232 sk_sp<SkSLShaderNode> node)
233 : DiscardableAdapterBase<SkSLShaderAdapter, SkSLShaderNode>(std::move(node))
234 , SkSLEffectBase(jprops, abuilder)
235 {
236 this->bindUniforms(jprops, abuilder, this);
237 }
238
239 private:
onSync()240 void onSync() override {
241 if (!fEffect) {
242 return;
243 }
244 sk_sp<SkShader> shader =
245 fEffect->makeShader(buildUniformData(), SkSpan(buildChildrenData(this->node())));
246 this->node()->setShader(std::move(shader));
247 }
248 };
249
250 class SkSLColorFilterAdapter final : public DiscardableAdapterBase<SkSLColorFilterAdapter,
251 sksg::ExternalColorFilter>
252 , public SkSLEffectBase {
253 public:
SkSLColorFilterAdapter(const skjson::ArrayValue & jprops,const AnimationBuilder & abuilder,sk_sp<sksg::ExternalColorFilter> node)254 SkSLColorFilterAdapter(const skjson::ArrayValue& jprops,
255 const AnimationBuilder& abuilder,
256 sk_sp<sksg::ExternalColorFilter> node)
257 : DiscardableAdapterBase<SkSLColorFilterAdapter, sksg::ExternalColorFilter>(std::move(node))
258 , SkSLEffectBase(jprops, abuilder)
259 {
260 this->bindUniforms(jprops, abuilder, this);
261 }
262
263 private:
onSync()264 void onSync() override {
265 if (!fEffect) {
266 return;
267 }
268 auto cf = fEffect->makeColorFilter(buildUniformData());
269 this->node()->setColorFilter(std::move(cf));
270 }
271 };
272
273 #endif // SK_ENABLE_SKOTTIE_SKSLEFFECT
274
attachSkSLShader(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const275 sk_sp<sksg::RenderNode> EffectBuilder::attachSkSLShader(const skjson::ArrayValue& jprops,
276 sk_sp<sksg::RenderNode> layer) const {
277 #if defined(SK_ENABLE_SKOTTIE_SKSLEFFECT)
278 auto shaderNode = sk_make_sp<SkSLShaderNode>(std::move(layer), fLayerSize);
279 return fBuilder->attachDiscardableAdapter<SkSLShaderAdapter>(jprops, *fBuilder,
280 std::move(shaderNode));
281 #else
282 return layer;
283 #endif
284 }
285
attachSkSLColorFilter(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const286 sk_sp<sksg::RenderNode> EffectBuilder::attachSkSLColorFilter(const skjson::ArrayValue& jprops,
287 sk_sp<sksg::RenderNode> layer) const {
288 #if defined(SK_ENABLE_SKOTTIE_SKSLEFFECT)
289 auto cfNode = sksg::ExternalColorFilter::Make(std::move(layer));
290 return fBuilder->attachDiscardableAdapter<SkSLColorFilterAdapter>(jprops, *fBuilder,
291 std::move(cfNode));
292 #else
293 return layer;
294 #endif
295 }
296
297 } // namespace skottie::internal
298