1 /* 2 * 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.fog 18 19 import android.graphics.BitmapShader 20 import android.graphics.Canvas 21 import android.graphics.Paint 22 import android.graphics.Shader 23 import android.util.SizeF 24 import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect 25 import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils 26 import com.google.android.wallpaper.weathereffects.graphics.utils.ImageCrop 27 import kotlin.math.sin 28 import kotlin.random.Random 29 30 /** Defines and generates the fog weather effect animation. */ 31 class FogEffect( 32 private val fogConfig: FogEffectConfig, 33 /** The initial size of the surface where the effect will be shown. */ 34 surfaceSize: SizeF 35 ) : WeatherEffect { 36 <lambda>null37 private val fogPaint = Paint().also { it.shader = fogConfig.colorGradingShader } 38 private var elapsedTime: Float = 0f 39 40 init { 41 updateTextureUniforms() 42 adjustCropping(surfaceSize) 43 prepareColorGrading() 44 setIntensity(fogConfig.intensity) 45 } 46 resizenull47 override fun resize(newSurfaceSize: SizeF) = adjustCropping(newSurfaceSize) 48 49 override fun update(deltaMillis: Long, frameTimeNanos: Long) { 50 val deltaTime = deltaMillis * MILLIS_TO_SECONDS 51 52 val time = frameTimeNanos.toFloat() * NANOS_TO_SECONDS 53 // Variation range [0.4, 1]. We don't want the variation to be 0. 54 val variation = sin(0.06f * time + sin(0.18f * time)) * 0.3f + 0.7f 55 elapsedTime += variation * deltaTime 56 57 val scaledElapsedTime = elapsedTime * 0.248f 58 59 val variationFgd0 = 0.256f * sin(scaledElapsedTime) 60 val variationFgd1 = 0.156f * sin(scaledElapsedTime) * sin(scaledElapsedTime) 61 val timeFgd0 = 0.4f * elapsedTime * 5f + variationFgd0 62 val timeFgd1 = 0.03f * elapsedTime * 5f + variationFgd1 63 64 val variationBgd0 = 0.156f * sin((scaledElapsedTime + Math.PI.toFloat() / 2.0f)) 65 val variationBgd1 = 66 0.0156f * sin((scaledElapsedTime + Math.PI.toFloat() / 3.0f)) * sin(scaledElapsedTime) 67 val timeBgd0 = 0.8f * elapsedTime * 5f + variationBgd0 68 val timeBgd1 = 0.2f * elapsedTime * 5f + variationBgd1 69 70 fogConfig.shader.setFloatUniform("time", timeFgd0, timeFgd1, timeBgd0, timeBgd1) 71 72 fogConfig.colorGradingShader.setInputShader("texture", fogConfig.shader) 73 } 74 drawnull75 override fun draw(canvas: Canvas) { 76 canvas.drawPaint(fogPaint) 77 } 78 resetnull79 override fun reset() { 80 elapsedTime = Random.nextFloat() * 90f 81 } 82 releasenull83 override fun release() { 84 fogConfig.lut?.recycle() 85 } 86 setIntensitynull87 override fun setIntensity(intensity: Float) { 88 fogConfig.shader.setFloatUniform("intensity", intensity) 89 fogConfig.colorGradingShader.setFloatUniform( 90 "intensity", 91 fogConfig.colorGradingIntensity * intensity 92 ) 93 } 94 adjustCroppingnull95 private fun adjustCropping(surfaceSize: SizeF) { 96 val imageCropFgd = 97 ImageCrop.centerCoverCrop( 98 surfaceSize.width, 99 surfaceSize.height, 100 fogConfig.foreground.width.toFloat(), 101 fogConfig.foreground.height.toFloat() 102 ) 103 fogConfig.shader.setFloatUniform( 104 "uvOffsetFgd", 105 imageCropFgd.leftOffset, 106 imageCropFgd.topOffset 107 ) 108 fogConfig.shader.setFloatUniform( 109 "uvScaleFgd", 110 imageCropFgd.horizontalScale, 111 imageCropFgd.verticalScale 112 ) 113 val imageCropBgd = 114 ImageCrop.centerCoverCrop( 115 surfaceSize.width, 116 surfaceSize.height, 117 fogConfig.background.width.toFloat(), 118 fogConfig.background.height.toFloat() 119 ) 120 fogConfig.shader.setFloatUniform( 121 "uvOffsetBgd", 122 imageCropBgd.leftOffset, 123 imageCropBgd.topOffset 124 ) 125 fogConfig.shader.setFloatUniform( 126 "uvScaleBgd", 127 imageCropBgd.horizontalScale, 128 imageCropBgd.verticalScale 129 ) 130 fogConfig.shader.setFloatUniform("screenSize", surfaceSize.width, surfaceSize.height) 131 fogConfig.shader.setFloatUniform( 132 "screenAspectRatio", 133 GraphicsUtils.getAspectRatio(surfaceSize) 134 ) 135 } 136 updateTextureUniformsnull137 private fun updateTextureUniforms() { 138 fogConfig.shader.setInputBuffer( 139 "foreground", 140 BitmapShader(fogConfig.foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR) 141 ) 142 143 fogConfig.shader.setInputBuffer( 144 "background", 145 BitmapShader(fogConfig.background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR) 146 ) 147 148 fogConfig.shader.setInputBuffer( 149 "clouds", 150 BitmapShader(fogConfig.cloudsTexture, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT) 151 ) 152 153 fogConfig.shader.setFloatUniform( 154 "cloudsSize", 155 fogConfig.cloudsTexture.width.toFloat(), 156 fogConfig.cloudsTexture.height.toFloat() 157 ) 158 159 fogConfig.shader.setInputBuffer( 160 "fog", 161 BitmapShader(fogConfig.fogTexture, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT) 162 ) 163 164 fogConfig.shader.setFloatUniform( 165 "fogSize", 166 fogConfig.fogTexture.width.toFloat(), 167 fogConfig.fogTexture.height.toFloat() 168 ) 169 170 fogConfig.shader.setFloatUniform("pixelDensity", fogConfig.pixelDensity) 171 } 172 prepareColorGradingnull173 private fun prepareColorGrading() { 174 fogConfig.colorGradingShader.setInputShader("texture", fogConfig.shader) 175 fogConfig.lut?.let { 176 fogConfig.colorGradingShader.setInputShader( 177 "lut", 178 BitmapShader(it, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR) 179 ) 180 } 181 fogConfig.colorGradingShader.setFloatUniform("intensity", fogConfig.colorGradingIntensity) 182 } 183 184 private companion object { 185 186 private const val MILLIS_TO_SECONDS = 1 / 1000f 187 private const val NANOS_TO_SECONDS = 1 / 1_000_000_000f 188 } 189 } 190