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