[skottie] Plumb external SkFontMgr
Allow embedders to pass a font manager.
In order to avoid excessive factory API clutter, introduce an
Animation::Builder helper to wrap factory options.
Also clean up various bits:
* hoist scene parsing out of the Animation ctor
* store the animation duration explicitly (instead of unused fps)
* plumb const SkFontMgr& internally (instead of a ref)
Change-Id: I3e180dfa85ba18c8462cfeb5a7385bef985ed6c4
Reviewed-on: https://skia-review.googlesource.com/148800
Reviewed-by: Ben Wagner <bungeman@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skottie/include/Skottie.h b/modules/skottie/include/Skottie.h
index 2375a2e..95d50c0 100644
--- a/modules/skottie/include/Skottie.h
+++ b/modules/skottie/include/Skottie.h
@@ -8,6 +8,7 @@
#ifndef Skottie_DEFINED
#define Skottie_DEFINED
+#include "SkFontMgr.h"
#include "SkRefCnt.h"
#include "SkSize.h"
#include "SkString.h"
@@ -37,19 +38,56 @@
class SK_API Animation : public SkRefCnt {
public:
- struct Stats {
- float fTotalLoadTimeMS,
- fJsonParseTimeMS,
- fSceneParseTimeMS;
- size_t fJsonSize,
- fAnimatorCount;
+
+ class Builder final {
+ public:
+ struct Stats {
+ float fTotalLoadTimeMS = 0, // Total animation instantiation time.
+ fJsonParseTimeMS = 0, // Time spent building a JSON DOM.
+ fSceneParseTimeMS = 0; // Time spent constructing the animation scene graph.
+ size_t fJsonSize = 0, // Input JSON size.
+ fAnimatorCount = 0; // Number of dynamically animated properties.
+ };
+
+ /**
+ * Returns various animation build stats.
+ *
+ * @return Stats (see above).
+ */
+ const Stats& getStats() const { return fStats; }
+
+ /**
+ * Specify a loader for external resources (images, etc.). Ownership stays with the caller
+ * (the ResrouceProvider must be valid for this builder's lifespan).
+ */
+ Builder& setResourceProvider(const ResourceProvider*);
+
+ /**
+ * Specify a font manager for loading animation fonts.
+ */
+ Builder& setFontManager(sk_sp<SkFontMgr>);
+
+ /**
+ * Animation factories.
+ */
+ sk_sp<Animation> make(SkStream*);
+ sk_sp<Animation> make(const char* data, size_t length);
+ sk_sp<Animation> makeFromFile(const char path[]);
+
+ private:
+ const ResourceProvider* fResourceProvider = nullptr;
+ sk_sp<SkFontMgr> fFontMgr;
+ Stats fStats;
};
- static sk_sp<Animation> Make(const char* data, size_t length,
- const ResourceProvider* = nullptr, Stats* = nullptr);
- static sk_sp<Animation> Make(SkStream*, const ResourceProvider* = nullptr, Stats* = nullptr);
- static sk_sp<Animation> MakeFromFile(const char path[], const ResourceProvider* = nullptr,
- Stats* = nullptr);
+ /**
+ * Animation factories.
+ *
+ * Use the Builder helper above for more options/control.
+ */
+ static sk_sp<Animation> Make(const char* data, size_t length);
+ static sk_sp<Animation> Make(SkStream*);
+ static sk_sp<Animation> MakeFromFile(const char path[]);
~Animation() override;
@@ -72,9 +110,7 @@
/**
* Returns the animation duration in seconds.
*/
- SkScalar duration() const {
- return (fOutPoint - fInPoint) / fFrameRate;
- }
+ SkScalar duration() const { return fDuration; }
const SkString& version() const { return fVersion; }
const SkSize& size() const { return fSize; }
@@ -82,16 +118,15 @@
void setShowInval(bool show);
private:
- Animation(const ResourceProvider&, SkString ver, const SkSize& size, SkScalar fps,
- SkScalar in, SkScalar out, const skjson::ObjectValue&, Stats*);
-
- SkString fVersion;
- SkSize fSize;
- SkScalar fFrameRate,
- fInPoint,
- fOutPoint;
+ Animation(std::unique_ptr<sksg::Scene>, SkString ver, const SkSize& size,
+ SkScalar inPoint, SkScalar outPoint, SkScalar duration);
std::unique_ptr<sksg::Scene> fScene;
+ const SkString fVersion;
+ const SkSize fSize;
+ const SkScalar fInPoint,
+ fOutPoint,
+ fDuration;
typedef SkRefCnt INHERITED;
};
diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp
index da1f835..75b0c4e 100644
--- a/modules/skottie/src/Skottie.cpp
+++ b/modules/skottie/src/Skottie.cpp
@@ -130,7 +130,7 @@
}
AnimationBuilder::AnimationBuilder(const ResourceProvider& rp, sk_sp<SkFontMgr> fontmgr,
- Animation::Stats* stats, float duration, float framerate)
+ Animation::Builder::Stats* stats, float duration, float framerate)
: fResourceProvider(rp)
, fFontMgr(std::move(fontmgr))
, fStats(stats)
@@ -163,7 +163,17 @@
} // namespace internal
-sk_sp<Animation> Animation::Make(SkStream* stream, const ResourceProvider* provider, Stats* stats) {
+Animation::Builder& Animation::Builder::setResourceProvider(const ResourceProvider* rp) {
+ fResourceProvider = rp;
+ return *this;
+}
+
+Animation::Builder& Animation::Builder::setFontManager(sk_sp<SkFontMgr> fmgr) {
+ fFontMgr = std::move(fmgr);
+ return *this;
+}
+
+sk_sp<Animation> Animation::Builder::make(SkStream* stream) {
if (!stream->hasLength()) {
// TODO: handle explicit buffering?
LOG("!! cannot parse streaming content\n");
@@ -176,17 +186,22 @@
return nullptr;
}
- return Make(static_cast<const char*>(data->data()), data->size(), provider, stats);
+ return this->make(static_cast<const char*>(data->data()), data->size());
}
-sk_sp<Animation> Animation::Make(const char* data, size_t data_len,
- const ResourceProvider* provider, Stats* stats) {
- Stats stats_storage;
- if (!stats)
- stats = &stats_storage;
- memset(stats, 0, sizeof(struct Stats));
+sk_sp<Animation> Animation::Builder::make(const char* data, size_t data_len) {
+ // Sanitize factory args.
+ class NullResourceProvider final : public ResourceProvider {
+ sk_sp<SkData> load(const char[], const char[]) const override { return nullptr; }
+ };
- stats->fJsonSize = data_len;
+ const NullResourceProvider nullProvider;
+ auto resolvedProvider = fResourceProvider ? fResourceProvider : &nullProvider;
+ auto resolvedFontMgr = fFontMgr ? fFontMgr : SkFontMgr::RefDefault();
+
+ memset(&fStats, 0, sizeof(struct Stats));
+
+ fStats.fJsonSize = data_len;
const auto t0 = SkTime::GetMSecs();
const skjson::DOM dom(data, data_len);
@@ -198,40 +213,43 @@
const auto& json = dom.root().as<skjson::ObjectValue>();
const auto t1 = SkTime::GetMSecs();
- stats->fJsonParseTimeMS = t1 - t0;
+ fStats.fJsonParseTimeMS = t1 - t0;
const auto version = ParseDefault<SkString>(json["v"], SkString());
const auto size = SkSize::Make(ParseDefault<float>(json["w"], 0.0f),
ParseDefault<float>(json["h"], 0.0f));
const auto fps = ParseDefault<float>(json["fr"], -1.0f),
inPoint = ParseDefault<float>(json["ip"], 0.0f),
- outPoint = SkTMax(ParseDefault<float>(json["op"], SK_ScalarMax), inPoint);
+ outPoint = SkTMax(ParseDefault<float>(json["op"], SK_ScalarMax), inPoint),
+ duration = (outPoint - inPoint) / fps;
if (size.isEmpty() || version.isEmpty() || fps <= 0 ||
- !SkScalarIsFinite(inPoint) || !SkScalarIsFinite(outPoint)) {
+ !SkScalarIsFinite(inPoint) || !SkScalarIsFinite(outPoint) || !SkScalarIsFinite(duration)) {
LOG("!! invalid animation params (version: %s, size: [%f %f], frame rate: %f, "
"in-point: %f, out-point: %f)\n",
version.c_str(), size.width(), size.height(), fps, inPoint, outPoint);
return nullptr;
}
- class NullResourceProvider final : public ResourceProvider {
- sk_sp<SkData> load(const char[], const char[]) const override { return nullptr; }
- };
+ SkASSERT(resolvedProvider);
+ SkASSERT(resolvedFontMgr);
+ internal::AnimationBuilder builder(*resolvedProvider, std::move(resolvedFontMgr), &fStats,
+ duration, fps);
+ auto scene = builder.parse(json);
- NullResourceProvider null_provider;
- const auto anim = sk_sp<Animation>(new Animation(provider ? *provider : null_provider,
- std::move(version), size, fps,
- inPoint, outPoint, json, stats));
const auto t2 = SkTime::GetMSecs();
- stats->fSceneParseTimeMS = t2 - t1;
- stats->fTotalLoadTimeMS = t2 - t0;
+ fStats.fSceneParseTimeMS = t2 - t1;
+ fStats.fTotalLoadTimeMS = t2 - t0;
- return anim;
+ if (!scene) {
+ LOG("!! could not parse animation.\n");
+ }
+
+ return sk_sp<Animation>(
+ new Animation(std::move(scene), std::move(version), size, inPoint, outPoint, duration));
}
-sk_sp<Animation> Animation::MakeFromFile(const char path[], const ResourceProvider* res,
- Stats* stats) {
+sk_sp<Animation> Animation::Builder::makeFromFile(const char path[]) {
class DirectoryResourceProvider final : public ResourceProvider {
public:
explicit DirectoryResourceProvider(SkString dir) : fDir(std::move(dir)) {}
@@ -246,33 +264,33 @@
const SkString fDir;
};
- const auto data = SkData::MakeFromFileName(path);
+ const auto data = SkData::MakeFromFileName(path);
if (!data)
return nullptr;
- std::unique_ptr<ResourceProvider> defaultProvider;
- if (!res) {
- defaultProvider = skstd::make_unique<DirectoryResourceProvider>(SkOSPath::Dirname(path));
+ const auto* origProvider = fResourceProvider;
+ std::unique_ptr<ResourceProvider> localProvider;
+ if (!fResourceProvider) {
+ localProvider = skstd::make_unique<DirectoryResourceProvider>(SkOSPath::Dirname(path));
+ fResourceProvider = localProvider.get();
}
- return Make(static_cast<const char*>(data->data()), data->size(),
- res ? res : defaultProvider.get(), stats);
+ auto animation = this->make(static_cast<const char*>(data->data()), data->size());
+
+ // Don't leak localProvider references.
+ fResourceProvider = origProvider;
+
+ return animation;
}
-Animation::Animation(const ResourceProvider& resources,
- SkString version, const SkSize& size,
- SkScalar fps, SkScalar in, SkScalar out,
- const skjson::ObjectValue& json, Stats* stats)
- : fVersion(std::move(version))
+Animation::Animation(std::unique_ptr<sksg::Scene> scene, SkString version, const SkSize& size,
+ SkScalar inPoint, SkScalar outPoint, SkScalar duration)
+ : fScene(std::move(scene))
+ , fVersion(std::move(version))
, fSize(size)
- , fFrameRate(fps)
- , fInPoint(in)
- , fOutPoint(out) {
-
- internal::AnimationBuilder builder(resources, SkFontMgr::RefDefault(), stats,
- this->duration(), fps);
-
- fScene = builder.parse(json);
+ , fInPoint(inPoint)
+ , fOutPoint(outPoint)
+ , fDuration(duration) {
// In case the client calls render before the first tick.
this->seek(0);
@@ -306,4 +324,16 @@
fScene->animate(fInPoint + SkTPin(t, 0.0f, 1.0f) * (fOutPoint - fInPoint));
}
+sk_sp<Animation> Animation::Make(const char* data, size_t length) {
+ return Builder().make(data, length);
+}
+
+sk_sp<Animation> Animation::Make(SkStream* stream) {
+ return Builder().make(stream);
+}
+
+sk_sp<Animation> Animation::MakeFromFile(const char path[]) {
+ return Builder().makeFromFile(path);
+}
+
} // namespace skottie
diff --git a/modules/skottie/src/SkottieLayer.cpp b/modules/skottie/src/SkottieLayer.cpp
index 457e535..55e5e33 100644
--- a/modules/skottie/src/SkottieLayer.cpp
+++ b/modules/skottie/src/SkottieLayer.cpp
@@ -8,6 +8,7 @@
#include "SkottiePriv.h"
#include "SkData.h"
+#include "SkFontMgr.h"
#include "SkImage.h"
#include "SkJSON.h"
#include "SkMakeUnique.h"
@@ -211,14 +212,15 @@
return nullptr;
}
- auto animation = Animation::Make(static_cast<const char*>(data->data()), data->size(),
- &fResourceProvider);
+ auto animation = Animation::Builder()
+ .setResourceProvider(&fResourceProvider)
+ .setFontManager(fFontMgr)
+ .make(static_cast<const char*>(data->data()), data->size());
if (!animation) {
LOG("!! Could not parse nested animation: %s\n", name);
return nullptr;
}
-
ascope->push_back(
skstd::make_unique<SkottieAnimatorAdapter>(animation, animation->duration() / fDuration));
diff --git a/modules/skottie/src/SkottiePriv.h b/modules/skottie/src/SkottiePriv.h
index aa83183..7300c9d 100644
--- a/modules/skottie/src/SkottiePriv.h
+++ b/modules/skottie/src/SkottiePriv.h
@@ -44,7 +44,7 @@
class AnimationBuilder final : public SkNoncopyable {
public:
- AnimationBuilder(const ResourceProvider&, sk_sp<SkFontMgr>, Animation::Stats*,
+ AnimationBuilder(const ResourceProvider&, sk_sp<SkFontMgr>, Animation::Builder::Stats*,
float duration, float framerate);
std::unique_ptr<sksg::Scene> parse(const skjson::ObjectValue&);
@@ -75,11 +75,11 @@
sk_sp<sksg::RenderNode> attachSolidLayer (const skjson::ObjectValue&, AnimatorScope*);
sk_sp<sksg::RenderNode> attachTextLayer (const skjson::ObjectValue&, AnimatorScope*);
- const ResourceProvider& fResourceProvider;
- const sk_sp<SkFontMgr> fFontMgr;
- Animation::Stats* fStats;
- const float fDuration,
- fFrameRate;
+ const ResourceProvider& fResourceProvider;
+ sk_sp<SkFontMgr> fFontMgr;
+ Animation::Builder::Stats* fStats;
+ const float fDuration,
+ fFrameRate;
struct AssetInfo {
const skjson::ObjectValue* fAsset;
diff --git a/platform_tools/android/apps/skottie/src/main/cpp/native-lib.cpp b/platform_tools/android/apps/skottie/src/main/cpp/native-lib.cpp
index 3cbedbc..d4b4a74 100644
--- a/platform_tools/android/apps/skottie/src/main/cpp/native-lib.cpp
+++ b/platform_tools/android/apps/skottie/src/main/cpp/native-lib.cpp
@@ -123,8 +123,7 @@
skottieAnimation->mRunner = skottieRunner;
skottieAnimation->mStream = std::move(stream);
- skottieAnimation->mAnimation = skottie::Animation::Make(skottieAnimation->mStream.get(),
- nullptr, nullptr);
+ skottieAnimation->mAnimation = skottie::Animation::Make(skottieAnimation->mStream.get());
skottieAnimation->mTimeBase = 0.0f; // force a time reset
skottieAnimation->mDuration = 1000 * skottieAnimation->mAnimation->duration();
diff --git a/tools/viewer/SkottieSlide.cpp b/tools/viewer/SkottieSlide.cpp
index aa08533..51b0311 100644
--- a/tools/viewer/SkottieSlide.cpp
+++ b/tools/viewer/SkottieSlide.cpp
@@ -15,7 +15,7 @@
#include <cmath>
-static void draw_stats_box(SkCanvas* canvas, const skottie::Animation::Stats& stats) {
+static void draw_stats_box(SkCanvas* canvas, const skottie::Animation::Builder::Stats& stats) {
static constexpr SkRect kR = { 10, 10, 280, 120 };
static constexpr SkScalar kTextSize = 20;
@@ -59,9 +59,11 @@
}
void SkottieSlide::load(SkScalar w, SkScalar h) {
- fAnimation = skottie::Animation::MakeFromFile(fPath.c_str(), nullptr, &fAnimationStats);
- fWinSize = SkSize::Make(w, h);
- fTimeBase = 0; // force a time reset
+ skottie::Animation::Builder builder;
+ fAnimation = builder.makeFromFile(fPath.c_str());
+ fAnimationStats = builder.getStats();
+ fWinSize = SkSize::Make(w, h);
+ fTimeBase = 0; // force a time reset
if (fAnimation) {
fAnimation->setShowInval(fShowAnimationInval);
diff --git a/tools/viewer/SkottieSlide.h b/tools/viewer/SkottieSlide.h
index 9ed4d78..9bf6bf5 100644
--- a/tools/viewer/SkottieSlide.h
+++ b/tools/viewer/SkottieSlide.h
@@ -32,13 +32,13 @@
bool onMouse(SkScalar x, SkScalar y, sk_app::Window::InputState, uint32_t modifiers) override;
private:
- SkString fPath;
- sk_sp<skottie::Animation> fAnimation;
- skottie::Animation::Stats fAnimationStats;
- SkSize fWinSize = SkSize::MakeEmpty();
- SkMSec fTimeBase = 0;
- bool fShowAnimationInval = false,
- fShowAnimationStats = false;
+ SkString fPath;
+ sk_sp<skottie::Animation> fAnimation;
+ skottie::Animation::Builder::Stats fAnimationStats;
+ SkSize fWinSize = SkSize::MakeEmpty();
+ SkMSec fTimeBase = 0;
+ bool fShowAnimationInval = false,
+ fShowAnimationStats = false;
typedef Slide INHERITED;
};