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 }