xref: /aosp_15_r20/external/skia/modules/skottie/src/effects/GradientEffect.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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