Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5fea62d5 authored by Yein Jo's avatar Yein Jo Committed by Android (Google) Code Review
Browse files

Merge "Add sparkle noise type in the TurbulenceNoiseShader." into main

parents f308af1b 2b61fecb
Loading
Loading
Loading
Loading
+9 −1
Original line number Diff line number Diff line
@@ -181,6 +181,11 @@ private constructor(
        turbulenceNoiseShader.setColor(newColor)
    }

    /** Updates the noise color that's screen blended on top. */
    fun updateScreenColor(newColor: Int) {
        turbulenceNoiseShader.setScreenColor(newColor)
    }

    /**
     * Retrieves the noise offset x, y, z values. This is useful for replaying the animation
     * smoothly from the last animation, by passing in the last values to the next animation.
@@ -322,7 +327,10 @@ private constructor(
    private fun draw() {
        paintCallback?.onDraw(paint!!)
        renderEffectCallback?.onDraw(
            RenderEffect.createRuntimeShaderEffect(turbulenceNoiseShader, "in_src")
            RenderEffect.createRuntimeShaderEffect(
                turbulenceNoiseShader,
                TurbulenceNoiseShader.BACKGROUND_UNIFORM
            )
        )
    }

+3 −3
Original line number Diff line number Diff line
@@ -52,7 +52,7 @@ data class TurbulenceNoiseAnimationConfig(
    /** Color of the effect. */
    val color: Int = DEFAULT_COLOR,
    /** Background color of the effect. */
    val backgroundColor: Int = DEFAULT_BACKGROUND_COLOR,
    val screenColor: Int = DEFAULT_SCREEN_COLOR,
    val width: Float = 0f,
    val height: Float = 0f,
    val maxDuration: Float = DEFAULT_MAX_DURATION_IN_MILLIS,
@@ -72,7 +72,7 @@ data class TurbulenceNoiseAnimationConfig(
     */
    val lumaMatteOverallBrightness: Float = DEFAULT_LUMA_MATTE_OVERALL_BRIGHTNESS,
    /** Whether to flip the luma mask. */
    val shouldInverseNoiseLuminosity: Boolean = false
    val shouldInverseNoiseLuminosity: Boolean = false,
) {
    companion object {
        const val DEFAULT_MAX_DURATION_IN_MILLIS = 30_000f // Max 30 sec
@@ -83,7 +83,7 @@ data class TurbulenceNoiseAnimationConfig(
        const val DEFAULT_COLOR = Color.WHITE
        const val DEFAULT_LUMA_MATTE_BLEND_FACTOR = 1f
        const val DEFAULT_LUMA_MATTE_OVERALL_BRIGHTNESS = 0f
        const val DEFAULT_BACKGROUND_COLOR = Color.BLACK
        const val DEFAULT_SCREEN_COLOR = Color.BLACK
        private val random = Random()
    }
}
+110 −25
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@
package com.android.systemui.surfaceeffects.turbulencenoise

import android.graphics.RuntimeShader
import com.android.systemui.surfaceeffects.shaders.SolidColorShader
import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary
import java.lang.Float.max

@@ -28,9 +29,11 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) :
    RuntimeShader(getShader(baseType)) {
    // language=AGSL
    companion object {
        /** Uniform name for the background buffer (e.g. image, solid color, etc.). */
        const val BACKGROUND_UNIFORM = "in_src"
        private const val UNIFORMS =
            """
            uniform shader in_src; // Needed to support RenderEffect.
            uniform shader ${BACKGROUND_UNIFORM};
            uniform float in_gridNum;
            uniform vec3 in_noiseMove;
            uniform vec2 in_size;
@@ -41,7 +44,7 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) :
            uniform half in_lumaMatteBlendFactor;
            uniform half in_lumaMatteOverallBrightness;
            layout(color) uniform vec4 in_color;
            layout(color) uniform vec4 in_backgroundColor;
            layout(color) uniform vec4 in_screenColor;
        """

        private const val SIMPLEX_SHADER =
@@ -50,22 +53,20 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) :
                vec2 uv = p / in_size.xy;
                uv.x *= in_aspectRatio;

                // Compute turbulence effect with the uv distorted with simplex noise.
                vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
                // Bring it to [0, 1] range.
                float luma = (simplex3d(noiseP) * in_inverseLuma) * 0.5 + 0.5;
                luma = saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness)
                        * in_opacity;
                vec3 mask = maskLuminosity(in_color.rgb, luma);
                vec3 color = in_backgroundColor.rgb + mask * 0.6;
                vec3 color = getColorTurbulenceMask(simplex3d(noiseP) * in_inverseLuma);

                // Blend the result with the background color.
                color = in_src.eval(p).rgb + color * 0.6;

                // Add dither with triangle distribution to avoid color banding. Dither in the
                // shader here as we are in gamma space.
                float dither = triangleNoise(p * in_pixelDensity) / 255.;
                color += dither.rrr;

                // The result color should be pre-multiplied, i.e. [R*A, G*A, B*A, A], thus need to
                // multiply rgb with a to get the correct result.
                color = (color + dither.rrr) * in_opacity;
                return vec4(color, in_opacity);
                // Return the pre-multiplied alpha result, i.e. [R*A, G*A, B*A, A].
                return vec4(color * in_opacity, in_opacity);
            }
        """

@@ -76,32 +77,105 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) :
                uv.x *= in_aspectRatio;

                vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
                // Bring it to [0, 1] range.
                float luma = (simplex3d_fractal(noiseP) * in_inverseLuma) * 0.5 + 0.5;
                luma = saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness)
                        * in_opacity;
                vec3 mask = maskLuminosity(in_color.rgb, luma);
                vec3 color = in_backgroundColor.rgb + mask * 0.6;
                vec3 color = getColorTurbulenceMask(simplex3d_fractal(noiseP) * in_inverseLuma);

                // Blend the result with the background color.
                color = in_src.eval(p).rgb + color * 0.6;

                // Skip dithering.
                return vec4(color * in_opacity, in_opacity);
            }
        """

        /**
         * This effect has two layers: color turbulence effect with sparkles on top.
         * 1. Gets the luma matte using Simplex noise.
         * 2. Generate a colored turbulence layer with the luma matte.
         * 3. Generate a colored sparkle layer with the same luma matter.
         * 4. Apply a screen color to the background image.
         * 5. Composite the previous result with the color turbulence.
         * 6. Composite the latest result with the sparkles.
         */
        private const val SIMPLEX_SPARKLE_SHADER =
            """
            vec4 main(vec2 p) {
                vec2 uv = p / in_size.xy;
                uv.x *= in_aspectRatio;

                vec3 noiseP = vec3(uv + in_noiseMove.xy, in_noiseMove.z) * in_gridNum;
                // Luma is used for both color and sparkle masks.
                float luma = simplex3d(noiseP) * in_inverseLuma;

                // Get color layer (color mask with in_color applied)
                vec3 colorLayer = getColorTurbulenceMask(simplex3d(noiseP) * in_inverseLuma);
                float dither = triangleNoise(p * in_pixelDensity) / 255.;
                colorLayer += dither.rrr;

                // Get sparkle layer (sparkle mask with particles & in_color applied)
                vec3 sparkleLayer = getSparkleTurbulenceMask(luma, p);

                // Composite with the background.
                half4 bgColor = in_src.eval(p);
                half sparkleOpacity = smoothstep(0, 0.75, in_opacity);

                half3 effect = screen(bgColor.rgb, in_screenColor.rgb);
                effect = screen(effect, colorLayer * 0.22);
                effect += sparkleLayer * sparkleOpacity;

                return mix(bgColor, vec4(effect, 1.), in_opacity);
            }
        """

        private const val COMMON_FUNCTIONS =
            /**
             * Below two functions generate turbulence layers (color or sparkles applied) with the
             * given luma matte. They both return a mask with in_color applied.
             */
            """
            vec3 getColorTurbulenceMask(float luma) {
                // Bring it to [0, 1] range.
                luma = luma * 0.5 + 0.5;

                half colorLuma =
                    saturate(luma * in_lumaMatteBlendFactor + in_lumaMatteOverallBrightness)
                    * in_opacity;
                vec3 colorLayer = maskLuminosity(in_color.rgb, colorLuma);

                return colorLayer;
            }

            vec3 getSparkleTurbulenceMask(float luma, vec2 p) {
                half lumaIntensity = 1.75;
                half lumaBrightness = -1.3;
                half sparkleLuma = max(luma * lumaIntensity + lumaBrightness, 0.);

                float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_noiseMove.z);
                vec3 sparkleLayer = maskLuminosity(in_color.rgb * sparkle, sparkleLuma);

                return sparkleLayer;
            }
        """
        private const val SIMPLEX_NOISE_SHADER =
            ShaderUtilLibrary.SHADER_LIB + UNIFORMS + SIMPLEX_SHADER
            ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + SIMPLEX_SHADER
        private const val FRACTAL_NOISE_SHADER =
            ShaderUtilLibrary.SHADER_LIB + UNIFORMS + FRACTAL_SHADER
        // TODO (b/282007590): Add NOISE_WITH_SPARKLE
            ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + FRACTAL_SHADER
        private const val SPARKLE_NOISE_SHADER =
            ShaderUtilLibrary.SHADER_LIB + UNIFORMS + COMMON_FUNCTIONS + SIMPLEX_SPARKLE_SHADER

        enum class Type {
            /** Effect with a simple color noise turbulence. */
            SIMPLEX_NOISE,
            /** Effect with a simple color noise turbulence, with fractal. */
            SIMPLEX_NOISE_FRACTAL,
            /** Effect with color & sparkle turbulence with screen color layer. */
            SIMPLEX_NOISE_SPARKLE
        }

        fun getShader(type: Type): String {
            return when (type) {
                Type.SIMPLEX_NOISE -> SIMPLEX_NOISE_SHADER
                Type.SIMPLEX_NOISE_FRACTAL -> FRACTAL_NOISE_SHADER
                Type.SIMPLEX_NOISE_SPARKLE -> SPARKLE_NOISE_SHADER
            }
        }
    }
@@ -111,7 +185,7 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) :
        setGridCount(config.gridCount)
        setPixelDensity(config.pixelDensity)
        setColor(config.color)
        setBackgroundColor(config.backgroundColor)
        setScreenColor(config.screenColor)
        setSize(config.width, config.height)
        setLumaMatteFactors(config.lumaMatteBlendFactor, config.lumaMatteOverallBrightness)
        setInverseNoiseLuminosity(config.shouldInverseNoiseLuminosity)
@@ -137,9 +211,20 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) :
        setColorUniform("in_color", color)
    }

    /** Sets the background color of the effect. Alpha is ignored. */
    /**
     * Sets the color that is used for blending on top of the background color/image. Only relevant
     * to [Type.SIMPLEX_NOISE_SPARKLE].
     */
    fun setScreenColor(color: Int) {
        setColorUniform("in_screenColor", color)
    }

    /**
     * Sets the background color of the effect. Alpha is ignored. If you are using [RenderEffect],
     * no need to call this function since the background image of the View will be used.
     */
    fun setBackgroundColor(color: Int) {
        setColorUniform("in_backgroundColor", color)
        setInputShader(BACKGROUND_UNIFORM, SolidColorShader(color))
    }

    /**
@@ -163,7 +248,7 @@ class TurbulenceNoiseShader(val baseType: Type = Type.SIMPLEX_NOISE) :
     *
     * @param lumaMatteBlendFactor increases or decreases the amount of variance in noise. Setting
     *   this a lower number removes variations. I.e. the turbulence noise will look more blended.
     *   Expected input range is [0, 1]. more dimmed.
     *   Expected input range is [0, 1].
     * @param lumaMatteOverallBrightness adds the overall brightness of the turbulence noise.
     *   Expected input range is [0, 1].
     *
+1 −1
Original line number Diff line number Diff line
@@ -1306,7 +1306,7 @@ public class MediaControlPanel {
                TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z,
                // Color will be correctly updated in ColorSchemeTransition.
                /* color= */ mColorSchemeTransition.getAccentPrimary().getCurrentColor(),
                /* backgroundColor= */ Color.BLACK,
                /* screenColor= */ Color.BLACK,
                width,
                height,
                TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS,
+40 −50
Original line number Diff line number Diff line
@@ -19,7 +19,9 @@ package com.android.systemui.surfaceeffects.loadingeffect
import android.graphics.Paint
import android.graphics.RenderEffect
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.animation.AnimatorTestRule
import com.android.systemui.model.SysUiStateTest
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.AnimationState.EASE_IN
@@ -31,18 +33,17 @@ import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion
import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect.Companion.RenderEffectDrawCallback
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig
import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
class LoadingEffectTest : SysUiStateTest() {

    private val fakeSystemClock = FakeSystemClock()
    private val fakeExecutor = FakeExecutor(fakeSystemClock)
    @get:Rule val animatorTestRule = AnimatorTestRule(this)

    @Test
    fun play_paintCallback_triggersDrawCallback() {
@@ -61,15 +62,13 @@ class LoadingEffectTest : SysUiStateTest() {
                animationStateChangedCallback = null
            )

        fakeExecutor.execute {
        assertThat(paintFromCallback).isNull()

        loadingEffect.play()
            fakeSystemClock.advanceTime(500L)
        animatorTestRule.advanceTimeBy(500L)

        assertThat(paintFromCallback).isNotNull()
    }
    }

    @Test
    fun play_renderEffectCallback_triggersDrawCallback() {
@@ -88,25 +87,22 @@ class LoadingEffectTest : SysUiStateTest() {
                animationStateChangedCallback = null
            )

        fakeExecutor.execute {
        assertThat(renderEffectFromCallback).isNull()

        loadingEffect.play()
            fakeSystemClock.advanceTime(500L)
        animatorTestRule.advanceTimeBy(500L)

        assertThat(renderEffectFromCallback).isNotNull()
    }
    }

    @Test
    fun play_animationStateChangesInOrder() {
        val config = TurbulenceNoiseAnimationConfig()
        val expectedStates = arrayOf(NOT_PLAYING, EASE_IN, MAIN, EASE_OUT, NOT_PLAYING)
        val actualStates = mutableListOf(NOT_PLAYING)
        val states = mutableListOf(NOT_PLAYING)
        val stateChangedCallback =
            object : AnimationStateChangedCallback {
                override fun onStateChanged(oldState: AnimationState, newState: AnimationState) {
                    actualStates.add(newState)
                    states.add(newState)
                }
            }
        val drawCallback =
@@ -121,16 +117,15 @@ class LoadingEffectTest : SysUiStateTest() {
                stateChangedCallback
            )

        val timeToAdvance =
            config.easeInDuration + config.maxDuration + config.easeOutDuration + 100

        fakeExecutor.execute {
        loadingEffect.play()

            fakeSystemClock.advanceTime(timeToAdvance.toLong())
        // Execute all the animators by advancing each duration with some buffer.
        animatorTestRule.advanceTimeBy(config.easeInDuration.toLong())
        animatorTestRule.advanceTimeBy(config.maxDuration.toLong())
        animatorTestRule.advanceTimeBy(config.easeOutDuration.toLong())
        animatorTestRule.advanceTimeBy(500)

            assertThat(actualStates).isEqualTo(expectedStates)
        }
        assertThat(states).containsExactly(NOT_PLAYING, EASE_IN, MAIN, EASE_OUT, NOT_PLAYING)
    }

    @Test
@@ -157,7 +152,6 @@ class LoadingEffectTest : SysUiStateTest() {
                stateChangedCallback
            )

        fakeExecutor.execute {
        assertThat(numPlay).isEqualTo(0)

        loadingEffect.play()
@@ -168,7 +162,6 @@ class LoadingEffectTest : SysUiStateTest() {

        assertThat(numPlay).isEqualTo(1)
    }
    }

    @Test
    fun finish_finishesLoadingEffect() {
@@ -181,7 +174,7 @@ class LoadingEffectTest : SysUiStateTest() {
        val stateChangedCallback =
            object : AnimationStateChangedCallback {
                override fun onStateChanged(oldState: AnimationState, newState: AnimationState) {
                    if (oldState == MAIN && newState == NOT_PLAYING) {
                    if (oldState == EASE_OUT && newState == NOT_PLAYING) {
                        isFinished = true
                    }
                }
@@ -194,19 +187,18 @@ class LoadingEffectTest : SysUiStateTest() {
                stateChangedCallback
            )

        fakeExecutor.execute {
        assertThat(isFinished).isFalse()

        loadingEffect.play()
            fakeSystemClock.advanceTime(config.easeInDuration.toLong() + 500L)
        animatorTestRule.advanceTimeBy(config.easeInDuration.toLong() + 500L)

        assertThat(isFinished).isFalse()

        loadingEffect.finish()
        animatorTestRule.advanceTimeBy(config.easeOutDuration.toLong() + 500L)

        assertThat(isFinished).isTrue()
    }
    }

    @Test
    fun finish_notMainState_hasNoEffect() {
@@ -232,14 +224,12 @@ class LoadingEffectTest : SysUiStateTest() {
                stateChangedCallback
            )

        fakeExecutor.execute {
        assertThat(isFinished).isFalse()

        loadingEffect.finish()

        assertThat(isFinished).isFalse()
    }
    }

    @Test
    fun getNoiseOffset_returnsNoiseOffset() {
Loading