[skottie] Support external annotations
Add a callback mechanism for passing opaque/external annotation string
dictionaries to the embedder.
Change-Id: I2b053d6697cdd51a310ced687e2ba454ab39a24a
Reviewed-on: https://skia-review.googlesource.com/c/160900
Reviewed-by: Mike Reed <reed@google.com>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skottie/include/Skottie.h b/modules/skottie/include/Skottie.h
index 7e85a77..2f832bc 100644
--- a/modules/skottie/include/Skottie.h
+++ b/modules/skottie/include/Skottie.h
@@ -103,6 +103,28 @@
virtual void log(Level, const char message[], const char* json = nullptr);
};
+/**
+ * Interface for receiving custom annotation events at Animation build time.
+ *
+ * Annotations are parsed as a top-level key-value string dictionary, e.g.:
+ *
+ * {
+ * ...
+ *
+ * "annotations": {
+ * "key1": "foo",
+ * "key2": "bar",
+ * "key3": "baz"
+ * },
+ *
+ * ...
+ * }
+ */
+class SK_API AnnotationObserver : public SkRefCnt {
+public:
+ virtual void onAnnotation(const char key[], const char value[]) = 0;
+};
+
class SK_API Animation : public SkNVRefCnt<Animation> {
public:
@@ -150,6 +172,11 @@
Builder& setLogger(sk_sp<Logger>);
/**
+ * Register an AnnotationObserver with this builder.
+ */
+ Builder& setAnnotationObserver(sk_sp<AnnotationObserver>);
+
+ /**
* Animation factories.
*/
sk_sp<Animation> make(SkStream*);
@@ -157,11 +184,12 @@
sk_sp<Animation> makeFromFile(const char path[]);
private:
- sk_sp<ResourceProvider> fResourceProvider;
- sk_sp<SkFontMgr> fFontMgr;
- sk_sp<PropertyObserver> fPropertyObserver;
- sk_sp<Logger> fLogger;
- Stats fStats;
+ sk_sp<ResourceProvider> fResourceProvider;
+ sk_sp<SkFontMgr> fFontMgr;
+ sk_sp<PropertyObserver> fPropertyObserver;
+ sk_sp<Logger> fLogger;
+ sk_sp<AnnotationObserver> fAnnotationObserver;
+ Stats fStats;
};
/**
diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp
index 3faca99..598a7c7 100644
--- a/modules/skottie/src/Skottie.cpp
+++ b/modules/skottie/src/Skottie.cpp
@@ -155,17 +155,21 @@
AnimationBuilder::AnimationBuilder(sk_sp<ResourceProvider> rp, sk_sp<SkFontMgr> fontmgr,
sk_sp<PropertyObserver> pobserver, sk_sp<Logger> logger,
+ sk_sp<AnnotationObserver> aobserver,
Animation::Builder::Stats* stats,
float duration, float framerate)
: fResourceProvider(std::move(rp))
, fLazyFontMgr(std::move(fontmgr))
, fPropertyObserver(std::move(pobserver))
, fLogger(std::move(logger))
+ , fAnnotationObserver(std::move(aobserver))
, fStats(stats)
, fDuration(duration)
, fFrameRate(framerate) {}
std::unique_ptr<sksg::Scene> AnimationBuilder::parse(const skjson::ObjectValue& jroot) {
+ this->dispatchAnnotations(jroot["annotations"]);
+
this->parseAssets(jroot["assets"]);
this->parseFonts(jroot["fonts"], jroot["chars"]);
@@ -189,6 +193,20 @@
}
}
+void AnimationBuilder::dispatchAnnotations(const skjson::ObjectValue* jannotations) const {
+ if (!fAnnotationObserver || !jannotations) {
+ return;
+ }
+
+ for (const auto& a : *jannotations) {
+ if (const skjson::StringValue* value = a.fValue) {
+ fAnnotationObserver->onAnnotation(a.fKey.begin(), value->begin());
+ } else {
+ this->log(Logger::Level::kWarning, &a.fValue, "Ignoring unexpected annotation value.");
+ }
+ }
+}
+
bool AnimationBuilder::dispatchColorProperty(const sk_sp<sksg::Color>& c) const {
bool dispatched = false;
@@ -278,6 +296,11 @@
return *this;
}
+Animation::Builder& Animation::Builder::setAnnotationObserver(sk_sp<AnnotationObserver> aobserver) {
+ fAnnotationObserver = std::move(aobserver);
+ return *this;
+}
+
sk_sp<Animation> Animation::Builder::make(SkStream* stream) {
if (!stream->hasLength()) {
// TODO: handle explicit buffering?
@@ -348,6 +371,7 @@
internal::AnimationBuilder builder(std::move(resolvedProvider), fFontMgr,
std::move(fPropertyObserver),
std::move(fLogger),
+ std::move(fAnnotationObserver),
&fStats, duration, fps);
auto scene = builder.parse(json);
diff --git a/modules/skottie/src/SkottiePriv.h b/modules/skottie/src/SkottiePriv.h
index 741c36a..0c50c91 100644
--- a/modules/skottie/src/SkottiePriv.h
+++ b/modules/skottie/src/SkottiePriv.h
@@ -44,7 +44,8 @@
class AnimationBuilder final : public SkNoncopyable {
public:
AnimationBuilder(sk_sp<ResourceProvider>, sk_sp<SkFontMgr>, sk_sp<PropertyObserver>,
- sk_sp<Logger>, Animation::Builder::Stats*, float duration, float framerate);
+ sk_sp<Logger>, sk_sp<AnnotationObserver>,
+ Animation::Builder::Stats*, float duration, float framerate);
std::unique_ptr<sksg::Scene> parse(const skjson::ObjectValue&);
@@ -86,6 +87,8 @@
void parseFonts (const skjson::ObjectValue* jfonts,
const skjson::ArrayValue* jchars);
+ void dispatchAnnotations(const skjson::ObjectValue*) const;
+
sk_sp<sksg::RenderNode> attachComposition(const skjson::ObjectValue&, AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachLayer(const skjson::ObjectValue*, AttachLayerContext*) const;
sk_sp<sksg::RenderNode> attachLayerEffects(const skjson::ArrayValue& jeffects, AnimatorScope*,
@@ -162,6 +165,7 @@
LazyResolveFontMgr fLazyFontMgr;
sk_sp<PropertyObserver> fPropertyObserver;
sk_sp<Logger> fLogger;
+ sk_sp<AnnotationObserver> fAnnotationObserver;
Animation::Builder::Stats* fStats;
const float fDuration,
fFrameRate;
diff --git a/modules/skottie/src/SkottieTest.cpp b/modules/skottie/src/SkottieTest.cpp
index e3bd68b..a44bc15 100644
--- a/modules/skottie/src/SkottieTest.cpp
+++ b/modules/skottie/src/SkottieTest.cpp
@@ -12,6 +12,7 @@
#include "Test.h"
+#include <tuple>
#include <vector>
using namespace skottie;
@@ -139,3 +140,59 @@
REPORTER_ASSERT(reporter, transforms[1].node_name.equals("layer_0"));
REPORTER_ASSERT(reporter, transforms[1].matrix == SkMatrix::I());
}
+
+DEF_TEST(Skottie_Annotations, reporter) {
+ static constexpr char json[] = R"({
+ "v": "5.2.1",
+ "w": 100,
+ "h": 100,
+ "fr": 1,
+ "ip": 0,
+ "op": 1,
+ "layers": [
+ {
+ "ty": 1,
+ "ind": 0,
+ "ip": 0,
+ "op": 1,
+ "ks": {
+ "o": { "a": 0, "k": 50 }
+ },
+ "sw": 100,
+ "sh": 100,
+ "sc": "#ffffff"
+ }
+ ],
+ "annotations": {
+ "key1": "foo",
+ "key2": "bar",
+ "key3": "baz"
+ }
+ })";
+
+ class TestAnnotationObserver final : public AnnotationObserver {
+ public:
+ void onAnnotation(const char key[], const char value[]) override {
+ fAnnotations.push_back(std::make_tuple(key, value));
+ }
+
+ std::vector<std::tuple<std::string, std::string>> fAnnotations;
+ };
+
+ SkMemoryStream stream(json, strlen(json));
+ auto observer = sk_make_sp<TestAnnotationObserver>();
+
+ auto animation = skottie::Animation::Builder()
+ .setAnnotationObserver(observer)
+ .make(&stream);
+
+ REPORTER_ASSERT(reporter, animation);
+
+ REPORTER_ASSERT(reporter, observer->fAnnotations.size() == 3ul);
+ REPORTER_ASSERT(reporter, std::get<0>(observer->fAnnotations[0]) == "key1");
+ REPORTER_ASSERT(reporter, std::get<1>(observer->fAnnotations[0]) == "foo");
+ REPORTER_ASSERT(reporter, std::get<0>(observer->fAnnotations[1]) == "key2");
+ REPORTER_ASSERT(reporter, std::get<1>(observer->fAnnotations[1]) == "bar");
+ REPORTER_ASSERT(reporter, std::get<0>(observer->fAnnotations[2]) == "key3");
+ REPORTER_ASSERT(reporter, std::get<1>(observer->fAnnotations[2]) == "baz");
+}