1/* 2 * Copyright 2019 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "modules/skottie/src/effects/Effects.h" 9 10#include "modules/skottie/src/Composition.h" 11#include "modules/skottie/src/Layer.h" 12#include "modules/skottie/src/SkottieJson.h" 13#include "modules/sksg/include/SkSGRenderEffect.h" 14#include "src/utils/SkJSON.h" 15 16#include <algorithm> 17#include <iterator> 18 19namespace skottie { 20namespace internal { 21 22EffectBuilder::EffectBuilder(const AnimationBuilder* abuilder, 23 const SkSize& layer_size, 24 CompositionBuilder* cbuilder) 25 : fBuilder(abuilder) 26 , fCompBuilder(cbuilder) 27 , fLayerSize(layer_size) {} 28 29EffectBuilder::EffectBuilderT EffectBuilder::findBuilder(const skjson::ObjectValue& jeffect) const { 30 static constexpr struct BuilderInfo { 31 const char* fName; 32 EffectBuilderT fBuilder; 33 } gBuilderInfo[] = { 34 // alphabetized for binary search lookup 35 { "ADBE Black&White" , &EffectBuilder::attachBlackAndWhiteEffect }, 36 { "ADBE Brightness & Contrast 2", &EffectBuilder::attachBrightnessContrastEffect }, 37 { "ADBE Bulge" , &EffectBuilder::attachBulgeEffect }, 38 { "ADBE Corner Pin" , &EffectBuilder::attachCornerPinEffect }, 39 { "ADBE Displacement Map" , &EffectBuilder::attachDisplacementMapEffect }, 40 { "ADBE Drop Shadow" , &EffectBuilder::attachDropShadowEffect }, 41 { "ADBE Easy Levels2" , &EffectBuilder::attachEasyLevelsEffect }, 42 { "ADBE Fill" , &EffectBuilder::attachFillEffect }, 43 { "ADBE Fractal Noise" , &EffectBuilder::attachFractalNoiseEffect }, 44 { "ADBE Gaussian Blur 2" , &EffectBuilder::attachGaussianBlurEffect }, 45 { "ADBE Geometry2" , &EffectBuilder::attachTransformEffect }, 46 { "ADBE HUE SATURATION" , &EffectBuilder::attachHueSaturationEffect }, 47 { "ADBE Invert" , &EffectBuilder::attachInvertEffect }, 48 { "ADBE Linear Wipe" , &EffectBuilder::attachLinearWipeEffect }, 49 { "ADBE Motion Blur" , &EffectBuilder::attachDirectionalBlurEffect }, 50 { "ADBE Pro Levels2" , &EffectBuilder::attachProLevelsEffect }, 51 { "ADBE Radial Wipe" , &EffectBuilder::attachRadialWipeEffect }, 52 { "ADBE Ramp" , &EffectBuilder::attachGradientEffect }, 53 { "ADBE Shift Channels" , &EffectBuilder::attachShiftChannelsEffect }, 54 { "ADBE Threshold2" , &EffectBuilder::attachThresholdEffect }, 55 { "ADBE Tile" , &EffectBuilder::attachMotionTileEffect }, 56 { "ADBE Tint" , &EffectBuilder::attachTintEffect }, 57 { "ADBE Tritone" , &EffectBuilder::attachTritoneEffect }, 58 { "ADBE Venetian Blinds" , &EffectBuilder::attachVenetianBlindsEffect }, 59 { "CC Sphere" , &EffectBuilder::attachSphereEffect }, 60 { "CC Toner" , &EffectBuilder::attachCCTonerEffect }, 61 { "SkSL Shader" , &EffectBuilder::attachSkSLEffect }, 62 }; 63 64 const skjson::StringValue* mn = jeffect["mn"]; 65 if (mn) { 66 const BuilderInfo key { mn->begin(), nullptr }; 67 const auto* binfo = std::lower_bound(std::begin(gBuilderInfo), 68 std::end (gBuilderInfo), 69 key, 70 [](const BuilderInfo& a, const BuilderInfo& b) { 71 return strcmp(a.fName, b.fName) < 0; 72 }); 73 if (binfo != std::end(gBuilderInfo) && !strcmp(binfo->fName, key.fName)) { 74 return binfo->fBuilder; 75 } 76 } 77 78 // Some legacy clients rely solely on the 'ty' field and generate (non-BM) JSON 79 // without a valid 'mn' string. TODO: we should update them and remove this fallback. 80 enum : int32_t { 81 kTint_Effect = 20, 82 kFill_Effect = 21, 83 kTritone_Effect = 23, 84 kDropShadow_Effect = 25, 85 kRadialWipe_Effect = 26, 86 kGaussianBlur_Effect = 29, 87 }; 88 89 switch (ParseDefault<int>(jeffect["ty"], -1)) { 90 case kTint_Effect: return &EffectBuilder::attachTintEffect; 91 case kFill_Effect: return &EffectBuilder::attachFillEffect; 92 case kTritone_Effect: return &EffectBuilder::attachTritoneEffect; 93 case kDropShadow_Effect: return &EffectBuilder::attachDropShadowEffect; 94 case kRadialWipe_Effect: return &EffectBuilder::attachRadialWipeEffect; 95 case kGaussianBlur_Effect: return &EffectBuilder::attachGaussianBlurEffect; 96 default: break; 97 } 98 99 fBuilder->log(Logger::Level::kWarning, &jeffect, 100 "Unsupported layer effect: %s", mn ? mn->begin() : "(unknown)"); 101 102 return nullptr; 103} 104 105sk_sp<sksg::RenderNode> EffectBuilder::attachEffects(const skjson::ArrayValue& jeffects, 106 sk_sp<sksg::RenderNode> layer) const { 107 if (!layer) { 108 return nullptr; 109 } 110 111 for (const skjson::ObjectValue* jeffect : jeffects) { 112 if (!jeffect) { 113 continue; 114 } 115 116 const auto builder = this->findBuilder(*jeffect); 117 const skjson::ArrayValue* jprops = (*jeffect)["ef"]; 118 if (!builder || !jprops) { 119 continue; 120 } 121 122 const AnimationBuilder::AutoPropertyTracker apt(fBuilder, *jeffect, PropertyObserver::NodeType::EFFECT); 123 layer = (this->*builder)(*jprops, std::move(layer)); 124 125 if (!layer) { 126 fBuilder->log(Logger::Level::kError, jeffect, "Invalid layer effect."); 127 return nullptr; 128 } 129 } 130 131 return layer; 132} 133 134sk_sp<sksg::RenderNode> EffectBuilder::attachStyles(const skjson::ArrayValue& jstyles, 135 sk_sp<sksg::RenderNode> layer) const { 136#if !defined(SKOTTIE_DISABLE_STYLES) 137 if (!layer) { 138 return nullptr; 139 } 140 141 using StyleBuilder = 142 sk_sp<sksg::RenderNode> (EffectBuilder::*)(const skjson::ObjectValue&, 143 sk_sp<sksg::RenderNode>) const; 144 static constexpr StyleBuilder gStyleBuilders[] = { 145 nullptr, // 'ty': 0 -> stroke 146 &EffectBuilder::attachDropShadowStyle, // 'ty': 1 -> drop shadow 147 &EffectBuilder::attachInnerShadowStyle, // 'ty': 2 -> inner shadow 148 &EffectBuilder::attachOuterGlowStyle, // 'ty': 3 -> outer glow 149 &EffectBuilder::attachInnerGlowStyle, // 'ty': 4 -> inner glow 150 }; 151 152 for (const skjson::ObjectValue* jstyle : jstyles) { 153 if (!jstyle) { 154 continue; 155 } 156 157 const auto style_type = 158 ParseDefault<size_t>((*jstyle)["ty"], std::numeric_limits<size_t>::max()); 159 auto builder = style_type < SK_ARRAY_COUNT(gStyleBuilders) ? gStyleBuilders[style_type] 160 : nullptr; 161 162 if (!builder) { 163 fBuilder->log(Logger::Level::kWarning, jstyle, "Unsupported layer style."); 164 continue; 165 } 166 167 layer = (this->*builder)(*jstyle, std::move(layer)); 168 } 169#endif // !defined(SKOTTIE_DISABLE_STYLES) 170 171 return layer; 172} 173 174const skjson::Value& EffectBuilder::GetPropValue(const skjson::ArrayValue& jprops, 175 size_t prop_index) { 176 static skjson::NullValue kNull; 177 178 if (prop_index >= jprops.size()) { 179 return kNull; 180 } 181 182 const skjson::ObjectValue* jprop = jprops[prop_index]; 183 184 return jprop ? (*jprop)["v"] : kNull; 185} 186 187MaskShaderEffectBase::MaskShaderEffectBase(sk_sp<sksg::RenderNode> child, const SkSize& ls) 188 : fMaskEffectNode(sksg::MaskShaderEffect::Make(std::move(child))) 189 , fLayerSize(ls) {} 190 191void MaskShaderEffectBase::onSync() { 192 const auto minfo = this->onMakeMask(); 193 194 fMaskEffectNode->setVisible(minfo.fVisible); 195 fMaskEffectNode->setShader(std::move(minfo.fMaskShader)); 196} 197 198} // namespace internal 199} // namespace skottie 200