• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.getScale
30 import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.invertAndTransposeMatrix
31 import kotlin.random.Random
32 
33 /** Give default implementation of some functions in WeatherEffect */
34 abstract class WeatherEffectBase(
35     protected var foreground: Bitmap,
36     protected var background: Bitmap,
37     /** The initial size of the surface where the effect will be shown. */
38     private var surfaceSize: SizeF,
39 ) : WeatherEffect {
40     private var centerCropMatrix: Matrix =
41         centerCropMatrix(
42             surfaceSize,
43             SizeF(background.width.toFloat(), background.height.toFloat()),
44         )
45     protected var parallaxMatrix = Matrix(centerCropMatrix)
46     // Currently, we use same transform for both foreground and background
47     protected open val transformMatrixBitmap: FloatArray = FloatArray(9)
48     // Apply to weather components not rely on image textures
49     // Should be identity matrix in editor, and only change when parallax applied in homescreen
50     private val transformMatrixWeather: FloatArray = FloatArray(9)
51     protected var bitmapScale = getScale(centerCropMatrix)
52     protected var elapsedTime: Float = 0f
53 
54     abstract val shader: RuntimeShader
55     abstract val colorGradingShader: RuntimeShader
56     abstract val lut: Bitmap?
57     abstract val colorGradingIntensity: Float
58 
setMatrixnull59     override fun setMatrix(matrix: Matrix) {
60         this.parallaxMatrix.set(matrix)
61         bitmapScale = getScale(parallaxMatrix)
62         adjustCropping(surfaceSize)
63     }
64 
adjustCroppingnull65     open fun adjustCropping(newSurfaceSize: SizeF) {
66         invertAndTransposeMatrix(parallaxMatrix, transformMatrixBitmap)
67         calculateTransformDifference(centerCropMatrix, parallaxMatrix, transformMatrixWeather)
68         shader.setFloatUniform("transformMatrixBitmap", transformMatrixBitmap)
69         shader.setFloatUniform("transformMatrixWeather", transformMatrixWeather)
70         shader.setFloatUniform("screenSize", newSurfaceSize.width, newSurfaceSize.height)
71         shader.setFloatUniform("screenAspectRatio", GraphicsUtils.getAspectRatio(newSurfaceSize))
72     }
73 
updateGridSizenull74     open fun updateGridSize(newSurfaceSize: SizeF) {}
75 
resizenull76     override fun resize(newSurfaceSize: SizeF) {
77         surfaceSize = newSurfaceSize
78         adjustCropping(newSurfaceSize)
79         updateGridSize(newSurfaceSize)
80     }
81 
updatenull82     abstract override fun update(deltaMillis: Long, frameTimeNanos: Long)
83 
84     abstract override fun draw(canvas: Canvas)
85 
86     override fun reset() {
87         elapsedTime = Random.nextFloat() * 90f
88     }
89 
releasenull90     override fun release() {
91         lut?.recycle()
92     }
93 
setIntensitynull94     override fun setIntensity(intensity: Float) {
95         shader.setFloatUniform("intensity", intensity)
96         colorGradingShader.setFloatUniform("intensity", colorGradingIntensity * intensity)
97     }
98 
setBitmapsnull99     override fun setBitmaps(foreground: Bitmap?, background: Bitmap): Boolean {
100         if (this.foreground == foreground && this.background == background) {
101             return false
102         }
103         // Only when background changes, we can infer the bitmap set changes
104         if (this.background != background) {
105             this.background.recycle()
106             this.foreground.recycle()
107         }
108         this.foreground = foreground ?: background
109         this.background = background
110 
111         centerCropMatrix =
112             centerCropMatrix(
113                 surfaceSize,
114                 SizeF(background.width.toFloat(), background.height.toFloat()),
115             )
116         parallaxMatrix.set(centerCropMatrix)
117         bitmapScale = getScale(centerCropMatrix)
118         shader.setInputBuffer(
119             "background",
120             BitmapShader(this.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR),
121         )
122         shader.setInputBuffer(
123             "foreground",
124             BitmapShader(this.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR),
125         )
126         adjustCropping(surfaceSize)
127         return true
128     }
129 
updateTextureUniformsnull130     open fun updateTextureUniforms() {
131         shader.setInputBuffer(
132             "foreground",
133             BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR),
134         )
135 
136         shader.setInputBuffer(
137             "background",
138             BitmapShader(background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR),
139         )
140     }
141 
142     companion object {
143         // When extracting the scale from the parallax matrix, there will be a very small difference
144         // due to floating-point precision.
145         // We use FLOAT_TOLERANCE to avoid triggering actions on these insignificant scale changes.
146         const val FLOAT_TOLERANCE = 0.0001F
147     }
148 }
149