Coverage Report

Created: 2024-09-14 07:19

/src/skia/modules/skottie/src/effects/RadialWipeEffect.cpp
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright 2019 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/SkColor.h"
10
#include "include/core/SkPoint.h"
11
#include "include/core/SkRect.h"
12
#include "include/core/SkRefCnt.h"
13
#include "include/core/SkScalar.h"
14
#include "include/core/SkShader.h"
15
#include "include/core/SkTileMode.h"
16
#include "include/effects/SkGradientShader.h"
17
#include "include/private/base/SkAssert.h"
18
#include "modules/skottie/src/Adapter.h"
19
#include "modules/skottie/src/SkottiePriv.h"
20
#include "modules/skottie/src/SkottieValue.h"
21
#include "modules/skottie/src/effects/Effects.h"
22
#include "modules/sksg/include/SkSGNode.h"
23
#include "modules/sksg/include/SkSGRenderNode.h"
24
25
#include <algorithm>
26
#include <cmath>
27
#include <cstddef>
28
#include <utility>
29
#include <vector>
30
31
class SkMatrix;
32
33
namespace skjson {
34
class ArrayValue;
35
}
36
namespace sksg {
37
class InvalidationController;
38
}
39
40
namespace skottie {
41
namespace internal {
42
43
namespace  {
44
45
class RWipeRenderNode final : public sksg::CustomRenderNode {
46
public:
47
    explicit RWipeRenderNode(sk_sp<sksg::RenderNode> layer)
48
1.80k
        : INHERITED({std::move(layer)}) {}
49
50
    SG_ATTRIBUTE(Completion, float  , fCompletion)
51
    SG_ATTRIBUTE(StartAngle, float  , fStartAngle)
52
    SG_ATTRIBUTE(WipeCenter, SkPoint, fWipeCenter)
53
    SG_ATTRIBUTE(Wipe      , float  , fWipe      )
54
    SG_ATTRIBUTE(Feather   , float  , fFeather   )
55
56
protected:
57
0
    const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
58
59
2.58k
    SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
60
2.58k
        SkASSERT(this->children().size() == 1ul);
61
2.58k
        const auto content_bounds = this->children()[0]->revalidate(ic, ctm);
62
63
2.58k
        if (fCompletion >= 100) {
64
16
            return SkRect::MakeEmpty();
65
16
        }
66
67
2.57k
        if (fCompletion <= 0) {
68
1.34k
            fMaskSigma  = 0;
69
1.34k
            fMaskShader = nullptr;
70
1.34k
        } else {
71
1.22k
            fMaskSigma = std::max(fFeather, 0.0f) * kBlurSizeToSigma;
72
73
1.22k
            const auto t = fCompletion * 0.01f;
74
75
            // Note: this could be simplified as a one-hard-stop gradient + local matrix
76
            // (to apply rotation).  Alas, local matrices are no longer supported in SkSG.
77
1.22k
            SkColor c0 = 0x00000000,
78
1.22k
                    c1 = 0xffffffff;
79
2.45k
            auto sanitize_angle = [](float a) {
80
2.45k
                a = std::fmod(a, 360);
81
2.45k
                if (a < 0) {
82
1.22k
                    a += 360;
83
1.22k
                }
84
2.45k
                return a;
85
2.45k
            };
86
87
1.22k
            auto a0 = sanitize_angle(fStartAngle - 90 + t * this->wipeAlignment()),
88
1.22k
                 a1 = sanitize_angle(a0 + t * 360);
89
1.22k
            if (a0 > a1) {
90
0
                std::swap(a0, a1);
91
0
                std::swap(c0, c1);
92
0
            }
93
94
1.22k
            const SkColor grad_colors[] = { c1, c0, c0, c1 };
95
1.22k
            const SkScalar   grad_pos[] = {  0,  0,  1,  1 };
96
97
1.22k
            fMaskShader = SkGradientShader::MakeSweep(fWipeCenter.x(), fWipeCenter.y(),
98
1.22k
                                                      grad_colors, grad_pos,
99
1.22k
                                                      std::size(grad_colors),
100
1.22k
                                                      SkTileMode::kClamp,
101
1.22k
                                                      a0, a1, 0, nullptr);
102
103
            // Edge feather requires a real blur.
104
1.22k
            if (fMaskSigma > 0) {
105
                // TODO: this feature is disabled ATM.
106
653
            }
107
1.22k
        }
108
109
2.57k
        return content_bounds;
110
2.58k
    }
111
112
410
    void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
113
410
        if (fCompletion >= 100) {
114
            // Fully masked out.
115
0
            return;
116
0
        }
117
118
410
        const auto local_ctx = ScopedRenderContext(canvas, ctx)
119
410
                                    .modulateMaskShader(fMaskShader, canvas->getTotalMatrix());
120
410
        this->children()[0]->render(canvas, local_ctx);
121
410
    }
122
123
private:
124
1.22k
    float wipeAlignment() const {
125
1.22k
        switch (SkScalarRoundToInt(fWipe)) {
126
16
        case 1: return    0.0f; // Clockwise
127
0
        case 2: return -360.0f; // Counterclockwise
128
0
        case 3: return -180.0f; // Both/center
129
1.21k
        default: break;
130
1.22k
        }
131
1.21k
        return 0.0f;
132
1.22k
    }
133
134
    SkPoint fWipeCenter = { 0, 0 };
135
    float   fCompletion = 0,
136
            fStartAngle = 0,
137
            fWipe       = 0,
138
            fFeather    = 0;
139
140
    // Cached during revalidation.
141
    sk_sp<SkShader> fMaskShader;
142
    float           fMaskSigma; // edge feather/blur
143
144
    using INHERITED = sksg::CustomRenderNode;
145
};
146
147
class RadialWipeAdapter final : public DiscardableAdapterBase<RadialWipeAdapter, RWipeRenderNode> {
148
public:
149
    RadialWipeAdapter(const skjson::ArrayValue& jprops,
150
                      sk_sp<sksg::RenderNode> layer,
151
                      const AnimationBuilder& abuilder)
152
1.80k
        : INHERITED(sk_make_sp<RWipeRenderNode>(std::move(layer))) {
153
154
1.80k
        enum : size_t {
155
1.80k
            kCompletion_Index = 0,
156
1.80k
            kStartAngle_Index = 1,
157
1.80k
            kWipeCenter_Index = 2,
158
1.80k
                  kWipe_Index = 3,
159
1.80k
               kFeather_Index = 4,
160
1.80k
        };
161
162
1.80k
        EffectBinder(jprops, abuilder, this)
163
1.80k
            .bind(kCompletion_Index, fCompletion)
164
1.80k
            .bind(kStartAngle_Index, fStartAngle)
165
1.80k
            .bind(kWipeCenter_Index, fWipeCenter)
166
1.80k
            .bind(      kWipe_Index, fWipe      )
167
1.80k
            .bind(   kFeather_Index, fFeather   );
168
1.80k
    }
169
170
private:
171
1.80k
    void onSync() override {
172
1.80k
        const auto& wiper = this->node();
173
174
1.80k
        wiper->setCompletion(fCompletion);
175
1.80k
        wiper->setStartAngle(fStartAngle);
176
1.80k
        wiper->setWipeCenter({fWipeCenter.x, fWipeCenter.y});
177
1.80k
        wiper->setWipe(fWipe);
178
1.80k
        wiper->setFeather(fFeather);
179
1.80k
    }
180
181
    Vec2Value   fWipeCenter = {0,0};
182
    ScalarValue fCompletion = 0,
183
                fStartAngle = 0,
184
                fWipe       = 0,
185
                fFeather    = 0;
186
187
    using INHERITED = DiscardableAdapterBase<RadialWipeAdapter, RWipeRenderNode>;
188
};
189
190
} // namespace
191
192
sk_sp<sksg::RenderNode> EffectBuilder::attachRadialWipeEffect(const skjson::ArrayValue& jprops,
193
1.80k
                                                              sk_sp<sksg::RenderNode> layer) const {
194
1.80k
    return fBuilder->attachDiscardableAdapter<RadialWipeAdapter>(jprops,
195
1.80k
                                                                 std::move(layer),
196
1.80k
                                                                 *fBuilder);
197
1.80k
}
198
199
} // namespace internal
200
} // namespace skottie