• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 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 "SkottiePriv.h"
9 
10 #include "SkJSON.h"
11 #include "SkottieAdapter.h"
12 #include "SkottieJson.h"
13 #include "SkottieValue.h"
14 #include "SkSGColor.h"
15 #include "SkSGRenderEffect.h"
16 #include "SkSGColorFilter.h"
17 
18 namespace skottie {
19 namespace internal {
20 
21 namespace {
22 
AttachTintLayerEffect(const skjson::ArrayValue & jprops,const AnimationBuilder * abuilder,AnimatorScope * ascope,sk_sp<sksg::RenderNode> layer)23 sk_sp<sksg::RenderNode> AttachTintLayerEffect(const skjson::ArrayValue& jprops,
24                                               const AnimationBuilder* abuilder,
25                                               AnimatorScope* ascope,
26                                               sk_sp<sksg::RenderNode> layer) {
27     enum : size_t {
28         kMapBlackTo_Index = 0,
29         kMapWhiteTo_Index = 1,
30         kAmount_Index     = 2,
31         // kOpacity_Index    = 3, // currently unused (not exported)
32 
33         kMax_Index        = kAmount_Index,
34     };
35 
36     if (jprops.size() <= kMax_Index) {
37         return nullptr;
38     }
39 
40     const skjson::ObjectValue* color0_prop = jprops[kMapBlackTo_Index];
41     const skjson::ObjectValue* color1_prop = jprops[kMapWhiteTo_Index];
42     const skjson::ObjectValue* amount_prop = jprops[    kAmount_Index];
43 
44     if (!color0_prop || !color1_prop || !amount_prop) {
45         return nullptr;
46     }
47 
48     auto tint_node =
49             sksg::GradientColorFilter::Make(std::move(layer),
50                                             abuilder->attachColor(*color0_prop, ascope, "v"),
51                                             abuilder->attachColor(*color1_prop, ascope, "v"));
52     if (!tint_node) {
53         return nullptr;
54     }
55 
56     abuilder->bindProperty<ScalarValue>((*amount_prop)["v"], ascope,
57         [tint_node](const ScalarValue& w) {
58             tint_node->setWeight(w / 100); // 100-based
59         });
60 
61     return std::move(tint_node);
62 }
63 
AttachTritoneLayerEffect(const skjson::ArrayValue & jprops,const AnimationBuilder * abuilder,AnimatorScope * ascope,sk_sp<sksg::RenderNode> layer)64 sk_sp<sksg::RenderNode> AttachTritoneLayerEffect(const skjson::ArrayValue& jprops,
65                                                  const AnimationBuilder* abuilder,
66                                                  AnimatorScope* ascope,
67                                                  sk_sp<sksg::RenderNode> layer) {
68     enum : size_t {
69         kHiColor_Index     = 0,
70         kMiColor_Index     = 1,
71         kLoColor_Index     = 2,
72         kBlendAmount_Index = 3,
73 
74         kMax_Index         = kBlendAmount_Index,
75     };
76 
77     if (jprops.size() <= kMax_Index) {
78         return nullptr;
79     }
80 
81     const skjson::ObjectValue* hicolor_prop = jprops[    kHiColor_Index];
82     const skjson::ObjectValue* micolor_prop = jprops[    kMiColor_Index];
83     const skjson::ObjectValue* locolor_prop = jprops[    kLoColor_Index];
84     const skjson::ObjectValue*   blend_prop = jprops[kBlendAmount_Index];
85 
86     if (!hicolor_prop || !micolor_prop || !locolor_prop || !blend_prop) {
87         return nullptr;
88     }
89 
90     auto tritone_node =
91             sksg::GradientColorFilter::Make(std::move(layer), {
92                                             abuilder->attachColor(*locolor_prop, ascope, "v"),
93                                             abuilder->attachColor(*micolor_prop, ascope, "v"),
94                                             abuilder->attachColor(*hicolor_prop, ascope, "v") });
95     if (!tritone_node) {
96         return nullptr;
97     }
98 
99     abuilder->bindProperty<ScalarValue>((*blend_prop)["v"], ascope,
100         [tritone_node](const ScalarValue& w) {
101             tritone_node->setWeight((100 - w) / 100); // 100-based, inverted (!?).
102         });
103 
104     return std::move(tritone_node);
105 }
106 
AttachFillLayerEffect(const skjson::ArrayValue & jprops,const AnimationBuilder * abuilder,AnimatorScope * ascope,sk_sp<sksg::RenderNode> layer)107 sk_sp<sksg::RenderNode> AttachFillLayerEffect(const skjson::ArrayValue& jprops,
108                                               const AnimationBuilder* abuilder,
109                                               AnimatorScope* ascope,
110                                               sk_sp<sksg::RenderNode> layer) {
111     enum : size_t {
112         kFillMask_Index = 0,
113         kAllMasks_Index = 1,
114         kColor_Index    = 2,
115         kInvert_Index   = 3,
116         kHFeather_Index = 4,
117         kVFeather_Index = 5,
118         kOpacity_Index  = 6,
119 
120         kMax_Index      = kOpacity_Index,
121     };
122 
123     if (jprops.size() <= kMax_Index) {
124         return nullptr;
125     }
126 
127     const skjson::ObjectValue*   color_prop = jprops[  kColor_Index];
128     const skjson::ObjectValue* opacity_prop = jprops[kOpacity_Index];
129     if (!color_prop || !opacity_prop) {
130         return nullptr;
131     }
132 
133     sk_sp<sksg::Color> color_node = abuilder->attachColor(*color_prop, ascope, "v");
134     if (!color_node) {
135         return nullptr;
136     }
137 
138     abuilder->bindProperty<ScalarValue>((*opacity_prop)["v"], ascope,
139         [color_node](const ScalarValue& o) {
140             const auto c = color_node->getColor();
141             const auto a = sk_float_round2int_no_saturate(SkTPin(o, 0.0f, 1.0f) * 255);
142             color_node->setColor(SkColorSetA(c, a));
143         });
144 
145     return sksg::ModeColorFilter::Make(std::move(layer),
146                                        std::move(color_node),
147                                        SkBlendMode::kSrcIn);
148 }
149 
AttachDropShadowLayerEffect(const skjson::ArrayValue & jprops,const AnimationBuilder * abuilder,AnimatorScope * ascope,sk_sp<sksg::RenderNode> layer)150 sk_sp<sksg::RenderNode> AttachDropShadowLayerEffect(const skjson::ArrayValue& jprops,
151                                                     const AnimationBuilder* abuilder,
152                                                     AnimatorScope* ascope,
153                                                     sk_sp<sksg::RenderNode> layer) {
154     enum : size_t {
155         kShadowColor_Index = 0,
156         kOpacity_Index     = 1,
157         kDirection_Index   = 2,
158         kDistance_Index    = 3,
159         kSoftness_Index    = 4,
160         kShadowOnly_Index  = 5,
161 
162         kMax_Index         = kShadowOnly_Index,
163     };
164 
165     if (jprops.size() <= kMax_Index) {
166         return nullptr;
167     }
168 
169     const skjson::ObjectValue*       color_prop = jprops[kShadowColor_Index];
170     const skjson::ObjectValue*     opacity_prop = jprops[    kOpacity_Index];
171     const skjson::ObjectValue*   direction_prop = jprops[  kDirection_Index];
172     const skjson::ObjectValue*    distance_prop = jprops[   kDistance_Index];
173     const skjson::ObjectValue*    softness_prop = jprops[   kSoftness_Index];
174     const skjson::ObjectValue* shadow_only_prop = jprops[ kShadowOnly_Index];
175 
176     if (!color_prop ||
177         !opacity_prop ||
178         !direction_prop ||
179         !distance_prop ||
180         !softness_prop ||
181         !shadow_only_prop) {
182         return nullptr;
183     }
184 
185     auto shadow_effect  = sksg::DropShadowImageFilter::Make();
186     auto shadow_adapter = sk_make_sp<DropShadowEffectAdapter>(shadow_effect);
187 
188     abuilder->bindProperty<VectorValue>((*color_prop)["v"], ascope,
189         [shadow_adapter](const VectorValue& c) {
190             shadow_adapter->setColor(ValueTraits<VectorValue>::As<SkColor>(c));
191         });
192     abuilder->bindProperty<ScalarValue>((*opacity_prop)["v"], ascope,
193         [shadow_adapter](const ScalarValue& o) {
194             shadow_adapter->setOpacity(o);
195         });
196     abuilder->bindProperty<ScalarValue>((*direction_prop)["v"], ascope,
197         [shadow_adapter](const ScalarValue& d) {
198             shadow_adapter->setDirection(d);
199         });
200     abuilder->bindProperty<ScalarValue>((*distance_prop)["v"], ascope,
201         [shadow_adapter](const ScalarValue& d) {
202             shadow_adapter->setDistance(d);
203         });
204     abuilder->bindProperty<ScalarValue>((*softness_prop)["v"], ascope,
205         [shadow_adapter](const ScalarValue& s) {
206             shadow_adapter->setSoftness(s);
207         });
208     abuilder->bindProperty<ScalarValue>((*shadow_only_prop)["v"], ascope,
209         [shadow_adapter](const ScalarValue& s) {
210             shadow_adapter->setShadowOnly(SkToBool(s));
211         });
212 
213     return sksg::ImageFilterEffect::Make(std::move(layer), std::move(shadow_effect));
214 }
215 
AttachGaussianBlurLayerEffect(const skjson::ArrayValue & jprops,const AnimationBuilder * abuilder,AnimatorScope * ascope,sk_sp<sksg::RenderNode> layer)216 sk_sp<sksg::RenderNode> AttachGaussianBlurLayerEffect(const skjson::ArrayValue& jprops,
217                                                       const AnimationBuilder* abuilder,
218                                                       AnimatorScope* ascope,
219                                                       sk_sp<sksg::RenderNode> layer) {
220     enum : size_t {
221         kBlurriness_Index = 0,
222         kDimensions_Index = 1,
223         kRepeatEdge_Index = 2,
224 
225         kMax_Index        = kRepeatEdge_Index,
226     };
227 
228     if (jprops.size() <= kMax_Index) {
229         return nullptr;
230     }
231 
232     const skjson::ObjectValue* blurriness_prop = jprops[kBlurriness_Index];
233     const skjson::ObjectValue* dimensions_prop = jprops[kDimensions_Index];
234     const skjson::ObjectValue* repeatedge_prop = jprops[kRepeatEdge_Index];
235 
236     if (!blurriness_prop || !dimensions_prop || !repeatedge_prop) {
237         return nullptr;
238     }
239 
240     auto blur_effect   = sksg::BlurImageFilter::Make();
241     auto blur_addapter = sk_make_sp<GaussianBlurEffectAdapter>(blur_effect);
242 
243     abuilder->bindProperty<ScalarValue>((*blurriness_prop)["v"], ascope,
244         [blur_addapter](const ScalarValue& b) {
245             blur_addapter->setBlurriness(b);
246         });
247     abuilder->bindProperty<ScalarValue>((*dimensions_prop)["v"], ascope,
248         [blur_addapter](const ScalarValue& d) {
249             blur_addapter->setDimensions(d);
250         });
251     abuilder->bindProperty<ScalarValue>((*repeatedge_prop)["v"], ascope,
252         [blur_addapter](const ScalarValue& r) {
253             blur_addapter->setRepeatEdge(r);
254         });
255 
256     return sksg::ImageFilterEffect::Make(std::move(layer), std::move(blur_effect));
257 }
258 
259 } // namespace
260 
attachLayerEffects(const skjson::ArrayValue & jeffects,AnimatorScope * ascope,sk_sp<sksg::RenderNode> layer) const261 sk_sp<sksg::RenderNode> AnimationBuilder::attachLayerEffects(const skjson::ArrayValue& jeffects,
262                                                              AnimatorScope* ascope,
263                                                              sk_sp<sksg::RenderNode> layer) const {
264     if (!layer) {
265         return nullptr;
266     }
267 
268     enum : int32_t {
269         kTint_Effect         = 20,
270         kFill_Effect         = 21,
271         kTritone_Effect      = 23,
272         kDropShadow_Effect   = 25,
273         kGaussianBlur_Effect = 29,
274     };
275 
276     for (const skjson::ObjectValue* jeffect : jeffects) {
277         if (!jeffect) {
278             continue;
279         }
280 
281         const skjson::ArrayValue* jprops = (*jeffect)["ef"];
282         if (!jprops) {
283             continue;
284         }
285 
286         switch (const auto ty = ParseDefault<int>((*jeffect)["ty"], -1)) {
287         case kTint_Effect:
288             layer = AttachTintLayerEffect(*jprops, this, ascope, std::move(layer));
289             break;
290         case kFill_Effect:
291             layer = AttachFillLayerEffect(*jprops, this, ascope, std::move(layer));
292             break;
293         case kTritone_Effect:
294             layer = AttachTritoneLayerEffect(*jprops, this, ascope, std::move(layer));
295             break;
296         case kDropShadow_Effect:
297             layer = AttachDropShadowLayerEffect(*jprops, this, ascope, std::move(layer));
298             break;
299         case kGaussianBlur_Effect:
300             layer = AttachGaussianBlurLayerEffect(*jprops, this, ascope, std::move(layer));
301             break;
302         default:
303             this->log(Logger::Level::kWarning, nullptr, "Unsupported layer effect type: %d.", ty);
304             break;
305         }
306 
307         if (!layer) {
308             this->log(Logger::Level::kError, jeffect, "Invalid layer effect.");
309             return nullptr;
310         }
311     }
312 
313     return layer;
314 }
315 
316 } // namespace internal
317 } // namespace skottie
318