• 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/SkottieValue.h"
11 #include "modules/sksg/include/SkSGGradient.h"
12 #include "modules/sksg/include/SkSGRenderEffect.h"
13 #include "src/utils/SkJSON.h"
14 
15 namespace skottie {
16 namespace internal {
17 
18 namespace  {
19 
20 class GradientRampEffectAdapter final : public AnimatablePropertyContainer {
21 public:
Make(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer,const AnimationBuilder * abuilder)22     static sk_sp<GradientRampEffectAdapter> Make(const skjson::ArrayValue& jprops,
23                                                  sk_sp<sksg::RenderNode> layer,
24                                                  const AnimationBuilder* abuilder) {
25         return sk_sp<GradientRampEffectAdapter>(new GradientRampEffectAdapter(jprops,
26                                                                               std::move(layer),
27                                                                               abuilder));
28     }
29 
node() const30     sk_sp<sksg::RenderNode> node() const { return fShaderEffect; }
31 
32 private:
GradientRampEffectAdapter(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer,const AnimationBuilder * abuilder)33     GradientRampEffectAdapter(const skjson::ArrayValue& jprops,
34                               sk_sp<sksg::RenderNode> layer,
35                               const AnimationBuilder* abuilder)
36         : fShaderEffect(sksg::ShaderEffect::Make(std::move(layer))) {
37         enum : size_t {
38              kStartPoint_Index = 0,
39              kStartColor_Index = 1,
40                kEndPoint_Index = 2,
41                kEndColor_Index = 3,
42               kRampShape_Index = 4,
43             kRampScatter_Index = 5,
44              kBlendRatio_Index = 6,
45         };
46 
47         EffectBinder(jprops, *abuilder, this)
48                 .bind( kStartPoint_Index, fStartPoint)
49                 .bind( kStartColor_Index, fStartColor)
50                 .bind(   kEndPoint_Index, fEndPoint  )
51                 .bind(   kEndColor_Index, fEndColor  )
52                 .bind(  kRampShape_Index, fShape     )
53                 .bind(kRampScatter_Index, fScatter   )
54                 .bind( 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, fStartColor},
83                                       {1,   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 = SkPoint{fStartPoint.x, fStartPoint.y},
96                      end_point = SkPoint{  fEndPoint.x,   fEndPoint.y};
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 fStartColor,
119                   fEndColor;
120     Vec2Value   fStartPoint = {0,0},
121                 fEndPoint   = {0,0};
122     ScalarValue fBlend   = 0,
123                 fScatter = 0,
124                 fShape   = 0; // 1 -> linear, 7 -> radial (?!)
125 };
126 
127 }  // namespace
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