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.Paint 23 import android.graphics.RenderEffect 24 import android.graphics.RuntimeShader 25 import android.graphics.Shader 26 import android.util.SizeF 27 import com.google.android.wallpaper.weathereffects.graphics.FrameBuffer 28 import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect.Companion.DEFAULT_INTENSITY 29 import com.google.android.wallpaper.weathereffects.graphics.WeatherEffectBase 30 import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils 31 import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.getScale 32 import com.google.android.wallpaper.weathereffects.graphics.utils.TimeUtils 33 import java.util.concurrent.Executor 34 35 /** Defines and generates the rain weather effect animation. */ 36 class RainEffect( 37 /** The config of the rain effect. */ 38 private val rainConfig: RainEffectConfig, 39 foreground: Bitmap, 40 background: Bitmap, 41 intensity: Float = DEFAULT_INTENSITY, 42 /** The initial size of the surface where the effect will be shown. */ 43 surfaceSize: SizeF, 44 private val mainExecutor: Executor, 45 ) : WeatherEffectBase(foreground, background, surfaceSize) { 46 47 private val rainPaint = Paint().also { it.shader = rainConfig.colorGradingShader } 48 // Outline buffer is set with bitmap size, so we need to multiply blur radius by scale to get 49 // consistent blur across different surface 50 private var outlineBuffer = 51 FrameBuffer(background.width, background.height).apply { 52 setRenderEffect( 53 RenderEffect.createBlurEffect( 54 BLUR_RADIUS / bitmapScale, 55 BLUR_RADIUS / bitmapScale, 56 Shader.TileMode.CLAMP, 57 ) 58 ) 59 } 60 private val outlineBufferPaint = Paint().also { it.shader = rainConfig.outlineShader } 61 62 init { 63 updateTextureUniforms() 64 adjustCropping(surfaceSize) 65 prepareColorGrading() 66 updateGridSize(surfaceSize) 67 setIntensity(intensity) 68 } 69 70 override fun update(deltaMillis: Long, frameTimeNanos: Long) { 71 elapsedTime += TimeUtils.millisToSeconds(deltaMillis) 72 73 rainConfig.rainShowerShader.setFloatUniform("time", elapsedTime) 74 rainConfig.colorGradingShader.setInputShader("texture", rainConfig.rainShowerShader) 75 } 76 77 override fun draw(canvas: Canvas) { 78 canvas.drawPaint(rainPaint) 79 } 80 81 override fun release() { 82 super.release() 83 outlineBuffer.close() 84 } 85 86 override fun setBitmaps(foreground: Bitmap?, background: Bitmap): Boolean { 87 if (!super.setBitmaps(foreground, background)) { 88 return false 89 } 90 outlineBuffer.close() 91 outlineBuffer = FrameBuffer(background.width, background.height) 92 93 bitmapScale = getScale(parallaxMatrix) 94 // Different from snow effects, we only need to change blur radius when bitmaps change 95 // it only gives the range of rain splashes and doesn't influence the visual effects 96 outlineBuffer.setRenderEffect( 97 RenderEffect.createBlurEffect( 98 BLUR_RADIUS / bitmapScale, 99 BLUR_RADIUS / bitmapScale, 100 Shader.TileMode.CLAMP, 101 ) 102 ) 103 104 updateTextureUniforms() 105 return true 106 } 107 108 override val shader: RuntimeShader 109 get() = rainConfig.rainShowerShader 110 111 override val colorGradingShader: RuntimeShader 112 get() = rainConfig.colorGradingShader 113 114 override val lut: Bitmap? 115 get() = rainConfig.lut 116 117 override val colorGradingIntensity: Float 118 get() = rainConfig.colorGradingIntensity 119 120 override fun updateTextureUniforms() { 121 val foregroundBuffer = 122 BitmapShader(super.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR) 123 rainConfig.rainShowerShader.setInputBuffer("foreground", foregroundBuffer) 124 rainConfig.outlineShader.setInputBuffer("texture", foregroundBuffer) 125 126 rainConfig.rainShowerShader.setInputBuffer( 127 "background", 128 BitmapShader(super.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), 129 ) 130 createOutlineBuffer() 131 } 132 133 /** 134 * It's necessary to create outline buffer only when bitmaps change, only intensity change won't 135 * create a new one cause we refer to intensity in each cell when drawing splashes. 136 */ 137 private fun createOutlineBuffer() { 138 rainConfig.outlineShader.setFloatUniform( 139 "thickness", 140 MAX_RAIN_OUTLINE_THICKNESS / bitmapScale, 141 ) 142 val canvas = outlineBuffer.beginDrawing() 143 canvas.drawPaint(outlineBufferPaint) 144 outlineBuffer.endDrawing() 145 146 outlineBuffer.tryObtainingImage( 147 { buffer -> 148 rainConfig.rainShowerShader.setInputBuffer( 149 "outlineBuffer", 150 BitmapShader(buffer, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), 151 ) 152 }, 153 mainExecutor, 154 ) 155 } 156 157 private fun prepareColorGrading() { 158 // Initialize the buffer with black, so that we don't ever draw garbage buffer. 159 rainConfig.colorGradingShader.setInputShader("texture", rainConfig.rainShowerShader) 160 rainConfig.lut?.let { 161 rainConfig.colorGradingShader.setInputShader( 162 "lut", 163 BitmapShader(it, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), 164 ) 165 } 166 } 167 168 override fun updateGridSize(newSurfaceSize: SizeF) { 169 val widthScreenScale = 170 GraphicsUtils.computeDefaultGridSize(newSurfaceSize, rainConfig.pixelDensity) 171 rainConfig.rainShowerShader.setFloatUniform("gridScale", widthScreenScale) 172 } 173 174 companion object { 175 const val MAX_RAIN_OUTLINE_THICKNESS = 11f 176 const val BLUR_RADIUS = 2f 177 } 178 } 179