[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:
parent
00dcf66865
commit
8ec9a60bdb
@ -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) {}
|
||||
|
@ -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) )
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>>);
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user