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.snow 18 19 import android.graphics.Bitmap 20 import android.graphics.BitmapShader 21 import android.graphics.Canvas 22 import android.graphics.Matrix 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.MathUtils 33 import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.getScale 34 import com.google.android.wallpaper.weathereffects.graphics.utils.TimeUtils 35 import java.util.concurrent.Executor 36 37 /** Defines and generates the rain weather effect animation. */ 38 class SnowEffect( 39 /** The config of the snow effect. */ 40 private val snowConfig: SnowEffectConfig, 41 foreground: Bitmap, 42 background: Bitmap, 43 private var intensity: Float = DEFAULT_INTENSITY, 44 /** The initial size of the surface where the effect will be shown. */ 45 private var surfaceSize: SizeF, 46 /** App main executor. */ 47 private val mainExecutor: Executor, 48 ) : WeatherEffectBase(foreground, background, surfaceSize) { 49 50 private var snowSpeed: Float = 0.8f 51 private val snowPaint = Paint().also { it.shader = snowConfig.colorGradingShader } 52 53 private var frameBuffer = FrameBuffer(background.width, background.height) 54 private val frameBufferPaint = Paint().also { it.shader = snowConfig.accumulatedSnowShader } 55 56 private var scale = getScale(parallaxMatrix) 57 58 init { 59 frameBuffer.setRenderEffect( 60 RenderEffect.createBlurEffect(4f / scale, 4f / scale, Shader.TileMode.CLAMP) 61 ) 62 updateTextureUniforms() 63 adjustCropping(surfaceSize) 64 prepareColorGrading() 65 updateGridSize(surfaceSize) 66 setIntensity(intensity) 67 68 // Generate accumulated snow at the end after we updated all the uniforms. 69 generateAccumulatedSnow() 70 } 71 72 override fun update(deltaMillis: Long, frameTimeNanos: Long) { 73 elapsedTime += snowSpeed * TimeUtils.millisToSeconds(deltaMillis) 74 75 snowConfig.shader.setFloatUniform("time", elapsedTime) 76 snowConfig.colorGradingShader.setInputShader("texture", snowConfig.shader) 77 } 78 79 override fun draw(canvas: Canvas) { 80 canvas.drawPaint(snowPaint) 81 } 82 83 override fun release() { 84 super.release() 85 frameBuffer.close() 86 } 87 88 override fun setIntensity(intensity: Float) { 89 super.setIntensity(intensity) 90 /** 91 * Increase effect speed as weather intensity decreases. This compensates for the floaty 92 * appearance when there are fewer particles at the original speed. 93 */ 94 snowSpeed = MathUtils.map(intensity, 0f, 1f, 2.5f, 1.7f) 95 this.intensity = intensity 96 // Regenerate accumulated snow since the uniform changed. 97 generateAccumulatedSnow() 98 } 99 100 override fun setBitmaps(foreground: Bitmap?, background: Bitmap) { 101 super.setBitmaps(foreground, background) 102 scale = getScale(parallaxMatrix) 103 frameBuffer = 104 FrameBuffer(background.width, background.height).apply { 105 setRenderEffect( 106 RenderEffect.createBlurEffect(4f / scale, 4f / scale, Shader.TileMode.CLAMP) 107 ) 108 } 109 // GenerateAccumulatedSnow needs foreground for accumulatedSnowShader, and needs frameBuffer 110 // which is also changed with background 111 generateAccumulatedSnow() 112 } 113 114 override val shader: RuntimeShader 115 get() = snowConfig.shader 116 117 override val colorGradingShader: RuntimeShader 118 get() = snowConfig.colorGradingShader 119 120 override val lut: Bitmap? 121 get() = snowConfig.lut 122 123 override val colorGradingIntensity: Float 124 get() = snowConfig.colorGradingIntensity 125 126 override fun setMatrix(matrix: Matrix) { 127 super.setMatrix(matrix) 128 generateAccumulatedSnow() 129 } 130 131 override fun updateTextureUniforms() { 132 super.updateTextureUniforms() 133 snowConfig.shader.setInputBuffer( 134 "noise", 135 BitmapShader(snowConfig.noiseTexture, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT), 136 ) 137 } 138 139 private fun prepareColorGrading() { 140 snowConfig.colorGradingShader.setInputShader("texture", snowConfig.shader) 141 snowConfig.lut?.let { 142 snowConfig.colorGradingShader.setInputShader( 143 "lut", 144 BitmapShader(it, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), 145 ) 146 } 147 } 148 149 private fun generateAccumulatedSnow() { 150 val renderingCanvas = frameBuffer.beginDrawing() 151 snowConfig.accumulatedSnowShader.setFloatUniform("scale", scale) 152 snowConfig.accumulatedSnowShader.setFloatUniform( 153 "snowThickness", 154 snowConfig.maxAccumulatedSnowThickness * intensity / scale, 155 ) 156 snowConfig.accumulatedSnowShader.setFloatUniform("screenWidth", surfaceSize.width) 157 snowConfig.accumulatedSnowShader.setInputBuffer( 158 "foreground", 159 BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), 160 ) 161 renderingCanvas.drawPaint(frameBufferPaint) 162 frameBuffer.endDrawing() 163 164 frameBuffer.tryObtainingImage( 165 { image -> 166 snowConfig.shader.setInputBuffer( 167 "accumulatedSnow", 168 BitmapShader(image, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), 169 ) 170 }, 171 mainExecutor, 172 ) 173 } 174 175 override fun updateGridSize(newSurfaceSize: SizeF) { 176 val gridSize = GraphicsUtils.computeDefaultGridSize(newSurfaceSize, snowConfig.pixelDensity) 177 snowConfig.shader.setFloatUniform("gridSize", 7 * gridSize, 2f * gridSize) 178 } 179 } 180