/* * Copyright (C) 2021 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.test.hwui import android.animation.ValueAnimator import android.app.Activity import android.graphics.Bitmap import android.graphics.BitmapShader import android.graphics.ImageDecoder import android.graphics.Matrix import android.graphics.Shader import android.graphics.RenderEffect import android.graphics.RuntimeShader import android.os.Bundle import android.view.View class RenderEffectViewActivity : Activity() { private val mDropsShader = RuntimeShader(dropsAGSL) private var mDropsAnimator = ValueAnimator.ofFloat(0f, 1f) private var mStartTime = System.currentTimeMillis() private lateinit var mScratchesImage: Bitmap private lateinit var mScratchesShader: BitmapShader override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.view_runtime_shader) val dropsView = findViewById(R.id.CardView)!! dropsView.isClickable = true dropsView.setOnClickListener { if (mDropsAnimator.isRunning) { mDropsAnimator.cancel() dropsView.setRenderEffect(null) } else { mDropsAnimator.start() } } val imgSource = ImageDecoder.createSource(resources, R.drawable.scratches) mScratchesImage = ImageDecoder.decodeBitmap(imgSource) mScratchesShader = BitmapShader(mScratchesImage, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) mDropsAnimator.duration = 1000 mDropsAnimator.repeatCount = ValueAnimator.INFINITE mDropsAnimator.addUpdateListener { _ -> val viewWidth = dropsView.width.toFloat() val viewHeight = dropsView.height.toFloat() val scratchesMatrix = Matrix() scratchesMatrix.postScale(viewWidth / mScratchesImage.width, viewHeight / mScratchesImage.height) mScratchesShader.setLocalMatrix(scratchesMatrix) mDropsShader.setInputShader("scratches", mScratchesShader) mDropsShader.setFloatUniform("elapsedSeconds", (System.currentTimeMillis() - mStartTime) / 1000f) mDropsShader.setFloatUniform("viewDimensions", viewWidth, viewHeight) val dropsEffect = RenderEffect.createRuntimeShaderEffect(mDropsShader, "background") val blurEffect = RenderEffect.createBlurEffect(10f, 10f, Shader.TileMode.CLAMP) dropsView.setRenderEffect(RenderEffect.createChainEffect(dropsEffect, blurEffect)) } } private companion object { const val dropsAGSL = """ uniform float elapsedSeconds; uniform vec2 viewDimensions; uniform shader background; uniform shader scratches; vec2 dropsUV(vec2 fragCoord ) { vec2 uv = fragCoord.xy / viewDimensions.xy; // 0 <> 1 vec2 offs = vec2(0.); return (offs + uv).xy; } const vec3 iFrostColorRGB = vec3(0.5, 0.5, 0.5); const float iFrostColorAlpha = .3; half4 main(float2 fragCoord) { half4 bg = background.eval(dropsUV(fragCoord)*viewDimensions.xy); float2 scratchCoord = fragCoord.xy / viewDimensions.xy;; scratchCoord += 1.5; scratchCoord = mod(scratchCoord, 1); half scratch = scratches.eval(scratchCoord*viewDimensions.xy).r; bg.rgb = mix(bg.rgb, iFrostColorRGB, iFrostColorAlpha); bg.rgb = mix(bg.rgb, half3(1), pow(scratch,3)); return bg; } """ } }