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