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