• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * 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.content.Context
21 import android.graphics.BitmapShader
22 import android.graphics.Canvas
23 import android.graphics.ImageDecoder
24 import android.graphics.Matrix
25 import android.graphics.Paint
26 import android.graphics.RuntimeShader
27 import android.graphics.Shader
28 import android.util.AttributeSet
29 import android.view.View
30 
31 class BitmapTransitionView @JvmOverloads constructor(
32     context: Context,
33     attrs: AttributeSet? = null,
34     defStyleAttr: Int = 0
35 ) : View(context, attrs, defStyleAttr) {
36 
37     private val mPaint = Paint()
38     private val mImageA = ImageDecoder.decodeBitmap(
39             ImageDecoder.createSource(context.resources, R.drawable.large_photo))
40     private val mImageB = ImageDecoder.decodeBitmap(
41             ImageDecoder.createSource(context.resources, R.drawable.very_large_photo))
42     private val mShaderA = BitmapShader(mImageA, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
43     private val mShaderB = BitmapShader(mImageB, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
44     private val mShader = RuntimeShader(AGSL)
45     private var mCurrentProgress = -1f
46     private var mForwardProgress = true
47     private var mCurrentAnimator = ValueAnimator.ofFloat(-1f, 1f)
48 
49     init {
50         isClickable = true
51 
52         mCurrentAnimator.duration = 1500
53         mCurrentAnimator.addUpdateListener { animation ->
54             mCurrentProgress = animation.animatedValue as Float
55             postInvalidate()
56         }
57     }
58 
59     override fun performClick(): Boolean {
60         if (super.performClick()) return true
61 
62         if (mCurrentAnimator.isRunning) {
63             mCurrentAnimator.reverse()
64             return true
65         }
66 
67         if (mForwardProgress) {
68             mCurrentAnimator.setFloatValues(-1f, 1f)
69             mForwardProgress = false
70         } else {
71             mCurrentAnimator.setFloatValues(1f, -1f)
72             mForwardProgress = true
73         }
74 
75         mCurrentAnimator.start()
76         postInvalidate()
77         return true
78     }
79 
80     override fun onSizeChanged(width: Int, height: Int, oldWidth: Int, oldHeight: Int) {
81         val matrixA = Matrix()
82         val matrixB = Matrix()
83 
84         matrixA.postScale(width.toFloat() / mImageA.width, height.toFloat() / mImageA.height)
85         matrixB.postScale(width.toFloat() / mImageB.width, height.toFloat() / mImageB.height)
86 
87         mShaderA.setLocalMatrix(matrixA)
88         mShaderB.setLocalMatrix(matrixB)
89     }
90 
91     override fun onDraw(canvas: Canvas) {
92         super.onDraw(canvas)
93 
94         mShader.setInputShader("imageA", mShaderA)
95         mShader.setInputShader("imageB", mShaderB)
96         mShader.setIntUniform("imageDimensions", width, height)
97         mShader.setFloatUniform("progress", mCurrentProgress)
98 
99         mPaint.shader = mShader
100         canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), mPaint)
101     }
102 
103     private companion object {
104         const val AGSL = """
105         uniform shader imageA;
106         uniform shader imageB;
107         uniform ivec2 imageDimensions;
108         uniform float progress;
109 
110         const vec2 iSize = vec2(48.0, 48.0);
111         const float iDir = 0.5;
112         const  float iRand = 0.81;
113 
114         float hash12(vec2 p) {
115             vec3 p3  = fract(vec3(p.xyx) * .1031);
116             p3 += dot(p3, p3.yzx + 33.33);
117             return fract((p3.x + p3.y) * p3.z);
118         }
119 
120         float ramp(float2 p) {
121           return mix(hash12(p),
122                      dot(p/vec2(imageDimensions), float2(iDir, 1 - iDir)),
123                      iRand);
124         }
125 
126         half4 main(float2 p) {
127           float2 lowRes = p / iSize;
128           float2 cellCenter = (floor(lowRes) + 0.5) * iSize;
129           float2 posInCell = fract(lowRes) * 2 - 1;
130 
131           float v = ramp(cellCenter) + progress;
132           float distToCenter = max(abs(posInCell.x), abs(posInCell.y));
133 
134           return distToCenter > v ? imageA.eval(p).rgb1 : imageB.eval(p).rgb1;
135         }
136         """
137     }
138 }