• 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/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