blob: 91cb4d209b18289edc7e8a00160bba629204d792 [file] [log] [blame]
/*
* Copyright 2020 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "include/private/SkTPin.h"
#include "modules/skottie/src/Adapter.h"
#include "modules/skottie/src/SkottieJson.h"
#include "modules/skottie/src/SkottiePriv.h"
#include "modules/skottie/src/SkottieValue.h"
#include "modules/skottie/src/layers/shapelayer/ShapeLayer.h"
#include "modules/sksg/include/SkSGRenderNode.h"
#include <vector>
namespace skottie {
namespace internal {
namespace {
class RepeaterRenderNode final : public sksg::CustomRenderNode {
public:
enum class CompositeMode { kBelow, kAbove };
RepeaterRenderNode(std::vector<sk_sp<RenderNode>>&& children, CompositeMode mode)
: INHERITED(std::move(children))
, fMode(mode) {}
SG_ATTRIBUTE(Count , size_t, fCount )
SG_ATTRIBUTE(Offset , float , fOffset )
SG_ATTRIBUTE(AnchorPoint , SkV2 , fAnchorPoint )
SG_ATTRIBUTE(Position , SkV2 , fPosition )
SG_ATTRIBUTE(Scale , SkV2 , fScale )
SG_ATTRIBUTE(Rotation , float , fRotation )
SG_ATTRIBUTE(StartOpacity, float , fStartOpacity)
SG_ATTRIBUTE(EndOpacity , float , fEndOpacity )
private:
const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
SkMatrix instanceTransform(size_t i) const {
const auto t = fOffset + i;
// Position, scale & rotation are "scaled" by index/offset.
return SkMatrix::Translate(t * fPosition.x + fAnchorPoint.x,
t * fPosition.y + fAnchorPoint.y)
* SkMatrix::RotateDeg(t * fRotation)
* SkMatrix::Scale(std::pow(fScale.x, t),
std::pow(fScale.y, t))
* SkMatrix::Translate(-fAnchorPoint.x,
-fAnchorPoint.y);
}
SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
fChildrenBounds = SkRect::MakeEmpty();
for (const auto& child : this->children()) {
fChildrenBounds.join(child->revalidate(ic, ctm));
}
auto bounds = SkRect::MakeEmpty();
for (size_t i = 0; i < fCount; ++i) {
bounds.join(this->instanceTransform(i).mapRect(fChildrenBounds));
}
return bounds;
}
void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
// To cover the full opacity range, the denominator below should be (fCount - 1).
// Interstingly, that's not what AE does. Off-by-one bug?
const auto dOpacity = fCount > 1 ? (fEndOpacity - fStartOpacity) / fCount : 0.0f;
for (size_t i = 0; i < fCount; ++i) {
const auto render_index = fMode == CompositeMode::kAbove ? i : fCount - i - 1;
const auto opacity = fStartOpacity + dOpacity * render_index;
if (opacity <= 0) {
continue;
}
SkAutoCanvasRestore acr(canvas, true);
canvas->concat(this->instanceTransform(render_index));
const auto& children = this->children();
const auto local_ctx = ScopedRenderContext(canvas, ctx)
.modulateOpacity(opacity)
.setIsolation(fChildrenBounds,
canvas->getTotalMatrix(),
children.size() > 1);
for (const auto& child : children) {
child->render(canvas, local_ctx);
}
}
}
const CompositeMode fMode;
SkRect fChildrenBounds = SkRect::MakeEmpty(); // cached
size_t fCount = 0;
float fOffset = 0,
fRotation = 0,
fStartOpacity = 1,
fEndOpacity = 1;
SkV2 fAnchorPoint = {0,0},
fPosition = {0,0},
fScale = {1,1};
using INHERITED = sksg::CustomRenderNode;
};
class RepeaterAdapter final : public DiscardableAdapterBase<RepeaterAdapter, RepeaterRenderNode> {
public:
RepeaterAdapter(const skjson::ObjectValue& jrepeater,
const skjson::ObjectValue& jtransform,
const AnimationBuilder& abuilder,
std::vector<sk_sp<sksg::RenderNode>>&& draws)
: INHERITED(sk_make_sp<RepeaterRenderNode>(std::move(draws),
(ParseDefault(jrepeater["m"], 1) == 1)
? RepeaterRenderNode::CompositeMode::kBelow
: RepeaterRenderNode::CompositeMode::kAbove))
{
this->bind(abuilder, jrepeater["c"], fCount);
this->bind(abuilder, jrepeater["o"], fOffset);
this->bind(abuilder, jtransform["a" ], fAnchorPoint);
this->bind(abuilder, jtransform["p" ], fPosition);
this->bind(abuilder, jtransform["s" ], fScale);
this->bind(abuilder, jtransform["r" ], fRotation);
this->bind(abuilder, jtransform["so"], fStartOpacity);
this->bind(abuilder, jtransform["eo"], fEndOpacity);
}
private:
void onSync() override {
static constexpr SkScalar kMaxCount = 1024;
this->node()->setCount(static_cast<size_t>(SkTPin(fCount, 0.0f, kMaxCount) + 0.5f));
this->node()->setOffset(fOffset);
this->node()->setAnchorPoint(fAnchorPoint);
this->node()->setPosition(fPosition);
this->node()->setScale(fScale * 0.01f);
this->node()->setRotation(fRotation);
this->node()->setStartOpacity(SkTPin(fStartOpacity * 0.01f, 0.0f, 1.0f));
this->node()->setEndOpacity (SkTPin(fEndOpacity * 0.01f, 0.0f, 1.0f));
}
// Repeater props
ScalarValue fCount = 0,
fOffset = 0;
// Transform props
Vec2Value fAnchorPoint = { 0, 0 },
fPosition = { 0, 0 },
fScale = { 100, 100 };
ScalarValue fRotation = 0,
fStartOpacity = 100,
fEndOpacity = 100;
using INHERITED = DiscardableAdapterBase<RepeaterAdapter, RepeaterRenderNode>;
};
} // namespace
std::vector<sk_sp<sksg::RenderNode>> ShapeBuilder::AttachRepeaterDrawEffect(
const skjson::ObjectValue& jrepeater,
const AnimationBuilder* abuilder,
std::vector<sk_sp<sksg::RenderNode>>&& draws) {
std::vector<sk_sp<sksg::RenderNode>> repeater_draws;
if (const skjson::ObjectValue* jtransform = jrepeater["tr"]) {
// input draws are in top->bottom order - reverse for paint order
std::reverse(draws.begin(), draws.end());
repeater_draws.reserve(1);
repeater_draws.push_back(
abuilder->attachDiscardableAdapter<RepeaterAdapter>(jrepeater,
*jtransform,
*abuilder,
std::move(draws)));
} else {
repeater_draws = std::move(draws);
}
return repeater_draws;
}
} // namespace internal
} // namespace skottie