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