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.animation.ValueAnimator
20 import android.app.Activity
21 import android.graphics.Bitmap
22 import android.graphics.BitmapShader
23 import android.graphics.ImageDecoder
24 import android.graphics.Matrix
25 import android.graphics.Shader
26 import android.graphics.RenderEffect
27 import android.graphics.RuntimeShader
28 import android.os.Bundle
29 import android.view.View
30 
31 class RenderEffectViewActivity : Activity() {
32 
33     private val mDropsShader = RuntimeShader(dropsAGSL)
34     private var mDropsAnimator = ValueAnimator.ofFloat(0f, 1f)
35     private var mStartTime = System.currentTimeMillis()
36     private lateinit var mScratchesImage: Bitmap
37     private lateinit var mScratchesShader: BitmapShader
38 
39     override fun onCreate(savedInstanceState: Bundle?) {
40         super.onCreate(savedInstanceState)
41         setContentView(R.layout.view_runtime_shader)
42 
43         val dropsView = findViewById<View>(R.id.CardView)!!
44         dropsView.isClickable = true
45         dropsView.setOnClickListener {
46             if (mDropsAnimator.isRunning) {
47                 mDropsAnimator.cancel()
48                 dropsView.setRenderEffect(null)
49             } else {
50                 mDropsAnimator.start()
51             }
52         }
53 
54         val imgSource = ImageDecoder.createSource(resources, R.drawable.scratches)
55         mScratchesImage = ImageDecoder.decodeBitmap(imgSource)
56         mScratchesShader = BitmapShader(mScratchesImage,
57                                         Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
58 
59         mDropsAnimator.duration = 1000
60         mDropsAnimator.repeatCount = ValueAnimator.INFINITE
61         mDropsAnimator.addUpdateListener { _ ->
62             val viewWidth = dropsView.width.toFloat()
63             val viewHeight = dropsView.height.toFloat()
64             val scratchesMatrix = Matrix()
65             scratchesMatrix.postScale(viewWidth / mScratchesImage.width,
66                     viewHeight / mScratchesImage.height)
67             mScratchesShader.setLocalMatrix(scratchesMatrix)
68 
69             mDropsShader.setInputShader("scratches", mScratchesShader)
70             mDropsShader.setFloatUniform("elapsedSeconds",
71                                     (System.currentTimeMillis() - mStartTime) / 1000f)
72             mDropsShader.setFloatUniform("viewDimensions", viewWidth, viewHeight)
73 
74             val dropsEffect = RenderEffect.createRuntimeShaderEffect(mDropsShader, "background")
75             val blurEffect = RenderEffect.createBlurEffect(10f, 10f, Shader.TileMode.CLAMP)
76 
77             dropsView.setRenderEffect(RenderEffect.createChainEffect(dropsEffect, blurEffect))
78         }
79     }
80 
81     private companion object {
82         const val dropsAGSL = """
83             uniform float elapsedSeconds;
84             uniform vec2 viewDimensions;
85             uniform shader background;
86             uniform shader scratches;
87 
88             vec2 dropsUV(vec2 fragCoord ) {
89                 vec2 uv = fragCoord.xy / viewDimensions.xy; // 0 <> 1
90                 vec2 offs = vec2(0.);
91                 return (offs + uv).xy;
92             }
93 
94             const vec3  iFrostColorRGB = vec3(0.5, 0.5, 0.5);
95             const float iFrostColorAlpha = .3;
96 
97             half4 main(float2 fragCoord) {
98                 half4 bg = background.eval(dropsUV(fragCoord)*viewDimensions.xy);
99                 float2 scratchCoord = fragCoord.xy / viewDimensions.xy;;
100                 scratchCoord += 1.5;
101                 scratchCoord = mod(scratchCoord, 1);
102                 half scratch = scratches.eval(scratchCoord*viewDimensions.xy).r;
103                 bg.rgb = mix(bg.rgb, iFrostColorRGB, iFrostColorAlpha);
104                 bg.rgb = mix(bg.rgb, half3(1), pow(scratch,3));
105                 return bg;
106             }
107         """
108     }
109 }