• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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