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 15namespace skottie { 16namespace internal { 17 18namespace { 19 20class GradientRampEffectAdapter final : public AnimatablePropertyContainer { 21public: 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 30 sk_sp<sksg::RenderNode> node() const { return fShaderEffect; } 31 32private: 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 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 129sk_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