xref: /aosp_15_r20/external/skia/gm/rippleshadergm.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2023 Google LLC
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 "gm/gm.h"
9*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkBitmap.h"
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkCanvas.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkData.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkPaint.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkRRect.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSize.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkString.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkSurface.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkGradientShader.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkImageFilters.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "include/effects/SkRuntimeEffect.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "include/gpu/ganesh/GrRecordingContext.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkRandom.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkColorSpacePriv.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/core/SkRuntimeEffectPriv.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "tools/DecodeUtils.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "tools/Resources.h"
26*c8dee2aaSAndroid Build Coastguard Worker 
27*c8dee2aaSAndroid Build Coastguard Worker #include <cmath>
28*c8dee2aaSAndroid Build Coastguard Worker 
29*c8dee2aaSAndroid Build Coastguard Worker class RippleShaderGM : public skiagm::GM {
30*c8dee2aaSAndroid Build Coastguard Worker public:
31*c8dee2aaSAndroid Build Coastguard Worker     static constexpr SkISize kSize = {512, 512};
32*c8dee2aaSAndroid Build Coastguard Worker 
onOnceBeforeDraw()33*c8dee2aaSAndroid Build Coastguard Worker     void onOnceBeforeDraw() override {
34*c8dee2aaSAndroid Build Coastguard Worker         // Load the mandrill into a shader.
35*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkImage> img = ToolUtils::GetResourceAsImage("images/mandrill_512.png");
36*c8dee2aaSAndroid Build Coastguard Worker         if (!img) {
37*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf("Unable to load mandrill_512 from resources directory");
38*c8dee2aaSAndroid Build Coastguard Worker             return;
39*c8dee2aaSAndroid Build Coastguard Worker         }
40*c8dee2aaSAndroid Build Coastguard Worker         fMandrill = img->makeShader(SkSamplingOptions());
41*c8dee2aaSAndroid Build Coastguard Worker 
42*c8dee2aaSAndroid Build Coastguard Worker         // Load RippleShader.rts into a SkRuntimeEffect.
43*c8dee2aaSAndroid Build Coastguard Worker         sk_sp<SkData> shaderData = GetResourceAsData("sksl/realistic/RippleShader.rts");
44*c8dee2aaSAndroid Build Coastguard Worker         if (!shaderData) {
45*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf("Unable to load ripple shader from resources directory");
46*c8dee2aaSAndroid Build Coastguard Worker             return;
47*c8dee2aaSAndroid Build Coastguard Worker         }
48*c8dee2aaSAndroid Build Coastguard Worker         auto [effect, error] = SkRuntimeEffect::MakeForShader(
49*c8dee2aaSAndroid Build Coastguard Worker                 SkString(static_cast<const char*>(shaderData->data()), shaderData->size()));
50*c8dee2aaSAndroid Build Coastguard Worker         if (!effect) {
51*c8dee2aaSAndroid Build Coastguard Worker             SkDebugf("Ripple shader failed to compile\n\n%s\n", error.c_str());
52*c8dee2aaSAndroid Build Coastguard Worker         }
53*c8dee2aaSAndroid Build Coastguard Worker         fEffect = std::move(effect);
54*c8dee2aaSAndroid Build Coastguard Worker     }
55*c8dee2aaSAndroid Build Coastguard Worker 
getName() const56*c8dee2aaSAndroid Build Coastguard Worker     SkString getName() const override { return SkString("rippleshader"); }
getISize()57*c8dee2aaSAndroid Build Coastguard Worker     SkISize getISize() override { return kSize; }
onAnimate(double nanos)58*c8dee2aaSAndroid Build Coastguard Worker     bool onAnimate(double nanos) override {
59*c8dee2aaSAndroid Build Coastguard Worker         fMillis = nanos / (1000. * 1000.);
60*c8dee2aaSAndroid Build Coastguard Worker         return true;
61*c8dee2aaSAndroid Build Coastguard Worker     }
62*c8dee2aaSAndroid Build Coastguard Worker 
onDraw(SkCanvas * canvas)63*c8dee2aaSAndroid Build Coastguard Worker     void onDraw(SkCanvas* canvas) override {
64*c8dee2aaSAndroid Build Coastguard Worker         SkPaint base;
65*c8dee2aaSAndroid Build Coastguard Worker         base.setShader(fMandrill);
66*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawRect(SkRect::MakeWH(kSize.width(), kSize.height()), base);
67*c8dee2aaSAndroid Build Coastguard Worker 
68*c8dee2aaSAndroid Build Coastguard Worker         // Uniform setting logic was imperfectly adapted from:
69*c8dee2aaSAndroid Build Coastguard Worker         //     frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java
70*c8dee2aaSAndroid Build Coastguard Worker         //     frameworks/base/graphics/java/android/graphics/drawable/RippleAnimationSession.java
71*c8dee2aaSAndroid Build Coastguard Worker 
72*c8dee2aaSAndroid Build Coastguard Worker         SkRuntimeShaderBuilder builder(fEffect);
73*c8dee2aaSAndroid Build Coastguard Worker         constexpr float ANIM_DURATION = 1500.0f;
74*c8dee2aaSAndroid Build Coastguard Worker         constexpr float NOISE_ANIMATION_DURATION = 7000.0f;
75*c8dee2aaSAndroid Build Coastguard Worker         constexpr float MAX_NOISE_PHASE = NOISE_ANIMATION_DURATION / 214.0f;
76*c8dee2aaSAndroid Build Coastguard Worker         constexpr float PI_ROTATE_RIGHT = SK_ScalarPI * 0.0078125f;
77*c8dee2aaSAndroid Build Coastguard Worker         constexpr float PI_ROTATE_LEFT = SK_ScalarPI * -0.0078125f;
78*c8dee2aaSAndroid Build Coastguard Worker 
79*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_origin")          = SkV2{kSize.width() / 2, kSize.height() / 2};
80*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_touch")           = SkV2{kSize.width() / 2, kSize.height() / 2};
81*c8dee2aaSAndroid Build Coastguard Worker         // Note that `in_progress` should actually be interpolated via FAST_OUT_SLOW_IN.
82*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_progress")        = this->sawtoothLerp(0.0f, 1.0f, ANIM_DURATION);
83*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_maxRadius")       = 400.0f;
84*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_resolutionScale") = SkV2{1.0f / kSize.width(), 1.0f / kSize.height()};
85*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_noiseScale")      = SkV2{2.1f / kSize.width(), 2.1f / kSize.height()};
86*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_hasMask")         = 1.0f;
87*c8dee2aaSAndroid Build Coastguard Worker 
88*c8dee2aaSAndroid Build Coastguard Worker         float phase = this->sawtoothLerp(0, MAX_NOISE_PHASE, NOISE_ANIMATION_DURATION);
89*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_noisePhase")      = phase;
90*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_turbulencePhase") = phase * 1000.0f;
91*c8dee2aaSAndroid Build Coastguard Worker 
92*c8dee2aaSAndroid Build Coastguard Worker         const float scale = 1.5f;
93*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_tCircle1") = SkV2{scale * .5f + (phase * 0.01f * cosf(scale * .55f)),
94*c8dee2aaSAndroid Build Coastguard Worker                                               scale * .5f + (phase * 0.01f * sinf(scale * .55f))};
95*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_tCircle2") = SkV2{scale * .2f + (phase * -.0066f * cosf(scale * .45f)),
96*c8dee2aaSAndroid Build Coastguard Worker                                               scale * .2f + (phase * -.0066f * sinf(scale * .45f))};
97*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_tCircle3") = SkV2{scale + (phase * -.0066f * cosf(scale * .35f)),
98*c8dee2aaSAndroid Build Coastguard Worker                                               scale + (phase * -.0066f * sinf(scale * .35f))};
99*c8dee2aaSAndroid Build Coastguard Worker 
100*c8dee2aaSAndroid Build Coastguard Worker         float rotation1 = phase * PI_ROTATE_RIGHT + 1.7f * SK_ScalarPI;
101*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_tRotation1") = SkV2{cosf(rotation1), sinf(rotation1)};
102*c8dee2aaSAndroid Build Coastguard Worker 
103*c8dee2aaSAndroid Build Coastguard Worker         float rotation2 = phase * PI_ROTATE_LEFT + 2.0f * SK_ScalarPI;
104*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_tRotation2") = SkV2{cosf(rotation2), sinf(rotation2)};
105*c8dee2aaSAndroid Build Coastguard Worker 
106*c8dee2aaSAndroid Build Coastguard Worker         float rotation3 = phase * PI_ROTATE_RIGHT + 2.75f * SK_ScalarPI;
107*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_tRotation3") = SkV2{cosf(rotation3), sinf(rotation3)};
108*c8dee2aaSAndroid Build Coastguard Worker 
109*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_color") = SkV4{0.0f, 0.6f, 0.0f, 1.0f};         // green
110*c8dee2aaSAndroid Build Coastguard Worker         builder.uniform("in_sparkleColor") = SkV4{1.0f, 1.0f, 1.0f, 1.0f};  // white
111*c8dee2aaSAndroid Build Coastguard Worker         builder.child("in_shader") = fMandrill;
112*c8dee2aaSAndroid Build Coastguard Worker 
113*c8dee2aaSAndroid Build Coastguard Worker         SkPaint sparkle;
114*c8dee2aaSAndroid Build Coastguard Worker         sparkle.setShader(builder.makeShader());
115*c8dee2aaSAndroid Build Coastguard Worker         canvas->drawRect(SkRect::MakeWH(kSize.width(), kSize.height()), sparkle);
116*c8dee2aaSAndroid Build Coastguard Worker     }
117*c8dee2aaSAndroid Build Coastguard Worker 
sawtoothLerp(float a,float b,float windowMs)118*c8dee2aaSAndroid Build Coastguard Worker     float sawtoothLerp(float a, float b, float windowMs) {
119*c8dee2aaSAndroid Build Coastguard Worker         float t = std::fmod(fMillis, windowMs) / windowMs;
120*c8dee2aaSAndroid Build Coastguard Worker         return a * (1. - t) + b * t;
121*c8dee2aaSAndroid Build Coastguard Worker     }
122*c8dee2aaSAndroid Build Coastguard Worker 
123*c8dee2aaSAndroid Build Coastguard Worker protected:
124*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkRuntimeEffect> fEffect;
125*c8dee2aaSAndroid Build Coastguard Worker     sk_sp<SkShader> fMandrill;
126*c8dee2aaSAndroid Build Coastguard Worker     float fMillis = 500.0f;  // this allows a non-animated single-frame capture to show the effect
127*c8dee2aaSAndroid Build Coastguard Worker 
128*c8dee2aaSAndroid Build Coastguard Worker };
129*c8dee2aaSAndroid Build Coastguard Worker 
130*c8dee2aaSAndroid Build Coastguard Worker DEF_GM(return new RippleShaderGM;)
131