| /* |
| * Copyright 2019 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "modules/skottie/src/effects/Effects.h" |
| |
| #include "modules/skottie/src/SkottieValue.h" |
| #include "modules/sksg/include/SkSGGradient.h" |
| #include "modules/sksg/include/SkSGRenderEffect.h" |
| #include "src/utils/SkJSON.h" |
| |
| namespace skottie { |
| namespace internal { |
| |
| namespace { |
| |
| class GradientRampEffectAdapter final : public AnimatablePropertyContainer { |
| public: |
| static sk_sp<GradientRampEffectAdapter> Make(const skjson::ArrayValue& jprops, |
| sk_sp<sksg::RenderNode> layer, |
| const AnimationBuilder* abuilder) { |
| return sk_sp<GradientRampEffectAdapter>(new GradientRampEffectAdapter(jprops, |
| std::move(layer), |
| abuilder)); |
| } |
| |
| sk_sp<sksg::RenderNode> node() const { return fShaderEffect; } |
| |
| private: |
| GradientRampEffectAdapter(const skjson::ArrayValue& jprops, |
| sk_sp<sksg::RenderNode> layer, |
| const AnimationBuilder* abuilder) |
| : fShaderEffect(sksg::ShaderEffect::Make(std::move(layer))) { |
| enum : size_t { |
| kStartPoint_Index = 0, |
| kStartColor_Index = 1, |
| kEndPoint_Index = 2, |
| kEndColor_Index = 3, |
| kRampShape_Index = 4, |
| kRampScatter_Index = 5, |
| kBlendRatio_Index = 6, |
| }; |
| |
| EffectBinder(jprops, *abuilder, this) |
| .bind( kStartPoint_Index, fStartPoint) |
| .bind( kStartColor_Index, fStartColor) |
| .bind( kEndPoint_Index, fEndPoint ) |
| .bind( kEndColor_Index, fEndColor ) |
| .bind( kRampShape_Index, fShape ) |
| .bind(kRampScatter_Index, fScatter ) |
| .bind( kBlendRatio_Index, fBlend ); |
| } |
| |
| enum class InstanceType { |
| kNone, |
| kLinear, |
| kRadial, |
| }; |
| |
| void onSync() override { |
| // This adapter manages a SG fragment with the following structure: |
| // |
| // - ShaderEffect [fRoot] |
| // \ GradientShader [fGradient] |
| // \ child/wrapped fragment |
| // |
| // The gradient shader is updated based on the (animatable) instance type (linear/radial). |
| |
| auto update_gradient = [this] (InstanceType new_type) { |
| if (new_type != fInstanceType) { |
| fGradient = new_type == InstanceType::kLinear |
| ? sk_sp<sksg::Gradient>(sksg::LinearGradient::Make()) |
| : sk_sp<sksg::Gradient>(sksg::RadialGradient::Make()); |
| |
| fShaderEffect->setShader(fGradient); |
| fInstanceType = new_type; |
| } |
| |
| fGradient->setColorStops({{0, fStartColor}, |
| {1, fEndColor}}); |
| }; |
| |
| static constexpr int kLinearShapeValue = 1; |
| const auto instance_type = (SkScalarRoundToInt(fShape) == kLinearShapeValue) |
| ? InstanceType::kLinear |
| : InstanceType::kRadial; |
| |
| // Sync the gradient shader instance if needed. |
| update_gradient(instance_type); |
| |
| // Sync instance-dependent gradient params. |
| const auto start_point = SkPoint{fStartPoint.x, fStartPoint.y}, |
| end_point = SkPoint{ fEndPoint.x, fEndPoint.y}; |
| if (instance_type == InstanceType::kLinear) { |
| auto* lg = static_cast<sksg::LinearGradient*>(fGradient.get()); |
| lg->setStartPoint(start_point); |
| lg->setEndPoint(end_point); |
| } else { |
| SkASSERT(instance_type == InstanceType::kRadial); |
| |
| auto* rg = static_cast<sksg::RadialGradient*>(fGradient.get()); |
| rg->setStartCenter(start_point); |
| rg->setEndCenter(start_point); |
| rg->setEndRadius(SkPoint::Distance(start_point, end_point)); |
| } |
| |
| // TODO: blend, scatter |
| } |
| |
| const sk_sp<sksg::ShaderEffect> fShaderEffect; |
| sk_sp<sksg::Gradient> fGradient; |
| |
| InstanceType fInstanceType = InstanceType::kNone; |
| |
| VectorValue fStartColor, |
| fEndColor; |
| Vec2Value fStartPoint = {0,0}, |
| fEndPoint = {0,0}; |
| ScalarValue fBlend = 0, |
| fScatter = 0, |
| fShape = 0; // 1 -> linear, 7 -> radial (?!) |
| }; |
| |
| } // namespace |
| |
| sk_sp<sksg::RenderNode> EffectBuilder::attachGradientEffect(const skjson::ArrayValue& jprops, |
| sk_sp<sksg::RenderNode> layer) const { |
| return fBuilder->attachDiscardableAdapter<GradientRampEffectAdapter>(jprops, |
| std::move(layer), |
| fBuilder); |
| } |
| |
| } // namespace internal |
| } // namespace skottie |