| /* |
| * Copyright 2018 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "modules/skottie/src/SkottiePriv.h" |
| |
| #include "modules/skottie/include/ExternalLayer.h" |
| #include "modules/skottie/src/Composition.h" |
| #include "modules/skottie/src/SkottieJson.h" |
| #include "modules/skottie/src/SkottieValue.h" |
| #include "modules/skottie/src/animator/Animator.h" |
| #include "modules/sksg/include/SkSGRenderNode.h" |
| #include "modules/sksg/include/SkSGScene.h" |
| #include "src/core/SkTLazy.h" |
| #include "src/utils/SkJSON.h" |
| |
| namespace skottie { |
| namespace internal { |
| |
| namespace { |
| |
| // "Animates" time based on the layer's "tm" property. |
| class TimeRemapper final : public AnimatablePropertyContainer { |
| public: |
| TimeRemapper(const skjson::ObjectValue& jtm, const AnimationBuilder* abuilder, float scale) |
| : fScale(scale) { |
| this->bind(*abuilder, jtm, fT); |
| } |
| |
| float t() const { return fT * fScale; } |
| |
| private: |
| void onSync() override { |
| // nothing to sync - we just track t |
| } |
| |
| const float fScale; |
| |
| ScalarValue fT = 0; |
| }; |
| |
| // Applies a bias/scale/remap t-adjustment to child animators. |
| class CompTimeMapper final : public Animator { |
| public: |
| CompTimeMapper(AnimatorScope&& layer_animators, |
| sk_sp<TimeRemapper> remapper, |
| float time_bias, float time_scale) |
| : fAnimators(std::move(layer_animators)) |
| , fRemapper(std::move(remapper)) |
| , fTimeBias(time_bias) |
| , fTimeScale(time_scale) {} |
| |
| StateChanged onSeek(float t) override { |
| if (fRemapper) { |
| // When time remapping is active, |t| is fully driven externally. |
| fRemapper->seek(t); |
| t = fRemapper->t(); |
| } else { |
| t = (t + fTimeBias) * fTimeScale; |
| } |
| |
| bool changed = false; |
| |
| for (const auto& anim : fAnimators) { |
| changed |= anim->seek(t); |
| } |
| |
| return changed; |
| } |
| |
| private: |
| const AnimatorScope fAnimators; |
| const sk_sp<TimeRemapper> fRemapper; |
| const float fTimeBias, |
| fTimeScale; |
| }; |
| |
| } // namespace |
| |
| sk_sp<sksg::RenderNode> AnimationBuilder::attachExternalPrecompLayer( |
| const skjson::ObjectValue& jlayer, |
| const LayerInfo& layer_info) const { |
| |
| if (!fPrecompInterceptor) { |
| return nullptr; |
| } |
| |
| const skjson::StringValue* id = jlayer["refId"]; |
| const skjson::StringValue* nm = jlayer["nm"]; |
| |
| if (!id || !nm) { |
| return nullptr; |
| } |
| |
| auto external_layer = fPrecompInterceptor->onLoadPrecomp(id->begin(), |
| nm->begin(), |
| layer_info.fSize); |
| if (!external_layer) { |
| return nullptr; |
| } |
| |
| // Attaches an ExternalLayer implementation to the animation scene graph. |
| class SGAdapter final : public sksg::RenderNode { |
| public: |
| SG_ATTRIBUTE(T, float, fCurrentT) |
| |
| SGAdapter(sk_sp<ExternalLayer> external, const SkSize& layer_size) |
| : fExternal(std::move(external)) |
| , fSize(layer_size) {} |
| |
| private: |
| SkRect onRevalidate(sksg::InvalidationController*, const SkMatrix&) override { |
| return SkRect::MakeSize(fSize); |
| } |
| |
| void onRender(SkCanvas* canvas, const RenderContext* ctx) const override { |
| // Commit all pending effects via a layer if needed, |
| // since we don't have knowledge of the external content. |
| const auto local_scope = |
| ScopedRenderContext(canvas, ctx).setIsolation(this->bounds(), |
| canvas->getTotalMatrix(), |
| true); |
| fExternal->render(canvas, static_cast<double>(fCurrentT)); |
| } |
| |
| const RenderNode* onNodeAt(const SkPoint& pt) const override { |
| SkASSERT(this->bounds().contains(pt.fX, pt.fY)); |
| return this; |
| } |
| |
| const sk_sp<ExternalLayer> fExternal; |
| const SkSize fSize; |
| float fCurrentT = 0; |
| }; |
| |
| // Connects an SGAdapter to the animator tree and dispatches seek events. |
| class AnimatorAdapter final : public Animator { |
| public: |
| AnimatorAdapter(sk_sp<SGAdapter> sg_adapter, float fps) |
| : fSGAdapter(std::move(sg_adapter)) |
| , fFps(fps) {} |
| |
| private: |
| StateChanged onSeek(float t) override { |
| fSGAdapter->setT(t / fFps); |
| |
| return true; |
| } |
| |
| const sk_sp<SGAdapter> fSGAdapter; |
| const float fFps; |
| }; |
| |
| auto sg_adapter = sk_make_sp<SGAdapter>(std::move(external_layer), layer_info.fSize); |
| |
| fCurrentAnimatorScope->push_back(sk_make_sp<AnimatorAdapter>(sg_adapter, fFrameRate)); |
| |
| return std::move(sg_adapter); |
| } |
| |
| sk_sp<sksg::RenderNode> AnimationBuilder::attachPrecompLayer(const skjson::ObjectValue& jlayer, |
| LayerInfo* layer_info) const { |
| sk_sp<TimeRemapper> time_remapper; |
| if (const skjson::ObjectValue* jtm = jlayer["tm"]) { |
| time_remapper = sk_make_sp<TimeRemapper>(*jtm, this, fFrameRate); |
| } |
| |
| const auto start_time = ParseDefault<float>(jlayer["st"], 0.0f), |
| stretch_time = ParseDefault<float>(jlayer["sr"], 1.0f); |
| const auto requires_time_mapping = !SkScalarNearlyEqual(start_time , 0) || |
| !SkScalarNearlyEqual(stretch_time, 1) || |
| time_remapper; |
| |
| // Precomp layers are sized explicitly. |
| layer_info->fSize = SkSize::Make(ParseDefault<float>(jlayer["w"], 0.0f), |
| ParseDefault<float>(jlayer["h"], 0.0f)); |
| |
| SkTLazy<AutoScope> local_scope; |
| if (requires_time_mapping) { |
| local_scope.init(this); |
| } |
| |
| auto precomp_layer = this->attachExternalPrecompLayer(jlayer, *layer_info); |
| |
| if (!precomp_layer) { |
| const ScopedAssetRef precomp_asset(this, jlayer); |
| if (precomp_asset) { |
| precomp_layer = |
| CompositionBuilder(*this, layer_info->fSize, *precomp_asset).build(*this); |
| } |
| } |
| |
| if (requires_time_mapping) { |
| const auto t_bias = -start_time, |
| t_scale = sk_ieee_float_divide(1, stretch_time); |
| auto time_mapper = sk_make_sp<CompTimeMapper>(local_scope->release(), |
| std::move(time_remapper), |
| t_bias, |
| sk_float_isfinite(t_scale) ? t_scale : 0); |
| |
| fCurrentAnimatorScope->push_back(std::move(time_mapper)); |
| } |
| |
| return precomp_layer; |
| } |
| |
| } // namespace internal |
| } // namespace skottie |