/* * 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 "include/core/SkPoint.h" #include "include/core/SkRefCnt.h" #include "include/core/SkScalar.h" #include "include/private/base/SkAssert.h" #include "modules/skottie/src/SkottiePriv.h" #include "modules/skottie/src/SkottieValue.h" #include "modules/skottie/src/animator/Animator.h" #include "modules/skottie/src/effects/Effects.h" #include "modules/sksg/include/SkSGGradient.h" #include "modules/sksg/include/SkSGRenderEffect.h" #include "modules/sksg/include/SkSGRenderNode.h" #include #include namespace skjson { class ArrayValue; } namespace skottie { namespace internal { namespace { class GradientRampEffectAdapter final : public AnimatablePropertyContainer { public: static sk_sp Make(const skjson::ArrayValue& jprops, sk_sp layer, const AnimationBuilder* abuilder) { return sk_sp(new GradientRampEffectAdapter(jprops, std::move(layer), abuilder)); } sk_sp node() const { return fShaderEffect; } private: GradientRampEffectAdapter(const skjson::ArrayValue& jprops, sk_sp 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::LinearGradient::Make()) : sk_sp(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(fGradient.get()); lg->setStartPoint(start_point); lg->setEndPoint(end_point); } else { SkASSERT(instance_type == InstanceType::kRadial); auto* rg = static_cast(fGradient.get()); rg->setStartCenter(start_point); rg->setEndCenter(start_point); rg->setEndRadius(SkPoint::Distance(start_point, end_point)); } // TODO: blend, scatter } const sk_sp fShaderEffect; sk_sp fGradient; InstanceType fInstanceType = InstanceType::kNone; ColorValue fStartColor, fEndColor; Vec2Value fStartPoint = {0,0}, fEndPoint = {0,0}; ScalarValue fBlend = 0, fScatter = 0, fShape = 0; // 1 -> linear, 7 -> radial (?!) }; } // namespace sk_sp EffectBuilder::attachGradientEffect(const skjson::ArrayValue& jprops, sk_sp layer) const { return fBuilder->attachDiscardableAdapter(jprops, std::move(layer), fBuilder); } } // namespace internal } // namespace skottie