/* * Copyright 2018 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkottiePriv_DEFINED #define SkottiePriv_DEFINED #include "modules/skottie/include/Skottie.h" #include "include/core/SkFontStyle.h" #include "include/core/SkString.h" #include "include/core/SkTypeface.h" #include "include/private/SkTHash.h" #include "modules/skottie/include/SkottieProperty.h" #include "modules/sksg/include/SkSGScene.h" #include "src/utils/SkUTF.h" #include class SkFontMgr; namespace skjson { class ArrayValue; class ObjectValue; class Value; } // namespace skjson namespace sksg { class Color; class Path; class RenderNode; class Transform; } // namespace sksg namespace skottie { class TransformAdapter2D; class TransformAdapter3D; namespace internal { class TextAdapter; using AnimatorScope = sksg::AnimatorList; class AnimationBuilder final : public SkNoncopyable { public: AnimationBuilder(sk_sp, sk_sp, sk_sp, sk_sp, sk_sp, Animation::Builder::Stats*, const SkSize& size, float duration, float framerate); std::unique_ptr parse(const skjson::ObjectValue&); struct FontInfo { SkString fFamily, fStyle; SkScalar fAscentPct; sk_sp fTypeface; bool matches(const char family[], const char style[]) const; }; const FontInfo* findFont(const SkString& name) const; // This is the workhorse for property binding: depending on whether the property is animated, // it will either apply immediately or instantiate and attach a keyframe animator. template bool bindProperty(const skjson::Value&, std::function&&, const T* default_igore = nullptr) const; template bool bindProperty(const skjson::Value& jv, std::function&& apply, const T& default_ignore) const { return this->bindProperty(jv, std::move(apply), &default_ignore); } void log(Logger::Level, const skjson::Value*, const char fmt[], ...) const; sk_sp attachColor(const skjson::ObjectValue&, const char prop_name[]) const; sk_sp attachMatrix2D(const skjson::ObjectValue&, sk_sp) const; sk_sp attachMatrix3D(const skjson::ObjectValue&, sk_sp, sk_sp = nullptr, bool precompose_parent = false) const; sk_sp attachOpacity(const skjson::ObjectValue&, sk_sp) const; sk_sp attachPath(const skjson::Value&) const; bool hasNontrivialBlending() const { return fHasNontrivialBlending; } class AutoScope final { public: explicit AutoScope(const AnimationBuilder* builder) : AutoScope(builder, AnimatorScope()) {} AutoScope(const AnimationBuilder* builder, AnimatorScope&& scope) : fBuilder(builder) , fCurrentScope(std::move(scope)) , fPrevScope(fBuilder->fCurrentAnimatorScope) { fBuilder->fCurrentAnimatorScope = &fCurrentScope; } AnimatorScope release() { fBuilder->fCurrentAnimatorScope = fPrevScope; SkDEBUGCODE(fBuilder = nullptr); return std::move(fCurrentScope); } ~AutoScope() { SkASSERT(!fBuilder); } private: const AnimationBuilder* fBuilder; AnimatorScope fCurrentScope; AnimatorScope* fPrevScope; }; template sk_sp attachDiscardableAdapter(Args&&... args) const { AutoScope ascope(this); auto adapter = T::Make(std::forward(args)...); auto adapter_animators = ascope.release(); if (!adapter) { return nullptr; } const auto& node = adapter->renderNode(); if (adapter_animators.empty()) { // Fire off a synthetic tick to force a single SG sync before discarding the adapter. adapter->tick(0); } else { adapter->setAnimators(std::move(adapter_animators)); fCurrentAnimatorScope->push_back(std::move(adapter)); } return node; } class AutoPropertyTracker { public: AutoPropertyTracker(const AnimationBuilder* builder, const skjson::ObjectValue& obj) : fBuilder(builder) , fPrevContext(builder->fPropertyObserverContext) { if (fBuilder->fPropertyObserver) { this->updateContext(builder->fPropertyObserver.get(), obj); } } ~AutoPropertyTracker() { if (fBuilder->fPropertyObserver) { fBuilder->fPropertyObserverContext = fPrevContext; } } private: void updateContext(PropertyObserver*, const skjson::ObjectValue&); const AnimationBuilder* fBuilder; const char* fPrevContext; }; private: struct AttachLayerContext; struct AttachShapeContext; struct ImageAssetInfo; struct LayerInfo; void parseAssets(const skjson::ArrayValue*); void parseFonts (const skjson::ObjectValue* jfonts, const skjson::ArrayValue* jchars); void dispatchMarkers(const skjson::ArrayValue*) const; sk_sp attachComposition(const skjson::ObjectValue&) const; sk_sp attachLayer(const skjson::ObjectValue*, AttachLayerContext*) const; sk_sp attachBlendMode(const skjson::ObjectValue&, sk_sp) const; sk_sp attachShape(const skjson::ArrayValue*, AttachShapeContext*) const; sk_sp attachAssetRef(const skjson::ObjectValue&, const std::function(const skjson::ObjectValue&)>&) const; const ImageAssetInfo* loadImageAsset(const skjson::ObjectValue&) const; sk_sp attachImageAsset(const skjson::ObjectValue&, LayerInfo*) const; sk_sp attachNestedAnimation(const char* name) const; sk_sp attachImageLayer (const skjson::ObjectValue&, LayerInfo*) const; sk_sp attachNullLayer (const skjson::ObjectValue&, LayerInfo*) const; sk_sp attachPrecompLayer(const skjson::ObjectValue&, LayerInfo*) const; sk_sp attachShapeLayer (const skjson::ObjectValue&, LayerInfo*) const; sk_sp attachSolidLayer (const skjson::ObjectValue&, LayerInfo*) const; sk_sp attachTextLayer (const skjson::ObjectValue&, LayerInfo*) const; bool dispatchColorProperty(const sk_sp&) const; bool dispatchOpacityProperty(const sk_sp&) const; bool dispatchTextProperty(const sk_sp&) const; bool dispatchTransformProperty(const sk_sp&) const; // Delay resolving the fontmgr until it is actually needed. struct LazyResolveFontMgr { LazyResolveFontMgr(sk_sp fontMgr) : fFontMgr(std::move(fontMgr)) {} const sk_sp& get() { if (!fFontMgr) { fFontMgr = SkFontMgr::RefDefault(); SkASSERT(fFontMgr); } return fFontMgr; } const sk_sp& getMaybeNull() const { return fFontMgr; } private: sk_sp fFontMgr; }; sk_sp fResourceProvider; LazyResolveFontMgr fLazyFontMgr; sk_sp fPropertyObserver; sk_sp fLogger; sk_sp fMarkerObserver; Animation::Builder::Stats* fStats; const SkSize fSize; const float fDuration, fFrameRate; mutable AnimatorScope* fCurrentAnimatorScope; mutable const char* fPropertyObserverContext; mutable bool fHasNontrivialBlending : 1; struct LayerInfo { SkSize fSize; const float fInPoint, fOutPoint; }; struct AssetInfo { const skjson::ObjectValue* fAsset; mutable bool fIsAttaching; // Used for cycle detection }; struct ImageAssetInfo { sk_sp fAsset; SkISize fSize; }; SkTHashMap fAssets; SkTHashMap fFonts; mutable SkTHashMap fImageAssetCache; using INHERITED = SkNoncopyable; }; struct AnimationBuilder::AttachLayerContext { explicit AttachLayerContext(const skjson::ArrayValue&); ~AttachLayerContext(); struct TransformRec { sk_sp fTransformNode; AnimatorScope fTransformScope; }; const skjson::ArrayValue& fLayerList; SkTHashMap fLayerTransformMap; sk_sp fCurrentMatte; sk_sp fCameraTransform; size_t fMotionBlurSamples = 1; float fMotionBlurAngle = 0, fMotionBlurPhase = 0; enum class TransformType { kLayer, kCamera }; TransformRec attachLayerTransform(const skjson::ObjectValue& jlayer, const AnimationBuilder* abuilder, TransformType type = TransformType::kLayer); bool hasMotionBlur(const skjson::ObjectValue& jlayer) const; private: sk_sp attachParentLayerTransform(const skjson::ObjectValue& jlayer, const AnimationBuilder* abuilder, int layer_index); sk_sp attachTransformNode(const skjson::ObjectValue& jlayer, const AnimationBuilder* abuilder, sk_sp parent_transform, TransformType type) const; TransformRec* attachLayerTransformImpl(const skjson::ObjectValue& jlayer, const AnimationBuilder* abuilder, TransformType type, int layer_index); }; } // namespace internal } // namespace skottie #endif // SkottiePriv_DEFINED