Coverage Report

Created: 2024-05-20 07:14

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