blob: 37fa5245f9901586d750b45915b827e24ffb2d16 [file] [log] [blame]
/*
* 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 "include/utils/SkCustomTypeface.h"
#include "modules/skottie/include/SkottieProperty.h"
#include "modules/skottie/src/animator/Animator.h"
#include "modules/sksg/include/SkSGScene.h"
#include "src/utils/SkUTF.h"
#include <vector>
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 {
namespace internal {
// Close-enough to AE.
static constexpr float kBlurSizeToSigma = 0.3f;
class TextAdapter;
class TransformAdapter2D;
class TransformAdapter3D;
using AnimatorScope = std::vector<sk_sp<Animator>>;
class AnimationBuilder final : public SkNoncopyable {
public:
AnimationBuilder(sk_sp<ResourceProvider>, sk_sp<SkFontMgr>, sk_sp<PropertyObserver>,
sk_sp<Logger>, sk_sp<MarkerObserver>, sk_sp<PrecompInterceptor>,
Animation::Builder::Stats*, const SkSize& comp_size,
float duration, float framerate, uint32_t flags);
struct AnimationInfo {
std::unique_ptr<sksg::Scene> fScene;
AnimatorScope fAnimators;
};
AnimationInfo parse(const skjson::ObjectValue&);
struct FontInfo {
SkString fFamily,
fStyle,
fPath;
SkScalar fAscentPct;
sk_sp<SkTypeface> fTypeface;
SkCustomTypefaceBuilder fCustomBuilder;
bool matches(const char family[], const char style[]) const;
};
const FontInfo* findFont(const SkString& name) const;
void log(Logger::Level, const skjson::Value*, const char fmt[], ...) const;
sk_sp<sksg::Transform> attachMatrix2D(const skjson::ObjectValue&, sk_sp<sksg::Transform>,
bool auto_orient = false) const;
sk_sp<sksg::Transform> attachMatrix3D(const skjson::ObjectValue&, sk_sp<sksg::Transform>,
bool auto_orient = false) const;
sk_sp<sksg::Transform> attachCamera(const skjson::ObjectValue& jlayer,
const skjson::ObjectValue& jtransform,
sk_sp<sksg::Transform>,
const SkSize&) const;
sk_sp<sksg::RenderNode> attachOpacity(const skjson::ObjectValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::Path> 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 <typename T>
void attachDiscardableAdapter(sk_sp<T> adapter) const {
if (adapter->isStatic()) {
// Fire off a synthetic tick to force a single SG sync before discarding.
adapter->seek(0);
} else {
fCurrentAnimatorScope->push_back(std::move(adapter));
}
}
template <typename T, typename... Args>
auto attachDiscardableAdapter(Args&&... args) const ->
typename std::decay<decltype(T::Make(std::forward<Args>(args)...)->node())>::type
{
using NodeType =
typename std::decay<decltype(T::Make(std::forward<Args>(args)...)->node())>::type;
NodeType node;
if (auto adapter = T::Make(std::forward<Args>(args)...)) {
node = adapter->node();
this->attachDiscardableAdapter(std::move(adapter));
}
return node;
}
class AutoPropertyTracker {
public:
AutoPropertyTracker(const AnimationBuilder* builder, const skjson::ObjectValue& obj)
: fBuilder(builder)
, fPrevContext(builder->fPropertyObserverContext) {
if (fBuilder->fPropertyObserver) {
auto observer = builder->fPropertyObserver.get();
this->updateContext(observer, obj);
observer->onEnterNode(fBuilder->fPropertyObserverContext);
}
}
~AutoPropertyTracker() {
if (fBuilder->fPropertyObserver) {
fBuilder->fPropertyObserver->onLeavingNode(fBuilder->fPropertyObserverContext);
fBuilder->fPropertyObserverContext = fPrevContext;
}
}
private:
void updateContext(PropertyObserver*, const skjson::ObjectValue&);
const AnimationBuilder* fBuilder;
const char* fPrevContext;
};
bool dispatchColorProperty(const sk_sp<sksg::Color>&) const;
bool dispatchOpacityProperty(const sk_sp<sksg::OpacityEffect>&) const;
bool dispatchTextProperty(const sk_sp<TextAdapter>&) const;
bool dispatchTransformProperty(const sk_sp<TransformAdapter2D>&) const;
private:
friend class CompositionBuilder;
friend class LayerBuilder;
struct AttachLayerContext;
struct AttachShapeContext;
struct FootageAssetInfo;
struct LayerInfo;
void parseAssets(const skjson::ArrayValue*);
void parseFonts (const skjson::ObjectValue* jfonts,
const skjson::ArrayValue* jchars);
// Return true iff all fonts were resolved.
bool resolveNativeTypefaces();
bool resolveEmbeddedTypefaces(const skjson::ArrayValue& jchars);
void dispatchMarkers(const skjson::ArrayValue*) const;
sk_sp<sksg::RenderNode> attachBlendMode(const skjson::ObjectValue&,
sk_sp<sksg::RenderNode>) const;
sk_sp<sksg::RenderNode> attachShape(const skjson::ArrayValue*, AttachShapeContext*,
bool suppress_draws = false) const;
const FootageAssetInfo* loadFootageAsset(const skjson::ObjectValue&) const;
sk_sp<sksg::RenderNode> attachFootageAsset(const skjson::ObjectValue&, LayerInfo*) const;
sk_sp<sksg::RenderNode> attachExternalPrecompLayer(const skjson::ObjectValue&,
const LayerInfo&) const;
sk_sp<sksg::RenderNode> attachFootageLayer(const skjson::ObjectValue&, LayerInfo*) const;
sk_sp<sksg::RenderNode> attachNullLayer (const skjson::ObjectValue&, LayerInfo*) const;
sk_sp<sksg::RenderNode> attachPrecompLayer(const skjson::ObjectValue&, LayerInfo*) const;
sk_sp<sksg::RenderNode> attachShapeLayer (const skjson::ObjectValue&, LayerInfo*) const;
sk_sp<sksg::RenderNode> attachSolidLayer (const skjson::ObjectValue&, LayerInfo*) const;
sk_sp<sksg::RenderNode> attachTextLayer (const skjson::ObjectValue&, LayerInfo*) const;
sk_sp<sksg::RenderNode> attachAudioLayer (const skjson::ObjectValue&, LayerInfo*) const;
// Delay resolving the fontmgr until it is actually needed.
struct LazyResolveFontMgr {
LazyResolveFontMgr(sk_sp<SkFontMgr> fontMgr) : fFontMgr(std::move(fontMgr)) {}
const sk_sp<SkFontMgr>& get() {
if (!fFontMgr) {
fFontMgr = SkFontMgr::RefDefault();
SkASSERT(fFontMgr);
}
return fFontMgr;
}
const sk_sp<SkFontMgr>& getMaybeNull() const { return fFontMgr; }
private:
sk_sp<SkFontMgr> fFontMgr;
};
sk_sp<ResourceProvider> fResourceProvider;
LazyResolveFontMgr fLazyFontMgr;
sk_sp<PropertyObserver> fPropertyObserver;
sk_sp<Logger> fLogger;
sk_sp<MarkerObserver> fMarkerObserver;
sk_sp<PrecompInterceptor> fPrecompInterceptor;
Animation::Builder::Stats* fStats;
const SkSize fCompSize;
const float fDuration,
fFrameRate;
const uint32_t fFlags;
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 FootageAssetInfo {
sk_sp<ImageAsset> fAsset;
SkISize fSize;
};
class ScopedAssetRef {
public:
ScopedAssetRef(const AnimationBuilder* abuilder, const skjson::ObjectValue& jlayer);
~ScopedAssetRef() {
if (fInfo) {
fInfo->fIsAttaching = false;
}
}
operator bool() const { return !!fInfo; }
const skjson::ObjectValue& operator*() const { return *fInfo->fAsset; }
private:
const AssetInfo* fInfo = nullptr;
};
SkTHashMap<SkString, AssetInfo> fAssets;
SkTHashMap<SkString, FontInfo> fFonts;
mutable SkTHashMap<SkString, FootageAssetInfo> fImageAssetCache;
using INHERITED = SkNoncopyable;
};
} // namespace internal
} // namespace skottie
#endif // SkottiePriv_DEFINED