/* * 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 #include 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[] = { { "ADBE Black&White" , &EffectBuilder::attachBlackAndWhiteEffect }, { "ADBE Brightness & Contrast 2", &EffectBuilder::attachBrightnessContrastEffect }, { "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 Pro Levels2" , &EffectBuilder::attachProLevelsEffect }, { "ADBE Radial Wipe" , &EffectBuilder::attachRadialWipeEffect }, { "ADBE Ramp" , &EffectBuilder::attachGradientEffect }, { "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 }, }; 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(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 EffectBuilder::attachEffects(const skjson::ArrayValue& jeffects, sk_sp 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); layer = (this->*builder)(*jprops, std::move(layer)); if (!layer) { fBuilder->log(Logger::Level::kError, jeffect, "Invalid layer effect."); return nullptr; } } return layer; } sk_sp EffectBuilder::attachStyles(const skjson::ArrayValue& jstyles, sk_sp layer) const { #if !defined(SKOTTIE_DISABLE_STYLES) if (!layer) { return nullptr; } using StyleBuilder = sk_sp (EffectBuilder::*)(const skjson::ObjectValue&, sk_sp) 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((*jstyle)["ty"], std::numeric_limits::max()); auto builder = style_type < SK_ARRAY_COUNT(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 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