1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.graphics.drawable; 18 19 import android.annotation.ColorInt; 20 import android.graphics.Color; 21 import android.graphics.RuntimeShader; 22 import android.graphics.Shader; 23 24 final class RippleShader extends RuntimeShader { 25 private static final String SHADER_UNIFORMS = "uniform vec2 in_origin;\n" 26 + "uniform vec2 in_touch;\n" 27 + "uniform float in_progress;\n" 28 + "uniform float in_maxRadius;\n" 29 + "uniform vec2 in_resolutionScale;\n" 30 + "uniform vec2 in_noiseScale;\n" 31 + "uniform float in_hasMask;\n" 32 + "uniform float in_noisePhase;\n" 33 + "uniform float in_turbulencePhase;\n" 34 + "uniform vec2 in_tCircle1;\n" 35 + "uniform vec2 in_tCircle2;\n" 36 + "uniform vec2 in_tCircle3;\n" 37 + "uniform vec2 in_tRotation1;\n" 38 + "uniform vec2 in_tRotation2;\n" 39 + "uniform vec2 in_tRotation3;\n" 40 + "uniform vec4 in_color;\n" 41 + "uniform vec4 in_sparkleColor;\n" 42 + "uniform shader in_shader;\n"; 43 private static final String SHADER_LIB = 44 "float triangleNoise(vec2 n) {\n" 45 + " n = fract(n * vec2(5.3987, 5.4421));\n" 46 + " n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));\n" 47 + " float xy = n.x * n.y;\n" 48 + " return fract(xy * 95.4307) + fract(xy * 75.04961) - 1.0;\n" 49 + "}" 50 + "const float PI = 3.1415926535897932384626;\n" 51 + "\n" 52 + "float threshold(float v, float l, float h) {\n" 53 + " return step(l, v) * (1.0 - step(h, v));\n" 54 + "}\n" 55 + "float sparkles(vec2 uv, float t) {\n" 56 + " float n = triangleNoise(uv);\n" 57 + " float s = 0.0;\n" 58 + " for (float i = 0; i < 4; i += 1) {\n" 59 + " float l = i * 0.1;\n" 60 + " float h = l + 0.05;\n" 61 + " float o = sin(PI * (t + 0.35 * i));\n" 62 + " s += threshold(n + o, l, h);\n" 63 + " }\n" 64 + " return saturate(s) * in_sparkleColor.a;\n" 65 + "}\n" 66 + "float softCircle(vec2 uv, vec2 xy, float radius, float blur) {\n" 67 + " float blurHalf = blur * 0.5;\n" 68 + " float d = distance(uv, xy);\n" 69 + " return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);\n" 70 + "}\n" 71 + "float softRing(vec2 uv, vec2 xy, float radius, float progress, float blur) {\n" 72 + " float thickness = 0.05 * radius;\n" 73 + " float currentRadius = radius * progress;\n" 74 + " float circle_outer = softCircle(uv, xy, currentRadius + thickness, blur);\n" 75 + " float circle_inner = softCircle(uv, xy, max(currentRadius - thickness, 0.), " 76 + " blur);\n" 77 + " return saturate(circle_outer - circle_inner);\n" 78 + "}\n" 79 + "float subProgress(float start, float end, float progress) {\n" 80 + " float sub = clamp(progress, start, end);\n" 81 + " return (sub - start) / (end - start); \n" 82 + "}\n" 83 + "mat2 rotate2d(vec2 rad){\n" 84 + " return mat2(rad.x, -rad.y, rad.y, rad.x);\n" 85 + "}\n" 86 + "float circle_grid(vec2 resolution, vec2 coord, float time, vec2 center,\n" 87 + " vec2 rotation, float cell_diameter) {\n" 88 + " coord = rotate2d(rotation) * (center - coord) + center;\n" 89 + " coord = mod(coord, cell_diameter) / resolution;\n" 90 + " float normal_radius = cell_diameter / resolution.y * 0.5;\n" 91 + " float radius = 0.65 * normal_radius;\n" 92 + " return softCircle(coord, vec2(normal_radius), radius, radius * 50.0);\n" 93 + "}\n" 94 + "float turbulence(vec2 uv, float t) {\n" 95 + " const vec2 scale = vec2(0.8);\n" 96 + " uv = uv * scale;\n" 97 + " float g1 = circle_grid(scale, uv, t, in_tCircle1, in_tRotation1, 0.17);\n" 98 + " float g2 = circle_grid(scale, uv, t, in_tCircle2, in_tRotation2, 0.2);\n" 99 + " float g3 = circle_grid(scale, uv, t, in_tCircle3, in_tRotation3, 0.275);\n" 100 + " float v = (g1 * g1 + g2 - g3) * 0.5;\n" 101 + " return saturate(0.45 + 0.8 * v);\n" 102 + "}\n"; 103 private static final String SHADER_MAIN = "vec4 main(vec2 p) {\n" 104 + " float fadeIn = subProgress(0., 0.13, in_progress);\n" 105 + " float scaleIn = subProgress(0., 1.0, in_progress);\n" 106 + " float fadeOutNoise = subProgress(0.4, 0.5, in_progress);\n" 107 + " float fadeOutRipple = subProgress(0.4, 1., in_progress);\n" 108 + " vec2 center = mix(in_touch, in_origin, saturate(in_progress * 2.0));\n" 109 + " float ring = softRing(p, center, in_maxRadius, scaleIn, 1.);\n" 110 + " float alpha = min(fadeIn, 1. - fadeOutNoise);\n" 111 + " vec2 uv = p * in_resolutionScale;\n" 112 + " vec2 densityUv = uv - mod(uv, in_noiseScale);\n" 113 + " float turbulence = turbulence(uv, in_turbulencePhase);\n" 114 + " float sparkleAlpha = sparkles(densityUv, in_noisePhase) * ring * alpha " 115 + "* turbulence;\n" 116 + " float fade = min(fadeIn, 1. - fadeOutRipple);\n" 117 + " float waveAlpha = softCircle(p, center, in_maxRadius * scaleIn, 1.) * fade " 118 + "* in_color.a;\n" 119 + " vec4 waveColor = vec4(in_color.rgb * waveAlpha, waveAlpha);\n" 120 + " vec4 sparkleColor = vec4(in_sparkleColor.rgb * in_sparkleColor.a, " 121 + "in_sparkleColor.a);\n" 122 + " float mask = in_hasMask == 1. ? sample(in_shader, p).a > 0. ? 1. : 0. : 1.;\n" 123 + " return mix(waveColor, sparkleColor, sparkleAlpha) * mask;\n" 124 + "}"; 125 private static final String SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN; 126 private static final double PI_ROTATE_RIGHT = Math.PI * 0.0078125; 127 private static final double PI_ROTATE_LEFT = Math.PI * -0.0078125; 128 RippleShader()129 RippleShader() { 130 super(SHADER, false); 131 } 132 setShader(Shader shader)133 public void setShader(Shader shader) { 134 if (shader != null) { 135 setInputShader("in_shader", shader); 136 } 137 setUniform("in_hasMask", shader == null ? 0 : 1); 138 } 139 setRadius(float radius)140 public void setRadius(float radius) { 141 setUniform("in_maxRadius", radius * 2.3f); 142 } 143 setOrigin(float x, float y)144 public void setOrigin(float x, float y) { 145 setUniform("in_origin", new float[] {x, y}); 146 } 147 setTouch(float x, float y)148 public void setTouch(float x, float y) { 149 setUniform("in_touch", new float[] {x, y}); 150 } 151 setProgress(float progress)152 public void setProgress(float progress) { 153 setUniform("in_progress", progress); 154 } 155 156 /** 157 * Continuous offset used as noise phase. 158 */ setNoisePhase(float phase)159 public void setNoisePhase(float phase) { 160 setUniform("in_noisePhase", phase * 0.001f); 161 162 // 163 // Keep in sync with: frameworks/base/libs/hwui/pipeline/skia/AnimatedDrawables.h 164 // 165 final float turbulencePhase = phase; 166 setUniform("in_turbulencePhase", turbulencePhase); 167 final float scale = 1.5f; 168 setUniform("in_tCircle1", new float[]{ 169 (float) (scale * 0.5 + (turbulencePhase * 0.01 * Math.cos(scale * 0.55))), 170 (float) (scale * 0.5 + (turbulencePhase * 0.01 * Math.sin(scale * 0.55))) 171 }); 172 setUniform("in_tCircle2", new float[]{ 173 (float) (scale * 0.2 + (turbulencePhase * -0.0066 * Math.cos(scale * 0.45))), 174 (float) (scale * 0.2 + (turbulencePhase * -0.0066 * Math.sin(scale * 0.45))) 175 }); 176 setUniform("in_tCircle3", new float[]{ 177 (float) (scale + (turbulencePhase * -0.0066 * Math.cos(scale * 0.35))), 178 (float) (scale + (turbulencePhase * -0.0066 * Math.sin(scale * 0.35))) 179 }); 180 final double rotation1 = turbulencePhase * PI_ROTATE_RIGHT + 1.7 * Math.PI; 181 setUniform("in_tRotation1", new float[]{ 182 (float) Math.cos(rotation1), (float) Math.sin(rotation1) 183 }); 184 final double rotation2 = turbulencePhase * PI_ROTATE_LEFT + 2 * Math.PI; 185 setUniform("in_tRotation2", new float[]{ 186 (float) Math.cos(rotation2), (float) Math.sin(rotation2) 187 }); 188 final double rotation3 = turbulencePhase * PI_ROTATE_RIGHT + 2.75 * Math.PI; 189 setUniform("in_tRotation3", new float[]{ 190 (float) Math.cos(rotation3), (float) Math.sin(rotation3) 191 }); 192 } 193 194 /** 195 * Color of the circle that's under the sparkles. Sparkles will always be white. 196 */ setColor(@olorInt int colorInt, @ColorInt int sparkleColorInt)197 public void setColor(@ColorInt int colorInt, @ColorInt int sparkleColorInt) { 198 Color color = Color.valueOf(colorInt); 199 Color sparkleColor = Color.valueOf(sparkleColorInt); 200 setUniform("in_color", new float[] {color.red(), 201 color.green(), color.blue(), color.alpha()}); 202 setUniform("in_sparkleColor", new float[] {sparkleColor.red(), 203 sparkleColor.green(), sparkleColor.blue(), sparkleColor.alpha()}); 204 } 205 setResolution(float w, float h)206 public void setResolution(float w, float h) { 207 final float densityScale = 2.1f; 208 setUniform("in_resolutionScale", new float[] {1f / w, 1f / h}); 209 setUniform("in_noiseScale", new float[] {densityScale / w, densityScale / h}); 210 } 211 } 212