blob: 434e151aa2cb742af6a17ca9f35c08cbff4545aa [file] [log] [blame]
/*
* Copyright 2019 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "modules/skottie/src/effects/Effects.h"
#include "modules/skottie/src/Composition.h"
#include "modules/skottie/src/Layer.h"
#include "modules/skottie/src/SkottieJson.h"
#include "modules/sksg/include/SkSGRenderEffect.h"
#include "src/utils/SkJSON.h"
#include <algorithm>
#include <iterator>
namespace skottie {
namespace internal {
EffectBuilder::EffectBuilder(const AnimationBuilder* abuilder,
const SkSize& layer_size,
CompositionBuilder* cbuilder)
: fBuilder(abuilder)
, fCompBuilder(cbuilder)
, fLayerSize(layer_size) {}
EffectBuilder::EffectBuilderT EffectBuilder::findBuilder(const skjson::ObjectValue& jeffect) const {
static constexpr struct BuilderInfo {
const char* fName;
EffectBuilderT fBuilder;
} gBuilderInfo[] = {
// alphabetized for binary search lookup
{ "ADBE Black&White" , &EffectBuilder::attachBlackAndWhiteEffect },
{ "ADBE Brightness & Contrast 2", &EffectBuilder::attachBrightnessContrastEffect },
{ "ADBE Bulge" , &EffectBuilder::attachBulgeEffect },
{ "ADBE Corner Pin" , &EffectBuilder::attachCornerPinEffect },
{ "ADBE Displacement Map" , &EffectBuilder::attachDisplacementMapEffect },
{ "ADBE Drop Shadow" , &EffectBuilder::attachDropShadowEffect },
{ "ADBE Easy Levels2" , &EffectBuilder::attachEasyLevelsEffect },
{ "ADBE Fill" , &EffectBuilder::attachFillEffect },
{ "ADBE Fractal Noise" , &EffectBuilder::attachFractalNoiseEffect },
{ "ADBE Gaussian Blur 2" , &EffectBuilder::attachGaussianBlurEffect },
{ "ADBE Geometry2" , &EffectBuilder::attachTransformEffect },
{ "ADBE HUE SATURATION" , &EffectBuilder::attachHueSaturationEffect },
{ "ADBE Invert" , &EffectBuilder::attachInvertEffect },
{ "ADBE Linear Wipe" , &EffectBuilder::attachLinearWipeEffect },
{ "ADBE Motion Blur" , &EffectBuilder::attachDirectionalBlurEffect },
{ "ADBE Pro Levels2" , &EffectBuilder::attachProLevelsEffect },
{ "ADBE Radial Wipe" , &EffectBuilder::attachRadialWipeEffect },
{ "ADBE Ramp" , &EffectBuilder::attachGradientEffect },
{ "ADBE Sharpen" , &EffectBuilder::attachSharpenEffect },
{ "ADBE Shift Channels" , &EffectBuilder::attachShiftChannelsEffect },
{ "ADBE Threshold2" , &EffectBuilder::attachThresholdEffect },
{ "ADBE Tile" , &EffectBuilder::attachMotionTileEffect },
{ "ADBE Tint" , &EffectBuilder::attachTintEffect },
{ "ADBE Tritone" , &EffectBuilder::attachTritoneEffect },
{ "ADBE Venetian Blinds" , &EffectBuilder::attachVenetianBlindsEffect },
{ "CC Sphere" , &EffectBuilder::attachSphereEffect },
{ "CC Toner" , &EffectBuilder::attachCCTonerEffect },
{ "SkSL Color Filter" , &EffectBuilder::attachSkSLColorFilter },
{ "SkSL Shader" , &EffectBuilder::attachSkSLShader },
};
const skjson::StringValue* mn = jeffect["mn"];
if (mn) {
const BuilderInfo key { mn->begin(), nullptr };
const auto* binfo = std::lower_bound(std::begin(gBuilderInfo),
std::end (gBuilderInfo),
key,
[](const BuilderInfo& a, const BuilderInfo& b) {
return strcmp(a.fName, b.fName) < 0;
});
if (binfo != std::end(gBuilderInfo) && !strcmp(binfo->fName, key.fName)) {
return binfo->fBuilder;
}
}
// Some legacy clients rely solely on the 'ty' field and generate (non-BM) JSON
// without a valid 'mn' string. TODO: we should update them and remove this fallback.
enum : int32_t {
kTint_Effect = 20,
kFill_Effect = 21,
kTritone_Effect = 23,
kDropShadow_Effect = 25,
kRadialWipe_Effect = 26,
kGaussianBlur_Effect = 29,
};
switch (ParseDefault<int>(jeffect["ty"], -1)) {
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 kRadialWipe_Effect: return &EffectBuilder::attachRadialWipeEffect;
case kGaussianBlur_Effect: return &EffectBuilder::attachGaussianBlurEffect;
default: break;
}
fBuilder->log(Logger::Level::kWarning, &jeffect,
"Unsupported layer effect: %s", mn ? mn->begin() : "(unknown)");
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;
}
const AnimationBuilder::AutoPropertyTracker apt(fBuilder, *jeffect, PropertyObserver::NodeType::EFFECT);
layer = (this->*builder)(*jprops, std::move(layer));
if (!layer) {
fBuilder->log(Logger::Level::kError, jeffect, "Invalid layer effect.");
return nullptr;
}
}
return layer;
}
sk_sp<sksg::RenderNode> EffectBuilder::attachStyles(const skjson::ArrayValue& jstyles,
sk_sp<sksg::RenderNode> layer) const {
#if !defined(SKOTTIE_DISABLE_STYLES)
if (!layer) {
return nullptr;
}
using StyleBuilder =
sk_sp<sksg::RenderNode> (EffectBuilder::*)(const skjson::ObjectValue&,
sk_sp<sksg::RenderNode>) const;
static constexpr StyleBuilder gStyleBuilders[] = {
nullptr, // 'ty': 0 -> stroke
&EffectBuilder::attachDropShadowStyle, // 'ty': 1 -> drop shadow
&EffectBuilder::attachInnerShadowStyle, // 'ty': 2 -> inner shadow
&EffectBuilder::attachOuterGlowStyle, // 'ty': 3 -> outer glow
&EffectBuilder::attachInnerGlowStyle, // 'ty': 4 -> inner glow
};
for (const skjson::ObjectValue* jstyle : jstyles) {
if (!jstyle) {
continue;
}
const auto style_type =
ParseDefault<size_t>((*jstyle)["ty"], std::numeric_limits<size_t>::max());
auto builder = style_type < std::size(gStyleBuilders) ? gStyleBuilders[style_type]
: nullptr;
if (!builder) {
fBuilder->log(Logger::Level::kWarning, jstyle, "Unsupported layer style.");
continue;
}
layer = (this->*builder)(*jstyle, std::move(layer));
}
#endif // !defined(SKOTTIE_DISABLE_STYLES)
return layer;
}
const skjson::Value& EffectBuilder::GetPropValue(const skjson::ArrayValue& jprops,
size_t prop_index) {
static skjson::NullValue kNull;
if (prop_index >= jprops.size()) {
return kNull;
}
const skjson::ObjectValue* jprop = jprops[prop_index];
return jprop ? (*jprop)["v"] : kNull;
}
MaskShaderEffectBase::MaskShaderEffectBase(sk_sp<sksg::RenderNode> child, const SkSize& ls)
: fMaskEffectNode(sksg::MaskShaderEffect::Make(std::move(child)))
, fLayerSize(ls) {}
void MaskShaderEffectBase::onSync() {
const auto minfo = this->onMakeMask();
fMaskEffectNode->setVisible(minfo.fVisible);
fMaskEffectNode->setShader(std::move(minfo.fMaskShader));
}
} // namespace internal
} // namespace skottie