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
19 namespace skottie {
20 namespace internal {
21
EffectBuilder(const AnimationBuilder * abuilder,const SkSize & layer_size,CompositionBuilder * cbuilder)22 EffectBuilder::EffectBuilder(const AnimationBuilder* abuilder,
23 const SkSize& layer_size,
24 CompositionBuilder* cbuilder)
25 : fBuilder(abuilder)
26 , fCompBuilder(cbuilder)
27 , fLayerSize(layer_size) {}
28
findBuilder(const skjson::ObjectValue & jeffect) const29 EffectBuilder::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
attachEffects(const skjson::ArrayValue & jeffects,sk_sp<sksg::RenderNode> layer) const105 sk_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
attachStyles(const skjson::ArrayValue & jstyles,sk_sp<sksg::RenderNode> layer) const134 sk_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
GetPropValue(const skjson::ArrayValue & jprops,size_t prop_index)174 const 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
MaskShaderEffectBase(sk_sp<sksg::RenderNode> child,const SkSize & ls)187 MaskShaderEffectBase::MaskShaderEffectBase(sk_sp<sksg::RenderNode> child, const SkSize& ls)
188 : fMaskEffectNode(sksg::MaskShaderEffect::Make(std::move(child)))
189 , fLayerSize(ls) {}
190
onSync()191 void 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