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