| /* | |
| * Copyright 2019 Google LLC | |
| * | |
| * 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/include/Skottie.h" | |
| #include "modules/skottie/include/SkottieProperty.h" | |
| #include "modules/skottie/src/Composition.h" | |
| #include "modules/skottie/src/Layer.h" | |
| #include "modules/skottie/src/SkottieJson.h" | |
| #include "modules/skottie/src/SkottiePriv.h" | |
| #include "modules/sksg/include/SkSGRenderEffect.h" | |
| #include "modules/sksg/include/SkSGRenderNode.h" | |
| #include <algorithm> | |
| #include <cstdint> | |
| #include <cstring> | |
| #include <iterator> | |
| #include <limits> | |
| #include <utility> | |
| 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 | |
| nullptr, // 'ty': 5 -> bevel/emboss | |
| nullptr, // 'ty': 6 -> satin | |
| &EffectBuilder::attachColorOverlayStyle, // 'ty': 7 -> color overlay | |
| }; | |
| 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; | |
| } | |
| EffectBuilder::LayerContent EffectBuilder::getLayerContent(int layer_index) const { | |
| if (LayerBuilder* lbuilder = fCompBuilder->layerBuilder(layer_index)) { | |
| return { lbuilder->getContentTree(*fBuilder, fCompBuilder), lbuilder->size() }; | |
| } | |
| return { nullptr, {0, 0} }; | |
| } | |
| 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 |