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