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