• 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/effects/SkGradientShader.h"
11 #include "include/effects/SkShaderMaskFilter.h"
12 #include "include/private/base/SkTPin.h"
13 #include "modules/skottie/src/SkottieValue.h"
14 #include "modules/sksg/include/SkSGRenderEffect.h"
15 #include "src/utils/SkJSON.h"
16 
17 #include <cmath>
18 #include <utility>
19 
20 namespace skottie {
21 namespace internal {
22 
23 namespace  {
24 
25 class LinearWipeAdapter final : public MaskShaderEffectBase {
26 public:
Make(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer,const SkSize & layer_size,const AnimationBuilder * abuilder)27     static sk_sp<LinearWipeAdapter> Make(const skjson::ArrayValue& jprops,
28                                          sk_sp<sksg::RenderNode> layer,
29                                          const SkSize& layer_size,
30                                          const AnimationBuilder* abuilder) {
31         return sk_sp<LinearWipeAdapter>(new LinearWipeAdapter(jprops,
32                                                               std::move(layer),
33                                                               layer_size,
34                                                               abuilder));
35     }
36 
37 private:
LinearWipeAdapter(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer,const SkSize & layer_size,const AnimationBuilder * abuilder)38     LinearWipeAdapter(const skjson::ArrayValue& jprops,
39                       sk_sp<sksg::RenderNode> layer,
40                       const SkSize& layer_size,
41                       const AnimationBuilder* abuilder)
42         : INHERITED(std::move(layer), layer_size) {
43         enum : size_t {
44             kCompletion_Index = 0,
45                  kAngle_Index = 1,
46                kFeather_Index = 2,
47         };
48 
49         EffectBinder(jprops, *abuilder, this)
50                 .bind(kCompletion_Index, fCompletion)
51                 .bind(     kAngle_Index, fAngle     )
52                 .bind(   kFeather_Index, fFeather   );
53     }
54 
onMakeMask() const55     MaskInfo onMakeMask() const override {
56         if (fCompletion >= 100) {
57             // The layer is fully disabled.
58             // TODO: fix layer controller visibility clash and pass a null shader instead.
59             return { SkShaders::Color(SK_ColorTRANSPARENT), false };
60         }
61 
62         if (fCompletion <= 0) {
63             // The layer is fully visible (no mask).
64             return { nullptr, true };
65         }
66 
67         const auto t = SkTPin(fCompletion * 0.01f, 0.0f, 1.0f),
68              feather = std::max(fFeather, 0.0f),
69                angle = SkDegreesToRadians(90 - fAngle),
70                 cos_ = std::cos(angle),
71                 sin_ = std::sin(angle);
72 
73         // Select the correct diagonal vector depending on quadrant.
74         const SkVector angle_v = {cos_, sin_},
75                         diag_v = {std::copysign(this->layerSize().width() , cos_),
76                                   std::copysign(this->layerSize().height(), sin_)};
77 
78         // The transition length is the projection of the diagonal onto the angle vector.
79         const auto len = SkVector::DotProduct(diag_v, angle_v);
80 
81         // Pad the gradient segment to accommodate optional feather ramps at both extremities.
82         const auto grad_len   = len + feather * 2;
83         const SkVector grad_v = angle_v * grad_len,
84               adjusted_grad_v = { grad_v.fX, -grad_v.fY }, // Y flipped for drawing space.
85                      center_v = {0.5f * this->layerSize().width(),
86                                  0.5f * this->layerSize().height()};
87 
88         // Gradient start/end points:
89         const SkPoint pts[] = {
90             center_v - adjusted_grad_v * 0.5f,
91             center_v + adjusted_grad_v * 0.5f,
92         };
93 
94         static constexpr SkColor colors[] = { 0x00000000,
95                                               0xffffffff };
96 
97         // To emulate the feather effect, we distance the color stops to generate
98         // a linear transition/ramp.  For t == 0 the ramp should be completely outside/before
99         // the transition domain, and for t == 1 it should be completely outside/after.
100         //
101         //                      [0 ................... |len|]
102         //
103         //   [0  <feather_ramp> [                           ] <feather_ramp> |grad_len|]
104         const auto adjusted_t = t * (len + feather) / grad_len;
105         const SkScalar  pos[] = { adjusted_t,
106                                   adjusted_t + feather / grad_len };
107 
108         return { SkGradientShader::MakeLinear(pts, colors, pos, 2, SkTileMode::kClamp), true };
109     }
110 
111     ScalarValue fCompletion = 0,
112                 fAngle      = 0,
113                 fFeather    = 0;
114 
115     using INHERITED = MaskShaderEffectBase;
116 };
117 
118 } // namespace
119 
attachLinearWipeEffect(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const120 sk_sp<sksg::RenderNode> EffectBuilder::attachLinearWipeEffect(const skjson::ArrayValue& jprops,
121                                                               sk_sp<sksg::RenderNode> layer) const {
122     return fBuilder->attachDiscardableAdapter<LinearWipeAdapter>(jprops,
123                                                                  std::move(layer),
124                                                                  fLayerSize,
125                                                                  fBuilder);
126 }
127 
128 } // namespace internal
129 } // namespace skottie
130 
131