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