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