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