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 "include/core/SkPoint.h"
9 #include "include/core/SkRefCnt.h"
10 #include "include/core/SkScalar.h"
11 #include "include/private/base/SkAssert.h"
12 #include "modules/skottie/src/SkottiePriv.h"
13 #include "modules/skottie/src/SkottieValue.h"
14 #include "modules/skottie/src/animator/Animator.h"
15 #include "modules/skottie/src/effects/Effects.h"
16 #include "modules/sksg/include/SkSGGradient.h"
17 #include "modules/sksg/include/SkSGRenderEffect.h"
18 #include "modules/sksg/include/SkSGRenderNode.h"
19
20 #include <cstddef>
21 #include <utility>
22
23 namespace skjson {
24 class ArrayValue;
25 }
26
27 namespace skottie {
28 namespace internal {
29
30 namespace {
31
32 class GradientRampEffectAdapter final : public AnimatablePropertyContainer {
33 public:
Make(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer,const AnimationBuilder * abuilder)34 static sk_sp<GradientRampEffectAdapter> Make(const skjson::ArrayValue& jprops,
35 sk_sp<sksg::RenderNode> layer,
36 const AnimationBuilder* abuilder) {
37 return sk_sp<GradientRampEffectAdapter>(new GradientRampEffectAdapter(jprops,
38 std::move(layer),
39 abuilder));
40 }
41
node() const42 sk_sp<sksg::RenderNode> node() const { return fShaderEffect; }
43
44 private:
GradientRampEffectAdapter(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer,const AnimationBuilder * abuilder)45 GradientRampEffectAdapter(const skjson::ArrayValue& jprops,
46 sk_sp<sksg::RenderNode> layer,
47 const AnimationBuilder* abuilder)
48 : fShaderEffect(sksg::ShaderEffect::Make(std::move(layer))) {
49 enum : size_t {
50 kStartPoint_Index = 0,
51 kStartColor_Index = 1,
52 kEndPoint_Index = 2,
53 kEndColor_Index = 3,
54 kRampShape_Index = 4,
55 kRampScatter_Index = 5,
56 kBlendRatio_Index = 6,
57 };
58
59 EffectBinder(jprops, *abuilder, this)
60 .bind( kStartPoint_Index, fStartPoint)
61 .bind( kStartColor_Index, fStartColor)
62 .bind( kEndPoint_Index, fEndPoint )
63 .bind( kEndColor_Index, fEndColor )
64 .bind( kRampShape_Index, fShape )
65 .bind(kRampScatter_Index, fScatter )
66 .bind( kBlendRatio_Index, fBlend );
67 }
68
69 enum class InstanceType {
70 kNone,
71 kLinear,
72 kRadial,
73 };
74
onSync()75 void onSync() override {
76 // This adapter manages a SG fragment with the following structure:
77 //
78 // - ShaderEffect [fRoot]
79 // \ GradientShader [fGradient]
80 // \ child/wrapped fragment
81 //
82 // The gradient shader is updated based on the (animatable) instance type (linear/radial).
83
84 auto update_gradient = [this] (InstanceType new_type) {
85 if (new_type != fInstanceType) {
86 fGradient = new_type == InstanceType::kLinear
87 ? sk_sp<sksg::Gradient>(sksg::LinearGradient::Make())
88 : sk_sp<sksg::Gradient>(sksg::RadialGradient::Make());
89
90 fShaderEffect->setShader(fGradient);
91 fInstanceType = new_type;
92 }
93
94 fGradient->setColorStops({{0, fStartColor},
95 {1, fEndColor}});
96 };
97
98 static constexpr int kLinearShapeValue = 1;
99 const auto instance_type = (SkScalarRoundToInt(fShape) == kLinearShapeValue)
100 ? InstanceType::kLinear
101 : InstanceType::kRadial;
102
103 // Sync the gradient shader instance if needed.
104 update_gradient(instance_type);
105
106 // Sync instance-dependent gradient params.
107 const auto start_point = SkPoint{fStartPoint.x, fStartPoint.y},
108 end_point = SkPoint{ fEndPoint.x, fEndPoint.y};
109 if (instance_type == InstanceType::kLinear) {
110 auto* lg = static_cast<sksg::LinearGradient*>(fGradient.get());
111 lg->setStartPoint(start_point);
112 lg->setEndPoint(end_point);
113 } else {
114 SkASSERT(instance_type == InstanceType::kRadial);
115
116 auto* rg = static_cast<sksg::RadialGradient*>(fGradient.get());
117 rg->setStartCenter(start_point);
118 rg->setEndCenter(start_point);
119 rg->setEndRadius(SkPoint::Distance(start_point, end_point));
120 }
121
122 // TODO: blend, scatter
123 }
124
125 const sk_sp<sksg::ShaderEffect> fShaderEffect;
126 sk_sp<sksg::Gradient> fGradient;
127
128 InstanceType fInstanceType = InstanceType::kNone;
129
130 ColorValue fStartColor,
131 fEndColor;
132 Vec2Value fStartPoint = {0,0},
133 fEndPoint = {0,0};
134 ScalarValue fBlend = 0,
135 fScatter = 0,
136 fShape = 0; // 1 -> linear, 7 -> radial (?!)
137 };
138
139 } // namespace
140
attachGradientEffect(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const141 sk_sp<sksg::RenderNode> EffectBuilder::attachGradientEffect(const skjson::ArrayValue& jprops,
142 sk_sp<sksg::RenderNode> layer) const {
143 return fBuilder->attachDiscardableAdapter<GradientRampEffectAdapter>(jprops,
144 std::move(layer),
145 fBuilder);
146 }
147
148 } // namespace internal
149 } // namespace skottie
150