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