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