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