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
17 namespace skottie {
18 namespace internal {
19
EffectBuilder(const AnimationBuilder * abuilder,const SkSize & layer_size)20 EffectBuilder::EffectBuilder(const AnimationBuilder* abuilder, const SkSize& layer_size)
21 : fBuilder(abuilder)
22 , fLayerSize(layer_size) {}
23
findBuilder(const skjson::ObjectValue & jeffect) const24 EffectBuilder::EffectBuilderT EffectBuilder::findBuilder(const skjson::ObjectValue& jeffect) const {
25 static constexpr struct BuilderInfo {
26 const char* fName;
27 EffectBuilderT fBuilder;
28 } gBuilderInfo[] = {
29 { "ADBE Drop Shadow" , &EffectBuilder::attachDropShadowEffect },
30 { "ADBE Easy Levels2" , &EffectBuilder::attachEasyLevelsEffect },
31 { "ADBE Fill" , &EffectBuilder::attachFillEffect },
32 { "ADBE Gaussian Blur 2", &EffectBuilder::attachGaussianBlurEffect },
33 { "ADBE Geometry2" , &EffectBuilder::attachTransformEffect },
34 { "ADBE HUE SATURATION" , &EffectBuilder::attachHueSaturationEffect },
35 { "ADBE Invert" , &EffectBuilder::attachInvertEffect },
36 { "ADBE Linear Wipe" , &EffectBuilder::attachLinearWipeEffect },
37 { "ADBE Pro Levels2" , &EffectBuilder::attachProLevelsEffect },
38 { "ADBE Radial Wipe" , &EffectBuilder::attachRadialWipeEffect },
39 { "ADBE Ramp" , &EffectBuilder::attachGradientEffect },
40 { "ADBE Shift Channels" , &EffectBuilder::attachShiftChannelsEffect },
41 { "ADBE Tile" , &EffectBuilder::attachMotionTileEffect },
42 { "ADBE Tint" , &EffectBuilder::attachTintEffect },
43 { "ADBE Tritone" , &EffectBuilder::attachTritoneEffect },
44 { "ADBE Venetian Blinds", &EffectBuilder::attachVenetianBlindsEffect },
45 };
46
47 const skjson::StringValue* mn = jeffect["mn"];
48 if (mn) {
49 const BuilderInfo key { mn->begin(), nullptr };
50 const auto* binfo = std::lower_bound(std::begin(gBuilderInfo),
51 std::end (gBuilderInfo),
52 key,
53 [](const BuilderInfo& a, const BuilderInfo& b) {
54 return strcmp(a.fName, b.fName) < 0;
55 });
56
57 if (binfo != std::end(gBuilderInfo) && !strcmp(binfo->fName, key.fName)) {
58 return binfo->fBuilder;
59 }
60 }
61
62 // Some legacy clients rely solely on the 'ty' field and generate (non-BM) JSON
63 // without a valid 'mn' string. TODO: we should update them and remove this fallback.
64 enum : int32_t {
65 kTint_Effect = 20,
66 kFill_Effect = 21,
67 kTritone_Effect = 23,
68 kDropShadow_Effect = 25,
69 kRadialWipe_Effect = 26,
70 kGaussianBlur_Effect = 29,
71 };
72
73 switch (ParseDefault<int>(jeffect["ty"], -1)) {
74 case kTint_Effect: return &EffectBuilder::attachTintEffect;
75 case kFill_Effect: return &EffectBuilder::attachFillEffect;
76 case kTritone_Effect: return &EffectBuilder::attachTritoneEffect;
77 case kDropShadow_Effect: return &EffectBuilder::attachDropShadowEffect;
78 case kRadialWipe_Effect: return &EffectBuilder::attachRadialWipeEffect;
79 case kGaussianBlur_Effect: return &EffectBuilder::attachGaussianBlurEffect;
80 default: break;
81 }
82
83 fBuilder->log(Logger::Level::kWarning, &jeffect,
84 "Unsupported layer effect: %s", mn ? mn->begin() : "(unknown)");
85
86 return nullptr;
87 }
88
attachEffects(const skjson::ArrayValue & jeffects,sk_sp<sksg::RenderNode> layer) const89 sk_sp<sksg::RenderNode> EffectBuilder::attachEffects(const skjson::ArrayValue& jeffects,
90 sk_sp<sksg::RenderNode> layer) const {
91 if (!layer) {
92 return nullptr;
93 }
94
95 for (const skjson::ObjectValue* jeffect : jeffects) {
96 if (!jeffect) {
97 continue;
98 }
99
100 const auto builder = this->findBuilder(*jeffect);
101 const skjson::ArrayValue* jprops = (*jeffect)["ef"];
102 if (!builder || !jprops) {
103 continue;
104 }
105
106 const AnimationBuilder::AutoPropertyTracker apt(fBuilder, *jeffect);
107 layer = (this->*builder)(*jprops, std::move(layer));
108
109 if (!layer) {
110 fBuilder->log(Logger::Level::kError, jeffect, "Invalid layer effect.");
111 return nullptr;
112 }
113 }
114
115 return layer;
116 }
117
GetPropValue(const skjson::ArrayValue & jprops,size_t prop_index)118 const skjson::Value& EffectBuilder::GetPropValue(const skjson::ArrayValue& jprops,
119 size_t prop_index) {
120 static skjson::NullValue kNull;
121
122 if (prop_index >= jprops.size()) {
123 return kNull;
124 }
125
126 const skjson::ObjectValue* jprop = jprops[prop_index];
127
128 return jprop ? (*jprop)["v"] : kNull;
129 }
130
MaskFilterEffectBase(sk_sp<sksg::RenderNode> child,const SkSize & ls)131 MaskFilterEffectBase::MaskFilterEffectBase(sk_sp<sksg::RenderNode> child, const SkSize& ls)
132 : fMaskNode(sksg::MaskFilter::Make(nullptr))
133 , fMaskEffectNode(sksg::MaskFilterEffect::Make(std::move(child), fMaskNode))
134 , fLayerSize(ls) {}
135
onSync()136 void MaskFilterEffectBase::onSync() {
137 const auto minfo = this->onMakeMask();
138
139 fMaskEffectNode->setVisible(minfo.fVisible);
140 fMaskNode->setMaskFilter(std::move(minfo.fMask));
141 }
142
143 } // namespace internal
144 } // namespace skottie
145