• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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