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