• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.test.hwui;
18 
19 import android.app.Activity;
20 import android.content.Context;
21 import android.graphics.Canvas;
22 import android.graphics.CanvasProperty;
23 import android.graphics.Color;
24 import android.graphics.Paint;
25 import android.graphics.RecordingCanvas;
26 import android.graphics.RuntimeShader;
27 import android.os.Bundle;
28 import android.os.Trace;
29 import android.view.RenderNodeAnimator;
30 import android.view.View;
31 import android.widget.LinearLayout;
32 import android.widget.LinearLayout.LayoutParams;
33 
34 import java.util.ArrayList;
35 
36 public class RippleActivity extends Activity {
37     @Override
onCreate(Bundle savedInstanceState)38     protected void onCreate(Bundle savedInstanceState) {
39         super.onCreate(savedInstanceState);
40 
41         final LinearLayout layout = new LinearLayout(this);
42         layout.setOrientation(LinearLayout.VERTICAL);
43         layout.addView(new RippleView(this),
44                 new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
45 
46         setContentView(layout);
47     }
48 
49     static class RippleView extends View {
50         static final int DURATION = 1000;
51         static final int MAX_RADIUS = 250;
52         private final int mColor = Color.RED;
53 
54         private boolean mToggle = false;
55         ArrayList<RenderNodeAnimator> mRunningAnimations = new ArrayList<RenderNodeAnimator>();
56 
57         CanvasProperty<Float> mX;
58         CanvasProperty<Float> mY;
59         CanvasProperty<Float> mRadius;
60         CanvasProperty<Float> mProgress;
61         CanvasProperty<Float> mNoisePhase;
62         CanvasProperty<Paint> mPaint;
63         RuntimeShader mRuntimeShader;
64 
65         static final String sSkSL = ""
66                 + "uniform float2 in_origin;"
67                 + "uniform float in_progress;\n"
68                 + "uniform float in_maxRadius;\n"
69                 + "uniform shader in_paintColor;\n"
70                 + "float dist2(float2 p0, float2 pf) { return sqrt((pf.x - p0.x) * (pf.x - p0.x) + "
71                 + "(pf.y - p0.y) * (pf.y - p0.y)); }\n"
72                 + "float mod2(float a, float b) { return a - (b * floor(a / b)); }\n"
73                 + "float rand(float2 src) { return fract(sin(dot(src.xy, float2(12.9898, 78.233)))"
74                 + " * 43758.5453123); }\n"
75                 + "float4 main(float2 p)\n"
76                 + "{\n"
77                 + "    float fraction = in_progress;\n"
78                 + "    float2 fragCoord = p;//sk_FragCoord.xy;\n"
79                 + "    float maxDist = in_maxRadius;\n"
80                 + "    float fragDist = dist2(in_origin, fragCoord.xy);\n"
81                 + "    float circleRadius = maxDist * fraction;\n"
82                 + "    float colorVal = (fragDist - circleRadius) / maxDist;\n"
83                 + "    float d = fragDist < circleRadius \n"
84                 + "        ? 1. - abs(colorVal * 2. * smoothstep(0., 1., fraction)) \n"
85                 + "        : 1. - abs(colorVal * 3.);\n"
86                 + "    d = smoothstep(0., 1., d);\n"
87                 + "    float divider = 2.;\n"
88                 + "    float x = floor(fragCoord.x / divider);\n"
89                 + "    float y = floor(fragCoord.y / divider);\n"
90                 + "    float density = .95;\n"
91                 + "    d = rand(float2(x, y)) > density ? d : d * .2;\n"
92                 + "    d = d * rand(float2(fraction, x * y));\n"
93                 + "    float alpha = 1. - pow(fraction, 3.);\n"
94                 + "    return float4(sample(in_paintColor, p).rgb, d * alpha);\n"
95                 + "}";
96 
RippleView(Context c)97         RippleView(Context c) {
98             super(c);
99             setClickable(true);
100 
101             mX = CanvasProperty.createFloat(200.0f);
102             mY = CanvasProperty.createFloat(200.0f);
103             mRadius = CanvasProperty.createFloat(150.0f);
104             mProgress = CanvasProperty.createFloat(0.0f);
105             mNoisePhase = CanvasProperty.createFloat(0.0f);
106 
107             Paint p = new Paint();
108             p.setAntiAlias(true);
109             p.setColor(mColor);
110             mPaint = CanvasProperty.createPaint(p);
111 
112             mRuntimeShader = new RuntimeShader(sSkSL, false);
113             mRuntimeShader.setUniform("in_maxRadius", MAX_RADIUS);
114         }
115 
116         @Override
onDraw(Canvas canvas)117         protected void onDraw(Canvas canvas) {
118             super.onDraw(canvas);
119 
120             if (canvas.isHardwareAccelerated()) {
121                 RecordingCanvas recordingCanvas = (RecordingCanvas) canvas;
122                 recordingCanvas.drawRipple(mX, mY, mRadius, mPaint, mProgress, mNoisePhase,
123                         mColor, mRuntimeShader);
124             }
125         }
126 
127         @Override
performClick()128         public boolean performClick() {
129             for (int i = 0; i < mRunningAnimations.size(); i++) {
130                 mRunningAnimations.get(i).cancel();
131             }
132             mRunningAnimations.clear();
133 
134             mToggle = !mToggle;
135 
136             mRunningAnimations.add(new RenderNodeAnimator(
137                     mX, mToggle ? 400.0f : 200.0f));
138 
139             mRunningAnimations.add(new RenderNodeAnimator(
140                     mY, mToggle ? 600.0f : 200.0f));
141 
142             mRunningAnimations.add(new RenderNodeAnimator(
143                     mRadius, mToggle ? MAX_RADIUS : 150.0f));
144 
145             mRunningAnimations.add(new RenderNodeAnimator(
146                     mProgress, mToggle ? 1.0f : 0.0f));
147 
148             mRunningAnimations.add(new RenderNodeAnimator(
149                     mNoisePhase, DURATION));
150 
151             mRunningAnimations.add(new RenderNodeAnimator(
152                     mPaint, RenderNodeAnimator.PAINT_ALPHA, 64.0f));
153 
154             // Will be "chained" to run after the above
155             mRunningAnimations.add(new RenderNodeAnimator(
156                     mPaint, RenderNodeAnimator.PAINT_ALPHA, 255.0f));
157 
158             for (int i = 0; i < mRunningAnimations.size(); i++) {
159                 RenderNodeAnimator anim = mRunningAnimations.get(i);
160                 anim.setDuration(DURATION);
161                 anim.setTarget(this);
162                 if (i == (mRunningAnimations.size() - 1)) {
163                     // "chain" test
164                     anim.setStartValue(64.0f);
165                     anim.setStartDelay(anim.getDuration());
166                 }
167                 anim.start();
168             }
169 
170             if (mToggle) {
171                 post(new Runnable() {
172                     @Override
173                     public void run() {
174                         Trace.traceBegin(Trace.TRACE_TAG_VIEW, "pretendBusy");
175                         try {
176                             Thread.sleep(DURATION);
177                         } catch (InterruptedException e) {
178                         }
179                         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
180                     }
181                 });
182             }
183             return true;
184         }
185     }
186 }
187