[skottie] Effects cleanup pass
Split-off effects into separate CUs. TBR= Change-Id: Ic214027d27e1c341085d9a8c74f605caba260630 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/221118 Reviewed-by: Florin Malita <fmalita@chromium.org> Commit-Queue: Florin Malita <fmalita@chromium.org>
This commit is contained in:
parent
d30e039d07
commit
941b9ff09b
@ -20,7 +20,6 @@ skia_skottie_sources = [
|
||||
"$_src/SkottieJson.cpp",
|
||||
"$_src/SkottieJson.h",
|
||||
"$_src/SkottieLayer.cpp",
|
||||
"$_src/SkottieLayerEffect.cpp",
|
||||
"$_src/SkottiePriv.h",
|
||||
"$_src/SkottiePrecompLayer.cpp",
|
||||
"$_src/SkottieProperty.cpp",
|
||||
@ -28,9 +27,16 @@ skia_skottie_sources = [
|
||||
"$_src/SkottieValue.cpp",
|
||||
"$_src/SkottieValue.h",
|
||||
|
||||
"$_src/effects/DropShadowEffect.cpp",
|
||||
"$_src/effects/Effects.cpp",
|
||||
"$_src/effects/Effects.h",
|
||||
"$_src/effects/FillEffect.cpp",
|
||||
"$_src/effects/GaussianBlurEffect.cpp",
|
||||
"$_src/effects/GradientEffect.cpp",
|
||||
"$_src/effects/LevelsEffect.cpp",
|
||||
"$_src/effects/TintEffect.cpp",
|
||||
"$_src/effects/TransformEffect.cpp",
|
||||
"$_src/effects/TritoneEffect.cpp",
|
||||
|
||||
"$_src/text/RangeSelector.cpp",
|
||||
"$_src/text/RangeSelector.h",
|
||||
|
@ -12,18 +12,15 @@
|
||||
#include "include/core/SkMatrix44.h"
|
||||
#include "include/core/SkPath.h"
|
||||
#include "include/core/SkRRect.h"
|
||||
#include "include/effects/SkTableColorFilter.h"
|
||||
#include "include/private/SkTo.h"
|
||||
#include "include/utils/Sk3D.h"
|
||||
#include "modules/skottie/src/SkottieValue.h"
|
||||
#include "modules/sksg/include/SkSGColorFilter.h"
|
||||
#include "modules/sksg/include/SkSGDraw.h"
|
||||
#include "modules/sksg/include/SkSGGradient.h"
|
||||
#include "modules/sksg/include/SkSGGroup.h"
|
||||
#include "modules/sksg/include/SkSGPaint.h"
|
||||
#include "modules/sksg/include/SkSGPath.h"
|
||||
#include "modules/sksg/include/SkSGRect.h"
|
||||
#include "modules/sksg/include/SkSGRenderEffect.h"
|
||||
#include "modules/sksg/include/SkSGTransform.h"
|
||||
#include "modules/sksg/include/SkSGTrimEffect.h"
|
||||
|
||||
@ -288,58 +285,6 @@ void RadialGradientAdapter::onApply() {
|
||||
grad->setEndRadius(SkPoint::Distance(this->startPoint(), this->endPoint()));
|
||||
}
|
||||
|
||||
GradientRampEffectAdapter::GradientRampEffectAdapter(sk_sp<sksg::RenderNode> child)
|
||||
: fRoot(sksg::ShaderEffect::Make(std::move(child))) {}
|
||||
|
||||
GradientRampEffectAdapter::~GradientRampEffectAdapter() = default;
|
||||
|
||||
void GradientRampEffectAdapter::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) intance 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());
|
||||
|
||||
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<sksg::LinearGradient*>(fGradient.get());
|
||||
lg->setStartPoint(fStartPoint);
|
||||
lg->setEndPoint(fEndPoint);
|
||||
} else {
|
||||
SkASSERT(instance_type == InstanceType::kRadial);
|
||||
|
||||
auto* rg = static_cast<sksg::RadialGradient*>(fGradient.get());
|
||||
rg->setStartCenter(fStartPoint);
|
||||
rg->setEndCenter(fStartPoint);
|
||||
rg->setEndRadius(SkPoint::Distance(fStartPoint, fEndPoint));
|
||||
}
|
||||
|
||||
// TODO: blend, scatter
|
||||
}
|
||||
|
||||
TrimEffectAdapter::TrimEffectAdapter(sk_sp<sksg::TrimEffect> trimEffect)
|
||||
: fTrimEffect(std::move(trimEffect)) {
|
||||
SkASSERT(fTrimEffect);
|
||||
@ -376,158 +321,4 @@ void TrimEffectAdapter::apply() {
|
||||
fTrimEffect->setMode(mode);
|
||||
}
|
||||
|
||||
DropShadowEffectAdapter::DropShadowEffectAdapter(sk_sp<sksg::DropShadowImageFilter> dropShadow)
|
||||
: fDropShadow(std::move(dropShadow)) {
|
||||
SkASSERT(fDropShadow);
|
||||
}
|
||||
|
||||
DropShadowEffectAdapter::~DropShadowEffectAdapter() = default;
|
||||
|
||||
void DropShadowEffectAdapter::apply() {
|
||||
// fColor -> RGB, fOpacity -> A
|
||||
fDropShadow->setColor(SkColorSetA(fColor, SkTPin(SkScalarRoundToInt(fOpacity), 0, 255)));
|
||||
|
||||
// The offset is specified in terms of a bearing angle + distance.
|
||||
SkScalar rad = SkDegreesToRadians(90 - fDirection);
|
||||
fDropShadow->setOffset(SkVector::Make( fDistance * SkScalarCos(rad),
|
||||
-fDistance * SkScalarSin(rad)));
|
||||
|
||||
// Close enough to AE.
|
||||
static constexpr SkScalar kSoftnessToSigmaFactor = 0.3f;
|
||||
const auto sigma = fSoftness * kSoftnessToSigmaFactor;
|
||||
fDropShadow->setSigma(SkVector::Make(sigma, sigma));
|
||||
|
||||
fDropShadow->setMode(fShadowOnly ? sksg::DropShadowImageFilter::Mode::kShadowOnly
|
||||
: sksg::DropShadowImageFilter::Mode::kShadowAndForeground);
|
||||
}
|
||||
|
||||
GaussianBlurEffectAdapter::GaussianBlurEffectAdapter(sk_sp<sksg::BlurImageFilter> blur)
|
||||
: fBlur(std::move(blur)) {
|
||||
SkASSERT(fBlur);
|
||||
}
|
||||
|
||||
GaussianBlurEffectAdapter::~GaussianBlurEffectAdapter() = default;
|
||||
|
||||
void GaussianBlurEffectAdapter::apply() {
|
||||
static constexpr SkVector kDimensionsMap[] = {
|
||||
{ 1, 1 }, // 1 -> horizontal and vertical
|
||||
{ 1, 0 }, // 2 -> horizontal
|
||||
{ 0, 1 }, // 3 -> vertical
|
||||
};
|
||||
|
||||
const auto dim_index = SkTPin<size_t>(static_cast<size_t>(fDimensions),
|
||||
1, SK_ARRAY_COUNT(kDimensionsMap)) - 1;
|
||||
|
||||
// Close enough to AE.
|
||||
static constexpr SkScalar kBlurrinessToSigmaFactor = 0.3f;
|
||||
const auto sigma = fBlurriness * kBlurrinessToSigmaFactor;
|
||||
|
||||
fBlur->setSigma({ sigma * kDimensionsMap[dim_index].x(),
|
||||
sigma * kDimensionsMap[dim_index].y() });
|
||||
|
||||
static constexpr SkBlurImageFilter::TileMode kRepeatEdgeMap[] = {
|
||||
SkBlurImageFilter::kClampToBlack_TileMode, // 0 -> repeat edge pixels: off
|
||||
SkBlurImageFilter:: kClamp_TileMode, // 1 -> repeat edge pixels: on
|
||||
};
|
||||
|
||||
const auto repeat_index = SkTPin<size_t>(static_cast<size_t>(fRepeatEdge),
|
||||
0, SK_ARRAY_COUNT(kRepeatEdgeMap) - 1);
|
||||
fBlur->setTileMode(kRepeatEdgeMap[repeat_index]);
|
||||
}
|
||||
|
||||
|
||||
// Levels color correction effect.
|
||||
//
|
||||
// Maps the selected channels from [inBlack...inWhite] to [outBlack, outWhite],
|
||||
// based on a gamma exponent.
|
||||
//
|
||||
// For [i0..i1] -> [o0..o1]:
|
||||
//
|
||||
// c' = o0 + (o1 - o0) * ((c - i0) / (i1 - i0)) ^ G
|
||||
//
|
||||
// The output is optionally clipped to the output range.
|
||||
//
|
||||
// In/out intervals are clampped to [0..1]. Inversion is allowed.
|
||||
LevelsEffectAdapter::LevelsEffectAdapter(sk_sp<sksg::RenderNode> child)
|
||||
: fEffect(sksg::ExternalColorFilter::Make(std::move(child))) {
|
||||
SkASSERT(fEffect);
|
||||
}
|
||||
|
||||
LevelsEffectAdapter::~LevelsEffectAdapter() = default;
|
||||
|
||||
void LevelsEffectAdapter::apply() {
|
||||
enum LottieChannel {
|
||||
kRGB_Channel = 1,
|
||||
kR_Channel = 2,
|
||||
kG_Channel = 3,
|
||||
kB_Channel = 4,
|
||||
kA_Channel = 5,
|
||||
};
|
||||
|
||||
const auto channel = SkScalarTruncToInt(fChannel);
|
||||
if (channel < kRGB_Channel || channel > kA_Channel) {
|
||||
fEffect->setColorFilter(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto in_0 = SkTPin(fInBlack, 0.0f, 1.0f),
|
||||
in_1 = SkTPin(fInWhite, 0.0f, 1.0f),
|
||||
out_0 = SkTPin(fOutBlack, 0.0f, 1.0f),
|
||||
out_1 = SkTPin(fOutWhite, 0.0f, 1.0f),
|
||||
g = 1 / SkTMax(fGamma, 0.0f);
|
||||
|
||||
float clip[] = {0, 1};
|
||||
const auto kLottieDoClip = 1;
|
||||
if (SkScalarTruncToInt(fClipBlack) == kLottieDoClip) {
|
||||
const auto idx = fOutBlack <= fOutWhite ? 0 : 1;
|
||||
clip[idx] = out_0;
|
||||
}
|
||||
if (SkScalarTruncToInt(fClipWhite) == kLottieDoClip) {
|
||||
const auto idx = fOutBlack <= fOutWhite ? 1 : 0;
|
||||
clip[idx] = out_1;
|
||||
}
|
||||
SkASSERT(clip[0] <= clip[1]);
|
||||
|
||||
auto dIn = in_1 - in_0,
|
||||
dOut = out_1 - out_0;
|
||||
|
||||
if (SkScalarNearlyZero(dIn)) {
|
||||
// Degenerate dIn == 0 makes the arithmetic below explode.
|
||||
//
|
||||
// We could specialize the builder to deal with that case, or we could just
|
||||
// nudge by epsilon to make it all work. The latter approach is simpler
|
||||
// and doesn't have any noticeable downsides.
|
||||
//
|
||||
// Also nudge in_0 towards 0.5, in case it was sqashed against an extremity.
|
||||
// This allows for some abrupt transition when the output interval is not
|
||||
// collapsed, and produces results closer to AE.
|
||||
static constexpr auto kEpsilon = 2 * SK_ScalarNearlyZero;
|
||||
dIn += std::copysign(kEpsilon, dIn);
|
||||
in_0 += std::copysign(kEpsilon, .5f - in_0);
|
||||
SkASSERT(!SkScalarNearlyZero(dIn));
|
||||
}
|
||||
|
||||
uint8_t lut[256];
|
||||
|
||||
auto t = -in_0 / dIn,
|
||||
dT = 1 / 255.0f / dIn;
|
||||
|
||||
// TODO: is linear gamma common-enough to warrant a fast path?
|
||||
for (size_t i = 0; i < 256; ++i) {
|
||||
const auto out = out_0 + dOut * std::pow(std::max(t, 0.0f), g);
|
||||
SkASSERT(!SkScalarIsNaN(out));
|
||||
|
||||
lut[i] = static_cast<uint8_t>(std::round(SkTPin(out, clip[0], clip[1]) * 255));
|
||||
|
||||
t += dT;
|
||||
}
|
||||
|
||||
fEffect->setColorFilter(SkTableColorFilter::MakeARGB(
|
||||
channel == kA_Channel ? lut : nullptr,
|
||||
channel == kR_Channel || channel == kRGB_Channel ? lut : nullptr,
|
||||
channel == kG_Channel || channel == kRGB_Channel ? lut : nullptr,
|
||||
channel == kB_Channel || channel == kRGB_Channel ? lut : nullptr
|
||||
));
|
||||
}
|
||||
|
||||
} // namespace skottie
|
||||
|
@ -234,37 +234,6 @@ private:
|
||||
using INHERITED = GradientAdapter;
|
||||
};
|
||||
|
||||
class GradientRampEffectAdapter final : public SkNVRefCnt<GradientRampEffectAdapter> {
|
||||
public:
|
||||
explicit GradientRampEffectAdapter(sk_sp<sksg::RenderNode> child);
|
||||
~GradientRampEffectAdapter();
|
||||
|
||||
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<sksg::ShaderEffect>& root() const { return fRoot; }
|
||||
|
||||
private:
|
||||
enum class InstanceType {
|
||||
kNone,
|
||||
kLinear,
|
||||
kRadial,
|
||||
};
|
||||
|
||||
void apply();
|
||||
|
||||
sk_sp<sksg::ShaderEffect> fRoot;
|
||||
sk_sp<sksg::Gradient> fGradient;
|
||||
InstanceType fInstanceType = InstanceType::kNone;
|
||||
};
|
||||
|
||||
class TrimEffectAdapter final : public SkNVRefCnt<TrimEffectAdapter> {
|
||||
public:
|
||||
explicit TrimEffectAdapter(sk_sp<sksg::TrimEffect>);
|
||||
@ -280,79 +249,6 @@ private:
|
||||
sk_sp<sksg::TrimEffect> fTrimEffect;
|
||||
};
|
||||
|
||||
class DropShadowEffectAdapter final : public SkNVRefCnt<DropShadowEffectAdapter> {
|
||||
public:
|
||||
explicit DropShadowEffectAdapter(sk_sp<sksg::DropShadowImageFilter>);
|
||||
~DropShadowEffectAdapter();
|
||||
|
||||
ADAPTER_PROPERTY(Color , SkColor , SK_ColorBLACK)
|
||||
ADAPTER_PROPERTY(Opacity , SkScalar, 255)
|
||||
ADAPTER_PROPERTY(Direction , SkScalar, 0)
|
||||
ADAPTER_PROPERTY(Distance , SkScalar, 0)
|
||||
ADAPTER_PROPERTY(Softness , SkScalar, 0)
|
||||
ADAPTER_PROPERTY(ShadowOnly, bool , false)
|
||||
|
||||
private:
|
||||
void apply();
|
||||
|
||||
const sk_sp<sksg::DropShadowImageFilter> fDropShadow;
|
||||
};
|
||||
|
||||
class GaussianBlurEffectAdapter final : public SkNVRefCnt<GaussianBlurEffectAdapter> {
|
||||
public:
|
||||
explicit GaussianBlurEffectAdapter(sk_sp<sksg::BlurImageFilter>);
|
||||
~GaussianBlurEffectAdapter();
|
||||
|
||||
// AE/BM model properties. These are all animatable/interpolatable.
|
||||
|
||||
// Controls the blur sigma.
|
||||
ADAPTER_PROPERTY(Blurriness, SkScalar, 0)
|
||||
|
||||
// Enum selecting the blur dimensionality:
|
||||
//
|
||||
// 1 -> horizontal & vertical
|
||||
// 2 -> horizontal
|
||||
// 3 -> vertical
|
||||
//
|
||||
ADAPTER_PROPERTY(Dimensions, SkScalar, 1)
|
||||
|
||||
// Enum selecting edge behavior:
|
||||
//
|
||||
// 0 -> clamp
|
||||
// 1 -> repeat
|
||||
//
|
||||
ADAPTER_PROPERTY(RepeatEdge, SkScalar, 0)
|
||||
|
||||
private:
|
||||
void apply();
|
||||
|
||||
const sk_sp<sksg::BlurImageFilter> fBlur;
|
||||
};
|
||||
|
||||
class LevelsEffectAdapter final : public SkNVRefCnt<LevelsEffectAdapter> {
|
||||
public:
|
||||
explicit LevelsEffectAdapter(sk_sp<sksg::RenderNode> child);
|
||||
~LevelsEffectAdapter();
|
||||
|
||||
// 1: RGB, 2: R, 3: G, 4: B, 5: A
|
||||
ADAPTER_PROPERTY( Channel, SkScalar, 1)
|
||||
ADAPTER_PROPERTY( InBlack, SkScalar, 0)
|
||||
ADAPTER_PROPERTY( InWhite, SkScalar, 1)
|
||||
ADAPTER_PROPERTY( OutBlack, SkScalar, 0)
|
||||
ADAPTER_PROPERTY( OutWhite, SkScalar, 1)
|
||||
ADAPTER_PROPERTY( Gamma, SkScalar, 1)
|
||||
// 1: clip, 2,3: don't clip
|
||||
ADAPTER_PROPERTY(ClipBlack, SkScalar, 1)
|
||||
ADAPTER_PROPERTY(ClipWhite, SkScalar, 1)
|
||||
|
||||
const sk_sp<sksg::ExternalColorFilter>& root() const { return fEffect; }
|
||||
|
||||
private:
|
||||
void apply();
|
||||
|
||||
sk_sp<sksg::ExternalColorFilter> fEffect;
|
||||
};
|
||||
|
||||
} // namespace skottie
|
||||
|
||||
#endif // SkottieAdapter_DEFINED
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include "modules/skottie/src/SkottieAdapter.h"
|
||||
#include "modules/skottie/src/SkottieJson.h"
|
||||
#include "modules/skottie/src/SkottieValue.h"
|
||||
#include "modules/skottie/src/effects/Effects.h"
|
||||
#include "modules/sksg/include/SkSGClipEffect.h"
|
||||
#include "modules/sksg/include/SkSGDraw.h"
|
||||
#include "modules/sksg/include/SkSGGroup.h"
|
||||
@ -625,7 +626,7 @@ sk_sp<sksg::RenderNode> AnimationBuilder::attachLayer(const skjson::ObjectValue*
|
||||
|
||||
// Optional layer effects.
|
||||
if (const skjson::ArrayValue* jeffects = (*jlayer)["ef"]) {
|
||||
layer = this->attachLayerEffects(*jeffects, &layer_animators, std::move(layer));
|
||||
layer = EffectBuilder(this, &layer_animators).attachEffects(*jeffects, std::move(layer));
|
||||
}
|
||||
|
||||
// Attach the transform after effects, when needed.
|
||||
|
@ -1,485 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 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/SkottieJson.h"
|
||||
#include "modules/skottie/src/SkottieValue.h"
|
||||
#include "modules/sksg/include/SkSGColorFilter.h"
|
||||
#include "modules/sksg/include/SkSGPaint.h"
|
||||
#include "modules/sksg/include/SkSGRenderEffect.h"
|
||||
#include "src/utils/SkJSON.h"
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
sk_sp<sksg::RenderNode> AttachGradientLayerEffect(const skjson::ArrayValue& jprops,
|
||||
const AnimationBuilder* abuilder,
|
||||
AnimatorScope* ascope,
|
||||
sk_sp<sksg::RenderNode> 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,
|
||||
|
||||
kMax_Index = kBlendRatio_Index,
|
||||
};
|
||||
|
||||
if (jprops.size() <= kMax_Index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const skjson::ObjectValue* p0 = jprops[ kStartPoint_Index];
|
||||
const skjson::ObjectValue* p1 = jprops[ kEndPoint_Index];
|
||||
const skjson::ObjectValue* c0 = jprops[ kStartColor_Index];
|
||||
const skjson::ObjectValue* c1 = jprops[ kEndColor_Index];
|
||||
const skjson::ObjectValue* sh = jprops[ kRampShape_Index];
|
||||
const skjson::ObjectValue* bl = jprops[ kBlendRatio_Index];
|
||||
const skjson::ObjectValue* sc = jprops[kRampScatter_Index];
|
||||
|
||||
if (!p0 || !p1 || !c0 || !c1 || !sh || !bl || !sc) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto adapter = sk_make_sp<GradientRampEffectAdapter>(std::move(layer));
|
||||
|
||||
abuilder->bindProperty<VectorValue>((*p0)["v"], ascope,
|
||||
[adapter](const VectorValue& p0) {
|
||||
adapter->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(p0));
|
||||
});
|
||||
abuilder->bindProperty<VectorValue>((*p1)["v"], ascope,
|
||||
[adapter](const VectorValue& p1) {
|
||||
adapter->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(p1));
|
||||
});
|
||||
abuilder->bindProperty<VectorValue>((*c0)["v"], ascope,
|
||||
[adapter](const VectorValue& c0) {
|
||||
adapter->setStartColor(ValueTraits<VectorValue>::As<SkColor>(c0));
|
||||
});
|
||||
abuilder->bindProperty<VectorValue>((*c1)["v"], ascope,
|
||||
[adapter](const VectorValue& c1) {
|
||||
adapter->setEndColor(ValueTraits<VectorValue>::As<SkColor>(c1));
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*sh)["v"], ascope,
|
||||
[adapter](const ScalarValue& shape) {
|
||||
adapter->setShape(shape);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*sh)["v"], ascope,
|
||||
[adapter](const ScalarValue& blend) {
|
||||
adapter->setBlend(blend);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*sc)["v"], ascope,
|
||||
[adapter](const ScalarValue& scatter) {
|
||||
adapter->setScatter(scatter);
|
||||
});
|
||||
|
||||
return adapter->root();
|
||||
}
|
||||
|
||||
sk_sp<sksg::RenderNode> AttachTintLayerEffect(const skjson::ArrayValue& jprops,
|
||||
const AnimationBuilder* abuilder,
|
||||
AnimatorScope* ascope,
|
||||
sk_sp<sksg::RenderNode> layer) {
|
||||
enum : size_t {
|
||||
kMapBlackTo_Index = 0,
|
||||
kMapWhiteTo_Index = 1,
|
||||
kAmount_Index = 2,
|
||||
// kOpacity_Index = 3, // currently unused (not exported)
|
||||
|
||||
kMax_Index = kAmount_Index,
|
||||
};
|
||||
|
||||
if (jprops.size() <= kMax_Index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const skjson::ObjectValue* color0_prop = jprops[kMapBlackTo_Index];
|
||||
const skjson::ObjectValue* color1_prop = jprops[kMapWhiteTo_Index];
|
||||
const skjson::ObjectValue* amount_prop = jprops[ kAmount_Index];
|
||||
|
||||
if (!color0_prop || !color1_prop || !amount_prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto tint_node =
|
||||
sksg::GradientColorFilter::Make(std::move(layer),
|
||||
abuilder->attachColor(*color0_prop, ascope, "v"),
|
||||
abuilder->attachColor(*color1_prop, ascope, "v"));
|
||||
if (!tint_node) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
abuilder->bindProperty<ScalarValue>((*amount_prop)["v"], ascope,
|
||||
[tint_node](const ScalarValue& w) {
|
||||
tint_node->setWeight(w / 100); // 100-based
|
||||
});
|
||||
|
||||
return std::move(tint_node);
|
||||
}
|
||||
|
||||
sk_sp<sksg::RenderNode> AttachTritoneLayerEffect(const skjson::ArrayValue& jprops,
|
||||
const AnimationBuilder* abuilder,
|
||||
AnimatorScope* ascope,
|
||||
sk_sp<sksg::RenderNode> layer) {
|
||||
enum : size_t {
|
||||
kHiColor_Index = 0,
|
||||
kMiColor_Index = 1,
|
||||
kLoColor_Index = 2,
|
||||
kBlendAmount_Index = 3,
|
||||
|
||||
kMax_Index = kBlendAmount_Index,
|
||||
};
|
||||
|
||||
if (jprops.size() <= kMax_Index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const skjson::ObjectValue* hicolor_prop = jprops[ kHiColor_Index];
|
||||
const skjson::ObjectValue* micolor_prop = jprops[ kMiColor_Index];
|
||||
const skjson::ObjectValue* locolor_prop = jprops[ kLoColor_Index];
|
||||
const skjson::ObjectValue* blend_prop = jprops[kBlendAmount_Index];
|
||||
|
||||
if (!hicolor_prop || !micolor_prop || !locolor_prop || !blend_prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto tritone_node =
|
||||
sksg::GradientColorFilter::Make(std::move(layer), {
|
||||
abuilder->attachColor(*locolor_prop, ascope, "v"),
|
||||
abuilder->attachColor(*micolor_prop, ascope, "v"),
|
||||
abuilder->attachColor(*hicolor_prop, ascope, "v") });
|
||||
if (!tritone_node) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
abuilder->bindProperty<ScalarValue>((*blend_prop)["v"], ascope,
|
||||
[tritone_node](const ScalarValue& w) {
|
||||
tritone_node->setWeight((100 - w) / 100); // 100-based, inverted (!?).
|
||||
});
|
||||
|
||||
return std::move(tritone_node);
|
||||
}
|
||||
|
||||
sk_sp<sksg::RenderNode> AttachFillLayerEffect(const skjson::ArrayValue& jprops,
|
||||
const AnimationBuilder* abuilder,
|
||||
AnimatorScope* ascope,
|
||||
sk_sp<sksg::RenderNode> layer) {
|
||||
enum : size_t {
|
||||
kFillMask_Index = 0,
|
||||
kAllMasks_Index = 1,
|
||||
kColor_Index = 2,
|
||||
kInvert_Index = 3,
|
||||
kHFeather_Index = 4,
|
||||
kVFeather_Index = 5,
|
||||
kOpacity_Index = 6,
|
||||
|
||||
kMax_Index = kOpacity_Index,
|
||||
};
|
||||
|
||||
if (jprops.size() <= kMax_Index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const skjson::ObjectValue* color_prop = jprops[ kColor_Index];
|
||||
const skjson::ObjectValue* opacity_prop = jprops[kOpacity_Index];
|
||||
if (!color_prop || !opacity_prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sk_sp<sksg::Color> color_node = abuilder->attachColor(*color_prop, ascope, "v");
|
||||
if (!color_node) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
abuilder->bindProperty<ScalarValue>((*opacity_prop)["v"], ascope,
|
||||
[color_node](const ScalarValue& o) {
|
||||
const auto c = color_node->getColor();
|
||||
const auto a = sk_float_round2int_no_saturate(SkTPin(o, 0.0f, 1.0f) * 255);
|
||||
color_node->setColor(SkColorSetA(c, a));
|
||||
});
|
||||
|
||||
return sksg::ModeColorFilter::Make(std::move(layer),
|
||||
std::move(color_node),
|
||||
SkBlendMode::kSrcIn);
|
||||
}
|
||||
|
||||
sk_sp<sksg::RenderNode> AttachDropShadowLayerEffect(const skjson::ArrayValue& jprops,
|
||||
const AnimationBuilder* abuilder,
|
||||
AnimatorScope* ascope,
|
||||
sk_sp<sksg::RenderNode> layer) {
|
||||
enum : size_t {
|
||||
kShadowColor_Index = 0,
|
||||
kOpacity_Index = 1,
|
||||
kDirection_Index = 2,
|
||||
kDistance_Index = 3,
|
||||
kSoftness_Index = 4,
|
||||
kShadowOnly_Index = 5,
|
||||
|
||||
kMax_Index = kShadowOnly_Index,
|
||||
};
|
||||
|
||||
if (jprops.size() <= kMax_Index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const skjson::ObjectValue* color_prop = jprops[kShadowColor_Index];
|
||||
const skjson::ObjectValue* opacity_prop = jprops[ kOpacity_Index];
|
||||
const skjson::ObjectValue* direction_prop = jprops[ kDirection_Index];
|
||||
const skjson::ObjectValue* distance_prop = jprops[ kDistance_Index];
|
||||
const skjson::ObjectValue* softness_prop = jprops[ kSoftness_Index];
|
||||
const skjson::ObjectValue* shadow_only_prop = jprops[ kShadowOnly_Index];
|
||||
|
||||
if (!color_prop ||
|
||||
!opacity_prop ||
|
||||
!direction_prop ||
|
||||
!distance_prop ||
|
||||
!softness_prop ||
|
||||
!shadow_only_prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto shadow_effect = sksg::DropShadowImageFilter::Make();
|
||||
auto shadow_adapter = sk_make_sp<DropShadowEffectAdapter>(shadow_effect);
|
||||
|
||||
abuilder->bindProperty<VectorValue>((*color_prop)["v"], ascope,
|
||||
[shadow_adapter](const VectorValue& c) {
|
||||
shadow_adapter->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*opacity_prop)["v"], ascope,
|
||||
[shadow_adapter](const ScalarValue& o) {
|
||||
shadow_adapter->setOpacity(o);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*direction_prop)["v"], ascope,
|
||||
[shadow_adapter](const ScalarValue& d) {
|
||||
shadow_adapter->setDirection(d);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*distance_prop)["v"], ascope,
|
||||
[shadow_adapter](const ScalarValue& d) {
|
||||
shadow_adapter->setDistance(d);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*softness_prop)["v"], ascope,
|
||||
[shadow_adapter](const ScalarValue& s) {
|
||||
shadow_adapter->setSoftness(s);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*shadow_only_prop)["v"], ascope,
|
||||
[shadow_adapter](const ScalarValue& s) {
|
||||
shadow_adapter->setShadowOnly(SkToBool(s));
|
||||
});
|
||||
|
||||
return sksg::ImageFilterEffect::Make(std::move(layer), std::move(shadow_effect));
|
||||
}
|
||||
|
||||
sk_sp<sksg::RenderNode> AttachGaussianBlurLayerEffect(const skjson::ArrayValue& jprops,
|
||||
const AnimationBuilder* abuilder,
|
||||
AnimatorScope* ascope,
|
||||
sk_sp<sksg::RenderNode> layer) {
|
||||
enum : size_t {
|
||||
kBlurriness_Index = 0,
|
||||
kDimensions_Index = 1,
|
||||
kRepeatEdge_Index = 2,
|
||||
|
||||
kMax_Index = kRepeatEdge_Index,
|
||||
};
|
||||
|
||||
if (jprops.size() <= kMax_Index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const skjson::ObjectValue* blurriness_prop = jprops[kBlurriness_Index];
|
||||
const skjson::ObjectValue* dimensions_prop = jprops[kDimensions_Index];
|
||||
const skjson::ObjectValue* repeatedge_prop = jprops[kRepeatEdge_Index];
|
||||
|
||||
if (!blurriness_prop || !dimensions_prop || !repeatedge_prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto blur_effect = sksg::BlurImageFilter::Make();
|
||||
auto blur_addapter = sk_make_sp<GaussianBlurEffectAdapter>(blur_effect);
|
||||
|
||||
abuilder->bindProperty<ScalarValue>((*blurriness_prop)["v"], ascope,
|
||||
[blur_addapter](const ScalarValue& b) {
|
||||
blur_addapter->setBlurriness(b);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*dimensions_prop)["v"], ascope,
|
||||
[blur_addapter](const ScalarValue& d) {
|
||||
blur_addapter->setDimensions(d);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*repeatedge_prop)["v"], ascope,
|
||||
[blur_addapter](const ScalarValue& r) {
|
||||
blur_addapter->setRepeatEdge(r);
|
||||
});
|
||||
|
||||
return sksg::ImageFilterEffect::Make(std::move(layer), std::move(blur_effect));
|
||||
}
|
||||
|
||||
sk_sp<sksg::RenderNode> AttachLevelsLayerEffect(const skjson::ArrayValue& jprops,
|
||||
const AnimationBuilder* abuilder,
|
||||
AnimatorScope* ascope,
|
||||
sk_sp<sksg::RenderNode> layer) {
|
||||
enum : size_t {
|
||||
kChannel_Index = 0,
|
||||
// ??? = 1,
|
||||
kInputBlack_Index = 2,
|
||||
kInputWhite_Index = 3,
|
||||
kGamma_Index = 4,
|
||||
kOutputBlack_Index = 5,
|
||||
kOutputWhite_Index = 6,
|
||||
kClipToOutBlack_Index = 7,
|
||||
kClipToOutWhite_Index = 8,
|
||||
|
||||
kMax_Index = kClipToOutWhite_Index,
|
||||
};
|
||||
|
||||
if (jprops.size() <= kMax_Index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const skjson::ObjectValue* channel_prop = jprops[ kChannel_Index];
|
||||
const skjson::ObjectValue* iblack_prop = jprops[ kInputBlack_Index];
|
||||
const skjson::ObjectValue* iwhite_prop = jprops[ kInputWhite_Index];
|
||||
const skjson::ObjectValue* gamma_prop = jprops[ kGamma_Index];
|
||||
const skjson::ObjectValue* oblack_prop = jprops[ kOutputBlack_Index];
|
||||
const skjson::ObjectValue* owhite_prop = jprops[ kOutputWhite_Index];
|
||||
const skjson::ObjectValue* clip_black_prop = jprops[kClipToOutBlack_Index];
|
||||
const skjson::ObjectValue* clip_white_prop = jprops[kClipToOutWhite_Index];
|
||||
|
||||
if (!channel_prop || !iblack_prop || !iwhite_prop || !gamma_prop ||
|
||||
!oblack_prop || !owhite_prop || !clip_black_prop || !clip_white_prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto adapter = sk_make_sp<LevelsEffectAdapter>(std::move(layer));
|
||||
|
||||
abuilder->bindProperty<ScalarValue>((*channel_prop)["v"], ascope,
|
||||
[adapter](const ScalarValue& channel) {
|
||||
adapter->setChannel(channel);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*iblack_prop)["v"], ascope,
|
||||
[adapter](const ScalarValue& ib) {
|
||||
adapter->setInBlack(ib);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*iwhite_prop)["v"], ascope,
|
||||
[adapter](const ScalarValue& iw) {
|
||||
adapter->setInWhite(iw);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*oblack_prop)["v"], ascope,
|
||||
[adapter](const ScalarValue& ob) {
|
||||
adapter->setOutBlack(ob);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*owhite_prop)["v"], ascope,
|
||||
[adapter](const ScalarValue& ow) {
|
||||
adapter->setOutWhite(ow);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*gamma_prop)["v"], ascope,
|
||||
[adapter](const ScalarValue& g) {
|
||||
adapter->setGamma(g);
|
||||
});
|
||||
|
||||
abuilder->bindProperty<ScalarValue>((*clip_black_prop)["v"], ascope,
|
||||
[adapter](const ScalarValue& cb) {
|
||||
adapter->setClipBlack(cb);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>((*clip_white_prop)["v"], ascope,
|
||||
[adapter](const ScalarValue& cw) {
|
||||
adapter->setClipWhite(cw);
|
||||
});
|
||||
|
||||
return adapter->root();
|
||||
}
|
||||
|
||||
using EffectBuilderT = sk_sp<sksg::RenderNode> (*)(const skjson::ArrayValue&,
|
||||
const AnimationBuilder*,
|
||||
AnimatorScope*,
|
||||
sk_sp<sksg::RenderNode>);
|
||||
|
||||
EffectBuilderT FindEffectBuilder(const AnimationBuilder* abuilder,
|
||||
const skjson::ObjectValue& jeffect) {
|
||||
// First, try assigned types.
|
||||
enum : int32_t {
|
||||
kTint_Effect = 20,
|
||||
kFill_Effect = 21,
|
||||
kTritone_Effect = 23,
|
||||
kDropShadow_Effect = 25,
|
||||
kGaussianBlur_Effect = 29,
|
||||
};
|
||||
|
||||
const auto ty = ParseDefault<int>(jeffect["ty"], -1);
|
||||
|
||||
switch (ty) {
|
||||
case kTint_Effect:
|
||||
return AttachTintLayerEffect;
|
||||
case kFill_Effect:
|
||||
return AttachFillLayerEffect;
|
||||
case kTritone_Effect:
|
||||
return AttachTritoneLayerEffect;
|
||||
case kDropShadow_Effect:
|
||||
return AttachDropShadowLayerEffect;
|
||||
case kGaussianBlur_Effect:
|
||||
return AttachGaussianBlurLayerEffect;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Some effects don't have an assigned type, but the data is still present.
|
||||
// Try a name-based lookup.
|
||||
|
||||
if (const skjson::StringValue* mn = jeffect["mn"]) {
|
||||
if (!strcmp(mn->begin(), "ADBE Ramp")) {
|
||||
return AttachGradientLayerEffect;
|
||||
}
|
||||
if (!strcmp(mn->begin(), "ADBE Easy Levels2")) {
|
||||
return AttachLevelsLayerEffect;
|
||||
}
|
||||
if (!strcmp(mn->begin(), "ADBE Geometry2")) {
|
||||
return AttachTransformEffect;
|
||||
}
|
||||
}
|
||||
|
||||
abuilder->log(Logger::Level::kWarning, nullptr, "Unsupported layer effect type: %d.", ty);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
sk_sp<sksg::RenderNode> AnimationBuilder::attachLayerEffects(const skjson::ArrayValue& jeffects,
|
||||
AnimatorScope* ascope,
|
||||
sk_sp<sksg::RenderNode> layer) const {
|
||||
if (!layer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const skjson::ObjectValue* jeffect : jeffects) {
|
||||
if (!jeffect) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto builder = FindEffectBuilder(this, *jeffect);
|
||||
const skjson::ArrayValue* jprops = (*jeffect)["ef"];
|
||||
if (!builder || !jprops) {
|
||||
continue;
|
||||
}
|
||||
|
||||
layer = builder(*jprops, this, ascope, std::move(layer));
|
||||
|
||||
if (!layer) {
|
||||
this->log(Logger::Level::kError, jeffect, "Invalid layer effect.");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace skottie
|
@ -101,8 +101,6 @@ private:
|
||||
|
||||
sk_sp<sksg::RenderNode> attachComposition(const skjson::ObjectValue&, AnimatorScope*) const;
|
||||
sk_sp<sksg::RenderNode> attachLayer(const skjson::ObjectValue*, AttachLayerContext*) const;
|
||||
sk_sp<sksg::RenderNode> attachLayerEffects(const skjson::ArrayValue& jeffects, AnimatorScope*,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
|
||||
sk_sp<sksg::RenderNode> attachBlendMode(const skjson::ObjectValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
|
101
modules/skottie/src/effects/DropShadowEffect.cpp
Normal file
101
modules/skottie/src/effects/DropShadowEffect.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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/SkSGRenderEffect.h"
|
||||
#include "src/utils/SkJSON.h"
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
class DropShadowAdapter final : public SkNVRefCnt<DropShadowAdapter> {
|
||||
public:
|
||||
explicit DropShadowAdapter(sk_sp<sksg::DropShadowImageFilter> dropShadow)
|
||||
: fDropShadow(std::move(dropShadow)) {
|
||||
SkASSERT(fDropShadow);
|
||||
}
|
||||
|
||||
ADAPTER_PROPERTY(Color , SkColor , SK_ColorBLACK)
|
||||
ADAPTER_PROPERTY(Opacity , SkScalar, 255)
|
||||
ADAPTER_PROPERTY(Direction , SkScalar, 0)
|
||||
ADAPTER_PROPERTY(Distance , SkScalar, 0)
|
||||
ADAPTER_PROPERTY(Softness , SkScalar, 0)
|
||||
ADAPTER_PROPERTY(ShadowOnly, bool , false)
|
||||
|
||||
private:
|
||||
void apply() {
|
||||
// fColor -> RGB, fOpacity -> A
|
||||
fDropShadow->setColor(SkColorSetA(fColor, SkTPin(SkScalarRoundToInt(fOpacity), 0, 255)));
|
||||
|
||||
// The offset is specified in terms of a bearing angle + distance.
|
||||
SkScalar rad = SkDegreesToRadians(90 - fDirection);
|
||||
fDropShadow->setOffset(SkVector::Make( fDistance * SkScalarCos(rad),
|
||||
-fDistance * SkScalarSin(rad)));
|
||||
|
||||
// Close enough to AE.
|
||||
static constexpr SkScalar kSoftnessToSigmaFactor = 0.3f;
|
||||
const auto sigma = fSoftness * kSoftnessToSigmaFactor;
|
||||
fDropShadow->setSigma(SkVector::Make(sigma, sigma));
|
||||
|
||||
fDropShadow->setMode(fShadowOnly ? sksg::DropShadowImageFilter::Mode::kShadowOnly
|
||||
: sksg::DropShadowImageFilter::Mode::kShadowAndForeground);
|
||||
}
|
||||
|
||||
const sk_sp<sksg::DropShadowImageFilter> fDropShadow;
|
||||
};
|
||||
|
||||
} // anonymous ns
|
||||
|
||||
sk_sp<sksg::RenderNode> EffectBuilder::attachDropShadowEffect(const skjson::ArrayValue& jprops,
|
||||
sk_sp<sksg::RenderNode> layer) const {
|
||||
enum : size_t {
|
||||
kShadowColor_Index = 0,
|
||||
kOpacity_Index = 1,
|
||||
kDirection_Index = 2,
|
||||
kDistance_Index = 3,
|
||||
kSoftness_Index = 4,
|
||||
kShadowOnly_Index = 5,
|
||||
};
|
||||
|
||||
auto shadow_effect = sksg::DropShadowImageFilter::Make();
|
||||
auto shadow_adapter = sk_make_sp<DropShadowAdapter>(shadow_effect);
|
||||
|
||||
fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kShadowColor_Index), fScope,
|
||||
[shadow_adapter](const VectorValue& c) {
|
||||
shadow_adapter->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kOpacity_Index), fScope,
|
||||
[shadow_adapter](const ScalarValue& o) {
|
||||
shadow_adapter->setOpacity(o);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kDirection_Index), fScope,
|
||||
[shadow_adapter](const ScalarValue& d) {
|
||||
shadow_adapter->setDirection(d);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kDistance_Index), fScope,
|
||||
[shadow_adapter](const ScalarValue& d) {
|
||||
shadow_adapter->setDistance(d);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kSoftness_Index), fScope,
|
||||
[shadow_adapter](const ScalarValue& s) {
|
||||
shadow_adapter->setSoftness(s);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kShadowOnly_Index), fScope,
|
||||
[shadow_adapter](const ScalarValue& s) {
|
||||
shadow_adapter->setShadowOnly(SkToBool(s));
|
||||
});
|
||||
|
||||
return sksg::ImageFilterEffect::Make(std::move(layer), std::move(shadow_effect));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace skottie
|
@ -7,11 +7,96 @@
|
||||
|
||||
#include "modules/skottie/src/effects/Effects.h"
|
||||
|
||||
#include "modules/skottie/src/SkottieJson.h"
|
||||
#include "modules/sksg/include/SkSGRenderNode.h"
|
||||
#include "src/utils/SkJSON.h"
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
EffectBuilder::EffectBuilder(const AnimationBuilder* abuilder, AnimatorScope* ascope)
|
||||
: fBuilder(abuilder)
|
||||
, fScope(ascope) {}
|
||||
|
||||
EffectBuilder::EffectBuilderT EffectBuilder::findBuilder(const skjson::ObjectValue& jeffect) const {
|
||||
// First, try assigned types.
|
||||
enum : int32_t {
|
||||
kTint_Effect = 20,
|
||||
kFill_Effect = 21,
|
||||
kTritone_Effect = 23,
|
||||
kDropShadow_Effect = 25,
|
||||
kGaussianBlur_Effect = 29,
|
||||
};
|
||||
|
||||
const auto ty = ParseDefault<int>(jeffect["ty"], -1);
|
||||
|
||||
switch (ty) {
|
||||
case kTint_Effect:
|
||||
return &EffectBuilder::attachTintEffect;
|
||||
case kFill_Effect:
|
||||
return &EffectBuilder::attachFillEffect;
|
||||
case kTritone_Effect:
|
||||
return &EffectBuilder::attachTritoneEffect;
|
||||
case kDropShadow_Effect:
|
||||
return &EffectBuilder::attachDropShadowEffect;
|
||||
case kGaussianBlur_Effect:
|
||||
return &EffectBuilder::attachGaussianBlurEffect;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Some effects don't have an assigned type, but the data is still present.
|
||||
// Try a name-based lookup.
|
||||
|
||||
static constexpr char kGradientEffectMN[] = "ADBE Ramp",
|
||||
kLevelsEffectMN[] = "ADBE Easy Levels2",
|
||||
kTransformEffectMN[] = "ADBE Geometry2";
|
||||
|
||||
if (const skjson::StringValue* mn = jeffect["mn"]) {
|
||||
if (!strcmp(mn->begin(), kGradientEffectMN)) {
|
||||
return &EffectBuilder::attachGradientEffect;
|
||||
}
|
||||
if (!strcmp(mn->begin(), kLevelsEffectMN)) {
|
||||
return &EffectBuilder::attachLevelsEffect;
|
||||
}
|
||||
if (!strcmp(mn->begin(), kTransformEffectMN)) {
|
||||
return &EffectBuilder::attachTransformEffect;
|
||||
}
|
||||
}
|
||||
|
||||
fBuilder->log(Logger::Level::kWarning, nullptr, "Unsupported layer effect type: %d.", ty);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
sk_sp<sksg::RenderNode> EffectBuilder::attachEffects(const skjson::ArrayValue& jeffects,
|
||||
sk_sp<sksg::RenderNode> layer) const {
|
||||
if (!layer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (const skjson::ObjectValue* jeffect : jeffects) {
|
||||
if (!jeffect) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto builder = this->findBuilder(*jeffect);
|
||||
const skjson::ArrayValue* jprops = (*jeffect)["ef"];
|
||||
if (!builder || !jprops) {
|
||||
continue;
|
||||
}
|
||||
|
||||
layer = (this->*builder)(*jprops, std::move(layer));
|
||||
|
||||
if (!layer) {
|
||||
fBuilder->log(Logger::Level::kError, jeffect, "Invalid layer effect.");
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return layer;
|
||||
}
|
||||
|
||||
const skjson::Value& EffectBuilder::GetPropValue(const skjson::ArrayValue& jprops,
|
||||
size_t prop_index) {
|
||||
static skjson::NullValue kNull;
|
||||
|
@ -13,20 +13,42 @@
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
class AnimationBuilder;
|
||||
|
||||
// TODO: relocate SkottieLayerEffect builder logic here.
|
||||
class EffectBuilder final : public SkNoncopyable {
|
||||
public:
|
||||
EffectBuilder(const AnimationBuilder*, AnimatorScope*);
|
||||
|
||||
sk_sp<sksg::RenderNode> attachEffects(const skjson::ArrayValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
|
||||
private:
|
||||
using EffectBuilderT = sk_sp<sksg::RenderNode>(EffectBuilder::*)(const skjson::ArrayValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
|
||||
sk_sp<sksg::RenderNode> attachTintEffect (const skjson::ArrayValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
sk_sp<sksg::RenderNode> attachFillEffect (const skjson::ArrayValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
sk_sp<sksg::RenderNode> attachTritoneEffect (const skjson::ArrayValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
sk_sp<sksg::RenderNode> attachDropShadowEffect (const skjson::ArrayValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
sk_sp<sksg::RenderNode> attachGaussianBlurEffect(const skjson::ArrayValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
sk_sp<sksg::RenderNode> attachGradientEffect (const skjson::ArrayValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
sk_sp<sksg::RenderNode> attachLevelsEffect (const skjson::ArrayValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
sk_sp<sksg::RenderNode> attachTransformEffect (const skjson::ArrayValue&,
|
||||
sk_sp<sksg::RenderNode>) const;
|
||||
|
||||
EffectBuilderT findBuilder(const skjson::ObjectValue&) const;
|
||||
|
||||
static const skjson::Value& GetPropValue(const skjson::ArrayValue& jprops, size_t prop_index);
|
||||
|
||||
const AnimationBuilder* fBuilder;
|
||||
AnimatorScope* fScope;
|
||||
};
|
||||
|
||||
sk_sp<sksg::RenderNode> AttachTransformEffect(const skjson::ArrayValue&,
|
||||
const AnimationBuilder*,
|
||||
AnimatorScope*,
|
||||
sk_sp<sksg::RenderNode>);
|
||||
|
||||
|
||||
} // namespace internal
|
||||
} // namespace skottie
|
||||
|
||||
|
59
modules/skottie/src/effects/FillEffect.cpp
Normal file
59
modules/skottie/src/effects/FillEffect.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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/SkSGColorFilter.h"
|
||||
#include "modules/sksg/include/SkSGPaint.h"
|
||||
#include "src/utils/SkJSON.h"
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
sk_sp<sksg::RenderNode> EffectBuilder::attachFillEffect(const skjson::ArrayValue& jprops,
|
||||
sk_sp<sksg::RenderNode> layer) const {
|
||||
enum : size_t {
|
||||
kFillMask_Index = 0,
|
||||
kAllMasks_Index = 1,
|
||||
kColor_Index = 2,
|
||||
kInvert_Index = 3,
|
||||
kHFeather_Index = 4,
|
||||
kVFeather_Index = 5,
|
||||
kOpacity_Index = 6,
|
||||
|
||||
kMax_Index = kOpacity_Index,
|
||||
};
|
||||
|
||||
if (jprops.size() <= kMax_Index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const skjson::ObjectValue* color_prop = jprops[ kColor_Index];
|
||||
const skjson::ObjectValue* opacity_prop = jprops[kOpacity_Index];
|
||||
if (!color_prop || !opacity_prop) {
|
||||
return nullptr;
|
||||
}
|
||||
sk_sp<sksg::Color> color_node = fBuilder->attachColor(*color_prop, fScope, "v");
|
||||
if (!color_node) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fBuilder->bindProperty<ScalarValue>((*opacity_prop)["v"], fScope,
|
||||
[color_node](const ScalarValue& o) {
|
||||
const auto c = color_node->getColor();
|
||||
const auto a = sk_float_round2int_no_saturate(SkTPin(o, 0.0f, 1.0f) * 255);
|
||||
color_node->setColor(SkColorSetA(c, a));
|
||||
});
|
||||
|
||||
return sksg::ModeColorFilter::Make(std::move(layer),
|
||||
std::move(color_node),
|
||||
SkBlendMode::kSrcIn);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace skottie
|
110
modules/skottie/src/effects/GaussianBlurEffect.cpp
Normal file
110
modules/skottie/src/effects/GaussianBlurEffect.cpp
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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 "include/effects/SkBlurImageFilter.h"
|
||||
#include "modules/skottie/src/SkottieAdapter.h"
|
||||
#include "modules/skottie/src/SkottieValue.h"
|
||||
#include "modules/sksg/include/SkSGRenderEffect.h"
|
||||
#include "src/utils/SkJSON.h"
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
class GaussianBlurEffectAdapter final : public SkNVRefCnt<GaussianBlurEffectAdapter> {
|
||||
public:
|
||||
explicit GaussianBlurEffectAdapter(sk_sp<sksg::BlurImageFilter> blur)
|
||||
: fBlur(std::move(blur)) {
|
||||
SkASSERT(fBlur);
|
||||
}
|
||||
|
||||
// AE/BM model properties. These are all animatable/interpolatable.
|
||||
|
||||
// Controls the blur sigma.
|
||||
ADAPTER_PROPERTY(Blurriness, SkScalar, 0)
|
||||
|
||||
// Enum selecting the blur dimensionality:
|
||||
//
|
||||
// 1 -> horizontal & vertical
|
||||
// 2 -> horizontal
|
||||
// 3 -> vertical
|
||||
//
|
||||
ADAPTER_PROPERTY(Dimensions, SkScalar, 1)
|
||||
|
||||
// Enum selecting edge behavior:
|
||||
//
|
||||
// 0 -> clamp
|
||||
// 1 -> repeat
|
||||
//
|
||||
ADAPTER_PROPERTY(RepeatEdge, SkScalar, 0)
|
||||
|
||||
private:
|
||||
void apply() {
|
||||
static constexpr SkVector kDimensionsMap[] = {
|
||||
{ 1, 1 }, // 1 -> horizontal and vertical
|
||||
{ 1, 0 }, // 2 -> horizontal
|
||||
{ 0, 1 }, // 3 -> vertical
|
||||
};
|
||||
|
||||
const auto dim_index = SkTPin<size_t>(static_cast<size_t>(fDimensions),
|
||||
1, SK_ARRAY_COUNT(kDimensionsMap)) - 1;
|
||||
|
||||
// Close enough to AE.
|
||||
static constexpr SkScalar kBlurrinessToSigmaFactor = 0.3f;
|
||||
const auto sigma = fBlurriness * kBlurrinessToSigmaFactor;
|
||||
|
||||
fBlur->setSigma({ sigma * kDimensionsMap[dim_index].x(),
|
||||
sigma * kDimensionsMap[dim_index].y() });
|
||||
|
||||
static constexpr SkBlurImageFilter::TileMode kRepeatEdgeMap[] = {
|
||||
SkBlurImageFilter::kClampToBlack_TileMode, // 0 -> repeat edge pixels: off
|
||||
SkBlurImageFilter:: kClamp_TileMode, // 1 -> repeat edge pixels: on
|
||||
};
|
||||
|
||||
const auto repeat_index = SkTPin<size_t>(static_cast<size_t>(fRepeatEdge),
|
||||
0, SK_ARRAY_COUNT(kRepeatEdgeMap) - 1);
|
||||
fBlur->setTileMode(kRepeatEdgeMap[repeat_index]);
|
||||
}
|
||||
|
||||
const sk_sp<sksg::BlurImageFilter> fBlur;
|
||||
};
|
||||
|
||||
} // anonymous ns
|
||||
|
||||
sk_sp<sksg::RenderNode> EffectBuilder::attachGaussianBlurEffect(
|
||||
const skjson::ArrayValue& jprops,
|
||||
sk_sp<sksg::RenderNode> layer) const {
|
||||
enum : size_t {
|
||||
kBlurriness_Index = 0,
|
||||
kDimensions_Index = 1,
|
||||
kRepeatEdge_Index = 2,
|
||||
};
|
||||
|
||||
auto blur_effect = sksg::BlurImageFilter::Make();
|
||||
auto blur_addapter = sk_make_sp<GaussianBlurEffectAdapter>(blur_effect);
|
||||
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kBlurriness_Index), fScope,
|
||||
[blur_addapter](const ScalarValue& b) {
|
||||
blur_addapter->setBlurriness(b);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kDimensions_Index), fScope,
|
||||
[blur_addapter](const ScalarValue& d) {
|
||||
blur_addapter->setDimensions(d);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kRepeatEdge_Index), fScope,
|
||||
[blur_addapter](const ScalarValue& r) {
|
||||
blur_addapter->setRepeatEdge(r);
|
||||
});
|
||||
|
||||
return sksg::ImageFilterEffect::Make(std::move(layer), std::move(blur_effect));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace skottie
|
146
modules/skottie/src/effects/GradientEffect.cpp
Normal file
146
modules/skottie/src/effects/GradientEffect.cpp
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* 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<GradientRampEffectAdapter> {
|
||||
public:
|
||||
explicit GradientRampEffectAdapter(sk_sp<sksg::RenderNode> 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<sksg::ShaderEffect>& 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::Gradient>(sksg::LinearGradient::Make())
|
||||
: sk_sp<sksg::Gradient>(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<sksg::LinearGradient*>(fGradient.get());
|
||||
lg->setStartPoint(fStartPoint);
|
||||
lg->setEndPoint(fEndPoint);
|
||||
} else {
|
||||
SkASSERT(instance_type == InstanceType::kRadial);
|
||||
|
||||
auto* rg = static_cast<sksg::RadialGradient*>(fGradient.get());
|
||||
rg->setStartCenter(fStartPoint);
|
||||
rg->setEndCenter(fStartPoint);
|
||||
rg->setEndRadius(SkPoint::Distance(fStartPoint, fEndPoint));
|
||||
}
|
||||
|
||||
// TODO: blend, scatter
|
||||
}
|
||||
|
||||
sk_sp<sksg::ShaderEffect> fRoot;
|
||||
sk_sp<sksg::Gradient> fGradient;
|
||||
InstanceType fInstanceType = InstanceType::kNone;
|
||||
};
|
||||
|
||||
} // anonymous ns
|
||||
|
||||
sk_sp<sksg::RenderNode> EffectBuilder::attachGradientEffect(const skjson::ArrayValue& jprops,
|
||||
sk_sp<sksg::RenderNode> 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<GradientRampEffectAdapter>(std::move(layer));
|
||||
|
||||
fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kStartPoint_Index), fScope,
|
||||
[adapter](const VectorValue& p0) {
|
||||
adapter->setStartPoint(ValueTraits<VectorValue>::As<SkPoint>(p0));
|
||||
});
|
||||
fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kEndPoint_Index), fScope,
|
||||
[adapter](const VectorValue& p1) {
|
||||
adapter->setEndPoint(ValueTraits<VectorValue>::As<SkPoint>(p1));
|
||||
});
|
||||
fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kStartColor_Index), fScope,
|
||||
[adapter](const VectorValue& c0) {
|
||||
adapter->setStartColor(ValueTraits<VectorValue>::As<SkColor>(c0));
|
||||
});
|
||||
fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kEndColor_Index), fScope,
|
||||
[adapter](const VectorValue& c1) {
|
||||
adapter->setEndColor(ValueTraits<VectorValue>::As<SkColor>(c1));
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kRampShape_Index), fScope,
|
||||
[adapter](const ScalarValue& shape) {
|
||||
adapter->setShape(shape);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kBlendRatio_Index), fScope,
|
||||
[adapter](const ScalarValue& blend) {
|
||||
adapter->setBlend(blend);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kRampScatter_Index), fScope,
|
||||
[adapter](const ScalarValue& scatter) {
|
||||
adapter->setScatter(scatter);
|
||||
});
|
||||
|
||||
return adapter->root();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace skottie
|
191
modules/skottie/src/effects/LevelsEffect.cpp
Normal file
191
modules/skottie/src/effects/LevelsEffect.cpp
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* 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 "include/effects/SkTableColorFilter.h"
|
||||
#include "modules/skottie/src/SkottieAdapter.h"
|
||||
#include "modules/skottie/src/SkottieValue.h"
|
||||
#include "modules/sksg/include/SkSGColorFilter.h"
|
||||
#include "src/utils/SkJSON.h"
|
||||
|
||||
#include <cmath>
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
// Levels color correction effect.
|
||||
//
|
||||
// Maps the selected channels from [inBlack...inWhite] to [outBlack, outWhite],
|
||||
// based on a gamma exponent.
|
||||
//
|
||||
// For [i0..i1] -> [o0..o1]:
|
||||
//
|
||||
// c' = o0 + (o1 - o0) * ((c - i0) / (i1 - i0)) ^ G
|
||||
//
|
||||
// The output is optionally clipped to the output range.
|
||||
//
|
||||
// In/out intervals are clampped to [0..1]. Inversion is allowed.
|
||||
|
||||
namespace {
|
||||
|
||||
class LevelsEffectAdapter final : public SkNVRefCnt<LevelsEffectAdapter> {
|
||||
public:
|
||||
explicit LevelsEffectAdapter(sk_sp<sksg::RenderNode> child)
|
||||
: fEffect(sksg::ExternalColorFilter::Make(std::move(child))) {
|
||||
SkASSERT(fEffect);
|
||||
}
|
||||
|
||||
// 1: RGB, 2: R, 3: G, 4: B, 5: A
|
||||
ADAPTER_PROPERTY( Channel, SkScalar, 1)
|
||||
ADAPTER_PROPERTY( InBlack, SkScalar, 0)
|
||||
ADAPTER_PROPERTY( InWhite, SkScalar, 1)
|
||||
ADAPTER_PROPERTY( OutBlack, SkScalar, 0)
|
||||
ADAPTER_PROPERTY( OutWhite, SkScalar, 1)
|
||||
ADAPTER_PROPERTY( Gamma, SkScalar, 1)
|
||||
// 1: clip, 2,3: don't clip
|
||||
ADAPTER_PROPERTY(ClipBlack, SkScalar, 1)
|
||||
ADAPTER_PROPERTY(ClipWhite, SkScalar, 1)
|
||||
|
||||
const sk_sp<sksg::ExternalColorFilter>& root() const { return fEffect; }
|
||||
|
||||
private:
|
||||
void apply() {
|
||||
enum LottieChannel {
|
||||
kRGB_Channel = 1,
|
||||
kR_Channel = 2,
|
||||
kG_Channel = 3,
|
||||
kB_Channel = 4,
|
||||
kA_Channel = 5,
|
||||
};
|
||||
|
||||
const auto channel = SkScalarTruncToInt(fChannel);
|
||||
if (channel < kRGB_Channel || channel > kA_Channel) {
|
||||
fEffect->setColorFilter(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
auto in_0 = SkTPin(fInBlack, 0.0f, 1.0f),
|
||||
in_1 = SkTPin(fInWhite, 0.0f, 1.0f),
|
||||
out_0 = SkTPin(fOutBlack, 0.0f, 1.0f),
|
||||
out_1 = SkTPin(fOutWhite, 0.0f, 1.0f),
|
||||
g = 1 / SkTMax(fGamma, 0.0f);
|
||||
|
||||
float clip[] = {0, 1};
|
||||
const auto kLottieDoClip = 1;
|
||||
if (SkScalarTruncToInt(fClipBlack) == kLottieDoClip) {
|
||||
const auto idx = fOutBlack <= fOutWhite ? 0 : 1;
|
||||
clip[idx] = out_0;
|
||||
}
|
||||
if (SkScalarTruncToInt(fClipWhite) == kLottieDoClip) {
|
||||
const auto idx = fOutBlack <= fOutWhite ? 1 : 0;
|
||||
clip[idx] = out_1;
|
||||
}
|
||||
SkASSERT(clip[0] <= clip[1]);
|
||||
|
||||
auto dIn = in_1 - in_0,
|
||||
dOut = out_1 - out_0;
|
||||
|
||||
if (SkScalarNearlyZero(dIn)) {
|
||||
// Degenerate dIn == 0 makes the arithmetic below explode.
|
||||
//
|
||||
// We could specialize the builder to deal with that case, or we could just
|
||||
// nudge by epsilon to make it all work. The latter approach is simpler
|
||||
// and doesn't have any noticeable downsides.
|
||||
//
|
||||
// Also nudge in_0 towards 0.5, in case it was sqashed against an extremity.
|
||||
// This allows for some abrupt transition when the output interval is not
|
||||
// collapsed, and produces results closer to AE.
|
||||
static constexpr auto kEpsilon = 2 * SK_ScalarNearlyZero;
|
||||
dIn += std::copysign(kEpsilon, dIn);
|
||||
in_0 += std::copysign(kEpsilon, .5f - in_0);
|
||||
SkASSERT(!SkScalarNearlyZero(dIn));
|
||||
}
|
||||
|
||||
uint8_t lut[256];
|
||||
|
||||
auto t = -in_0 / dIn,
|
||||
dT = 1 / 255.0f / dIn;
|
||||
|
||||
// TODO: is linear gamma common-enough to warrant a fast path?
|
||||
for (size_t i = 0; i < 256; ++i) {
|
||||
const auto out = out_0 + dOut * std::pow(std::max(t, 0.0f), g);
|
||||
SkASSERT(!SkScalarIsNaN(out));
|
||||
|
||||
lut[i] = static_cast<uint8_t>(std::round(SkTPin(out, clip[0], clip[1]) * 255));
|
||||
|
||||
t += dT;
|
||||
}
|
||||
|
||||
fEffect->setColorFilter(SkTableColorFilter::MakeARGB(
|
||||
channel == kA_Channel ? lut : nullptr,
|
||||
channel == kR_Channel || channel == kRGB_Channel ? lut : nullptr,
|
||||
channel == kG_Channel || channel == kRGB_Channel ? lut : nullptr,
|
||||
channel == kB_Channel || channel == kRGB_Channel ? lut : nullptr
|
||||
));
|
||||
}
|
||||
|
||||
sk_sp<sksg::ExternalColorFilter> fEffect;
|
||||
};
|
||||
|
||||
} // anonymous ns
|
||||
|
||||
sk_sp<sksg::RenderNode> EffectBuilder::attachLevelsEffect(const skjson::ArrayValue& jprops,
|
||||
sk_sp<sksg::RenderNode> layer) const {
|
||||
enum : size_t {
|
||||
kChannel_Index = 0,
|
||||
// ??? = 1,
|
||||
kInputBlack_Index = 2,
|
||||
kInputWhite_Index = 3,
|
||||
kGamma_Index = 4,
|
||||
kOutputBlack_Index = 5,
|
||||
kOutputWhite_Index = 6,
|
||||
kClipToOutBlack_Index = 7,
|
||||
kClipToOutWhite_Index = 8,
|
||||
};
|
||||
|
||||
auto adapter = sk_make_sp<LevelsEffectAdapter>(std::move(layer));
|
||||
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kChannel_Index), fScope,
|
||||
[adapter](const ScalarValue& channel) {
|
||||
adapter->setChannel(channel);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kInputBlack_Index), fScope,
|
||||
[adapter](const ScalarValue& ib) {
|
||||
adapter->setInBlack(ib);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kInputWhite_Index), fScope,
|
||||
[adapter](const ScalarValue& iw) {
|
||||
adapter->setInWhite(iw);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kOutputBlack_Index), fScope,
|
||||
[adapter](const ScalarValue& ob) {
|
||||
adapter->setOutBlack(ob);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kOutputWhite_Index), fScope,
|
||||
[adapter](const ScalarValue& ow) {
|
||||
adapter->setOutWhite(ow);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kGamma_Index), fScope,
|
||||
[adapter](const ScalarValue& g) {
|
||||
adapter->setGamma(g);
|
||||
});
|
||||
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kClipToOutBlack_Index), fScope,
|
||||
[adapter](const ScalarValue& cb) {
|
||||
adapter->setClipBlack(cb);
|
||||
});
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kClipToOutWhite_Index), fScope,
|
||||
[adapter](const ScalarValue& cw) {
|
||||
adapter->setClipWhite(cw);
|
||||
});
|
||||
|
||||
return adapter->root();
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace skottie
|
58
modules/skottie/src/effects/TintEffect.cpp
Normal file
58
modules/skottie/src/effects/TintEffect.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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/SkSGPaint.h"
|
||||
#include "modules/sksg/include/SkSGColorFilter.h"
|
||||
#include "src/utils/SkJSON.h"
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
sk_sp<sksg::RenderNode> EffectBuilder::attachTintEffect(const skjson::ArrayValue& jprops,
|
||||
sk_sp<sksg::RenderNode> layer) const {
|
||||
enum : size_t {
|
||||
kMapBlackTo_Index = 0,
|
||||
kMapWhiteTo_Index = 1,
|
||||
kAmount_Index = 2,
|
||||
// kOpacity_Index = 3, // currently unused (not exported)
|
||||
|
||||
kMax_Index = kAmount_Index,
|
||||
};
|
||||
|
||||
if (jprops.size() <= kMax_Index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const skjson::ObjectValue* color0_prop = jprops[kMapBlackTo_Index];
|
||||
const skjson::ObjectValue* color1_prop = jprops[kMapWhiteTo_Index];
|
||||
const skjson::ObjectValue* amount_prop = jprops[ kAmount_Index];
|
||||
|
||||
if (!color0_prop || !color1_prop || !amount_prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto tint_node =
|
||||
sksg::GradientColorFilter::Make(std::move(layer),
|
||||
fBuilder->attachColor(*color0_prop, fScope, "v"),
|
||||
fBuilder->attachColor(*color1_prop, fScope, "v"));
|
||||
if (!tint_node) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fBuilder->bindProperty<ScalarValue>((*amount_prop)["v"], fScope,
|
||||
[tint_node](const ScalarValue& w) {
|
||||
tint_node->setWeight(w / 100); // 100-based
|
||||
});
|
||||
|
||||
return std::move(tint_node);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace skottie
|
@ -41,10 +41,8 @@ private:
|
||||
|
||||
} // anonymous ns
|
||||
|
||||
sk_sp<sksg::RenderNode> AttachTransformEffect(const skjson::ArrayValue& jprops,
|
||||
const AnimationBuilder* abuilder,
|
||||
AnimatorScope* ascope,
|
||||
sk_sp<sksg::RenderNode> layer) {
|
||||
sk_sp<sksg::RenderNode> EffectBuilder::attachTransformEffect(const skjson::ArrayValue& jprops,
|
||||
sk_sp<sksg::RenderNode> layer) const {
|
||||
enum : size_t {
|
||||
kAnchorPoint_Index = 0,
|
||||
kPosition_Index = 1,
|
||||
@ -64,44 +62,36 @@ sk_sp<sksg::RenderNode> AttachTransformEffect(const skjson::ArrayValue& jprops,
|
||||
auto t_adapter = sk_make_sp<TransformAdapter2D>(matrix);
|
||||
auto s_adapter = sk_make_sp<ScaleAdapter>(t_adapter);
|
||||
|
||||
abuilder->bindProperty<VectorValue>(EffectBuilder::GetPropValue(jprops, kAnchorPoint_Index),
|
||||
ascope,
|
||||
fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kAnchorPoint_Index), fScope,
|
||||
[t_adapter](const VectorValue& ap) {
|
||||
t_adapter->setAnchorPoint(ValueTraits<VectorValue>::As<SkPoint>(ap));
|
||||
});
|
||||
abuilder->bindProperty<VectorValue>(EffectBuilder::GetPropValue(jprops, kPosition_Index),
|
||||
ascope,
|
||||
fBuilder->bindProperty<VectorValue>(GetPropValue(jprops, kPosition_Index), fScope,
|
||||
[t_adapter](const VectorValue& p) {
|
||||
t_adapter->setPosition(ValueTraits<VectorValue>::As<SkPoint>(p));
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kRotation_Index),
|
||||
ascope,
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kRotation_Index), fScope,
|
||||
[t_adapter](const ScalarValue& r) {
|
||||
t_adapter->setRotation(r);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kSkew_Index),
|
||||
ascope,
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kSkew_Index), fScope,
|
||||
[t_adapter](const ScalarValue& s) {
|
||||
t_adapter->setSkew(s);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kSkewAxis_Index),
|
||||
ascope,
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kSkewAxis_Index), fScope,
|
||||
[t_adapter](const ScalarValue& sa) {
|
||||
t_adapter->setSkewAxis(sa);
|
||||
});
|
||||
|
||||
abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kUniformScale_Index),
|
||||
ascope,
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kUniformScale_Index), fScope,
|
||||
[s_adapter](const ScalarValue& u) {
|
||||
s_adapter->setIsUniform(SkScalarRoundToInt(u));
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kScaleHeight_Index),
|
||||
ascope,
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kScaleHeight_Index), fScope,
|
||||
[s_adapter](const ScalarValue& sh) {
|
||||
s_adapter->setScaleHeight(sh);
|
||||
});
|
||||
abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kScaleWidth_Index),
|
||||
ascope,
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kScaleWidth_Index), fScope,
|
||||
[s_adapter](const ScalarValue& sw) {
|
||||
s_adapter->setScaleWidth(sw);
|
||||
});
|
||||
@ -109,8 +99,7 @@ sk_sp<sksg::RenderNode> AttachTransformEffect(const skjson::ArrayValue& jprops,
|
||||
auto opacity_node = sksg::OpacityEffect::Make(sksg::TransformEffect::Make(std::move(layer),
|
||||
std::move(matrix)));
|
||||
|
||||
abuilder->bindProperty<ScalarValue>(EffectBuilder::GetPropValue(jprops, kOpacity_Index),
|
||||
ascope,
|
||||
fBuilder->bindProperty<ScalarValue>(GetPropValue(jprops, kOpacity_Index), fScope,
|
||||
[opacity_node](const ScalarValue& o) {
|
||||
opacity_node->setOpacity(o * 0.01f);
|
||||
});
|
||||
|
60
modules/skottie/src/effects/TritoneEffect.cpp
Normal file
60
modules/skottie/src/effects/TritoneEffect.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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/SkSGColorFilter.h"
|
||||
#include "modules/sksg/include/SkSGPaint.h"
|
||||
#include "src/utils/SkJSON.h"
|
||||
|
||||
namespace skottie {
|
||||
namespace internal {
|
||||
|
||||
sk_sp<sksg::RenderNode> EffectBuilder::attachTritoneEffect(const skjson::ArrayValue& jprops,
|
||||
sk_sp<sksg::RenderNode> layer) const {
|
||||
enum : size_t {
|
||||
kHiColor_Index = 0,
|
||||
kMiColor_Index = 1,
|
||||
kLoColor_Index = 2,
|
||||
kBlendAmount_Index = 3,
|
||||
|
||||
kMax_Index = kBlendAmount_Index,
|
||||
};
|
||||
|
||||
if (jprops.size() <= kMax_Index) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const skjson::ObjectValue* hicolor_prop = jprops[ kHiColor_Index];
|
||||
const skjson::ObjectValue* micolor_prop = jprops[ kMiColor_Index];
|
||||
const skjson::ObjectValue* locolor_prop = jprops[ kLoColor_Index];
|
||||
const skjson::ObjectValue* blend_prop = jprops[kBlendAmount_Index];
|
||||
|
||||
if (!hicolor_prop || !micolor_prop || !locolor_prop || !blend_prop) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto tritone_node =
|
||||
sksg::GradientColorFilter::Make(std::move(layer), {
|
||||
fBuilder->attachColor(*locolor_prop, fScope, "v"),
|
||||
fBuilder->attachColor(*micolor_prop, fScope, "v"),
|
||||
fBuilder->attachColor(*hicolor_prop, fScope, "v") });
|
||||
if (!tritone_node) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
fBuilder->bindProperty<ScalarValue>((*blend_prop)["v"], fScope,
|
||||
[tritone_node](const ScalarValue& w) {
|
||||
tritone_node->setWeight((100 - w) / 100); // 100-based, inverted (!?).
|
||||
});
|
||||
|
||||
return std::move(tritone_node);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace skottie
|
Loading…
Reference in New Issue
Block a user