• 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 "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