1 /* <lambda>null2 * Copyright (C) 2023 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.google.android.wallpaper.weathereffects.graphics.rain 18 19 import android.graphics.Bitmap 20 import android.graphics.BitmapShader 21 import android.graphics.Canvas 22 import android.graphics.Color 23 import android.graphics.Paint 24 import android.graphics.RenderEffect 25 import android.graphics.RuntimeShader 26 import android.graphics.Shader 27 import android.util.SizeF 28 import com.google.android.wallpaper.weathereffects.graphics.FrameBuffer 29 import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect.Companion.DEFAULT_INTENSITY 30 import com.google.android.wallpaper.weathereffects.graphics.WeatherEffectBase 31 import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils 32 import com.google.android.wallpaper.weathereffects.graphics.utils.SolidColorShader 33 import com.google.android.wallpaper.weathereffects.graphics.utils.TimeUtils 34 import java.util.concurrent.Executor 35 36 /** Defines and generates the rain weather effect animation. */ 37 class RainEffect( 38 /** The config of the rain effect. */ 39 private val rainConfig: RainEffectConfig, 40 foreground: Bitmap, 41 background: Bitmap, 42 intensity: Float = DEFAULT_INTENSITY, 43 /** The initial size of the surface where the effect will be shown. */ 44 surfaceSize: SizeF, 45 private val mainExecutor: Executor, 46 ) : WeatherEffectBase(foreground, background, surfaceSize) { 47 48 private val rainPaint = Paint().also { it.shader = rainConfig.colorGradingShader } 49 50 // Set blur effect to reduce the outline noise. No need to set blur effect every time we 51 // re-generate the outline buffer. 52 private var outlineBuffer = 53 FrameBuffer(background.width, background.height).apply { 54 setRenderEffect(RenderEffect.createBlurEffect(2f, 2f, Shader.TileMode.CLAMP)) 55 } 56 private val outlineBufferPaint = Paint().also { it.shader = rainConfig.outlineShader } 57 58 init { 59 updateTextureUniforms() 60 adjustCropping(surfaceSize) 61 prepareColorGrading() 62 updateGridSize(surfaceSize) 63 setIntensity(intensity) 64 } 65 66 override fun update(deltaMillis: Long, frameTimeNanos: Long) { 67 elapsedTime += TimeUtils.millisToSeconds(deltaMillis) 68 69 rainConfig.rainShowerShader.setFloatUniform("time", elapsedTime) 70 rainConfig.glassRainShader.setFloatUniform("time", elapsedTime) 71 72 rainConfig.glassRainShader.setInputShader("texture", rainConfig.rainShowerShader) 73 rainConfig.colorGradingShader.setInputShader("texture", rainConfig.glassRainShader) 74 } 75 76 override fun draw(canvas: Canvas) { 77 canvas.drawPaint(rainPaint) 78 } 79 80 override fun release() { 81 super.release() 82 outlineBuffer.close() 83 } 84 85 override fun setIntensity(intensity: Float) { 86 super.setIntensity(intensity) 87 rainConfig.glassRainShader.setFloatUniform("intensity", intensity) 88 val thickness = 1f + intensity * 10f 89 rainConfig.outlineShader.setFloatUniform("thickness", thickness) 90 91 // Need to recreate the outline buffer as the uniform has changed. 92 createOutlineBuffer() 93 } 94 95 override fun setBitmaps(foreground: Bitmap?, background: Bitmap) { 96 super.setBitmaps(foreground, background) 97 outlineBuffer = 98 FrameBuffer(background.width, background.height).apply { 99 setRenderEffect(RenderEffect.createBlurEffect(2f, 2f, Shader.TileMode.CLAMP)) 100 } 101 updateTextureUniforms() 102 103 // Need to recreate the outline buffer as the outlineBuffer has changed due to background 104 createOutlineBuffer() 105 } 106 107 override val shader: RuntimeShader 108 get() = rainConfig.rainShowerShader 109 110 override val colorGradingShader: RuntimeShader 111 get() = rainConfig.colorGradingShader 112 113 override val lut: Bitmap? 114 get() = rainConfig.lut 115 116 override val colorGradingIntensity: Float 117 get() = rainConfig.colorGradingIntensity 118 119 override fun adjustCropping(newSurfaceSize: SizeF) { 120 super.adjustCropping(newSurfaceSize) 121 rainConfig.glassRainShader.setFloatUniform( 122 "screenSize", 123 newSurfaceSize.width, 124 newSurfaceSize.height, 125 ) 126 127 val screenAspectRatio = GraphicsUtils.getAspectRatio(newSurfaceSize) 128 rainConfig.glassRainShader.setFloatUniform("screenAspectRatio", screenAspectRatio) 129 } 130 131 override fun updateTextureUniforms() { 132 val foregroundBuffer = 133 BitmapShader(super.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR) 134 rainConfig.rainShowerShader.setInputBuffer("foreground", foregroundBuffer) 135 rainConfig.outlineShader.setInputBuffer("texture", foregroundBuffer) 136 137 rainConfig.rainShowerShader.setInputBuffer( 138 "background", 139 BitmapShader(super.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), 140 ) 141 } 142 143 private fun createOutlineBuffer() { 144 val canvas = outlineBuffer.beginDrawing() 145 canvas.drawPaint(outlineBufferPaint) 146 outlineBuffer.endDrawing() 147 148 outlineBuffer.tryObtainingImage( 149 { buffer -> 150 rainConfig.rainShowerShader.setInputBuffer( 151 "outlineBuffer", 152 BitmapShader(buffer, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), 153 ) 154 }, 155 mainExecutor, 156 ) 157 } 158 159 private fun prepareColorGrading() { 160 // Initialize the buffer with black, so that we don't ever draw garbage buffer. 161 rainConfig.glassRainShader.setInputShader("texture", SolidColorShader(Color.BLACK)) 162 rainConfig.colorGradingShader.setInputShader("texture", rainConfig.glassRainShader) 163 rainConfig.lut?.let { 164 rainConfig.colorGradingShader.setInputShader( 165 "lut", 166 BitmapShader(it, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), 167 ) 168 } 169 } 170 171 override fun updateGridSize(newSurfaceSize: SizeF) { 172 val widthScreenScale = 173 GraphicsUtils.computeDefaultGridSize(newSurfaceSize, rainConfig.pixelDensity) 174 rainConfig.rainShowerShader.setFloatUniform("gridScale", widthScreenScale) 175 rainConfig.glassRainShader.setFloatUniform("gridScale", widthScreenScale) 176 } 177 } 178