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/SkottiePriv.h"
9
10 #include "modules/skottie/src/SkottieAdapter.h"
11 #include "modules/skottie/src/SkottieJson.h"
12 #include "modules/skottie/src/effects/Effects.h"
13 #include "modules/skottie/src/effects/MotionBlurEffect.h"
14 #include "modules/sksg/include/SkSGClipEffect.h"
15 #include "modules/sksg/include/SkSGDraw.h"
16 #include "modules/sksg/include/SkSGGroup.h"
17 #include "modules/sksg/include/SkSGMaskEffect.h"
18 #include "modules/sksg/include/SkSGMerge.h"
19 #include "modules/sksg/include/SkSGPaint.h"
20 #include "modules/sksg/include/SkSGPath.h"
21 #include "modules/sksg/include/SkSGRect.h"
22 #include "modules/sksg/include/SkSGRenderEffect.h"
23 #include "modules/sksg/include/SkSGRenderNode.h"
24 #include "modules/sksg/include/SkSGTransform.h"
25
26 namespace skottie {
27 namespace internal {
28
29 namespace {
30
31 static constexpr int kNullLayerType = 3;
32 static constexpr int kCameraLayerType = 13;
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
AttachMask(const skjson::ArrayValue * jmask,const AnimationBuilder * abuilder,sk_sp<sksg::RenderNode> childNode)63 sk_sp<sksg::RenderNode> AttachMask(const skjson::ArrayValue* jmask,
64 const AnimationBuilder* abuilder,
65 sk_sp<sksg::RenderNode> childNode) {
66 if (!jmask) return childNode;
67
68 struct MaskRecord {
69 sk_sp<sksg::Path> mask_path; // for clipping and masking
70 sk_sp<sksg::Color> mask_paint; // for masking
71 sk_sp<sksg::BlurImageFilter> mask_blur; // for masking
72 sksg::Merge::Mode merge_mode; // for clipping
73 };
74
75 SkSTArray<4, MaskRecord, true> mask_stack;
76
77 bool has_effect = false;
78 auto blur_effect = sksg::BlurImageFilter::Make();
79
80 for (const skjson::ObjectValue* m : *jmask) {
81 if (!m) continue;
82
83 const skjson::StringValue* jmode = (*m)["mode"];
84 if (!jmode || jmode->size() != 1) {
85 abuilder->log(Logger::Level::kError, &(*m)["mode"], "Invalid mask mode.");
86 continue;
87 }
88
89 const auto mode = *jmode->begin();
90 if (mode == 'n') {
91 // "None" masks have no effect.
92 continue;
93 }
94
95 const auto* mask_info = GetMaskInfo(mode);
96 if (!mask_info) {
97 abuilder->log(Logger::Level::kWarning, nullptr, "Unsupported mask mode: '%c'.", mode);
98 continue;
99 }
100
101 auto mask_path = abuilder->attachPath((*m)["pt"]);
102 if (!mask_path) {
103 abuilder->log(Logger::Level::kError, m, "Could not parse mask path.");
104 continue;
105 }
106
107 // "inv" is cumulative with mask info fInvertGeometry
108 const auto inverted =
109 (mask_info->fInvertGeometry != ParseDefault<bool>((*m)["inv"], false));
110 mask_path->setFillType(inverted ? SkPath::kInverseWinding_FillType
111 : SkPath::kWinding_FillType);
112
113 auto mask_paint = sksg::Color::Make(SK_ColorBLACK);
114 mask_paint->setAntiAlias(true);
115 // First mask in the stack initializes the mask buffer.
116 mask_paint->setBlendMode(mask_stack.empty() ? SkBlendMode::kSrc
117 : mask_info->fBlendMode);
118
119 has_effect |= abuilder->bindProperty<ScalarValue>((*m)["o"],
120 [mask_paint](const ScalarValue& o) {
121 mask_paint->setOpacity(o * 0.01f);
122 }, 100.0f);
123
124 static const VectorValue default_feather = { 0, 0 };
125 if (abuilder->bindProperty<VectorValue>((*m)["f"],
126 [blur_effect](const VectorValue& feather) {
127 // Close enough to AE.
128 static constexpr SkScalar kFeatherToSigma = 0.38f;
129 auto sX = feather.size() > 0 ? feather[0] * kFeatherToSigma : 0,
130 sY = feather.size() > 1 ? feather[1] * kFeatherToSigma : 0;
131 blur_effect->setSigma({ sX, sY });
132 }, default_feather)) {
133
134 has_effect = true;
135 mask_stack.push_back({ mask_path,
136 mask_paint,
137 std::move(blur_effect),
138 mask_info->fMergeMode});
139 blur_effect = sksg::BlurImageFilter::Make();
140 } else {
141 mask_stack.push_back({mask_path, mask_paint, nullptr, mask_info->fMergeMode});
142 }
143 }
144
145 if (mask_stack.empty())
146 return childNode;
147
148 // If the masks are fully opaque, we can clip.
149 if (!has_effect) {
150 sk_sp<sksg::GeometryNode> clip_node;
151
152 if (mask_stack.count() == 1) {
153 // Single path -> just clip.
154 clip_node = std::move(mask_stack.front().mask_path);
155 } else {
156 // Multiple clip paths -> merge.
157 std::vector<sksg::Merge::Rec> merge_recs;
158 merge_recs.reserve(SkToSizeT(mask_stack.count()));
159
160 for (auto& mask : mask_stack) {
161 const auto mode = merge_recs.empty() ? sksg::Merge::Mode::kMerge : mask.merge_mode;
162 merge_recs.push_back({std::move(mask.mask_path), mode});
163 }
164 clip_node = sksg::Merge::Make(std::move(merge_recs));
165 }
166
167 return sksg::ClipEffect::Make(std::move(childNode), std::move(clip_node), true);
168 }
169
170 const auto make_mask = [](const MaskRecord& rec) {
171 auto mask = sksg::Draw::Make(std::move(rec.mask_path),
172 std::move(rec.mask_paint));
173 // Optional mask blur (feather).
174 return sksg::ImageFilterEffect::Make(std::move(mask), std::move(rec.mask_blur));
175 };
176
177 sk_sp<sksg::RenderNode> maskNode;
178 if (mask_stack.count() == 1) {
179 // no group needed for single mask
180 maskNode = make_mask(mask_stack.front());
181 } else {
182 std::vector<sk_sp<sksg::RenderNode>> masks;
183 masks.reserve(SkToSizeT(mask_stack.count()));
184 for (auto& rec : mask_stack) {
185 masks.push_back(make_mask(rec));
186 }
187
188 maskNode = sksg::Group::Make(std::move(masks));
189 }
190
191 return sksg::MaskEffect::Make(std::move(childNode), std::move(maskNode));
192 }
193
194 class LayerController final : public sksg::Animator {
195 public:
LayerController(sksg::AnimatorList && layer_animators,sk_sp<sksg::RenderNode> layer,size_t tanim_count,float in,float out)196 LayerController(sksg::AnimatorList&& layer_animators,
197 sk_sp<sksg::RenderNode> layer,
198 size_t tanim_count, float in, float out)
199 : fLayerAnimators(std::move(layer_animators))
200 , fLayerNode(std::move(layer))
201 , fTransformAnimatorsCount(tanim_count)
202 , fIn(in)
203 , fOut(out) {}
204
205 protected:
onTick(float t)206 void onTick(float t) override {
207 const auto active = (t >= fIn && t < fOut);
208
209 if (fLayerNode) {
210 fLayerNode->setVisible(active);
211 }
212
213 // When active, dispatch ticks to all layer animators.
214 // When inactive, we must still dispatch ticks to the layer transform animators
215 // (active child layers depend on transforms being updated).
216 const auto dispatch_count = active ? fLayerAnimators.size()
217 : fTransformAnimatorsCount;
218 for (size_t i = 0; i < dispatch_count; ++i) {
219 fLayerAnimators[i]->tick(t);
220 }
221 }
222
223 private:
224 const sksg::AnimatorList fLayerAnimators;
225 const sk_sp<sksg::RenderNode> fLayerNode;
226 const size_t fTransformAnimatorsCount;
227 const float fIn,
228 fOut;
229 };
230
231 class MotionBlurController final : public sksg::Animator {
232 public:
MotionBlurController(sk_sp<MotionBlurEffect> mbe)233 explicit MotionBlurController(sk_sp<MotionBlurEffect> mbe)
234 : fMotionBlurEffect(std::move(mbe)) {}
235
236 protected:
237 // When motion blur is present, time ticks are not passed to layer animators
238 // but to the motion blur effect. The effect then drives the animators/scene-graph
239 // during reval and render phases.
onTick(float t)240 void onTick(float t) override {
241 fMotionBlurEffect->setT(t);
242 }
243
244 private:
245 const sk_sp<MotionBlurEffect> fMotionBlurEffect;
246 };
247
248 } // namespace
249
AttachLayerContext(const skjson::ArrayValue & jlayers)250 AnimationBuilder::AttachLayerContext::AttachLayerContext(const skjson::ArrayValue& jlayers)
251 : fLayerList(jlayers) {}
252
253 AnimationBuilder::AttachLayerContext::~AttachLayerContext() = default;
254
255 AnimationBuilder::AttachLayerContext::TransformRec
attachLayerTransform(const skjson::ObjectValue & jlayer,const AnimationBuilder * abuilder,TransformType type)256 AnimationBuilder::AttachLayerContext::attachLayerTransform(const skjson::ObjectValue& jlayer,
257 const AnimationBuilder* abuilder,
258 TransformType type) {
259 TransformRec result;
260
261 const auto layer_index = ParseDefault<int>(jlayer["ind"], -1);
262 if (layer_index >= 0) {
263 auto* rec = fLayerTransformMap.find(layer_index);
264 if (!rec) {
265 rec = this->attachLayerTransformImpl(jlayer, abuilder, type, layer_index);
266 }
267 SkASSERT(rec);
268
269 // Note: the transform animator scope is *moved* to the result, because
270 // we want the animators transferred to the LayerController.
271 //
272 // This is safe because a) the scope is not used internally, and
273 // b) there is exactly one attachLayerTransform call per layer.
274 // The transform node OTOH may be used at a later time for parenting.
275 result.fTransformNode = rec->fTransformNode;
276 result.fTransformScope = std::move(rec->fTransformScope);
277 }
278
279 return result;
280 }
281
282 sk_sp<sksg::Transform>
attachParentLayerTransform(const skjson::ObjectValue & jlayer,const AnimationBuilder * abuilder,int layer_index)283 AnimationBuilder::AttachLayerContext::attachParentLayerTransform(const skjson::ObjectValue& jlayer,
284 const AnimationBuilder* abuilder,
285 int layer_index) {
286 const auto parent_index = ParseDefault<int>(jlayer["parent"], -1);
287 if (parent_index < 0 || parent_index == layer_index)
288 return nullptr;
289
290 if (const auto* rec = fLayerTransformMap.find(parent_index))
291 return rec->fTransformNode;
292
293 for (const skjson::ObjectValue* l : fLayerList) {
294 if (!l) continue;
295
296 if (ParseDefault<int>((*l)["ind"], -1) == parent_index) {
297 const auto parent_type = ParseDefault<int>((*l)["ty"], -1) == kCameraLayerType
298 ? TransformType::kCamera
299 : TransformType::kLayer;
300 return this->attachLayerTransformImpl(*l,
301 abuilder,
302 parent_type,
303 parent_index)->fTransformNode;
304 }
305 }
306
307 return nullptr;
308 }
309
310 sk_sp<sksg::Transform>
attachTransformNode(const skjson::ObjectValue & jlayer,const AnimationBuilder * abuilder,sk_sp<sksg::Transform> parent_transform,TransformType type) const311 AnimationBuilder::AttachLayerContext::attachTransformNode(const skjson::ObjectValue& jlayer,
312 const AnimationBuilder* abuilder,
313 sk_sp<sksg::Transform> parent_transform,
314 TransformType type) const {
315 const skjson::ObjectValue* jtransform = jlayer["ks"];
316 if (!jtransform) {
317 return nullptr;
318 }
319
320 if (type == TransformType::kCamera) {
321 auto camera_adapter = sk_make_sp<CameraAdapter>(abuilder->fSize);
322
323 abuilder->bindProperty<ScalarValue>(jlayer["pe"],
324 [camera_adapter] (const ScalarValue& pe) {
325 // 'pe' (perspective?) corresponds to AE's "zoom" camera property.
326 camera_adapter->setZoom(pe);
327 });
328
329 // parent_transform applies to the camera itself => it pre-composes inverted to the
330 // camera/view/adapter transform.
331 //
332 // T_camera' = T_camera x Inv(parent_transform)
333 //
334 parent_transform = sksg::Transform::MakeInverse(std::move(parent_transform));
335
336 return abuilder->attachMatrix3D(*jtransform,
337 std::move(parent_transform),
338 std::move(camera_adapter),
339 true); // pre-compose parent
340 }
341
342 return (ParseDefault<int>(jlayer["ddd"], 0) == 0)
343 ? abuilder->attachMatrix2D(*jtransform, std::move(parent_transform))
344 : abuilder->attachMatrix3D(*jtransform, std::move(parent_transform));
345 }
346
347 AnimationBuilder::AttachLayerContext::TransformRec*
attachLayerTransformImpl(const skjson::ObjectValue & jlayer,const AnimationBuilder * abuilder,TransformType type,int layer_index)348 AnimationBuilder::AttachLayerContext::attachLayerTransformImpl(const skjson::ObjectValue& jlayer,
349 const AnimationBuilder* abuilder,
350 TransformType type,
351 int layer_index) {
352 SkASSERT(!fLayerTransformMap.find(layer_index));
353
354 // Add a stub entry to break recursion cycles.
355 fLayerTransformMap.set(layer_index, { nullptr, {} });
356
357 auto parent_matrix = this->attachParentLayerTransform(jlayer, abuilder, layer_index);
358
359 AutoScope ascope(abuilder);
360 auto transform = this->attachTransformNode(jlayer,
361 abuilder,
362 std::move(parent_matrix),
363 type);
364
365 return fLayerTransformMap.set(layer_index, { std::move(transform), ascope.release() });
366 }
367
hasMotionBlur(const skjson::ObjectValue & jlayer) const368 bool AnimationBuilder::AttachLayerContext::hasMotionBlur(const skjson::ObjectValue& jlayer) const {
369 return fMotionBlurSamples > 1
370 && fMotionBlurAngle > 0
371 && ParseDefault(jlayer["mb"], false);
372 }
373
attachLayer(const skjson::ObjectValue * jlayer,AttachLayerContext * layerCtx) const374 sk_sp<sksg::RenderNode> AnimationBuilder::attachLayer(const skjson::ObjectValue* jlayer,
375 AttachLayerContext* layerCtx) const {
376 if (!jlayer) {
377 return nullptr;
378 }
379
380 LayerInfo layer_info = {
381 fSize,
382 ParseDefault<float>((*jlayer)["ip"], 0.0f),
383 ParseDefault<float>((*jlayer)["op"], 0.0f),
384 };
385 if (layer_info.fInPoint >= layer_info.fOutPoint) {
386 this->log(Logger::Level::kError, nullptr,
387 "Invalid layer in/out points: %f/%f.", layer_info.fInPoint, layer_info.fOutPoint);
388 return nullptr;
389 }
390
391 const AutoPropertyTracker apt(this, *jlayer);
392
393 using LayerBuilder = sk_sp<sksg::RenderNode> (AnimationBuilder::*)(const skjson::ObjectValue&,
394 LayerInfo*) const;
395
396 // AE is annoyingly inconsistent in how effects interact with layer transforms: depending on
397 // the layer type, effects are applied before or after the content is transformed.
398 //
399 // Empirically, pre-rendered layers (for some loose meaning of "pre-rendered") are in the
400 // former category (effects are subject to transformation), while the remaining types are in
401 // the latter.
402 enum : uint32_t {
403 kTransformEffects = 1, // The layer transform also applies to its effects.
404 };
405
406 static constexpr struct {
407 LayerBuilder fBuilder;
408 uint32_t fFlags;
409 } gLayerBuildInfo[] = {
410 { &AnimationBuilder::attachPrecompLayer, kTransformEffects }, // 'ty': 0 -> precomp
411 { &AnimationBuilder::attachSolidLayer , kTransformEffects }, // 'ty': 1 -> solid
412 { &AnimationBuilder::attachImageLayer , kTransformEffects }, // 'ty': 2 -> image
413 { &AnimationBuilder::attachNullLayer , 0 }, // 'ty': 3 -> null
414 { &AnimationBuilder::attachShapeLayer , 0 }, // 'ty': 4 -> shape
415 { &AnimationBuilder::attachTextLayer , 0 }, // 'ty': 5 -> text
416 };
417
418 const auto type = ParseDefault<int>((*jlayer)["ty"], -1);
419 if ((type < 0) ||
420 (type >= SkTo<int>(SK_ARRAY_COUNT(gLayerBuildInfo)) && type != kCameraLayerType)) {
421 return nullptr;
422 }
423
424 // Optional layer transform.
425 const auto transform_type = (type == kCameraLayerType)
426 ? AttachLayerContext::TransformType::kCamera
427 : AttachLayerContext::TransformType::kLayer;
428 auto layer_transform_rec = layerCtx->attachLayerTransform(*jlayer, this, transform_type);
429
430 if (type == kCameraLayerType) {
431 // Camera layers are special: they don't build normal SG fragments, but drive a root-level
432 // transform.
433 if (layerCtx->fCameraTransform) {
434 this->log(Logger::Level::kWarning, jlayer, "Ignoring duplicate camera layer.");
435 return nullptr;
436 }
437
438 layerCtx->fCameraTransform = layer_transform_rec.fTransformNode;
439 }
440
441 AutoScope ascope(this, std::move(layer_transform_rec.fTransformScope));
442 const auto transform_animator_count = fCurrentAnimatorScope->size();
443
444 const auto is_hidden = ParseDefault<bool>((*jlayer)["hd"], false) || type == kCameraLayerType;
445 const auto& build_info = gLayerBuildInfo[is_hidden ? kNullLayerType : type];
446
447 // Build the layer content fragment.
448 auto layer = (this->*(build_info.fBuilder))(*jlayer, &layer_info);
449
450 // Clip layers with explicit dimensions.
451 float w = 0, h = 0;
452 if (Parse<float>((*jlayer)["w"], &w) && Parse<float>((*jlayer)["h"], &h)) {
453 layer = sksg::ClipEffect::Make(std::move(layer),
454 sksg::Rect::Make(SkRect::MakeWH(w, h)),
455 true);
456 }
457
458 // Optional layer mask.
459 layer = AttachMask((*jlayer)["masksProperties"], this, std::move(layer));
460
461 // Does the transform apply to effects also?
462 // (AE quirk: it doesn't - except for solid layers)
463 const auto transform_effects = (build_info.fFlags & kTransformEffects);
464
465 // Attach the transform before effects, when needed.
466 if (layer_transform_rec.fTransformNode && !transform_effects) {
467 layer = sksg::TransformEffect::Make(std::move(layer), layer_transform_rec.fTransformNode);
468 }
469
470 // Optional layer effects.
471 if (const skjson::ArrayValue* jeffects = (*jlayer)["ef"]) {
472 layer = EffectBuilder(this, layer_info.fSize).attachEffects(*jeffects, std::move(layer));
473 }
474
475 // Attach the transform after effects, when needed.
476 if (layer_transform_rec.fTransformNode && transform_effects) {
477 layer = sksg::TransformEffect::Make(std::move(layer),
478 std::move(layer_transform_rec.fTransformNode));
479 }
480
481 // Optional layer opacity.
482 // TODO: de-dupe this "ks" lookup with matrix above.
483 if (const skjson::ObjectValue* jtransform = (*jlayer)["ks"]) {
484 layer = this->attachOpacity(*jtransform, std::move(layer));
485 }
486
487 // Optional blend mode.
488 layer = this->attachBlendMode(*jlayer, std::move(layer));
489
490 const auto has_animators = !fCurrentAnimatorScope->empty();
491
492 sk_sp<sksg::Animator> controller = sk_make_sp<LayerController>(ascope.release(),
493 layer,
494 transform_animator_count,
495 layer_info.fInPoint,
496 layer_info.fOutPoint);
497
498 // Optional motion blur.
499 if (has_animators && layerCtx->hasMotionBlur(*jlayer)) {
500 SkASSERT(layerCtx->fMotionBlurAngle >= 0);
501
502 // Wrap both the layer node and the controller.
503 auto motion_blur = MotionBlurEffect::Make(std::move(controller), std::move(layer),
504 layerCtx->fMotionBlurSamples,
505 layerCtx->fMotionBlurAngle,
506 layerCtx->fMotionBlurPhase);
507 controller = sk_make_sp<MotionBlurController>(motion_blur);
508 layer = std::move(motion_blur);
509 }
510
511 fCurrentAnimatorScope->push_back(std::move(controller));
512
513 if (!layer) {
514 return nullptr;
515 }
516
517 if (ParseDefault<bool>((*jlayer)["td"], false)) {
518 // This layer is a matte. We apply it as a mask to the next layer.
519 layerCtx->fCurrentMatte = std::move(layer);
520 return nullptr;
521 }
522
523 if (layerCtx->fCurrentMatte) {
524 // There is a pending matte. Apply and reset.
525 static constexpr sksg::MaskEffect::Mode gMaskModes[] = {
526 sksg::MaskEffect::Mode::kNormal, // tt: 1
527 sksg::MaskEffect::Mode::kInvert, // tt: 2
528 };
529 const auto matteType = ParseDefault<size_t>((*jlayer)["tt"], 1) - 1;
530
531 if (matteType < SK_ARRAY_COUNT(gMaskModes)) {
532 return sksg::MaskEffect::Make(std::move(layer),
533 std::move(layerCtx->fCurrentMatte),
534 gMaskModes[matteType]);
535 }
536 layerCtx->fCurrentMatte.reset();
537 }
538
539 return layer;
540 }
541
542 } // namespace internal
543 } // namespace skottie
544