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