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