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/Layer.h"
9
10 #include "modules/skottie/src/Camera.h"
11 #include "modules/skottie/src/Composition.h"
12 #include "modules/skottie/src/SkottieJson.h"
13 #include "modules/skottie/src/effects/Effects.h"
14 #include "modules/skottie/src/effects/MotionBlurEffect.h"
15 #include "modules/sksg/include/SkSGClipEffect.h"
16 #include "modules/sksg/include/SkSGDraw.h"
17 #include "modules/sksg/include/SkSGGroup.h"
18 #include "modules/sksg/include/SkSGMaskEffect.h"
19 #include "modules/sksg/include/SkSGMerge.h"
20 #include "modules/sksg/include/SkSGPaint.h"
21 #include "modules/sksg/include/SkSGPath.h"
22 #include "modules/sksg/include/SkSGRect.h"
23 #include "modules/sksg/include/SkSGRenderEffect.h"
24 #include "modules/sksg/include/SkSGRenderNode.h"
25 #include "modules/sksg/include/SkSGTransform.h"
26
27 namespace skottie {
28 namespace internal {
29
30 namespace {
31
32 static constexpr int kNullLayerType = 3;
33
34 struct MaskInfo {
35 SkBlendMode fBlendMode; // used when masking with layers/blending
36 sksg::Merge::Mode fMergeMode; // used when clipping
37 bool fInvertGeometry;
38 };
39
GetMaskInfo(char mode)40 const MaskInfo* GetMaskInfo(char mode) {
41 static constexpr MaskInfo k_add_info =
42 { SkBlendMode::kSrcOver , sksg::Merge::Mode::kUnion , false };
43 static constexpr MaskInfo k_int_info =
44 { SkBlendMode::kSrcIn , sksg::Merge::Mode::kIntersect , false };
45 // AE 'subtract' is the same as 'intersect' + inverted geometry
46 // (draws the opacity-adjusted paint *outside* the shape).
47 static constexpr MaskInfo k_sub_info =
48 { SkBlendMode::kSrcIn , sksg::Merge::Mode::kIntersect , true };
49 static constexpr MaskInfo k_dif_info =
50 { SkBlendMode::kDifference, sksg::Merge::Mode::kDifference, false };
51
52 switch (mode) {
53 case 'a': return &k_add_info;
54 case 'f': return &k_dif_info;
55 case 'i': return &k_int_info;
56 case 's': return &k_sub_info;
57 default: break;
58 }
59
60 return nullptr;
61 }
62
63 class MaskAdapter final : public AnimatablePropertyContainer {
64 public:
MaskAdapter(const skjson::ObjectValue & jmask,const AnimationBuilder & abuilder,SkBlendMode bm)65 MaskAdapter(const skjson::ObjectValue& jmask, const AnimationBuilder& abuilder, SkBlendMode bm)
66 : fMaskPaint(sksg::Color::Make(SK_ColorBLACK)) {
67 fMaskPaint->setAntiAlias(true);
68 fMaskPaint->setBlendMode(bm);
69
70 this->bind(abuilder, jmask["o"], fOpacity);
71
72 if (this->bind(abuilder, jmask["f"], fFeather)) {
73 fMaskFilter = sksg::BlurImageFilter::Make();
74 }
75 }
76
hasEffect() const77 bool hasEffect() const {
78 return !this->isStatic()
79 || fOpacity < 100
80 || ValueTraits<VectorValue>::As<SkVector>(fFeather) != SkVector{ 0, 0 };
81 }
82
makeMask(sk_sp<sksg::Path> mask_path) const83 sk_sp<sksg::RenderNode> makeMask(sk_sp<sksg::Path> mask_path) const {
84 auto mask = sksg::Draw::Make(std::move(mask_path), fMaskPaint);
85
86 // Optional mask blur (feather).
87 return sksg::ImageFilterEffect::Make(std::move(mask), fMaskFilter);
88 }
89
90 private:
onSync()91 void onSync() override {
92 fMaskPaint->setOpacity(fOpacity * 0.01f);
93 if (fMaskFilter) {
94 const auto f = ValueTraits<VectorValue>::As<SkVector>(fFeather);
95
96 // Close enough to AE.
97 static constexpr SkScalar kFeatherToSigma = 0.38f;
98 fMaskFilter->setSigma(f * kFeatherToSigma);
99 }
100 }
101
102 const sk_sp<sksg::PaintNode> fMaskPaint;
103 sk_sp<sksg::BlurImageFilter> fMaskFilter; // optional "feather"
104
105 VectorValue fFeather;
106 ScalarValue fOpacity = 100;
107 };
108
AttachMask(const skjson::ArrayValue * jmask,const AnimationBuilder * abuilder,sk_sp<sksg::RenderNode> childNode)109 sk_sp<sksg::RenderNode> AttachMask(const skjson::ArrayValue* jmask,
110 const AnimationBuilder* abuilder,
111 sk_sp<sksg::RenderNode> childNode) {
112 if (!jmask) return childNode;
113
114 struct MaskRecord {
115 sk_sp<sksg::Path> mask_path; // for clipping and masking
116 sk_sp<MaskAdapter> mask_adapter; // for masking
117 sksg::Merge::Mode merge_mode; // for clipping
118 };
119
120 SkSTArray<4, MaskRecord, true> mask_stack;
121 bool has_effect = false;
122
123 for (const skjson::ObjectValue* m : *jmask) {
124 if (!m) continue;
125
126 const skjson::StringValue* jmode = (*m)["mode"];
127 if (!jmode || jmode->size() != 1) {
128 abuilder->log(Logger::Level::kError, &(*m)["mode"], "Invalid mask mode.");
129 continue;
130 }
131
132 const auto mode = *jmode->begin();
133 if (mode == 'n') {
134 // "None" masks have no effect.
135 continue;
136 }
137
138 const auto* mask_info = GetMaskInfo(mode);
139 if (!mask_info) {
140 abuilder->log(Logger::Level::kWarning, nullptr, "Unsupported mask mode: '%c'.", mode);
141 continue;
142 }
143
144 auto mask_path = abuilder->attachPath((*m)["pt"]);
145 if (!mask_path) {
146 abuilder->log(Logger::Level::kError, m, "Could not parse mask path.");
147 continue;
148 }
149
150 // "inv" is cumulative with mask info fInvertGeometry
151 const auto inverted =
152 (mask_info->fInvertGeometry != ParseDefault<bool>((*m)["inv"], false));
153 mask_path->setFillType(inverted ? SkPathFillType::kInverseWinding
154 : SkPathFillType::kWinding);
155
156 const auto blend_mode = mask_stack.empty() ? SkBlendMode::kSrc
157 : mask_info->fBlendMode;
158
159 auto mask_adapter = sk_make_sp<MaskAdapter>(*m, *abuilder, blend_mode);
160 abuilder->attachDiscardableAdapter(mask_adapter);
161
162 has_effect |= mask_adapter->hasEffect();
163
164
165 mask_stack.push_back({ std::move(mask_path),
166 std::move(mask_adapter),
167 mask_info->fMergeMode });
168 }
169
170
171 if (mask_stack.empty())
172 return childNode;
173
174 // If the masks are fully opaque, we can clip.
175 if (!has_effect) {
176 sk_sp<sksg::GeometryNode> clip_node;
177
178 if (mask_stack.count() == 1) {
179 // Single path -> just clip.
180 clip_node = std::move(mask_stack.front().mask_path);
181 } else {
182 // Multiple clip paths -> merge.
183 std::vector<sksg::Merge::Rec> merge_recs;
184 merge_recs.reserve(SkToSizeT(mask_stack.count()));
185
186 for (auto& mask : mask_stack) {
187 const auto mode = merge_recs.empty() ? sksg::Merge::Mode::kMerge : mask.merge_mode;
188 merge_recs.push_back({std::move(mask.mask_path), mode});
189 }
190 clip_node = sksg::Merge::Make(std::move(merge_recs));
191 }
192
193 return sksg::ClipEffect::Make(std::move(childNode), std::move(clip_node), true);
194 }
195
196 // Complex masks (non-opaque or blurred) turn into a mask node stack.
197 sk_sp<sksg::RenderNode> maskNode;
198 if (mask_stack.count() == 1) {
199 // no group needed for single mask
200 const auto rec = mask_stack.front();
201 maskNode = rec.mask_adapter->makeMask(std::move(rec.mask_path));
202 } else {
203 std::vector<sk_sp<sksg::RenderNode>> masks;
204 masks.reserve(SkToSizeT(mask_stack.count()));
205 for (auto& rec : mask_stack) {
206 masks.push_back(rec.mask_adapter->makeMask(std::move(rec.mask_path)));
207 }
208
209 maskNode = sksg::Group::Make(std::move(masks));
210 }
211
212 return sksg::MaskEffect::Make(std::move(childNode), std::move(maskNode));
213 }
214
215 class LayerController final : public sksg::Animator {
216 public:
LayerController(sksg::AnimatorList && layer_animators,sk_sp<sksg::RenderNode> layer,size_t tanim_count,float in,float out)217 LayerController(sksg::AnimatorList&& layer_animators,
218 sk_sp<sksg::RenderNode> layer,
219 size_t tanim_count, float in, float out)
220 : fLayerAnimators(std::move(layer_animators))
221 , fLayerNode(std::move(layer))
222 , fTransformAnimatorsCount(tanim_count)
223 , fIn(in)
224 , fOut(out) {}
225
226 protected:
onTick(float t)227 void onTick(float t) override {
228 const auto active = (t >= fIn && t < fOut);
229
230 if (fLayerNode) {
231 fLayerNode->setVisible(active);
232 }
233
234 // When active, dispatch ticks to all layer animators.
235 // When inactive, we must still dispatch ticks to the layer transform animators
236 // (active child layers depend on transforms being updated).
237 const auto dispatch_count = active ? fLayerAnimators.size()
238 : fTransformAnimatorsCount;
239 for (size_t i = 0; i < dispatch_count; ++i) {
240 fLayerAnimators[i]->tick(t);
241 }
242 }
243
244 private:
245 const sksg::AnimatorList fLayerAnimators;
246 const sk_sp<sksg::RenderNode> fLayerNode;
247 const size_t fTransformAnimatorsCount;
248 const float fIn,
249 fOut;
250 };
251
252 class MotionBlurController final : public sksg::Animator {
253 public:
MotionBlurController(sk_sp<MotionBlurEffect> mbe)254 explicit MotionBlurController(sk_sp<MotionBlurEffect> mbe)
255 : fMotionBlurEffect(std::move(mbe)) {}
256
257 protected:
258 // When motion blur is present, time ticks are not passed to layer animators
259 // but to the motion blur effect. The effect then drives the animators/scene-graph
260 // during reval and render phases.
onTick(float t)261 void onTick(float t) override {
262 fMotionBlurEffect->setT(t);
263 }
264
265 private:
266 const sk_sp<MotionBlurEffect> fMotionBlurEffect;
267 };
268
269 } // namespace
270
LayerBuilder(const skjson::ObjectValue & jlayer)271 LayerBuilder::LayerBuilder(const skjson::ObjectValue& jlayer)
272 : fJlayer(jlayer)
273 , fIndex(ParseDefault<int>(jlayer["ind"], -1))
274 , fParentIndex(ParseDefault<int>(jlayer["parent"], -1))
275 , fType(ParseDefault<int>(jlayer["ty"], -1)) {
276
277 if (this->isCamera() || ParseDefault<int>(jlayer["ddd"], 0)) {
278 fFlags |= Flags::kIs3D;
279 }
280 }
281
282 LayerBuilder::~LayerBuilder() = default;
283
isCamera() const284 bool LayerBuilder::isCamera() const {
285 static constexpr int kCameraLayerType = 13;
286
287 return fType == kCameraLayerType;
288 }
289
buildTransform(const AnimationBuilder & abuilder,CompositionBuilder * cbuilder)290 sk_sp<sksg::Transform> LayerBuilder::buildTransform(const AnimationBuilder& abuilder,
291 CompositionBuilder* cbuilder) {
292 // Depending on the leaf node type, we treat the whole transform chain as either 2D or 3D.
293 const auto transform_chain_type = this->is3D() ? TransformType::k3D
294 : TransformType::k2D;
295 fLayerTransform = this->getTransform(abuilder, cbuilder, transform_chain_type);
296
297 return fLayerTransform;
298 }
299
getTransform(const AnimationBuilder & abuilder,CompositionBuilder * cbuilder,TransformType ttype)300 sk_sp<sksg::Transform> LayerBuilder::getTransform(const AnimationBuilder& abuilder,
301 CompositionBuilder* cbuilder,
302 TransformType ttype) {
303 const auto cache_valid_mask = (1ul << ttype);
304 if (!(fFlags & cache_valid_mask)) {
305 // Set valid flag upfront to break cycles.
306 fFlags |= cache_valid_mask;
307
308 const AnimationBuilder::AutoPropertyTracker apt(&abuilder, fJlayer);
309 AnimationBuilder::AutoScope ascope(&abuilder, std::move(fLayerScope));
310 fTransformCache[ttype] = this->doAttachTransform(abuilder, cbuilder, ttype);
311 fLayerScope = ascope.release();
312 fTransformAnimatorCount = fLayerScope.size();
313 }
314
315 return fTransformCache[ttype];
316 }
317
getParentTransform(const AnimationBuilder & abuilder,CompositionBuilder * cbuilder,TransformType ttype)318 sk_sp<sksg::Transform> LayerBuilder::getParentTransform(const AnimationBuilder& abuilder,
319 CompositionBuilder* cbuilder,
320 TransformType ttype) {
321 if (auto* parent_builder = cbuilder->layerBuilder(fParentIndex)) {
322 // Explicit parent layer.
323 return parent_builder->getTransform(abuilder, cbuilder, ttype);
324 }
325
326 if (ttype == TransformType::k3D) {
327 // During camera transform attachment, cbuilder->getCameraTransform() is null.
328 // This prevents camera->camera transform chain cycles.
329 SkASSERT(!this->isCamera() || !cbuilder->getCameraTransform());
330
331 // 3D transform chains are implicitly rooted onto the camera.
332 return cbuilder->getCameraTransform();
333 }
334
335 return nullptr;
336 }
337
doAttachTransform(const AnimationBuilder & abuilder,CompositionBuilder * cbuilder,TransformType ttype)338 sk_sp<sksg::Transform> LayerBuilder::doAttachTransform(const AnimationBuilder& abuilder,
339 CompositionBuilder* cbuilder,
340 TransformType ttype) {
341 const skjson::ObjectValue* jtransform = fJlayer["ks"];
342 if (!jtransform) {
343 return nullptr;
344 }
345
346 auto parent_transform = this->getParentTransform(abuilder, cbuilder, ttype);
347
348 if (this->isCamera()) {
349 // parent_transform applies to the camera itself => it pre-composes inverted to the
350 // camera/view/adapter transform.
351 //
352 // T_camera' = T_camera x Inv(parent_transform)
353 //
354 return abuilder.attachCamera(fJlayer,
355 *jtransform,
356 sksg::Transform::MakeInverse(std::move(parent_transform)),
357 cbuilder->fSize);
358 }
359
360 return this->is3D()
361 ? abuilder.attachMatrix3D(*jtransform, std::move(parent_transform))
362 : abuilder.attachMatrix2D(*jtransform, std::move(parent_transform));
363 }
364
hasMotionBlur(const CompositionBuilder * cbuilder) const365 bool LayerBuilder::hasMotionBlur(const CompositionBuilder* cbuilder) const {
366 return cbuilder->fMotionBlurSamples > 1
367 && cbuilder->fMotionBlurAngle > 0
368 && ParseDefault(fJlayer["mb"], false);
369 }
370
buildRenderTree(const AnimationBuilder & abuilder,CompositionBuilder * cbuilder,const LayerBuilder * prev_layer)371 sk_sp<sksg::RenderNode> LayerBuilder::buildRenderTree(const AnimationBuilder& abuilder,
372 CompositionBuilder* cbuilder,
373 const LayerBuilder* prev_layer) {
374 AnimationBuilder::LayerInfo layer_info = {
375 cbuilder->fSize,
376 ParseDefault<float>(fJlayer["ip"], 0.0f),
377 ParseDefault<float>(fJlayer["op"], 0.0f),
378 };
379 if (layer_info.fInPoint >= layer_info.fOutPoint) {
380 abuilder.log(Logger::Level::kError, nullptr,
381 "Invalid layer in/out points: %f/%f.",
382 layer_info.fInPoint, layer_info.fOutPoint);
383 return nullptr;
384 }
385
386 const AnimationBuilder::AutoPropertyTracker apt(&abuilder, fJlayer);
387
388 using LayerBuilder =
389 sk_sp<sksg::RenderNode> (AnimationBuilder::*)(const skjson::ObjectValue&,
390 AnimationBuilder::LayerInfo*) const;
391
392 // AE is annoyingly inconsistent in how effects interact with layer transforms: depending on
393 // the layer type, effects are applied before or after the content is transformed.
394 //
395 // Empirically, pre-rendered layers (for some loose meaning of "pre-rendered") are in the
396 // former category (effects are subject to transformation), while the remaining types are in
397 // the latter.
398 enum : uint32_t {
399 kTransformEffects = 1, // The layer transform also applies to its effects.
400 };
401
402 static constexpr struct {
403 LayerBuilder fBuilder;
404 uint32_t fFlags;
405 } gLayerBuildInfo[] = {
406 { &AnimationBuilder::attachPrecompLayer, kTransformEffects }, // 'ty': 0 -> precomp
407 { &AnimationBuilder::attachSolidLayer , kTransformEffects }, // 'ty': 1 -> solid
408 { &AnimationBuilder::attachImageLayer , kTransformEffects }, // 'ty': 2 -> image
409 { &AnimationBuilder::attachNullLayer , 0 }, // 'ty': 3 -> null
410 { &AnimationBuilder::attachShapeLayer , 0 }, // 'ty': 4 -> shape
411 { &AnimationBuilder::attachTextLayer , 0 }, // 'ty': 5 -> text
412 };
413
414 if (SkToSizeT(fType) >= SK_ARRAY_COUNT(gLayerBuildInfo) && !this->isCamera()) {
415 return nullptr;
416 }
417
418 // Switch to the layer animator scope (which at this point holds transform-only animators).
419 AnimationBuilder::AutoScope ascope(&abuilder, std::move(fLayerScope));
420
421 const auto is_hidden = ParseDefault<bool>(fJlayer["hd"], false) || this->isCamera();
422 const auto& build_info = gLayerBuildInfo[is_hidden ? kNullLayerType : SkToSizeT(fType)];
423
424 // Build the layer content fragment.
425 auto layer = (abuilder.*(build_info.fBuilder))(fJlayer, &layer_info);
426
427 // Clip layers with explicit dimensions.
428 float w = 0, h = 0;
429 if (Parse<float>(fJlayer["w"], &w) && Parse<float>(fJlayer["h"], &h)) {
430 layer = sksg::ClipEffect::Make(std::move(layer),
431 sksg::Rect::Make(SkRect::MakeWH(w, h)),
432 true);
433 }
434
435 // Optional layer mask.
436 layer = AttachMask(fJlayer["masksProperties"], &abuilder, std::move(layer));
437
438 // Does the transform apply to effects also?
439 // (AE quirk: it doesn't - except for solid layers)
440 const auto transform_effects = (build_info.fFlags & kTransformEffects);
441
442 // Attach the transform before effects, when needed.
443 if (fLayerTransform && !transform_effects) {
444 layer = sksg::TransformEffect::Make(std::move(layer), fLayerTransform);
445 }
446
447 // Optional layer effects.
448 if (const skjson::ArrayValue* jeffects = fJlayer["ef"]) {
449 layer = EffectBuilder(&abuilder, layer_info.fSize).attachEffects(*jeffects,
450 std::move(layer));
451 }
452
453 // Attach the transform after effects, when needed.
454 if (fLayerTransform && transform_effects) {
455 layer = sksg::TransformEffect::Make(std::move(layer), std::move(fLayerTransform));
456 }
457
458 // Optional layer opacity.
459 // TODO: de-dupe this "ks" lookup with matrix above.
460 if (const skjson::ObjectValue* jtransform = fJlayer["ks"]) {
461 layer = abuilder.attachOpacity(*jtransform, std::move(layer));
462 }
463
464 const auto has_animators = !abuilder.fCurrentAnimatorScope->empty();
465
466 sk_sp<sksg::Animator> controller = sk_make_sp<LayerController>(ascope.release(),
467 layer,
468 fTransformAnimatorCount,
469 layer_info.fInPoint,
470 layer_info.fOutPoint);
471
472 // Optional motion blur.
473 if (layer && has_animators && this->hasMotionBlur(cbuilder)) {
474 // Wrap both the layer node and the controller.
475 auto motion_blur = MotionBlurEffect::Make(std::move(controller), std::move(layer),
476 cbuilder->fMotionBlurSamples,
477 cbuilder->fMotionBlurAngle,
478 cbuilder->fMotionBlurPhase);
479 controller = sk_make_sp<MotionBlurController>(motion_blur);
480 layer = std::move(motion_blur);
481 }
482
483 abuilder.fCurrentAnimatorScope->push_back(std::move(controller));
484
485 // Stash the content tree in case it is needed for later mattes.
486 fContentTree = layer;
487
488 if (ParseDefault<bool>(fJlayer["td"], false)) {
489 // |layer| is a track matte. We apply it as a mask to the next layer.
490 return nullptr;
491 }
492
493 // Optional matte.
494 size_t matte_mode;
495 if (prev_layer && Parse(fJlayer["tt"], &matte_mode)) {
496 static constexpr sksg::MaskEffect::Mode gMatteModes[] = {
497 sksg::MaskEffect::Mode::kAlphaNormal, // tt: 1
498 sksg::MaskEffect::Mode::kAlphaInvert, // tt: 2
499 sksg::MaskEffect::Mode::kLumaNormal, // tt: 3
500 sksg::MaskEffect::Mode::kLumaInvert, // tt: 4
501 };
502
503 if (matte_mode > 0 && matte_mode <= SK_ARRAY_COUNT(gMatteModes)) {
504 // The current layer is masked with the previous layer *content*.
505 layer = sksg::MaskEffect::Make(std::move(layer),
506 prev_layer->fContentTree,
507 gMatteModes[matte_mode - 1]);
508 } else {
509 abuilder.log(Logger::Level::kError, nullptr,
510 "Unknown track matte mode: %zu\n", matte_mode);
511 }
512 }
513
514 // Finally, attach an optional blend mode.
515 // NB: blend modes are never applied to matte sources (layer content only).
516 return abuilder.attachBlendMode(fJlayer, std::move(layer));
517 }
518
519 } // namespace internal
520 } // namespace skottie
521