[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>
diff --git a/modules/skottie/skottie.gni b/modules/skottie/skottie.gni
index 342860b..7d67294 100644
--- a/modules/skottie/skottie.gni
+++ b/modules/skottie/skottie.gni
@@ -20,7 +20,6 @@
"$_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 @@
"$_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",
diff --git a/modules/skottie/src/SkottieAdapter.cpp b/modules/skottie/src/SkottieAdapter.cpp
index b2b799d..3c5f2ac 100644
--- a/modules/skottie/src/SkottieAdapter.cpp
+++ b/modules/skottie/src/SkottieAdapter.cpp
@@ -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 @@
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 @@
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
diff --git a/modules/skottie/src/SkottieAdapter.h b/modules/skottie/src/SkottieAdapter.h
index 2619399..8cf11a8 100644
--- a/modules/skottie/src/SkottieAdapter.h
+++ b/modules/skottie/src/SkottieAdapter.h
@@ -234,37 +234,6 @@
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 @@
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
diff --git a/modules/skottie/src/SkottieLayer.cpp b/modules/skottie/src/SkottieLayer.cpp
index dde866c..22b02f2 100644
--- a/modules/skottie/src/SkottieLayer.cpp
+++ b/modules/skottie/src/SkottieLayer.cpp
@@ -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 @@
// 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.
diff --git a/modules/skottie/src/SkottieLayerEffect.cpp b/modules/skottie/src/SkottieLayerEffect.cpp
deleted file mode 100644
index 995c4de..0000000
--- a/modules/skottie/src/SkottieLayerEffect.cpp
+++ /dev/null
@@ -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
diff --git a/modules/skottie/src/SkottiePriv.h b/modules/skottie/src/SkottiePriv.h
index 44218f7..1bdd3a6 100644
--- a/modules/skottie/src/SkottiePriv.h
+++ b/modules/skottie/src/SkottiePriv.h
@@ -101,8 +101,6 @@
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;
diff --git a/modules/skottie/src/effects/DropShadowEffect.cpp b/modules/skottie/src/effects/DropShadowEffect.cpp
new file mode 100644
index 0000000..de5d3e8
--- /dev/null
+++ b/modules/skottie/src/effects/DropShadowEffect.cpp
@@ -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
diff --git a/modules/skottie/src/effects/Effects.cpp b/modules/skottie/src/effects/Effects.cpp
index f6c62c8..ea6fcdb 100644
--- a/modules/skottie/src/effects/Effects.cpp
+++ b/modules/skottie/src/effects/Effects.cpp
@@ -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;
diff --git a/modules/skottie/src/effects/Effects.h b/modules/skottie/src/effects/Effects.h
index f3d5883..a66aec6 100644
--- a/modules/skottie/src/effects/Effects.h
+++ b/modules/skottie/src/effects/Effects.h
@@ -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
diff --git a/modules/skottie/src/effects/FillEffect.cpp b/modules/skottie/src/effects/FillEffect.cpp
new file mode 100644
index 0000000..9a15618
--- /dev/null
+++ b/modules/skottie/src/effects/FillEffect.cpp
@@ -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
diff --git a/modules/skottie/src/effects/GaussianBlurEffect.cpp b/modules/skottie/src/effects/GaussianBlurEffect.cpp
new file mode 100644
index 0000000..1a18754
--- /dev/null
+++ b/modules/skottie/src/effects/GaussianBlurEffect.cpp
@@ -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
diff --git a/modules/skottie/src/effects/GradientEffect.cpp b/modules/skottie/src/effects/GradientEffect.cpp
new file mode 100644
index 0000000..a4e67b1
--- /dev/null
+++ b/modules/skottie/src/effects/GradientEffect.cpp
@@ -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
diff --git a/modules/skottie/src/effects/LevelsEffect.cpp b/modules/skottie/src/effects/LevelsEffect.cpp
new file mode 100644
index 0000000..bc9ef35
--- /dev/null
+++ b/modules/skottie/src/effects/LevelsEffect.cpp
@@ -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
diff --git a/modules/skottie/src/effects/TintEffect.cpp b/modules/skottie/src/effects/TintEffect.cpp
new file mode 100644
index 0000000..f43fe58
--- /dev/null
+++ b/modules/skottie/src/effects/TintEffect.cpp
@@ -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
diff --git a/modules/skottie/src/effects/TransformEffect.cpp b/modules/skottie/src/effects/TransformEffect.cpp
index 7dfb134..5904b9d 100644
--- a/modules/skottie/src/effects/TransformEffect.cpp
+++ b/modules/skottie/src/effects/TransformEffect.cpp
@@ -41,10 +41,8 @@
} // 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 @@
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 @@
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);
});
diff --git a/modules/skottie/src/effects/TritoneEffect.cpp b/modules/skottie/src/effects/TritoneEffect.cpp
new file mode 100644
index 0000000..4ef60d3
--- /dev/null
+++ b/modules/skottie/src/effects/TritoneEffect.cpp
@@ -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