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

Commit 373540e8 authored by Yein Jo's avatar Yein Jo
Browse files

Add SparkleShader in the ShaderLib

As part of Shared shader library, SparkleShader is added to support
modularity.

Test: SparkleShaderTest, SolidColorShaderTest
Test: Manual in debug app (locally. will upload apk in experimental
package.)

Change-Id: I13677567814e8e3aab46704106dcea9a739a0631
parent bd019a86
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */
package com.android.systemui.surfaceeffects.shaders

import android.graphics.RuntimeShader

/** Simply renders a solid color. */
class SolidColorShader(color: Int) : RuntimeShader(SHADER) {
    // language=AGSL
    private companion object {
        private const val SHADER =
            """
                layout(color) uniform vec4 in_color;
                vec4 main(vec2 p) {
                    return in_color;
                }
            """
    }

    init {
        setColorUniform("in_color", color)
    }
}
+115 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */
package com.android.systemui.surfaceeffects.shaders

import android.graphics.Color
import android.graphics.RuntimeShader
import android.graphics.Shader
import com.android.systemui.surfaceeffects.shaderutil.ShaderUtilLibrary

/**
 * Renders sparkles based on the luma matte.
 *
 * For example, you can pass in simplex noise as the luma matte and have a cloud looking sparkles.
 *
 * You may want to utilize this shader by: (Preferred) 1. Create a RuntimeShaderEffect and set the
 * [RenderEffect] to the target [View].
 * 2. Create a custom [View], set the shader to the [Paint] and use [Canvas.drawPaint] in [onDraw].
 */
class SparkleShader : RuntimeShader(SPARKLE_SHADER) {
    // language=AGSL
    companion object {
        private const val UNIFORMS =
            """
            // Used it for RenderEffect. For example:
            // myView.setRenderEffect(
            //     RenderEffect.createRuntimeShaderEffect(SparkleShader(), "in_src")
            // )
            uniform shader in_src;
            uniform half in_time;
            uniform half in_pixelate;
            uniform shader in_lumaMatte;
            layout(color) uniform vec4 in_color;
        """
        private const val MAIN_SHADER =
            """vec4 main(vec2 p) {
            half3 src = in_src.eval(p).rgb;
            half luma = getLuminosity(in_lumaMatte.eval(p).rgb);
            half sparkle = sparkles(p - mod(p, in_pixelate), in_time);
            half3 mask = maskLuminosity(in_color.rgb * sparkle, luma);

            return vec4(src * mask * in_color.a, in_color.a);
        }
        """
        private const val SPARKLE_SHADER = UNIFORMS + ShaderUtilLibrary.SHADER_LIB + MAIN_SHADER

        /** Highly recommended to use this value unless specified by design spec. */
        const val DEFAULT_SPARKLE_PIXELATE_AMOUNT = 0.8f
    }

    init {
        // Initializes the src and luma matte to be white.
        setInputShader("in_src", SolidColorShader(Color.WHITE))
        setLumaMatteColor(Color.WHITE)
    }

    /**
     * Sets the time of the sparkle animation.
     *
     * This is used for animating sparkles. Note that this only makes the sparkles sparkle in place.
     * In order to move the sparkles in x, y directions, move the luma matte input instead.
     */
    fun setTime(time: Float) {
        setFloatUniform("in_time", time)
    }

    /**
     * Sets pixelated amount of the sparkle.
     *
     * This value *must* be based on [resources.displayMetrics.density]. Otherwise, this will result
     * in having different sparkle sizes on different screens.
     *
     * Expected to be used as follows:
     * <pre>
     *     {@code
     *     val pixelDensity = context.resources.displayMetrics.density
     *     // Sparkles will be 0.8 of the pixel size.
     *     val sparkleShader = SparkleShader().apply { setPixelateAmount(pixelDensity * 0.8f) }
     *     }
     * </pre>
     */
    fun setPixelateAmount(pixelateAmount: Float) {
        setFloatUniform("in_pixelate", pixelateAmount)
    }

    /**
     * Sets the luma matte for the sparkles. The luminosity determines the sparkle's visibility.
     * Useful for setting a complex mask (e.g. simplex noise, texture, etc.)
     */
    fun setLumaMatte(lumaMatte: Shader) {
        setInputShader("in_lumaMatte", lumaMatte)
    }

    /** Sets the luma matte for the sparkles. Useful for setting a solid color. */
    fun setLumaMatteColor(color: Int) {
        setInputShader("in_lumaMatte", SolidColorShader(color))
    }

    /** Sets the color of the sparkles. Expect to have the alpha value encoded. */
    fun setColor(color: Int) {
        setColorUniform("in_color", color)
    }
}
+33 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */
package com.android.systemui.surfaceeffects.shaders

import android.graphics.Color
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidTestingRunner::class)
class SolidColorShaderTest : SysuiTestCase() {

    @Test
    fun compilesShader() {
        SolidColorShader(Color.RED)
    }
}
+44 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2023 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.
 */
package com.android.systemui.surfaceeffects.shaders

import android.graphics.Color
import android.testing.AndroidTestingRunner
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidTestingRunner::class)
class SparkleShaderTest : SysuiTestCase() {

    private lateinit var sparkleShader: SparkleShader

    @Test
    fun compilesSparkleShader() {
        sparkleShader =
            SparkleShader().apply {
                setPixelateAmount(
                    context.resources.displayMetrics.density *
                        SparkleShader.DEFAULT_SPARKLE_PIXELATE_AMOUNT
                )
                setColor(Color.RED)
                setTime(0.01f)
                setLumaMatteColor(Color.WHITE)
            }
    }
}