1 /*
2 * Copyright 2020 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkCanvas.h"
9 #include "include/private/base/SkTPin.h"
10 #include "modules/skottie/src/Adapter.h"
11 #include "modules/skottie/src/SkottieJson.h"
12 #include "modules/skottie/src/SkottiePriv.h"
13 #include "modules/skottie/src/SkottieValue.h"
14 #include "modules/skottie/src/layers/shapelayer/ShapeLayer.h"
15 #include "modules/sksg/include/SkSGRenderNode.h"
16
17 #include <vector>
18
19 namespace skottie {
20 namespace internal {
21
22 namespace {
23
24 class RepeaterRenderNode final : public sksg::CustomRenderNode {
25 public:
26 enum class CompositeMode { kBelow, kAbove };
27
RepeaterRenderNode(std::vector<sk_sp<RenderNode>> && children,CompositeMode mode)28 RepeaterRenderNode(std::vector<sk_sp<RenderNode>>&& children, CompositeMode mode)
29 : INHERITED(std::move(children))
30 , fMode(mode) {}
31
32 SG_ATTRIBUTE(Count , size_t, fCount )
33 SG_ATTRIBUTE(Offset , float , fOffset )
34 SG_ATTRIBUTE(AnchorPoint , SkV2 , fAnchorPoint )
35 SG_ATTRIBUTE(Position , SkV2 , fPosition )
36 SG_ATTRIBUTE(Scale , SkV2 , fScale )
37 SG_ATTRIBUTE(Rotation , float , fRotation )
38 SG_ATTRIBUTE(StartOpacity, float , fStartOpacity)
39 SG_ATTRIBUTE(EndOpacity , float , fEndOpacity )
40
41 private:
onNodeAt(const SkPoint &) const42 const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
43
instanceTransform(size_t i) const44 SkMatrix instanceTransform(size_t i) const {
45 const auto t = fOffset + i;
46
47 // Position, scale & rotation are "scaled" by index/offset.
48 return SkMatrix::Translate(t * fPosition.x + fAnchorPoint.x,
49 t * fPosition.y + fAnchorPoint.y)
50 * SkMatrix::RotateDeg(t * fRotation)
51 * SkMatrix::Scale(std::pow(fScale.x, t),
52 std::pow(fScale.y, t))
53 * SkMatrix::Translate(-fAnchorPoint.x,
54 -fAnchorPoint.y);
55 }
56
onRevalidate(sksg::InvalidationController * ic,const SkMatrix & ctm)57 SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
58 fChildrenBounds = SkRect::MakeEmpty();
59 for (const auto& child : this->children()) {
60 fChildrenBounds.join(child->revalidate(ic, ctm));
61 }
62
63 auto bounds = SkRect::MakeEmpty();
64 for (size_t i = 0; i < fCount; ++i) {
65 bounds.join(this->instanceTransform(i).mapRect(fChildrenBounds));
66 }
67
68 return bounds;
69 }
70
onRender(SkCanvas * canvas,const RenderContext * ctx) const71 void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
72 // To cover the full opacity range, the denominator below should be (fCount - 1).
73 // Interstingly, that's not what AE does. Off-by-one bug?
74 const auto dOpacity = fCount > 1 ? (fEndOpacity - fStartOpacity) / fCount : 0.0f;
75
76 for (size_t i = 0; i < fCount; ++i) {
77 const auto render_index = fMode == CompositeMode::kAbove ? i : fCount - i - 1;
78 const auto opacity = fStartOpacity + dOpacity * render_index;
79
80 if (opacity <= 0) {
81 continue;
82 }
83
84 SkAutoCanvasRestore acr(canvas, true);
85 canvas->concat(this->instanceTransform(render_index));
86
87 const auto& children = this->children();
88 const auto local_ctx = ScopedRenderContext(canvas, ctx)
89 .modulateOpacity(opacity)
90 .setIsolation(fChildrenBounds,
91 canvas->getTotalMatrix(),
92 children.size() > 1);
93 for (const auto& child : children) {
94 child->render(canvas, local_ctx);
95 }
96 }
97 }
98
99 const CompositeMode fMode;
100
101 SkRect fChildrenBounds = SkRect::MakeEmpty(); // cached
102
103 size_t fCount = 0;
104 float fOffset = 0,
105 fRotation = 0,
106 fStartOpacity = 1,
107 fEndOpacity = 1;
108 SkV2 fAnchorPoint = {0,0},
109 fPosition = {0,0},
110 fScale = {1,1};
111
112 using INHERITED = sksg::CustomRenderNode;
113 };
114
115 class RepeaterAdapter final : public DiscardableAdapterBase<RepeaterAdapter, RepeaterRenderNode> {
116 public:
RepeaterAdapter(const skjson::ObjectValue & jrepeater,const skjson::ObjectValue & jtransform,const AnimationBuilder & abuilder,std::vector<sk_sp<sksg::RenderNode>> && draws)117 RepeaterAdapter(const skjson::ObjectValue& jrepeater,
118 const skjson::ObjectValue& jtransform,
119 const AnimationBuilder& abuilder,
120 std::vector<sk_sp<sksg::RenderNode>>&& draws)
121 : INHERITED(sk_make_sp<RepeaterRenderNode>(std::move(draws),
122 (ParseDefault(jrepeater["m"], 1) == 1)
123 ? RepeaterRenderNode::CompositeMode::kBelow
124 : RepeaterRenderNode::CompositeMode::kAbove))
125 {
126 this->bind(abuilder, jrepeater["c"], fCount);
127 this->bind(abuilder, jrepeater["o"], fOffset);
128
129 this->bind(abuilder, jtransform["a" ], fAnchorPoint);
130 this->bind(abuilder, jtransform["p" ], fPosition);
131 this->bind(abuilder, jtransform["s" ], fScale);
132 this->bind(abuilder, jtransform["r" ], fRotation);
133 this->bind(abuilder, jtransform["so"], fStartOpacity);
134 this->bind(abuilder, jtransform["eo"], fEndOpacity);
135 }
136
137 private:
onSync()138 void onSync() override {
139 static constexpr SkScalar kMaxCount = 1024;
140 this->node()->setCount(static_cast<size_t>(SkTPin(fCount, 0.0f, kMaxCount) + 0.5f));
141 this->node()->setOffset(fOffset);
142 this->node()->setAnchorPoint(fAnchorPoint);
143 this->node()->setPosition(fPosition);
144 this->node()->setScale(fScale * 0.01f);
145 this->node()->setRotation(fRotation);
146 this->node()->setStartOpacity(SkTPin(fStartOpacity * 0.01f, 0.0f, 1.0f));
147 this->node()->setEndOpacity (SkTPin(fEndOpacity * 0.01f, 0.0f, 1.0f));
148 }
149
150 // Repeater props
151 ScalarValue fCount = 0,
152 fOffset = 0;
153
154 // Transform props
155 Vec2Value fAnchorPoint = { 0, 0 },
156 fPosition = { 0, 0 },
157 fScale = { 100, 100 };
158 ScalarValue fRotation = 0,
159 fStartOpacity = 100,
160 fEndOpacity = 100;
161
162 using INHERITED = DiscardableAdapterBase<RepeaterAdapter, RepeaterRenderNode>;
163 };
164
165 } // namespace
166
AttachRepeaterDrawEffect(const skjson::ObjectValue & jrepeater,const AnimationBuilder * abuilder,std::vector<sk_sp<sksg::RenderNode>> && draws)167 std::vector<sk_sp<sksg::RenderNode>> ShapeBuilder::AttachRepeaterDrawEffect(
168 const skjson::ObjectValue& jrepeater,
169 const AnimationBuilder* abuilder,
170 std::vector<sk_sp<sksg::RenderNode>>&& draws) {
171 std::vector<sk_sp<sksg::RenderNode>> repeater_draws;
172
173 if (const skjson::ObjectValue* jtransform = jrepeater["tr"]) {
174 // input draws are in top->bottom order - reverse for paint order
175 std::reverse(draws.begin(), draws.end());
176
177 repeater_draws.reserve(1);
178 repeater_draws.push_back(
179 abuilder->attachDiscardableAdapter<RepeaterAdapter>(jrepeater,
180 *jtransform,
181 *abuilder,
182 std::move(draws)));
183 } else {
184 repeater_draws = std::move(draws);
185 }
186
187 return repeater_draws;
188 }
189
190 } // namespace internal
191 } // namespace skottie
192