/* * 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/SkottieAdapter.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 SkNVRefCnt { public: explicit GradientRampEffectAdapter(sk_sp child) : fRoot(sksg::ShaderEffect::Make(std::move(child))) {} ADAPTER_PROPERTY(StartPoint, SkPoint , SkPoint::Make(0, 0)) ADAPTER_PROPERTY(EndPoint , SkPoint , SkPoint::Make(0, 0)) ADAPTER_PROPERTY(StartColor, SkColor , SK_ColorBLACK) ADAPTER_PROPERTY(EndColor , SkColor , SK_ColorBLACK) ADAPTER_PROPERTY(Blend , SkScalar, 0) ADAPTER_PROPERTY(Scatter , SkScalar, 0) // Really an enum: 1 -> linear, 7 -> radial (?!) ADAPTER_PROPERTY(Shape , SkScalar, 0) const sk_sp& root() const { return fRoot; } private: enum class InstanceType { kNone, kLinear, kRadial, }; void apply() { // 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()); fRoot->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. if (instance_type == InstanceType::kLinear) { auto* lg = static_cast(fGradient.get()); lg->setStartPoint(fStartPoint); lg->setEndPoint(fEndPoint); } else { SkASSERT(instance_type == InstanceType::kRadial); auto* rg = static_cast(fGradient.get()); rg->setStartCenter(fStartPoint); rg->setEndCenter(fStartPoint); rg->setEndRadius(SkPoint::Distance(fStartPoint, fEndPoint)); } // TODO: blend, scatter } sk_sp fRoot; sk_sp fGradient; InstanceType fInstanceType = InstanceType::kNone; }; } // anonymous ns sk_sp EffectBuilder::attachGradientEffect(const skjson::ArrayValue& jprops, sk_sp layer) const { enum : size_t { kStartPoint_Index = 0, kStartColor_Index = 1, kEndPoint_Index = 2, kEndColor_Index = 3, kRampShape_Index = 4, kRampScatter_Index = 5, kBlendRatio_Index = 6, }; auto adapter = sk_make_sp(std::move(layer)); fBuilder->bindProperty(GetPropValue(jprops, kStartPoint_Index), [adapter](const VectorValue& p0) { adapter->setStartPoint(ValueTraits::As(p0)); }); fBuilder->bindProperty(GetPropValue(jprops, kEndPoint_Index), [adapter](const VectorValue& p1) { adapter->setEndPoint(ValueTraits::As(p1)); }); fBuilder->bindProperty(GetPropValue(jprops, kStartColor_Index), [adapter](const VectorValue& c0) { adapter->setStartColor(ValueTraits::As(c0)); }); fBuilder->bindProperty(GetPropValue(jprops, kEndColor_Index), [adapter](const VectorValue& c1) { adapter->setEndColor(ValueTraits::As(c1)); }); fBuilder->bindProperty(GetPropValue(jprops, kRampShape_Index), [adapter](const ScalarValue& shape) { adapter->setShape(shape); }); fBuilder->bindProperty(GetPropValue(jprops, kBlendRatio_Index), [adapter](const ScalarValue& blend) { adapter->setBlend(blend); }); fBuilder->bindProperty(GetPropValue(jprops, kRampScatter_Index), [adapter](const ScalarValue& scatter) { adapter->setScatter(scatter); }); return adapter->root(); } } // namespace internal } // namespace skottie