1 /*
2  * Copyright 2021 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 "include/effects/SkRuntimeEffect.h"
11 #include "include/utils/SkRandom.h"
12 #include "modules/skottie/src/Adapter.h"
13 #include "modules/skottie/src/SkottieJson.h"
14 #include "modules/skottie/src/SkottieValue.h"
15 #include "modules/sksg/include/SkSGRenderNode.h"
16 
17 namespace skottie::internal {
18 namespace {
19 
20 // An implementation of the ADBE Fractal Noise effect:
21 //
22 //  - multiple noise sublayers (octaves) are combined using a weighted average
23 //  - each layer is subject to a (cumulative) transform, filter and post-sampling options
24 //
25 // Parameters:
26 //
27 //   * Noise Type    -- controls noise layer post-sampling filtering
28 //                      (Block, Linear, Soft Linear, Spline)
29 //   * Fractal Type  -- determines a noise layer post-filtering transformation
30 //                      (Basic, Turbulent Smooth, Turbulent Basic, etc)
31 //   * Transform     -- offset/scale/rotate the noise effect (local matrix)
32 //
33 //   * Complexity    -- number of sublayers;
34 //                      can be fractional, where the fractional part modulates the last layer
35 //   * Evolution     -- controls noise topology in a gradual manner (can be animated for smooth
36 //                      noise transitions)
37 //   * Sub Influence -- relative amplitude weight for sublayers (cumulative)
38 //
39 //   * Sub Scaling/Rotation/Offset -- relative scale for sublayers (cumulative)
40 //
41 //   * Invert        -- invert noise values
42 //
43 //   * Contrast      -- apply a contrast to the noise result
44 //
45 //   * Brightness    -- apply a brightness effect to the noise result
46 //
47 //
48 // TODO:
49 //   - Invert
50 //   - Contrast/Brightness
51 
52 static constexpr char gNoiseEffectSkSL[] =
53     "uniform half3x3 u_submatrix;" // sublayer transform
54 
55     "uniform half u_octaves,"      // number of octaves (can be fractional)
56                  "u_persistence,"  // relative octave weight
57                  "u_evolution;"    // evolution/seed
58 
59     // Hash based on hash13 (https://www.shadertoy.com/view/4djSRW).
60     "half hash(half3 v) {"
61         "v  = fract(v*0.1031);"
62         "v += dot(v, v.zxy + 31.32);"
63         "return fract((v.x + v.y)*v.z);"
64     "}"
65 
66     // The general idea is to compute a coherent hash for two planes in discretized (x,y,e) space,
67     // and interpolate between them.  This yields gradual changes when animating |e| - which is the
68     // desired outcome.
69     "half sample_noise(vec2 xy) {"
70         "xy = floor(xy);"
71 
72         "half e_ = floor(u_evolution),"
73              "t  = u_evolution - e_,"
74              "n0 = hash(half3(xy, e_ + 0)),"
75              "n1 = hash(half3(xy, e_ + 1));"
76 
77         // Note: Ideally we would use 4 samples (-1, 0, 1, 2) and cubic interpolation for
78         //       better results -- but that's significantly more expensive than lerp.
79 
80         "return mix(n0, n1, t);"
81     "}"
82 
83     // filter() placeholder
84     "%s"
85 
86     // fractal() placeholder
87     "%s"
88 
89     // Generate ceil(u_octaves) noise layers and combine based on persistentce and sublayer xform.
90     "half4 main(vec2 xy) {"
91         "half oct = u_octaves," // initial octave count (this is the effective loop counter)
92              "amp = 1,"         // initial layer amplitude
93             "wacc = 0,"         // weight accumulator
94                "n = 0;"         // noise accumulator
95 
96         // Constant loop counter chosen to be >= ceil(u_octaves).
97         // The logical counter is actually 'oct'.
98         "for (half i = 0; i < %u; ++i) {"
99             // effective layer weight computed to accommodate fixed loop counters
100             //
101             //   -- for full octaves:              layer amplitude
102             //   -- for fractional octave:         layer amplitude modulated by fractional part
103             //   -- for octaves > ceil(u_octaves): 0
104             //
105             // e.g. for 6 loops and u_octaves = 2.3, this generates the sequence [1,1,.3,0,0]
106             "half w = amp*saturate(oct);"
107 
108             "n += w*fractal(filter(xy));"
109 
110             "wacc += w;"
111             "amp  *= u_persistence;"
112             "oct  -= 1;"
113 
114             "xy = (u_submatrix*half3(xy,1)).xy;"
115         "}"
116 
117         "n /= wacc;"
118 
119         // TODO: fractal functions
120 
121         "return half4(n,n,n,1);"
122     "}";
123 
124 static constexpr char gFilterNearestSkSL[] =
125     "half filter(half2 xy) {"
126         "return sample_noise(xy);"
127     "}";
128 
129 static constexpr char gFilterLinearSkSL[] =
130     "half filter(half2 xy) {"
131         "xy -= 0.5;"
132 
133         "half n00 = sample_noise(xy + half2(0,0)),"
134              "n10 = sample_noise(xy + half2(1,0)),"
135              "n01 = sample_noise(xy + half2(0,1)),"
136              "n11 = sample_noise(xy + half2(1,1));"
137 
138         "half2 t = fract(xy);"
139 
140         "return mix(mix(n00, n10, t.x), mix(n01, n11, t.x), t.y);"
141     "}";
142 
143 static constexpr char gFilterSoftLinearSkSL[] =
144     "half filter(half2 xy) {"
145         "xy -= 0.5;"
146 
147         "half n00 = sample_noise(xy + half2(0,0)),"
148              "n10 = sample_noise(xy + half2(1,0)),"
149              "n01 = sample_noise(xy + half2(0,1)),"
150              "n11 = sample_noise(xy + half2(1,1));"
151 
152         "half2 t = smoothstep(0, 1, fract(xy));"
153 
154         "return mix(mix(n00, n10, t.x), mix(n01, n11, t.x), t.y);"
155     "}";
156 
157 static constexpr char gFractalBasicSkSL[] =
158     "half fractal(half n) {"
159         "return n;"
160     "}";
161 
162 static constexpr char gFractalTurbulentBasicSkSL[] =
163     "half fractal(half n) {"
164         "return 2*abs(0.5 - n);"
165     "}";
166 
167 static constexpr char gFractalTurbulentSmoothSkSL[] =
168     "half fractal(half n) {"
169         "n = 2*abs(0.5 - n);"
170         "return n*n;"
171     "}";
172 
173 static constexpr char gFractalTurbulentSharpSkSL[] =
174     "half fractal(half n) {"
175         "return sqrt(2*abs(0.5 - n));"
176     "}";
177 
178 enum class NoiseFilter {
179     kNearest,
180     kLinear,
181     kSoftLinear,
182     // TODO: kSpline?
183 };
184 
185 enum class NoiseFractal {
186     kBasic,
187     kTurbulentBasic,
188     kTurbulentSmooth,
189     kTurbulentSharp,
190 };
191 
make_noise_effect(unsigned loops,const char * filter,const char * fractal)192 sk_sp<SkRuntimeEffect> make_noise_effect(unsigned loops, const char* filter, const char* fractal) {
193     auto result = SkRuntimeEffect::MakeForShader(
194             SkStringPrintf(gNoiseEffectSkSL, filter, fractal, loops), {});
195 
196     if (0 && !result.effect) {
197         printf("!!! %s\n", result.errorText.c_str());
198     }
199 
200     return std::move(result.effect);
201 }
202 
203 template <unsigned LOOPS, NoiseFilter FILTER, NoiseFractal FRACTAL>
noise_effect()204 sk_sp<SkRuntimeEffect> noise_effect() {
205     static constexpr char const* gFilters[] = {
206         gFilterNearestSkSL,
207         gFilterLinearSkSL,
208         gFilterSoftLinearSkSL
209     };
210 
211     static constexpr char const* gFractals[] = {
212         gFractalBasicSkSL,
213         gFractalTurbulentBasicSkSL,
214         gFractalTurbulentSmoothSkSL,
215         gFractalTurbulentSharpSkSL
216     };
217 
218     static_assert(static_cast<size_t>(FILTER)  < SK_ARRAY_COUNT(gFilters));
219     static_assert(static_cast<size_t>(FRACTAL) < SK_ARRAY_COUNT(gFractals));
220 
221     static const SkRuntimeEffect* effect =
222             make_noise_effect(LOOPS,
223                               gFilters[static_cast<size_t>(FILTER)],
224                               gFractals[static_cast<size_t>(FRACTAL)])
225             .release();
226 
227     SkASSERT(effect);
228     return sk_ref_sp(effect);
229 }
230 
231 class FractalNoiseNode final : public sksg::CustomRenderNode {
232 public:
FractalNoiseNode(sk_sp<RenderNode> child)233     explicit FractalNoiseNode(sk_sp<RenderNode> child) : INHERITED({std::move(child)}) {}
234 
235     SG_ATTRIBUTE(Matrix      , SkMatrix    , fMatrix     )
236     SG_ATTRIBUTE(SubMatrix   , SkMatrix    , fSubMatrix  )
237 
238     SG_ATTRIBUTE(NoiseFilter , NoiseFilter , fFilter     )
239     SG_ATTRIBUTE(NoiseFractal, NoiseFractal, fFractal    )
240     SG_ATTRIBUTE(Octaves     , float       , fOctaves    )
241     SG_ATTRIBUTE(Persistence , float       , fPersistence)
242     SG_ATTRIBUTE(Evolution   , float       , fEvolution  )
243 
244 private:
245     template <NoiseFilter FI, NoiseFractal FR>
getEffect() const246     sk_sp<SkRuntimeEffect> getEffect() const {
247         // Bin the loop counter based on the number of octaves (range: [1..20]).
248         // Low complexities are common, so we maximize resolution for the low end.
249         if (fOctaves > 8) return noise_effect<20, FI, FR>();
250         if (fOctaves > 4) return noise_effect< 8, FI, FR>();
251         if (fOctaves > 3) return noise_effect< 4, FI, FR>();
252         if (fOctaves > 2) return noise_effect< 3, FI, FR>();
253         if (fOctaves > 1) return noise_effect< 2, FI, FR>();
254 
255         return noise_effect<1, FI, FR>();
256     }
257 
258     template <NoiseFilter FI>
getEffect() const259     sk_sp<SkRuntimeEffect> getEffect() const {
260         switch (fFractal) {
261             case NoiseFractal::kBasic:
262                 return this->getEffect<FI, NoiseFractal::kBasic>();
263             case NoiseFractal::kTurbulentBasic:
264                 return this->getEffect<FI, NoiseFractal::kTurbulentBasic>();
265             case NoiseFractal::kTurbulentSmooth:
266                 return this->getEffect<FI, NoiseFractal::kTurbulentSmooth>();
267             case NoiseFractal::kTurbulentSharp:
268                 return this->getEffect<FI, NoiseFractal::kTurbulentSharp>();
269         }
270         SkUNREACHABLE;
271     }
272 
getEffect() const273     sk_sp<SkRuntimeEffect> getEffect() const {
274         switch (fFilter) {
275             case NoiseFilter::kNearest   : return this->getEffect<NoiseFilter::kNearest>();
276             case NoiseFilter::kLinear    : return this->getEffect<NoiseFilter::kLinear>();
277             case NoiseFilter::kSoftLinear: return this->getEffect<NoiseFilter::kSoftLinear>();
278         }
279         SkUNREACHABLE;
280     }
281 
buildEffectShader() const282     sk_sp<SkShader> buildEffectShader() const {
283         SkRuntimeShaderBuilder builder(this->getEffect());
284 
285         builder.uniform("u_octaves")     = fOctaves;
286         builder.uniform("u_persistence") = fPersistence;
287         builder.uniform("u_evolution")   = fEvolution;
288         builder.uniform("u_submatrix")   = std::array<float,9>{
289             fSubMatrix.rc(0,0), fSubMatrix.rc(1,0), fSubMatrix.rc(2,0),
290             fSubMatrix.rc(0,1), fSubMatrix.rc(1,1), fSubMatrix.rc(2,1),
291             fSubMatrix.rc(0,2), fSubMatrix.rc(1,2), fSubMatrix.rc(2,2),
292         };
293 
294         return builder.makeShader(&fMatrix, false);
295     }
296 
onRevalidate(sksg::InvalidationController * ic,const SkMatrix & ctm)297     SkRect onRevalidate(sksg::InvalidationController* ic, const SkMatrix& ctm) override {
298         const auto& child = this->children()[0];
299         const auto bounds = child->revalidate(nullptr, SkMatrix::I());
300 
301         fEffectShader = this->buildEffectShader();
302 
303         return bounds;
304     }
305 
onRender(SkCanvas * canvas,const RenderContext * ctx) const306     void onRender(SkCanvas* canvas, const RenderContext* ctx) const override {
307         const auto& bounds = this->bounds();
308         const auto local_ctx = ScopedRenderContext(canvas, ctx)
309                 .setIsolation(bounds, canvas->getTotalMatrix(), true);
310 
311         canvas->saveLayer(&bounds, nullptr);
312         this->children()[0]->render(canvas, local_ctx);
313 
314         SkPaint effect_paint;
315         effect_paint.setShader(fEffectShader);
316         effect_paint.setBlendMode(SkBlendMode::kSrcIn);
317 
318         canvas->drawPaint(effect_paint);
319     }
320 
onNodeAt(const SkPoint &) const321     const RenderNode* onNodeAt(const SkPoint&) const override { return nullptr; } // no hit-testing
322 
323     sk_sp<SkShader> fEffectShader;
324 
325     SkMatrix     fMatrix,
326                  fSubMatrix;
327     NoiseFilter  fFilter      = NoiseFilter::kNearest;
328     NoiseFractal fFractal     = NoiseFractal::kBasic;
329     float        fOctaves     = 1,
330                  fPersistence = 1,
331                  fEvolution   = 0;
332 
333     using INHERITED = sksg::CustomRenderNode;
334 };
335 
336 class FractalNoiseAdapter final : public DiscardableAdapterBase<FractalNoiseAdapter,
337                                                                 FractalNoiseNode> {
338 public:
FractalNoiseAdapter(const skjson::ArrayValue & jprops,const AnimationBuilder * abuilder,sk_sp<FractalNoiseNode> node)339     FractalNoiseAdapter(const skjson::ArrayValue& jprops,
340                         const AnimationBuilder* abuilder,
341                         sk_sp<FractalNoiseNode> node)
342         : INHERITED(std::move(node))
343     {
344         EffectBinder(jprops, *abuilder, this)
345             .bind( 0, fFractalType     )
346             .bind( 1, fNoiseType       )
347             .bind( 2, fInvert          )
348             .bind( 3, fContrast        )
349             .bind( 4, fBrightness      )
350              // 5 -- overflow
351              // 6 -- transform begin-group
352             .bind( 7, fRotation        )
353             .bind( 8, fUniformScaling  )
354             .bind( 9, fScale           )
355             .bind(10, fScaleWidth      )
356             .bind(11, fScaleHeight     )
357             .bind(12, fOffset          )
358              // 13 -- TODO: perspective offset
359              // 14 -- transform end-group
360             .bind(15, fComplexity      )
361              // 16 -- sub settings begin-group
362             .bind(17, fSubInfluence    )
363             .bind(18, fSubScale        )
364             .bind(19, fSubRotation     )
365             .bind(20, fSubOffset       )
366              // 21 -- center subscale
367              // 22 -- sub settings end-group
368             .bind(23, fEvolution       )
369              // 24 -- evolution options begin-group
370              // 25 -- cycle evolution
371              // 26 -- cycle revolution
372             .bind(27, fRandomSeed      )
373              // 28 -- evolution options end-group
374             .bind(29, fOpacity         );
375             // 30 -- TODO: blending mode
376     }
377 
378 private:
evolution() const379     float evolution() const {
380         // Constant chosen to visually match AE's evolution rate.
381         const auto evo = SkDegreesToRadians(fEvolution) * 0.25f;
382 
383         // The random seed determines an arbitrary start plane.
384         const auto base = SkRandom(static_cast<uint32_t>(fRandomSeed)).nextRangeU(0, 100);
385 
386         return evo + base;
387     }
388 
shaderMatrix() const389     SkMatrix shaderMatrix() const {
390         static constexpr float kGridSize = 64;
391 
392         const auto scale = (SkScalarRoundToInt(fUniformScaling) == 1)
393                 ? SkV2{fScale, fScale}
394                 : SkV2{fScaleWidth, fScaleHeight};
395 
396         return SkMatrix::Translate(fOffset.x, fOffset.y)
397              * SkMatrix::RotateDeg(fRotation)
398              * SkMatrix::Scale(SkTPin(scale.x, 1.0f, 10000.0f) * 0.01f,
399                                SkTPin(scale.y, 1.0f, 10000.0f) * 0.01f)
400              * SkMatrix::Scale(kGridSize, kGridSize);
401     }
402 
subMatrix() const403     SkMatrix subMatrix() const {
404         const auto scale = 100 / SkTPin(fSubScale, 10.0f, 10000.0f);
405 
406         return SkMatrix::Translate(-fSubOffset.x * 0.01f, -fSubOffset.y * 0.01f)
407              * SkMatrix::RotateDeg(-fSubRotation)
408              * SkMatrix::Scale(scale, scale);
409     }
410 
noiseFilter() const411     NoiseFilter noiseFilter() const {
412         switch (SkScalarRoundToInt(fNoiseType)) {
413             case 1:  return NoiseFilter::kNearest;
414             case 2:  return NoiseFilter::kLinear;
415             default: return NoiseFilter::kSoftLinear;
416         }
417         SkUNREACHABLE;
418     }
419 
noiseFractal() const420     NoiseFractal noiseFractal() const {
421         switch (SkScalarRoundToInt(fFractalType)) {
422             case 1:  return NoiseFractal::kBasic;
423             case 3:  return NoiseFractal::kTurbulentSmooth;
424             case 4:  return NoiseFractal::kTurbulentBasic;
425             default: return NoiseFractal::kTurbulentSharp;
426         }
427         SkUNREACHABLE;
428     }
429 
onSync()430     void onSync() override {
431         const auto& n = this->node();
432 
433         n->setOctaves(SkTPin(fComplexity, 1.0f, 20.0f));
434         n->setPersistence(SkTPin(fSubInfluence * 0.01f, 0.0f, 100.0f));
435         n->setEvolution(this->evolution());
436         n->setNoiseFilter(this->noiseFilter());
437         n->setNoiseFractal(this->noiseFractal());
438         n->setMatrix(this->shaderMatrix());
439         n->setSubMatrix(this->subMatrix());
440     }
441 
442     Vec2Value   fOffset           = {0,0},
443                 fSubOffset        = {0,0};
444 
445     ScalarValue fFractalType      =     0,
446                 fNoiseType        =     0,
447 
448                 fRotation         =     0,
449                 fUniformScaling   =     0,
450                 fScale            =   100,  // used when uniform scaling is selected
451                 fScaleWidth       =   100,  // used when uniform scaling is not selected
452                 fScaleHeight      =   100,  // ^
453 
454                 fComplexity       =     1,
455                 fSubInfluence     =   100,
456                 fSubScale         =    50,
457                 fSubRotation      =     0,
458 
459                 fEvolution        =     0,
460                 fRandomSeed       =     0,
461 
462                 fOpacity          =   100, // TODO
463                 fInvert           =     0, // TODO
464                 fContrast         =   100, // TODO
465                 fBrightness       =     0; // TODO
466 
467     using INHERITED = DiscardableAdapterBase<FractalNoiseAdapter, FractalNoiseNode>;
468 };
469 
470 } // namespace
471 
attachFractalNoiseEffect(const skjson::ArrayValue & jprops,sk_sp<sksg::RenderNode> layer) const472 sk_sp<sksg::RenderNode> EffectBuilder::attachFractalNoiseEffect(
473         const skjson::ArrayValue& jprops, sk_sp<sksg::RenderNode> layer) const {
474     auto fractal_noise = sk_make_sp<FractalNoiseNode>(std::move(layer));
475 
476     return fBuilder->attachDiscardableAdapter<FractalNoiseAdapter>(jprops, fBuilder,
477                                                                    std::move(fractal_noise));
478 }
479 
480 } // namespace skottie::internal
481