Loading weathereffects/graphics/assets/shaders/snow_accumulation.agsl→weathereffects/graphics/assets/shaders/snow_accumulation_outline.agsl +0 −0 File moved. View file weathereffects/graphics/assets/shaders/snow_accumulation_result.agsl 0 → 100644 +56 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // `foregroundOutline`: Original size bitmap with blurred foreground outline uniform shader foregroundOutline; uniform shader noise; uniform mat3 transformMatrixBitmapScaleOnly; #include "shaders/simplex2d.agsl" #include "shaders/utils.agsl" /** * This shader generates ready-to-use snow accumulation with noise and a fluffy effect * added to the foreground outline. It also proportionally scales the input bitmap * to fit the screen size for memory efficiency. * * For snow_effects, only transform `fragCoord` based on parallax translation to access this result. * - R channel: Controls accumulation thickness based on intensity. * - G and B channels: Cache intermediate values to avoid redundant per-frame calculations. */ vec4 main(float2 fragCoord) { vec4 color = vec4(0, 0, 0, 1.0); // Apply transform matrix to fragCoord to scale down output float2 adjustedUv = transformPoint(transformMatrixBitmapScaleOnly, fragCoord); // Load noise texture to give "fluffiness" to the snow. Displace the sampling of the noise. vec3 cloudsNoise = noise.eval(adjustedUv * 7000 + vec2(fragCoord.y, -fragCoord.x)).rgb; // Add dither to give texture to the snow and ruffle the edges. float dither = abs(triangleNoise(fragCoord * 0.01)); // Get the accumulated snow buffer. r contains its mask, g contains some random noise. vec2 accSnow = foregroundOutline.eval(adjustedUv).rg; // R channel as intensity threshold color.r = accSnow.r; // Sharpen the mask of the accumulated snow, but not in excess. // Makes the edges of the snow layer accumulation rougher. color.g = 1. - cloudsNoise.b - 0.3 * dither; // Load snow texture and dither. Make it have gray-ish values. float accSnowTexture = smoothstep(0.2, 0.7, /* noise= */ accSnow.g) * 0.7; accSnowTexture = map(accSnowTexture, dither - 1, 1, 0, 1); // Adjust snow texture coverage/shape. accSnowTexture = map(accSnowTexture, 0.67, 0.8, 0, 1); color.b = 1.- 0.6 * accSnowTexture - 0.35 * dither; return color; } weathereffects/graphics/assets/shaders/snow_effect.agsl +6 −22 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ uniform shader foreground; uniform shader background; uniform shader accumulatedSnow; uniform shader noise; uniform float2 gridSize; uniform float time; uniform float screenAspectRatio; Loading Loading @@ -53,7 +52,8 @@ vec4 main(float2 fragCoord) { // Apply transform matrix to fragCoord float2 adjustedUv = transformPoint(transformMatrixBitmap, fragCoord); // Calculate uv for snow based on transformed coordinates float2 uv = transformPoint(transformMatrixWeather, fragCoord) / screenSize; float2 weatherUv = transformPoint(transformMatrixWeather, fragCoord); float2 uv = weatherUv / screenSize; float2 uvAdjusted = vec2(uv.x, uv.y / screenAspectRatio); vec4 colorForeground = foreground.eval(adjustedUv); Loading Loading @@ -93,27 +93,11 @@ vec4 main(float2 fragCoord) { color.rgb = normalBlend(color.rgb, colorForeground.rgb, colorForeground.a); // 4. Add accumulated snow layer. // Load noise texture to give "fluffy-ness" to the snow. Displace the sampling of the noise. vec3 cloudsNoise = noise.eval(uvAdjusted * 7000 + vec2(fragCoord.y, -fragCoord.x)).rgb; // Add dither to give texture to the snow and ruffle the edges. float dither = abs(triangleNoise(fragCoord * 0.01)); // Get the accumulated snow buffer. r contains its mask, g contains some random noise. vec2 accSnow = accumulatedSnow.eval(adjustedUv).rg; // Sharpen the mask of the accumulated snow, but not in excess. vec3 accSnow = accumulatedSnow.eval(weatherUv).rgb; float accSnowMask = smoothstep( (1.-intensity), 1.0, /* mask= */accSnow.r); if (accSnowMask > 0) { // Makes the edges of the snow layer accumulation rougher. accSnowMask = map(accSnowMask, 1. - cloudsNoise.b - 0.3 * dither, 1., 0., 1.); // Load snow texture and dither. Make it have gray-ish values. float accSnowTexture = smoothstep(0.2, 0.7, /* noise= */ accSnow.g) * 0.7; accSnowTexture = map(accSnowTexture, dither - 1, 1, 0, 1); // Adjust snow texture coverage/shape. accSnowTexture = map(accSnowTexture, 0.67, 0.8, 0, 1); accSnowMask = map(accSnowMask, 0., 1., 0., 1.- 0.6 * accSnowTexture - 0.35 * dither); accSnowMask = map(accSnowMask, accSnow.g, 1., 0., 1.); accSnowMask = map(accSnowMask, 0., 1., 0., accSnow.b); color.rgb = normalBlendNotPremultiplied(color.rgb, snowColor.rgb, snowColor.a * accSnowMask); } // 5. Generate snow in front of the subject. for (float i = midSnowLayerIndex; i >= closestSnowLayerIndex; i--) { Loading weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/WeatherEffectBase.kt +14 −9 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import android.graphics.RuntimeShader import android.graphics.Shader import android.util.SizeF import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.calculateTranslationDifference import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.calculateTransformDifference import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.centerCropMatrix import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.getScaleFromMatrixValues import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.invertAndTransposeMatrix Loading @@ -37,7 +37,7 @@ abstract class WeatherEffectBase( /** The initial size of the surface where the effect will be shown. */ private var surfaceSize: SizeF, ) : WeatherEffect { private var centerCropMatrix: Matrix = protected var centerCropMatrix: Matrix = centerCropMatrix( surfaceSize, SizeF(background.width.toFloat(), background.height.toFloat()), Loading @@ -54,6 +54,7 @@ abstract class WeatherEffectBase( FloatArray(9).apply { parallaxMatrix.getValues(this) } // Currently, we use same transform for both foreground and background protected open val transformMatrixBitmap: FloatArray = FloatArray(9) protected open val transformMatrixCenterCrop: FloatArray = FloatArray(9) // Apply to weather components not rely on image textures // Should be identity matrix in editor, and only change when parallax applied in homescreen private val transformMatrixWeather: FloatArray = FloatArray(9) Loading @@ -66,6 +67,10 @@ abstract class WeatherEffectBase( abstract val colorGradingIntensity: Float override fun setMatrix(matrix: Matrix) { if (matrix == this.parallaxMatrix) { return } this.parallaxMatrix.setAndUpdateFloatArray(matrix, parallaxMatrixValues) bitmapScale = getScaleFromMatrixValues(parallaxMatrixValues) adjustCropping(surfaceSize) Loading @@ -74,15 +79,10 @@ abstract class WeatherEffectBase( /** This function will be called every time parallax changes, don't do heavy things here */ open fun adjustCropping(newSurfaceSize: SizeF) { invertAndTransposeMatrix(parallaxMatrix, transformMatrixBitmap) calculateTranslationDifference( centerCropMatrixValues, parallaxMatrixValues, transformMatrixWeather, ) invertAndTransposeMatrix(centerCropMatrix, transformMatrixCenterCrop) calculateTransformDifference(centerCropMatrix, parallaxMatrix, transformMatrixWeather) shader.setFloatUniform("transformMatrixBitmap", transformMatrixBitmap) shader.setFloatUniform("transformMatrixWeather", transformMatrixWeather) shader.setFloatUniform("screenSize", newSurfaceSize.width, newSurfaceSize.height) shader.setFloatUniform("screenAspectRatio", GraphicsUtils.getAspectRatio(newSurfaceSize)) } open fun updateGridSize(newSurfaceSize: SizeF) {} Loading @@ -94,6 +94,8 @@ abstract class WeatherEffectBase( surfaceSize, SizeF(background.width.toFloat(), background.height.toFloat()), ) shader.setFloatUniform("screenSize", newSurfaceSize.width, newSurfaceSize.height) shader.setFloatUniform("screenAspectRatio", GraphicsUtils.getAspectRatio(newSurfaceSize)) adjustCropping(newSurfaceSize) updateGridSize(newSurfaceSize) } Loading Loading @@ -156,6 +158,9 @@ abstract class WeatherEffectBase( "background", BitmapShader(background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), ) shader.setFloatUniform("screenSize", surfaceSize.width, surfaceSize.height) shader.setFloatUniform("screenAspectRatio", GraphicsUtils.getAspectRatio(surfaceSize)) } private fun Matrix.setAndUpdateFloatArray(src: Matrix, targetFloatArray: FloatArray) { Loading weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffect.kt +86 −20 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.graphics.RenderEffect import android.graphics.RuntimeShader import android.graphics.Shader import android.util.SizeF import androidx.core.graphics.createBitmap import com.google.android.wallpaper.weathereffects.graphics.FrameBuffer import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect.Companion.DEFAULT_INTENSITY import com.google.android.wallpaper.weathereffects.graphics.WeatherEffectBase Loading @@ -51,11 +52,23 @@ class SnowEffect( private var snowSpeed: Float = 0.8f private val snowPaint = Paint().also { it.shader = snowConfig.colorGradingShader } private var frameBuffer = FrameBuffer(background.width, background.height) private val frameBufferPaint = Paint().also { it.shader = snowConfig.accumulatedSnowShader } // Use outlineFrameBuffer and outlineFrameBufferPaint to get foreground outline // its process requires blur effects private var outlineFrameBuffer = FrameBuffer(background.width, background.height) private val outlineFrameBufferPaint = Paint().also { it.shader = snowConfig.accumulatedSnowOutlineShader } // accumulationFrameBuffer and accumulationFrameBufferPaint will get the result from // outlineFrameBuffer and add noise to snow fluffiness private var accumulationFrameBuffer = FrameBuffer( (background.width * bitmapScale).toInt(), (background.height * bitmapScale).toInt(), ) private val accumulationFrameBufferPaint = Paint().also { it.shader = snowConfig.accumulatedSnowResultShader } init { frameBuffer.setRenderEffect( outlineFrameBuffer.setRenderEffect( RenderEffect.createBlurEffect( BLUR_RADIUS / bitmapScale, BLUR_RADIUS / bitmapScale, Loading Loading @@ -85,7 +98,8 @@ class SnowEffect( override fun release() { super.release() frameBuffer.close() outlineFrameBuffer.close() accumulationFrameBuffer.close() } override fun setIntensity(intensity: Float) { Loading @@ -105,11 +119,17 @@ class SnowEffect( return false } frameBuffer.close() frameBuffer = FrameBuffer(background.width, background.height) outlineFrameBuffer.close() accumulationFrameBuffer.close() outlineFrameBuffer = FrameBuffer(background.width, background.height) val newScale = getScale(parallaxMatrix) bitmapScale = newScale frameBuffer.setRenderEffect( accumulationFrameBuffer = FrameBuffer( (background.width * bitmapScale).toInt(), (background.height * bitmapScale).toInt(), ) outlineFrameBuffer.setRenderEffect( RenderEffect.createBlurEffect( BLUR_RADIUS / bitmapScale, BLUR_RADIUS / bitmapScale, Loading Loading @@ -139,20 +159,34 @@ class SnowEffect( super.setMatrix(matrix) // Blur radius should change with scale because it decides the fluffiness of snow if (abs(bitmapScale - oldScale) > FLOAT_TOLERANCE) { frameBuffer.setRenderEffect( outlineFrameBuffer.close() accumulationFrameBuffer.close() outlineFrameBuffer = FrameBuffer((background.width), (background.height)) outlineFrameBuffer.setRenderEffect( RenderEffect.createBlurEffect( BLUR_RADIUS / bitmapScale, BLUR_RADIUS / bitmapScale, Shader.TileMode.CLAMP, ) ) accumulationFrameBuffer = FrameBuffer( (background.width * bitmapScale).toInt(), (background.height * bitmapScale).toInt(), ) snowConfig.shader.setInputShader( "accumulatedSnow", BitmapShader(blankBitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), ) generateAccumulatedSnow() } } override fun updateTextureUniforms() { super.updateTextureUniforms() snowConfig.shader.setInputBuffer( snowConfig.accumulatedSnowResultShader.setInputBuffer( "noise", BitmapShader(snowConfig.noiseTexture, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT), ) Loading @@ -168,28 +202,55 @@ class SnowEffect( } } // Generate accumulated snow requires two passes, first is to generate blurred foreground // outline, second is to add snow fluffiness to it. // It should only be called when bitmaps or screensize change, and should not be called // per frame. private fun generateAccumulatedSnow() { val renderingCanvas = frameBuffer.beginDrawing() snowConfig.accumulatedSnowShader.setFloatUniform("scale", bitmapScale) snowConfig.accumulatedSnowShader.setFloatUniform( // Generate foreground outline val renderingCanvas = outlineFrameBuffer.beginDrawing() snowConfig.accumulatedSnowOutlineShader.setFloatUniform("scale", bitmapScale) snowConfig.accumulatedSnowOutlineShader.setFloatUniform( "snowThickness", SNOW_THICKNESS / bitmapScale, ) snowConfig.accumulatedSnowShader.setFloatUniform("screenWidth", surfaceSize.width) snowConfig.accumulatedSnowShader.setInputBuffer( snowConfig.accumulatedSnowOutlineShader.setFloatUniform("screenWidth", surfaceSize.width) snowConfig.accumulatedSnowOutlineShader.setInputBuffer( "foreground", BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), ) renderingCanvas.drawPaint(frameBufferPaint) frameBuffer.endDrawing() renderingCanvas.drawPaint(outlineFrameBufferPaint) outlineFrameBuffer.endDrawing() outlineFrameBuffer.tryObtainingImage( ::generateAccumulatedSnowWithBlurredOutline, mainExecutor, ) } /** @param outlineImage is generated by outlineShader */ private fun generateAccumulatedSnowWithBlurredOutline(outlineImage: Bitmap) { val renderingCanvas = accumulationFrameBuffer.beginDrawing() snowConfig.accumulatedSnowResultShader.setInputBuffer( "foregroundOutline", BitmapShader(outlineImage, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), ) // Actually, we should not generate it with bitmap snowConfig.accumulatedSnowResultShader.setFloatUniform( "transformMatrixBitmapScaleOnly", transformMatrixCenterCrop, ) renderingCanvas.drawPaint(accumulationFrameBufferPaint) accumulationFrameBuffer.endDrawing() frameBuffer.tryObtainingImage( accumulationFrameBuffer.tryObtainingImage( { image -> snowConfig.shader.setInputBuffer( "accumulatedSnow", BitmapShader(image, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), BitmapShader(image, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP), ) outlineFrameBuffer.close() }, mainExecutor, ) Loading @@ -201,10 +262,15 @@ class SnowEffect( } companion object { val BLUR_RADIUS = 4f const val BLUR_RADIUS = 4f // Use blur effect for both blurring the snow accumulation and generating a gradient edge // so that intensity can control snow thickness by cut the gradient edge in snow_effect // shader. val SNOW_THICKNESS = 6f const val SNOW_THICKNESS = 6f // During wallpaper resizing, the updated accumulation texture might not be immediately // available. // To prevent displaying outdated accumulation, we use a tiny blank bitmap to temporarily // clear the rendering area before the new texture is ready. private val blankBitmap = createBitmap(1, 1) } } Loading
weathereffects/graphics/assets/shaders/snow_accumulation.agsl→weathereffects/graphics/assets/shaders/snow_accumulation_outline.agsl +0 −0 File moved. View file
weathereffects/graphics/assets/shaders/snow_accumulation_result.agsl 0 → 100644 +56 −0 Original line number Diff line number Diff line /* * Copyright (C) 2025 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ // `foregroundOutline`: Original size bitmap with blurred foreground outline uniform shader foregroundOutline; uniform shader noise; uniform mat3 transformMatrixBitmapScaleOnly; #include "shaders/simplex2d.agsl" #include "shaders/utils.agsl" /** * This shader generates ready-to-use snow accumulation with noise and a fluffy effect * added to the foreground outline. It also proportionally scales the input bitmap * to fit the screen size for memory efficiency. * * For snow_effects, only transform `fragCoord` based on parallax translation to access this result. * - R channel: Controls accumulation thickness based on intensity. * - G and B channels: Cache intermediate values to avoid redundant per-frame calculations. */ vec4 main(float2 fragCoord) { vec4 color = vec4(0, 0, 0, 1.0); // Apply transform matrix to fragCoord to scale down output float2 adjustedUv = transformPoint(transformMatrixBitmapScaleOnly, fragCoord); // Load noise texture to give "fluffiness" to the snow. Displace the sampling of the noise. vec3 cloudsNoise = noise.eval(adjustedUv * 7000 + vec2(fragCoord.y, -fragCoord.x)).rgb; // Add dither to give texture to the snow and ruffle the edges. float dither = abs(triangleNoise(fragCoord * 0.01)); // Get the accumulated snow buffer. r contains its mask, g contains some random noise. vec2 accSnow = foregroundOutline.eval(adjustedUv).rg; // R channel as intensity threshold color.r = accSnow.r; // Sharpen the mask of the accumulated snow, but not in excess. // Makes the edges of the snow layer accumulation rougher. color.g = 1. - cloudsNoise.b - 0.3 * dither; // Load snow texture and dither. Make it have gray-ish values. float accSnowTexture = smoothstep(0.2, 0.7, /* noise= */ accSnow.g) * 0.7; accSnowTexture = map(accSnowTexture, dither - 1, 1, 0, 1); // Adjust snow texture coverage/shape. accSnowTexture = map(accSnowTexture, 0.67, 0.8, 0, 1); color.b = 1.- 0.6 * accSnowTexture - 0.35 * dither; return color; }
weathereffects/graphics/assets/shaders/snow_effect.agsl +6 −22 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ uniform shader foreground; uniform shader background; uniform shader accumulatedSnow; uniform shader noise; uniform float2 gridSize; uniform float time; uniform float screenAspectRatio; Loading Loading @@ -53,7 +52,8 @@ vec4 main(float2 fragCoord) { // Apply transform matrix to fragCoord float2 adjustedUv = transformPoint(transformMatrixBitmap, fragCoord); // Calculate uv for snow based on transformed coordinates float2 uv = transformPoint(transformMatrixWeather, fragCoord) / screenSize; float2 weatherUv = transformPoint(transformMatrixWeather, fragCoord); float2 uv = weatherUv / screenSize; float2 uvAdjusted = vec2(uv.x, uv.y / screenAspectRatio); vec4 colorForeground = foreground.eval(adjustedUv); Loading Loading @@ -93,27 +93,11 @@ vec4 main(float2 fragCoord) { color.rgb = normalBlend(color.rgb, colorForeground.rgb, colorForeground.a); // 4. Add accumulated snow layer. // Load noise texture to give "fluffy-ness" to the snow. Displace the sampling of the noise. vec3 cloudsNoise = noise.eval(uvAdjusted * 7000 + vec2(fragCoord.y, -fragCoord.x)).rgb; // Add dither to give texture to the snow and ruffle the edges. float dither = abs(triangleNoise(fragCoord * 0.01)); // Get the accumulated snow buffer. r contains its mask, g contains some random noise. vec2 accSnow = accumulatedSnow.eval(adjustedUv).rg; // Sharpen the mask of the accumulated snow, but not in excess. vec3 accSnow = accumulatedSnow.eval(weatherUv).rgb; float accSnowMask = smoothstep( (1.-intensity), 1.0, /* mask= */accSnow.r); if (accSnowMask > 0) { // Makes the edges of the snow layer accumulation rougher. accSnowMask = map(accSnowMask, 1. - cloudsNoise.b - 0.3 * dither, 1., 0., 1.); // Load snow texture and dither. Make it have gray-ish values. float accSnowTexture = smoothstep(0.2, 0.7, /* noise= */ accSnow.g) * 0.7; accSnowTexture = map(accSnowTexture, dither - 1, 1, 0, 1); // Adjust snow texture coverage/shape. accSnowTexture = map(accSnowTexture, 0.67, 0.8, 0, 1); accSnowMask = map(accSnowMask, 0., 1., 0., 1.- 0.6 * accSnowTexture - 0.35 * dither); accSnowMask = map(accSnowMask, accSnow.g, 1., 0., 1.); accSnowMask = map(accSnowMask, 0., 1., 0., accSnow.b); color.rgb = normalBlendNotPremultiplied(color.rgb, snowColor.rgb, snowColor.a * accSnowMask); } // 5. Generate snow in front of the subject. for (float i = midSnowLayerIndex; i >= closestSnowLayerIndex; i--) { Loading
weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/WeatherEffectBase.kt +14 −9 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ import android.graphics.RuntimeShader import android.graphics.Shader import android.util.SizeF import com.google.android.wallpaper.weathereffects.graphics.utils.GraphicsUtils import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.calculateTranslationDifference import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.calculateTransformDifference import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.centerCropMatrix import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.getScaleFromMatrixValues import com.google.android.wallpaper.weathereffects.graphics.utils.MatrixUtils.invertAndTransposeMatrix Loading @@ -37,7 +37,7 @@ abstract class WeatherEffectBase( /** The initial size of the surface where the effect will be shown. */ private var surfaceSize: SizeF, ) : WeatherEffect { private var centerCropMatrix: Matrix = protected var centerCropMatrix: Matrix = centerCropMatrix( surfaceSize, SizeF(background.width.toFloat(), background.height.toFloat()), Loading @@ -54,6 +54,7 @@ abstract class WeatherEffectBase( FloatArray(9).apply { parallaxMatrix.getValues(this) } // Currently, we use same transform for both foreground and background protected open val transformMatrixBitmap: FloatArray = FloatArray(9) protected open val transformMatrixCenterCrop: FloatArray = FloatArray(9) // Apply to weather components not rely on image textures // Should be identity matrix in editor, and only change when parallax applied in homescreen private val transformMatrixWeather: FloatArray = FloatArray(9) Loading @@ -66,6 +67,10 @@ abstract class WeatherEffectBase( abstract val colorGradingIntensity: Float override fun setMatrix(matrix: Matrix) { if (matrix == this.parallaxMatrix) { return } this.parallaxMatrix.setAndUpdateFloatArray(matrix, parallaxMatrixValues) bitmapScale = getScaleFromMatrixValues(parallaxMatrixValues) adjustCropping(surfaceSize) Loading @@ -74,15 +79,10 @@ abstract class WeatherEffectBase( /** This function will be called every time parallax changes, don't do heavy things here */ open fun adjustCropping(newSurfaceSize: SizeF) { invertAndTransposeMatrix(parallaxMatrix, transformMatrixBitmap) calculateTranslationDifference( centerCropMatrixValues, parallaxMatrixValues, transformMatrixWeather, ) invertAndTransposeMatrix(centerCropMatrix, transformMatrixCenterCrop) calculateTransformDifference(centerCropMatrix, parallaxMatrix, transformMatrixWeather) shader.setFloatUniform("transformMatrixBitmap", transformMatrixBitmap) shader.setFloatUniform("transformMatrixWeather", transformMatrixWeather) shader.setFloatUniform("screenSize", newSurfaceSize.width, newSurfaceSize.height) shader.setFloatUniform("screenAspectRatio", GraphicsUtils.getAspectRatio(newSurfaceSize)) } open fun updateGridSize(newSurfaceSize: SizeF) {} Loading @@ -94,6 +94,8 @@ abstract class WeatherEffectBase( surfaceSize, SizeF(background.width.toFloat(), background.height.toFloat()), ) shader.setFloatUniform("screenSize", newSurfaceSize.width, newSurfaceSize.height) shader.setFloatUniform("screenAspectRatio", GraphicsUtils.getAspectRatio(newSurfaceSize)) adjustCropping(newSurfaceSize) updateGridSize(newSurfaceSize) } Loading Loading @@ -156,6 +158,9 @@ abstract class WeatherEffectBase( "background", BitmapShader(background, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), ) shader.setFloatUniform("screenSize", surfaceSize.width, surfaceSize.height) shader.setFloatUniform("screenAspectRatio", GraphicsUtils.getAspectRatio(surfaceSize)) } private fun Matrix.setAndUpdateFloatArray(src: Matrix, targetFloatArray: FloatArray) { Loading
weathereffects/graphics/src/main/java/com/google/android/wallpaper/weathereffects/graphics/snow/SnowEffect.kt +86 −20 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import android.graphics.RenderEffect import android.graphics.RuntimeShader import android.graphics.Shader import android.util.SizeF import androidx.core.graphics.createBitmap import com.google.android.wallpaper.weathereffects.graphics.FrameBuffer import com.google.android.wallpaper.weathereffects.graphics.WeatherEffect.Companion.DEFAULT_INTENSITY import com.google.android.wallpaper.weathereffects.graphics.WeatherEffectBase Loading @@ -51,11 +52,23 @@ class SnowEffect( private var snowSpeed: Float = 0.8f private val snowPaint = Paint().also { it.shader = snowConfig.colorGradingShader } private var frameBuffer = FrameBuffer(background.width, background.height) private val frameBufferPaint = Paint().also { it.shader = snowConfig.accumulatedSnowShader } // Use outlineFrameBuffer and outlineFrameBufferPaint to get foreground outline // its process requires blur effects private var outlineFrameBuffer = FrameBuffer(background.width, background.height) private val outlineFrameBufferPaint = Paint().also { it.shader = snowConfig.accumulatedSnowOutlineShader } // accumulationFrameBuffer and accumulationFrameBufferPaint will get the result from // outlineFrameBuffer and add noise to snow fluffiness private var accumulationFrameBuffer = FrameBuffer( (background.width * bitmapScale).toInt(), (background.height * bitmapScale).toInt(), ) private val accumulationFrameBufferPaint = Paint().also { it.shader = snowConfig.accumulatedSnowResultShader } init { frameBuffer.setRenderEffect( outlineFrameBuffer.setRenderEffect( RenderEffect.createBlurEffect( BLUR_RADIUS / bitmapScale, BLUR_RADIUS / bitmapScale, Loading Loading @@ -85,7 +98,8 @@ class SnowEffect( override fun release() { super.release() frameBuffer.close() outlineFrameBuffer.close() accumulationFrameBuffer.close() } override fun setIntensity(intensity: Float) { Loading @@ -105,11 +119,17 @@ class SnowEffect( return false } frameBuffer.close() frameBuffer = FrameBuffer(background.width, background.height) outlineFrameBuffer.close() accumulationFrameBuffer.close() outlineFrameBuffer = FrameBuffer(background.width, background.height) val newScale = getScale(parallaxMatrix) bitmapScale = newScale frameBuffer.setRenderEffect( accumulationFrameBuffer = FrameBuffer( (background.width * bitmapScale).toInt(), (background.height * bitmapScale).toInt(), ) outlineFrameBuffer.setRenderEffect( RenderEffect.createBlurEffect( BLUR_RADIUS / bitmapScale, BLUR_RADIUS / bitmapScale, Loading Loading @@ -139,20 +159,34 @@ class SnowEffect( super.setMatrix(matrix) // Blur radius should change with scale because it decides the fluffiness of snow if (abs(bitmapScale - oldScale) > FLOAT_TOLERANCE) { frameBuffer.setRenderEffect( outlineFrameBuffer.close() accumulationFrameBuffer.close() outlineFrameBuffer = FrameBuffer((background.width), (background.height)) outlineFrameBuffer.setRenderEffect( RenderEffect.createBlurEffect( BLUR_RADIUS / bitmapScale, BLUR_RADIUS / bitmapScale, Shader.TileMode.CLAMP, ) ) accumulationFrameBuffer = FrameBuffer( (background.width * bitmapScale).toInt(), (background.height * bitmapScale).toInt(), ) snowConfig.shader.setInputShader( "accumulatedSnow", BitmapShader(blankBitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), ) generateAccumulatedSnow() } } override fun updateTextureUniforms() { super.updateTextureUniforms() snowConfig.shader.setInputBuffer( snowConfig.accumulatedSnowResultShader.setInputBuffer( "noise", BitmapShader(snowConfig.noiseTexture, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT), ) Loading @@ -168,28 +202,55 @@ class SnowEffect( } } // Generate accumulated snow requires two passes, first is to generate blurred foreground // outline, second is to add snow fluffiness to it. // It should only be called when bitmaps or screensize change, and should not be called // per frame. private fun generateAccumulatedSnow() { val renderingCanvas = frameBuffer.beginDrawing() snowConfig.accumulatedSnowShader.setFloatUniform("scale", bitmapScale) snowConfig.accumulatedSnowShader.setFloatUniform( // Generate foreground outline val renderingCanvas = outlineFrameBuffer.beginDrawing() snowConfig.accumulatedSnowOutlineShader.setFloatUniform("scale", bitmapScale) snowConfig.accumulatedSnowOutlineShader.setFloatUniform( "snowThickness", SNOW_THICKNESS / bitmapScale, ) snowConfig.accumulatedSnowShader.setFloatUniform("screenWidth", surfaceSize.width) snowConfig.accumulatedSnowShader.setInputBuffer( snowConfig.accumulatedSnowOutlineShader.setFloatUniform("screenWidth", surfaceSize.width) snowConfig.accumulatedSnowOutlineShader.setInputBuffer( "foreground", BitmapShader(foreground, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), ) renderingCanvas.drawPaint(frameBufferPaint) frameBuffer.endDrawing() renderingCanvas.drawPaint(outlineFrameBufferPaint) outlineFrameBuffer.endDrawing() outlineFrameBuffer.tryObtainingImage( ::generateAccumulatedSnowWithBlurredOutline, mainExecutor, ) } /** @param outlineImage is generated by outlineShader */ private fun generateAccumulatedSnowWithBlurredOutline(outlineImage: Bitmap) { val renderingCanvas = accumulationFrameBuffer.beginDrawing() snowConfig.accumulatedSnowResultShader.setInputBuffer( "foregroundOutline", BitmapShader(outlineImage, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), ) // Actually, we should not generate it with bitmap snowConfig.accumulatedSnowResultShader.setFloatUniform( "transformMatrixBitmapScaleOnly", transformMatrixCenterCrop, ) renderingCanvas.drawPaint(accumulationFrameBufferPaint) accumulationFrameBuffer.endDrawing() frameBuffer.tryObtainingImage( accumulationFrameBuffer.tryObtainingImage( { image -> snowConfig.shader.setInputBuffer( "accumulatedSnow", BitmapShader(image, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR), BitmapShader(image, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP), ) outlineFrameBuffer.close() }, mainExecutor, ) Loading @@ -201,10 +262,15 @@ class SnowEffect( } companion object { val BLUR_RADIUS = 4f const val BLUR_RADIUS = 4f // Use blur effect for both blurring the snow accumulation and generating a gradient edge // so that intensity can control snow thickness by cut the gradient edge in snow_effect // shader. val SNOW_THICKNESS = 6f const val SNOW_THICKNESS = 6f // During wallpaper resizing, the updated accumulation texture might not be immediately // available. // To prevent displaying outdated accumulation, we use a tiny blank bitmap to temporarily // clear the rendering area before the new texture is ready. private val blankBitmap = createBitmap(1, 1) } }