blob: aa8b8e0789188a3e6ea1c6d99cab3db4f45f46c1 [file] [log] [blame]
/*
* 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