[skottie] Expose composition markers to embedders
Add a MarkerObserver interface (to replace the current
AnnotationObserver), and update CustomPropertyManager to intercept both
properties and markers.
TBR=
Change-Id: If79de419066916bc596316f0a551c75564069239
Reviewed-on: https://skia-review.googlesource.com/c/173766
Reviewed-by: Florin Malita <fmalita@chromium.org>
Commit-Queue: Florin Malita <fmalita@chromium.org>
diff --git a/modules/skottie/gm/SkottieGM.cpp b/modules/skottie/gm/SkottieGM.cpp
index 8febf25..c8a4a27 100644
--- a/modules/skottie/gm/SkottieGM.cpp
+++ b/modules/skottie/gm/SkottieGM.cpp
@@ -104,11 +104,10 @@
void onOnceBeforeDraw() override {
if (auto stream = GetResourceAsStream("skottie/skottie_sample_search.json")) {
- auto propBuilder = sk_make_sp<CustomPropertyManagerBuilder>();
+ fPropManager = skstd::make_unique<CustomPropertyManager>();
fAnimation = Animation::Builder()
- .setPropertyObserver(propBuilder)
+ .setPropertyObserver(fPropManager->getPropertyObserver())
.make(stream.get());
- fPropManager = propBuilder->build();
fColors = fPropManager->getColorProps();
}
}
diff --git a/modules/skottie/include/Skottie.h b/modules/skottie/include/Skottie.h
index 2f832bc..15b175e 100644
--- a/modules/skottie/include/Skottie.h
+++ b/modules/skottie/include/Skottie.h
@@ -104,25 +104,12 @@
};
/**
- * 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"
- * },
- *
- * ...
- * }
+ * Interface for receiving AE composition markers at Animation build time.
*/
-class SK_API AnnotationObserver : public SkRefCnt {
+class SK_API MarkerObserver : public SkRefCnt {
public:
- virtual void onAnnotation(const char key[], const char value[]) = 0;
+ // t0,t1 are in the Animation::seek() domain.
+ virtual void onMarker(const char name[], float t0, float t1) = 0;
};
class SK_API Animation : public SkNVRefCnt<Animation> {
@@ -172,9 +159,9 @@
Builder& setLogger(sk_sp<Logger>);
/**
- * Register an AnnotationObserver with this builder.
+ * Register a MarkerObserver with this builder.
*/
- Builder& setAnnotationObserver(sk_sp<AnnotationObserver>);
+ Builder& setMarkerObserver(sk_sp<MarkerObserver>);
/**
* Animation factories.
@@ -184,12 +171,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;
- sk_sp<AnnotationObserver> fAnnotationObserver;
- Stats fStats;
+ sk_sp<ResourceProvider> fResourceProvider;
+ sk_sp<SkFontMgr> fFontMgr;
+ sk_sp<PropertyObserver> fPropertyObserver;
+ sk_sp<Logger> fLogger;
+ sk_sp<MarkerObserver> fMarkerObserver;
+ Stats fStats;
};
/**
diff --git a/modules/skottie/src/Skottie.cpp b/modules/skottie/src/Skottie.cpp
index 66aee73..7b31d99 100644
--- a/modules/skottie/src/Skottie.cpp
+++ b/modules/skottie/src/Skottie.cpp
@@ -155,20 +155,20 @@
AnimationBuilder::AnimationBuilder(sk_sp<ResourceProvider> rp, sk_sp<SkFontMgr> fontmgr,
sk_sp<PropertyObserver> pobserver, sk_sp<Logger> logger,
- sk_sp<AnnotationObserver> aobserver,
+ sk_sp<MarkerObserver> mobserver,
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))
+ , fMarkerObserver(std::move(mobserver))
, fStats(stats)
, fDuration(duration)
, fFrameRate(framerate) {}
std::unique_ptr<sksg::Scene> AnimationBuilder::parse(const skjson::ObjectValue& jroot) {
- this->dispatchAnnotations(jroot["annotations"]);
+ this->dispatchMarkers(jroot["markers"]);
this->parseAssets(jroot["assets"]);
this->parseFonts(jroot["fonts"], jroot["chars"]);
@@ -193,16 +193,31 @@
}
}
-void AnimationBuilder::dispatchAnnotations(const skjson::ObjectValue* jannotations) const {
- if (!fAnnotationObserver || !jannotations) {
+void AnimationBuilder::dispatchMarkers(const skjson::ArrayValue* jmarkers) const {
+ if (!fMarkerObserver || !jmarkers) {
return;
}
- for (const auto& a : *jannotations) {
- if (const skjson::StringValue* value = a.fValue) {
- fAnnotationObserver->onAnnotation(a.fKey.begin(), value->begin());
+ // For frame-number -> t conversions.
+ const auto frameRatio = 1 / (fFrameRate * fDuration);
+
+ for (const skjson::ObjectValue* m : *jmarkers) {
+ if (!m) continue;
+
+ const skjson::StringValue* name = (*m)["cm"];
+ const auto time = ParseDefault((*m)["tm"], -1.0f),
+ duration = ParseDefault((*m)["dr"], -1.0f);
+
+ if (name && time >= 0 && duration >= 0) {
+ fMarkerObserver->onMarker(
+ name->begin(),
+ // "tm" is in frames
+ time * frameRatio,
+ // ... as is "dr"
+ (time + duration) * frameRatio
+ );
} else {
- this->log(Logger::Level::kWarning, &a.fValue, "Ignoring unexpected annotation value.");
+ this->log(Logger::Level::kWarning, m, "Ignoring unexpected marker.");
}
}
}
@@ -296,8 +311,8 @@
return *this;
}
-Animation::Builder& Animation::Builder::setAnnotationObserver(sk_sp<AnnotationObserver> aobserver) {
- fAnnotationObserver = std::move(aobserver);
+Animation::Builder& Animation::Builder::setMarkerObserver(sk_sp<MarkerObserver> mobserver) {
+ fMarkerObserver = std::move(mobserver);
return *this;
}
@@ -371,7 +386,7 @@
internal::AnimationBuilder builder(std::move(resolvedProvider), fFontMgr,
std::move(fPropertyObserver),
std::move(fLogger),
- std::move(fAnnotationObserver),
+ std::move(fMarkerObserver),
&fStats, duration, fps);
auto scene = builder.parse(json);
diff --git a/modules/skottie/src/SkottiePriv.h b/modules/skottie/src/SkottiePriv.h
index 0c50c91..b3c15e5 100644
--- a/modules/skottie/src/SkottiePriv.h
+++ b/modules/skottie/src/SkottiePriv.h
@@ -44,7 +44,7 @@
class AnimationBuilder final : public SkNoncopyable {
public:
AnimationBuilder(sk_sp<ResourceProvider>, sk_sp<SkFontMgr>, sk_sp<PropertyObserver>,
- sk_sp<Logger>, sk_sp<AnnotationObserver>,
+ sk_sp<Logger>, sk_sp<MarkerObserver>,
Animation::Builder::Stats*, float duration, float framerate);
std::unique_ptr<sksg::Scene> parse(const skjson::ObjectValue&);
@@ -87,7 +87,7 @@
void parseFonts (const skjson::ObjectValue* jfonts,
const skjson::ArrayValue* jchars);
- void dispatchAnnotations(const skjson::ObjectValue*) const;
+ void dispatchMarkers(const skjson::ArrayValue*) const;
sk_sp<sksg::RenderNode> attachComposition(const skjson::ObjectValue&, AnimatorScope*) const;
sk_sp<sksg::RenderNode> attachLayer(const skjson::ObjectValue*, AttachLayerContext*) const;
@@ -165,7 +165,7 @@
LazyResolveFontMgr fLazyFontMgr;
sk_sp<PropertyObserver> fPropertyObserver;
sk_sp<Logger> fLogger;
- sk_sp<AnnotationObserver> fAnnotationObserver;
+ sk_sp<MarkerObserver> fMarkerObserver;
Animation::Builder::Stats* fStats;
const float fDuration,
fFrameRate;
diff --git a/modules/skottie/src/SkottieTest.cpp b/modules/skottie/src/SkottieTest.cpp
index 268b5dd..b29d735 100644
--- a/modules/skottie/src/SkottieTest.cpp
+++ b/modules/skottie/src/SkottieTest.cpp
@@ -160,9 +160,9 @@
"v": "5.2.1",
"w": 100,
"h": 100,
- "fr": 1,
+ "fr": 10,
"ip": 0,
- "op": 1,
+ "op": 100,
"layers": [
{
"ty": 1,
@@ -177,36 +177,43 @@
"sc": "#ffffff"
}
],
- "annotations": {
- "key1": "foo",
- "key2": "bar",
- "key3": "baz"
- }
+ "markers": [
+ {
+ "cm": "marker_1",
+ "dr": 25,
+ "tm": 25
+ },
+ {
+ "cm": "marker_2",
+ "dr": 0,
+ "tm": 75
+ }
+ ]
})";
- class TestAnnotationObserver final : public AnnotationObserver {
+ class TestMarkerObserver final : public MarkerObserver {
public:
- void onAnnotation(const char key[], const char value[]) override {
- fAnnotations.push_back(std::make_tuple(key, value));
+ void onMarker(const char name[], float t0, float t1) override {
+ fMarkers.push_back(std::make_tuple(name, t0, t1));
}
- std::vector<std::tuple<std::string, std::string>> fAnnotations;
+ std::vector<std::tuple<std::string, float, float>> fMarkers;
};
SkMemoryStream stream(json, strlen(json));
- auto observer = sk_make_sp<TestAnnotationObserver>();
+ auto observer = sk_make_sp<TestMarkerObserver>();
auto animation = skottie::Animation::Builder()
- .setAnnotationObserver(observer)
+ .setMarkerObserver(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");
+ REPORTER_ASSERT(reporter, observer->fMarkers.size() == 2ul);
+ REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[0]) == "marker_1");
+ REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[0]) == 0.25f);
+ REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[0]) == 0.50f);
+ REPORTER_ASSERT(reporter, std::get<0>(observer->fMarkers[1]) == "marker_2");
+ REPORTER_ASSERT(reporter, std::get<1>(observer->fMarkers[1]) == 0.75f);
+ REPORTER_ASSERT(reporter, std::get<2>(observer->fMarkers[1]) == 0.75f);
}
diff --git a/modules/skottie/utils/SkottieUtils.cpp b/modules/skottie/utils/SkottieUtils.cpp
index 92efc2f..4e28a41 100644
--- a/modules/skottie/utils/SkottieUtils.cpp
+++ b/modules/skottie/utils/SkottieUtils.cpp
@@ -60,52 +60,67 @@
return MultiFrameImageAsset::Make(this->load(resource_path, resource_name));
}
-CustomPropertyManagerBuilder::CustomPropertyManagerBuilder() = default;
-CustomPropertyManagerBuilder::~CustomPropertyManagerBuilder() = default;
+class CustomPropertyManager::PropertyInterceptor final : public skottie::PropertyObserver {
+public:
+ explicit PropertyInterceptor(CustomPropertyManager* mgr) : fMgr(mgr) {}
-std::unique_ptr<CustomPropertyManager> CustomPropertyManagerBuilder::build() {
- return std::unique_ptr<CustomPropertyManager>(
- new CustomPropertyManager(std::move(fColorMap),
- std::move(fOpacityMap),
- std::move(fTransformMap)));
-}
-
-void CustomPropertyManagerBuilder::onColorProperty(
- const char node_name[],
- const LazyHandle<skottie::ColorPropertyHandle>& c) {
- const auto key = this->acceptProperty(node_name);
- if (!key.empty()) {
- fColorMap[key].push_back(c());
+ void onColorProperty(const char node_name[],
+ const LazyHandle<skottie::ColorPropertyHandle>& c) override {
+ const auto key = fMgr->acceptKey(node_name);
+ if (!key.empty()) {
+ fMgr->fColorMap[key].push_back(c());
+ }
}
-}
-void CustomPropertyManagerBuilder::onOpacityProperty(
- const char node_name[],
- const LazyHandle<skottie::OpacityPropertyHandle>& o) {
- const auto key = this->acceptProperty(node_name);
- if (!key.empty()) {
- fOpacityMap[key].push_back(o());
+ void onOpacityProperty(const char node_name[],
+ const LazyHandle<skottie::OpacityPropertyHandle>& o) override {
+ const auto key = fMgr->acceptKey(node_name);
+ if (!key.empty()) {
+ fMgr->fOpacityMap[key].push_back(o());
+ }
}
-}
-void CustomPropertyManagerBuilder::onTransformProperty(
- const char node_name[],
- const LazyHandle<skottie::TransformPropertyHandle>& t) {
- const auto key = this->acceptProperty(node_name);
- if (!key.empty()) {
- fTransformMap[key].push_back(t());
+ void onTransformProperty(const char node_name[],
+ const LazyHandle<skottie::TransformPropertyHandle>& t) override {
+ const auto key = fMgr->acceptKey(node_name);
+ if (!key.empty()) {
+ fMgr->fTransformMap[key].push_back(t());
+ }
}
-}
-CustomPropertyManager::CustomPropertyManager(PropMap<skottie::ColorPropertyHandle> cmap,
- PropMap<skottie::OpacityPropertyHandle> omap,
- PropMap<skottie::TransformPropertyHandle> tmap)
- : fColorMap(std::move(cmap))
- , fOpacityMap(std::move(omap))
- , fTransformMap(std::move(tmap)) {}
+private:
+ CustomPropertyManager* fMgr;
+};
+
+class CustomPropertyManager::MarkerInterceptor final : public skottie::MarkerObserver {
+public:
+ explicit MarkerInterceptor(CustomPropertyManager* mgr) : fMgr(mgr) {}
+
+ void onMarker(const char name[], float t0, float t1) override {
+ const auto key = fMgr->acceptKey(name);
+ if (!key.empty()) {
+ fMgr->fMarkers.push_back({ std::move(key), t0, t1 });
+ }
+ }
+
+private:
+ CustomPropertyManager* fMgr;
+};
+
+CustomPropertyManager::CustomPropertyManager()
+ : fPropertyInterceptor(sk_make_sp<PropertyInterceptor>(this))
+ , fMarkerInterceptor(sk_make_sp<MarkerInterceptor>(this)) {}
CustomPropertyManager::~CustomPropertyManager() = default;
+sk_sp<skottie::PropertyObserver> CustomPropertyManager::getPropertyObserver() const {
+ return fPropertyInterceptor;
+}
+
+sk_sp<skottie::MarkerObserver> CustomPropertyManager::getMarkerObserver() const {
+ return fMarkerInterceptor;
+}
+
template <typename T>
std::vector<CustomPropertyManager::PropKey>
CustomPropertyManager::getProps(const PropMap<T>& container) const {
diff --git a/modules/skottie/utils/SkottieUtils.h b/modules/skottie/utils/SkottieUtils.h
index 23dec6c..cb28b5d 100644
--- a/modules/skottie/utils/SkottieUtils.h
+++ b/modules/skottie/utils/SkottieUtils.h
@@ -66,11 +66,12 @@
* - getters return all the managed property groups, and the first value within each of them
* (unchecked assumption: all properties within the same group have the same value)
*
- * Use CustomPropertyManagerBuilder to filter nodes at animation build time, and instantiate a
- * CustomPropertyManager.
+ * Attach to an Animation::Builder using the utility methods below to intercept properties and
+ * markers at build time.
*/
class CustomPropertyManager final {
public:
+ CustomPropertyManager();
~CustomPropertyManager();
using PropKey = std::string;
@@ -87,8 +88,32 @@
skottie::TransformPropertyValue getTransform(const PropKey&) const;
bool setTransform(const PropKey&, const skottie::TransformPropertyValue&);
+ struct MarkerInfo {
+ std::string name;
+ float t0, t1;
+ };
+ const std::vector<MarkerInfo>& markers() const { return fMarkers; }
+
+ // Returns a property observer to be attached to an animation builder.
+ sk_sp<skottie::PropertyObserver> getPropertyObserver() const;
+
+ // Returns a marker observer to be attached to an animation builder.
+ sk_sp<skottie::MarkerObserver> getMarkerObserver() const;
+
private:
- friend class CustomPropertyManagerBuilder;
+ class PropertyInterceptor;
+ class MarkerInterceptor;
+
+ static std::string acceptKey(const char* name) {
+ static constexpr char kPrefix = '$';
+
+ return (name[0] == kPrefix && name[1] != '\0')
+ ? std::string(name + 1)
+ : std::string();
+ }
+
+ sk_sp<PropertyInterceptor> fPropertyInterceptor;
+ sk_sp<MarkerInterceptor> fMarkerInterceptor;
template <typename T>
using PropGroup = std::vector<std::unique_ptr<T>>;
@@ -105,46 +130,10 @@
template <typename V, typename T>
bool set(const PropKey&, const V&, const PropMap<T>& container);
- CustomPropertyManager(PropMap<skottie::ColorPropertyHandle>,
- PropMap<skottie::OpacityPropertyHandle>,
- PropMap<skottie::TransformPropertyHandle>);
-
PropMap<skottie::ColorPropertyHandle> fColorMap;
PropMap<skottie::OpacityPropertyHandle> fOpacityMap;
PropMap<skottie::TransformPropertyHandle> fTransformMap;
-};
-
-/**
- * A builder for CustomPropertyManager. Only accepts node names starting with '$'.
- */
-class CustomPropertyManagerBuilder final : public skottie::PropertyObserver {
-public:
- CustomPropertyManagerBuilder();
- ~CustomPropertyManagerBuilder() override;
-
- std::unique_ptr<CustomPropertyManager> build();
-
- void onColorProperty (const char node_name[],
- const LazyHandle<skottie::ColorPropertyHandle>&) override;
- void onOpacityProperty (const char node_name[],
- const LazyHandle<skottie::OpacityPropertyHandle>&) override;
- void onTransformProperty(const char node_name[],
- const LazyHandle<skottie::TransformPropertyHandle>&) override;
-
-private:
- std::string acceptProperty(const char* name) const {
- static constexpr char kPrefix = '$';
-
- return (name[0] == kPrefix && name[1] != '\0')
- ? std::string(name + 1)
- : std::string();
- }
-
- CustomPropertyManager::PropMap<skottie::ColorPropertyHandle> fColorMap;
- CustomPropertyManager::PropMap<skottie::OpacityPropertyHandle> fOpacityMap;
- CustomPropertyManager::PropMap<skottie::TransformPropertyHandle> fTransformMap;
-
- using INHERITED = skottie::PropertyObserver;
+ std::vector<MarkerInfo> fMarkers;
};
} // namespace skottie_utils