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