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