• 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 "include/effects/SkShaderMaskFilter.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             fMaskFilter = nullptr;
49         } else {
50             static constexpr float kFeatherToSigma = 0.3f; // close enough to AE
51             fMaskSigma = std::max(fFeather, 0.0f) * kFeatherToSigma;
52 
53             // The gradient is inverted between non-blurred and blurred (latter requires dstOut).
54             const SkColor c0 = fMaskSigma > 0 ? 0xffffffff : 0x00000000,
55                           c1 = 0xffffffff - c0;
56             auto t = fCompletion * 0.01f;
57 
58             const SkColor grad_colors[] = { c0, c1 };
59             const SkScalar   grad_pos[] = {  t,  t };
60 
61             SkMatrix lm;
62             lm.setRotate(fStartAngle - 90 + t * this->wipeAlignment(),
63                          fWipeCenter.x(), fWipeCenter.y());
64 
65             fMaskFilter = SkShaderMaskFilter::Make(
66                             SkGradientShader::MakeSweep(fWipeCenter.x(), fWipeCenter.y(),
67                                                         grad_colors, grad_pos,
68                                                         SK_ARRAY_COUNT(grad_colors), 0, &lm));
69 
70             // Edge feather requires a real blur.
71             if (fMaskSigma > 0) {
72                 fMaskFilter = SkMaskFilter::MakeCompose(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle,
73                                                                                fMaskSigma),
74                                                         std::move(fMaskFilter));
75             }
76         }
77 
78         return content_bounds;
79     }
80 
onRender(SkCanvas * canvas,const RenderContext * ctx) const81     void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
82         if (fCompletion >= 100) {
83             // Fully masked out.
84             return;
85         }
86 
87         if (!fMaskSigma) {
88             // No mask filter, or a shader-only mask filter: we can draw the content directly.
89             const auto local_ctx = ScopedRenderContext(canvas, ctx)
90                                         .modulateMaskFilter(fMaskFilter, canvas->getTotalMatrix());
91             this->children()[0]->render(canvas, local_ctx);
92             return;
93         }
94 
95         // Blurred mask filters require a separate layer.
96         SkAutoCanvasRestore acr(canvas, false);
97         canvas->saveLayer(this->bounds(), nullptr);
98 
99         this->children()[0]->render(canvas, ctx);
100 
101         // Outset the mask to clip-out any edge blur.
102         const auto mask_bounds = this->bounds().makeOutset(fMaskSigma * 3, fMaskSigma * 3);
103 
104         SkPaint mask_paint;
105         mask_paint.setBlendMode(SkBlendMode::kDstOut);
106         mask_paint.setMaskFilter(fMaskFilter);
107         canvas->drawRect(mask_bounds, mask_paint);
108     }
109 
110 private:
wipeAlignment() const111     float wipeAlignment() const {
112         switch (SkScalarRoundToInt(fWipe)) {
113         case 1: return    0.0f; // Clockwise
114         case 2: return -360.0f; // Counterclockwise
115         case 3: return -180.0f; // Both/center
116         default: break;
117         }
118         return 0.0f;
119     }
120 
121     SkPoint fWipeCenter = { 0, 0 };
122     float   fCompletion = 0,
123             fStartAngle = 0,
124             fWipe       = 0,
125             fFeather    = 0;
126 
127     // Cached during revalidation.
128     sk_sp<SkMaskFilter> fMaskFilter;
129     float               fMaskSigma; // edge feather/blur
130 
131     using INHERITED = sksg::CustomRenderNode;
132 };
133 
134 } // namespace
135 
attachRadialWipeEffect(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const136 sk_sp<sksg::RenderNode> EffectBuilder::attachRadialWipeEffect(const skjson::ArrayValue& jprops,
137                                                               sk_sp<sksg::RenderNode> layer) const {
138     enum : size_t {
139         kCompletion_Index = 0,
140         kStartAngle_Index = 1,
141         kWipeCenter_Index = 2,
142         kWipe_Index       = 3,
143         kFeather_Index    = 4,
144     };
145 
146     auto wiper = sk_make_sp<RWipeRenderNode>(std::move(layer));
147 
148     fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kCompletion_Index),
149         [wiper](const ScalarValue& c) {
150             wiper->setCompletion(c);
151         });
152     fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kStartAngle_Index),
153         [wiper](const ScalarValue& sa) {
154             wiper->setStartAngle(sa);
155         });
156     fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kWipeCenter_Index),
157         [wiper](const VectorValue& c) {
158             wiper->setWipeCenter(ValueTraits<VectorValue>::As<SkPoint>(c));
159         });
160     fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kWipe_Index),
161         [wiper](const ScalarValue& w) {
162             wiper->setWipe(w);
163         });
164     fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kFeather_Index),
165         [wiper](const ScalarValue& f) {
166             wiper->setFeather(f);
167         });
168 
169     return wiper;
170 }
171 
172 } // namespace internal
173 } // namespace skottie
174