[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 <fmalita@chromium.org>
Reviewed-by: Florin Malita <fmalita@chromium.org>
This commit is contained in:
Florin Malita 2019-02-01 17:59:25 -05:00 committed by Skia Commit-Bot
parent 00dcf66865
commit 8ec9a60bdb
5 changed files with 163 additions and 0 deletions

View File

@ -102,6 +102,43 @@ void TransformAdapter3D::apply() {
fMatrixNode->setMatrix(this->totalMatrix());
}
RepeaterAdapter::RepeaterAdapter(sk_sp<sksg::RenderNode> 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<size_t>(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<sksg::Path> wrapped_node, Type t)
: fPathNode(std::move(wrapped_node))
, fType(t) {}

View File

@ -24,8 +24,10 @@ template <typename>
class Matrix;
class Path;
class RadialGradient;
class RenderNode;
class RRect;
class TextBlob;
class TransformEffect;
class TrimEffect;
};
@ -133,6 +135,36 @@ private:
sk_sp<sksg::Matrix<SkMatrix44>> fMatrixNode;
};
class RepeaterAdapter final : public SkNVRefCnt<RepeaterAdapter> {
public:
enum class Composite { kAbove, kBelow };
RepeaterAdapter(sk_sp<sksg::RenderNode>, 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<sksg::Group>& root() const { return fRoot; }
private:
void apply();
const sk_sp<sksg::RenderNode> fRepeaterNode;
const Composite fComposite;
sk_sp<sksg::Group> fRoot;
};
class GradientAdapter : public SkRefCnt {
public:
ADAPTER_PROPERTY(StartPoint, SkPoint , SkPoint::Make(0, 0) )

View File

@ -357,6 +357,71 @@ std::vector<sk_sp<sksg::GeometryNode>> AttachRoundGeometryEffect(
return rounded;
}
std::vector<sk_sp<sksg::RenderNode>> AttachRepeaterDrawEffect(
const skjson::ObjectValue& jrepeater,
const AnimationBuilder* abuilder,
AnimatorScope* ascope,
std::vector<sk_sp<sksg::RenderNode>>&& draws) {
std::vector<sk_sp<sksg::RenderNode>> repeater_draws;
if (const skjson::ObjectValue* jtransform = jrepeater["tr"]) {
sk_sp<sksg::RenderNode> 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<RepeaterAdapter>(std::move(repeater_node),
repeater_composite);
abuilder->bindProperty<ScalarValue>(jrepeater["c"], ascope,
[adapter](const ScalarValue& c) {
adapter->setCount(c);
});
abuilder->bindProperty<ScalarValue>(jrepeater["o"], ascope,
[adapter](const ScalarValue& o) {
adapter->setOffset(o);
});
abuilder->bindProperty<VectorValue>((*jtransform)["a"], ascope,
[adapter](const VectorValue& a) {
adapter->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(a));
});
abuilder->bindProperty<VectorValue>((*jtransform)["p"], ascope,
[adapter](const VectorValue& p) {
adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
});
abuilder->bindProperty<VectorValue>((*jtransform)["s"], ascope,
[adapter](const VectorValue& s) {
adapter->setScale(ValueTraits<VectorValue>::As<SkVector>(s));
});
abuilder->bindProperty<ScalarValue>((*jtransform)["r"], ascope,
[adapter](const ScalarValue& r) {
adapter->setRotation(r);
});
abuilder->bindProperty<ScalarValue>((*jtransform)["so"], ascope,
[adapter](const ScalarValue& so) {
adapter->setStartOpacity(so);
});
abuilder->bindProperty<ScalarValue>((*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<sksg::GeometryNode> (*)(const skjson::ObjectValue&,
const AnimationBuilder*, AnimatorScope*);
static constexpr GeometryAttacherT gGeometryAttachers[] = {
@ -385,12 +450,22 @@ static constexpr GeometryEffectAttacherT gGeometryEffectAttachers[] = {
AttachRoundGeometryEffect,
};
using DrawEffectAttacherT =
std::vector<sk_sp<sksg::RenderNode>> (*)(const skjson::ObjectValue&,
const AnimationBuilder*, AnimatorScope*,
std::vector<sk_sp<sksg::RenderNode>>&&);
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<sksg::RenderNode> 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;
}

View File

@ -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<sk_sp<RenderNode>>);

View File

@ -24,6 +24,13 @@ Group::~Group() {
}
}
void Group::clear() {
for (const auto& child : fChildren) {
this->unobserveInval(child);
}
fChildren.clear();
}
void Group::addChild(sk_sp<RenderNode> node) {
// should we allow duplicates?
for (const auto& child : fChildren) {