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