1 /*
2  * Copyright (C) 2024 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
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.RuntimeShader
24 import android.graphics.Shader
25 import android.util.SizeF
26 import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils
27 import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.calculateTransformDifference
28 import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.centerCropMatrix
29 import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.invertAndTransposeMatrix
30 import kotlin.random.Random
31 
32 /** Give default implementation of some functions in WeatherEffect */
33 abstract class WeatherEffectBase(
34     protected var foreground: Bitmap,
35     protected var background: Bitmap,
36     /** The initial size of the surface where the effect will be shown. */
37     private var surfaceSize: SizeF,
38 ) : WeatherEffect {
39     private var centerCropMatrix: Matrix =
40         centerCropMatrix(
41             surfaceSize,
42             SizeF(background.width.toFloat(), background.height.toFloat()),
43         )
44     protected var parallaxMatrix = Matrix(centerCropMatrix)
45     // Currently, we use same transform for both foreground and background
46     protected open val transformMatrixBitmap: FloatArray = FloatArray(9)
47     // Apply to weather components not rely on image textures
48     // Should be identity matrix in editor, and only change when parallax applied in homescreen
49     private val transformMatrixWeather: FloatArray = FloatArray(9)
50     protected var elapsedTime: Float = 0f
51 
52     abstract val shader: RuntimeShader
53     abstract val colorGradingShader: RuntimeShader
54     abstract val lut: Bitmap?
55     abstract val colorGradingIntensity: Float
56 
setMatrixnull57     override fun setMatrix(matrix: Matrix) {
58         this.parallaxMatrix.set(matrix)
59         adjustCropping(surfaceSize)
60     }
61 
adjustCroppingnull62     open fun adjustCropping(newSurfaceSize: SizeF) {
63         invertAndTransposeMatrix(parallaxMatrix, transformMatrixBitmap)
64         calculateTransformDifference(centerCropMatrix, parallaxMatrix, transformMatrixWeather)
65         shader.setFloatUniform("transformMatrixBitmap", transformMatrixBitmap)
66         shader.setFloatUniform("transformMatrixWeather", transformMatrixWeather)
67         shader.setFloatUniform("screenSize", newSurfaceSize.width, newSurfaceSize.height)
68         shader.setFloatUniform("screenAspectRatio", GraphicsUtils.getAspectRatio(newSurfaceSize))
69     }
70 
updateGridSizenull71     open fun updateGridSize(newSurfaceSize: SizeF) {}
72 
resizenull73     override fun resize(newSurfaceSize: SizeF) {
74         surfaceSize = newSurfaceSize
75         adjustCropping(newSurfaceSize)
76         updateGridSize(newSurfaceSize)
77     }
78 
updatenull79     abstract override fun update(deltaMillis: Long, frameTimeNanos: Long)
80 
81     abstract override fun draw(canvas: Canvas)
82 
83     override fun reset() {
84         elapsedTime = Random.nextFloat() * 90f
85     }
86 
releasenull87     override fun release() {
88         lut?.recycle()
89     }
90 
setIntensitynull91     override fun setIntensity(intensity: Float) {
92         shader.setFloatUniform("intensity", intensity)
93         colorGradingShader.setFloatUniform("intensity", colorGradingIntensity * intensity)
94     }
95 
setBitmapsnull96     override fun setBitmaps(foreground: Bitmap?, background: Bitmap) {
97         if (this.foreground == foreground && this.background == background) {
98             return
99         }
100         // Only when background changes, we can infer the bitmap set changes
101         if (this.background != background) {
102             this.background.recycle()
103             this.foreground.recycle()
104         }
105         this.foreground = foreground ?: background
106         this.background = background
107 
108         centerCropMatrix =
109             centerCropMatrix(
110                 surfaceSize,
111                 SizeF(background.width.toFloat(), background.height.toFloat()),
112             )
113         parallaxMatrix.set(centerCropMatrix)
114         shader.setInputBuffer(
115             "background",
116             BitmapShader(this.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR),
117         )
118         shader.setInputBuffer(
119             "foreground",
120             BitmapShader(this.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR),
121         )
122         adjustCropping(surfaceSize)
123     }
124 
updateTextureUniformsnull125     open fun updateTextureUniforms() {
126         shader.setInputBuffer(
127             "foreground",
128             BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR),
129         )
130 
131         shader.setInputBuffer(
132             "background",
133             BitmapShader(background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR),
134         )
135     }
136 }
137