From 8ec9a60bdb6f63e7d0d1a7d994f25bf82cc40859 Mon Sep 17 00:00:00 2001 From: Florin Malita Date: Fri, 1 Feb 2019 17:59:25 -0500 Subject: [PATCH] [skottie] Initial repeater support TBR=reed Bug: skia:8399 Change-Id: Iadaf6b7b363e345d736efd3fd3a5f401963a457b Reviewed-on: https://skia-review.googlesource.com/c/188631 Commit-Queue: Florin Malita Reviewed-by: Florin Malita --- modules/skottie/src/SkottieAdapter.cpp | 37 ++++++++++ modules/skottie/src/SkottieAdapter.h | 32 +++++++++ modules/skottie/src/SkottieShapeLayer.cpp | 86 +++++++++++++++++++++++ modules/sksg/include/SkSGGroup.h | 1 + modules/sksg/src/SkSGGroup.cpp | 7 ++ 5 files changed, 163 insertions(+) diff --git a/modules/skottie/src/SkottieAdapter.cpp b/modules/skottie/src/SkottieAdapter.cpp index 8e8b93dcdf..fed35b3ef5 100644 --- a/modules/skottie/src/SkottieAdapter.cpp +++ b/modules/skottie/src/SkottieAdapter.cpp @@ -102,6 +102,43 @@ void TransformAdapter3D::apply() { fMatrixNode->setMatrix(this->totalMatrix()); } +RepeaterAdapter::RepeaterAdapter(sk_sp repeater_node, Composite composite) + : fRepeaterNode(repeater_node) + , fComposite(composite) + , fRoot(sksg::Group::Make()) {} + +RepeaterAdapter::~RepeaterAdapter() = default; + +void RepeaterAdapter::apply() { + static constexpr SkScalar kMaxCount = 512; + const auto count = static_cast(SkTPin(fCount, 0.0f, kMaxCount) + 0.5f); + + const auto& compute_transform = [this] (size_t index) { + const auto t = fOffset + index; + + // Position, scale & rotation are "scaled" by index/offset. + SkMatrix m = SkMatrix::MakeTrans(-fAnchorPoint.x(), + -fAnchorPoint.y()); + m.postScale(std::pow(fScale.x() * .01f, fOffset), + std::pow(fScale.y() * .01f, fOffset)); + m.postRotate(t * fRotation); + m.postTranslate(t * fPosition.x() + fAnchorPoint.x(), + t * fPosition.y() + fAnchorPoint.y()); + + return m; + }; + + // TODO: start/end opacity support. + + // TODO: we can avoid rebuilding all the fragments in most cases. + fRoot->clear(); + for (size_t i = 0; i < count; ++i) { + const auto insert_index = (fComposite == Composite::kAbove) ? i : count - i - 1; + fRoot->addChild(sksg::TransformEffect::Make(fRepeaterNode, + compute_transform(insert_index))); + } +} + PolyStarAdapter::PolyStarAdapter(sk_sp wrapped_node, Type t) : fPathNode(std::move(wrapped_node)) , fType(t) {} diff --git a/modules/skottie/src/SkottieAdapter.h b/modules/skottie/src/SkottieAdapter.h index 35e4ac3ecf..817ed661cb 100644 --- a/modules/skottie/src/SkottieAdapter.h +++ b/modules/skottie/src/SkottieAdapter.h @@ -24,8 +24,10 @@ template class Matrix; class Path; class RadialGradient; +class RenderNode; class RRect; class TextBlob; +class TransformEffect; class TrimEffect; }; @@ -133,6 +135,36 @@ private: sk_sp> fMatrixNode; }; +class RepeaterAdapter final : public SkNVRefCnt { +public: + enum class Composite { kAbove, kBelow }; + + RepeaterAdapter(sk_sp, Composite); + ~RepeaterAdapter(); + + // Repeater props + ADAPTER_PROPERTY(Count , SkScalar, 0) + ADAPTER_PROPERTY(Offset , SkScalar, 0) + + // Transform props + ADAPTER_PROPERTY(AnchorPoint , SkPoint , SkPoint::Make(0, 0)) + ADAPTER_PROPERTY(Position , SkPoint , SkPoint::Make(0, 0)) + ADAPTER_PROPERTY(Scale , SkVector, SkPoint::Make(100, 100)) + ADAPTER_PROPERTY(Rotation , SkScalar, 0) + ADAPTER_PROPERTY(StartOpacity, SkScalar, 100) + ADAPTER_PROPERTY(EndOpacity , SkScalar, 100) + + const sk_sp& root() const { return fRoot; } + +private: + void apply(); + + const sk_sp fRepeaterNode; + const Composite fComposite; + + sk_sp fRoot; +}; + class GradientAdapter : public SkRefCnt { public: ADAPTER_PROPERTY(StartPoint, SkPoint , SkPoint::Make(0, 0) ) diff --git a/modules/skottie/src/SkottieShapeLayer.cpp b/modules/skottie/src/SkottieShapeLayer.cpp index c6acf2b274..48e20d28dc 100644 --- a/modules/skottie/src/SkottieShapeLayer.cpp +++ b/modules/skottie/src/SkottieShapeLayer.cpp @@ -357,6 +357,71 @@ std::vector> AttachRoundGeometryEffect( return rounded; } +std::vector> AttachRepeaterDrawEffect( + const skjson::ObjectValue& jrepeater, + const AnimationBuilder* abuilder, + AnimatorScope* ascope, + std::vector>&& draws) { + + std::vector> repeater_draws; + + if (const skjson::ObjectValue* jtransform = jrepeater["tr"]) { + sk_sp repeater_node; + if (draws.size() > 1) { + repeater_node = sksg::Group::Make(std::move(draws)); + } else { + repeater_node = std::move(draws[0]); + } + + const auto repeater_composite = (ParseDefault(jrepeater["m"], 1) == 1) + ? RepeaterAdapter::Composite::kAbove + : RepeaterAdapter::Composite::kBelow; + + auto adapter = sk_make_sp(std::move(repeater_node), + repeater_composite); + + abuilder->bindProperty(jrepeater["c"], ascope, + [adapter](const ScalarValue& c) { + adapter->setCount(c); + }); + abuilder->bindProperty(jrepeater["o"], ascope, + [adapter](const ScalarValue& o) { + adapter->setOffset(o); + }); + abuilder->bindProperty((*jtransform)["a"], ascope, + [adapter](const VectorValue& a) { + adapter->setAnchorPoint(ValueTraits::As(a)); + }); + abuilder->bindProperty((*jtransform)["p"], ascope, + [adapter](const VectorValue& p) { + adapter->setPosition(ValueTraits::As(p)); + }); + abuilder->bindProperty((*jtransform)["s"], ascope, + [adapter](const VectorValue& s) { + adapter->setScale(ValueTraits::As(s)); + }); + abuilder->bindProperty((*jtransform)["r"], ascope, + [adapter](const ScalarValue& r) { + adapter->setRotation(r); + }); + abuilder->bindProperty((*jtransform)["so"], ascope, + [adapter](const ScalarValue& so) { + adapter->setStartOpacity(so); + }); + abuilder->bindProperty((*jtransform)["eo"], ascope, + [adapter](const ScalarValue& eo) { + adapter->setEndOpacity(eo); + }); + + repeater_draws.reserve(1); + repeater_draws.push_back(adapter->root()); + } else { + repeater_draws = std::move(draws); + } + + return repeater_draws; +} + using GeometryAttacherT = sk_sp (*)(const skjson::ObjectValue&, const AnimationBuilder*, AnimatorScope*); static constexpr GeometryAttacherT gGeometryAttachers[] = { @@ -385,12 +450,22 @@ static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = { AttachRoundGeometryEffect, }; +using DrawEffectAttacherT = + std::vector> (*)(const skjson::ObjectValue&, + const AnimationBuilder*, AnimatorScope*, + std::vector>&&); + +static constexpr DrawEffectAttacherT gDrawEffectAttachers[] = { + AttachRepeaterDrawEffect, +}; + enum class ShapeType { kGeometry, kGeometryEffect, kPaint, kGroup, kTransform, + kDrawEffect, }; struct ShapeInfo { @@ -409,6 +484,7 @@ const ShapeInfo* FindShapeInfo(const skjson::ObjectValue& jshape) { { "mm", ShapeType::kGeometryEffect, 0 }, // merge -> AttachMergeGeometryEffect { "rc", ShapeType::kGeometry , 1 }, // rrect -> AttachRRectGeometry { "rd", ShapeType::kGeometryEffect, 2 }, // round -> AttachRoundGeometryEffect + { "rp", ShapeType::kDrawEffect , 0 }, // repeater -> AttachRepeaterDrawEffect { "sh", ShapeType::kGeometry , 0 }, // shape -> AttachPathGeometry { "sr", ShapeType::kGeometry , 3 }, // polystar -> AttachPolyStarGeometry { "st", ShapeType::kPaint , 1 }, // stroke -> AttachColorStroke @@ -574,6 +650,16 @@ sk_sp AnimationBuilder::attachShape(const skjson::ArrayValue* draws.push_back(sksg::Draw::Make(std::move(geo), std::move(paint))); ctx->fCommittedAnimators = ctx->fScope->size(); } break; + case ShapeType::kDrawEffect: { + SkASSERT(rec->fInfo.fAttacherIndex < SK_ARRAY_COUNT(gDrawEffectAttachers)); + if (!draws.empty()) { + draws = gDrawEffectAttachers[rec->fInfo.fAttacherIndex](rec->fJson, + this, + ctx->fScope, + std::move(draws)); + ctx->fCommittedAnimators = ctx->fScope->size(); + } + } break; default: break; } diff --git a/modules/sksg/include/SkSGGroup.h b/modules/sksg/include/SkSGGroup.h index cc8635ae57..bb5355538d 100644 --- a/modules/sksg/include/SkSGGroup.h +++ b/modules/sksg/include/SkSGGroup.h @@ -32,6 +32,7 @@ public: size_t size() const { return fChildren.size(); } bool empty() const { return fChildren.empty(); } + void clear(); protected: explicit Group(std::vector>); diff --git a/modules/sksg/src/SkSGGroup.cpp b/modules/sksg/src/SkSGGroup.cpp index df696c47b9..fff74363db 100644 --- a/modules/sksg/src/SkSGGroup.cpp +++ b/modules/sksg/src/SkSGGroup.cpp @@ -24,6 +24,13 @@ Group::~Group() { } } +void Group::clear() { + for (const auto& child : fChildren) { + this->unobserveInval(child); + } + fChildren.clear(); +} + void Group::addChild(sk_sp node) { // should we allow duplicates? for (const auto& child : fChildren) {