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

Commit 436b6628 authored by Yein Jo's avatar Yein Jo
Browse files

Add ellipse shape to the ripple.

Bug: b/203800342
Test: Manual, RippleViewTest
Change-Id: I29fe4d05a868305814a980ef2bde53a608df0ce2
parent a37c26f5
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import com.android.systemui.ripple.RippleView
 */
class ReceiverChipRippleView(context: Context?, attrs: AttributeSet?) : RippleView(context, attrs) {
    init {
        // TODO: use RippleShape#ELLIPSE when calling setupShader.
        setupShader()
        setRippleFill(true)
        duration = 3000L
+36 −12
Original line number Diff line number Diff line
@@ -36,7 +36,8 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
    /** Shapes that the [RippleShader] supports. */
    enum class RippleShape {
        CIRCLE,
        ROUNDED_BOX
        ROUNDED_BOX,
        ELLIPSE
    }

    companion object {
@@ -57,12 +58,10 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
                uniform float in_sparkle_strength;"""

        private const val SHADER_CIRCLE_MAIN = """vec4 main(vec2 p) {
                // distortion only applies to circle
                vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);
                float radius = in_size.x * 0.5;

                float sparkleRing = softRing(p_distorted, in_center, radius, in_blur);
                float inside = softCircle(p_distorted, in_center, radius * 1.2, in_blur);
                float sparkleRing = soften(circleRing(p_distorted-in_center, radius), in_blur);
                float inside = soften(sdCircle(p_distorted-in_center, radius * 1.2), in_blur);
                float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
                    * (1.-sparkleRing) * in_fadeSparkle;

@@ -75,9 +74,26 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
        """

        private const val SHADER_ROUNDED_BOX_MAIN = """vec4 main(vec2 p) {
                float sparkleRing = softRoundedBoxRing(p, in_center, in_size, in_cornerRadius,
                    in_thickness, in_blur);
                float inside = softRoundedBox(p, in_center, in_size * 1.2, in_cornerRadius, in_blur);
                float sparkleRing = soften(roundedBoxRing(p-in_center, in_size, in_cornerRadius,
                    in_thickness), in_blur);
                float inside = soften(sdRoundedBox(p-in_center, in_size * 1.2, in_cornerRadius),
                    in_blur);
                float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
                    * (1.-sparkleRing) * in_fadeSparkle;

                float rippleInsideAlpha = (1.-inside) * in_fadeFill;
                float rippleRingAlpha = (1.-sparkleRing) * in_fadeRing;
                float rippleAlpha = max(rippleInsideAlpha, rippleRingAlpha) * 0.45;
                vec4 ripple = in_color * rippleAlpha;
                return mix(ripple, vec4(sparkle), sparkle * in_sparkle_strength);
            }
        """

        private const val SHADER_ELLIPSE_MAIN = """vec4 main(vec2 p) {
                vec2 p_distorted = distort(p, in_time, in_distort_radial, in_distort_xy);

                float sparkleRing = soften(ellipseRing(p_distorted-in_center, in_size), in_blur);
                float inside = soften(sdEllipse(p_distorted-in_center, in_size * 1.2), in_blur);
                float sparkle = sparkles(p - mod(p, in_pixelDensity * 0.8), in_time * 0.00175)
                    * (1.-sparkleRing) * in_fadeSparkle;

@@ -90,13 +106,21 @@ class RippleShader internal constructor(rippleShape: RippleShape = RippleShape.C
        """

        private const val CIRCLE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
                SdfShaderLibrary.CIRCLE_SDF + SHADER_CIRCLE_MAIN
                SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.CIRCLE_SDF +
                SHADER_CIRCLE_MAIN
        private const val ROUNDED_BOX_SHADER = SHADER_UNIFORMS +
                RippleShaderUtilLibrary.SHADER_LIB + SdfShaderLibrary.ROUNDED_BOX_SDF +
                SHADER_ROUNDED_BOX_MAIN
                RippleShaderUtilLibrary.SHADER_LIB + SdfShaderLibrary.SHADER_SDF_OPERATION_LIB +
                SdfShaderLibrary.ROUNDED_BOX_SDF + SHADER_ROUNDED_BOX_MAIN
        private const val ELLIPSE_SHADER = SHADER_UNIFORMS + RippleShaderUtilLibrary.SHADER_LIB +
                SdfShaderLibrary.SHADER_SDF_OPERATION_LIB + SdfShaderLibrary.ELLIPSE_SDF +
                SHADER_ELLIPSE_MAIN

        private fun buildShader(rippleShape: RippleShape): String =
                if (rippleShape == RippleShape.CIRCLE) CIRCLE_SHADER else ROUNDED_BOX_SHADER
                when (rippleShape) {
                    RippleShape.CIRCLE -> CIRCLE_SHADER
                    RippleShape.ROUNDED_BOX -> ROUNDED_BOX_SHADER
                    RippleShape.ELLIPSE -> ELLIPSE_SHADER
                }

        private fun subProgress(start: Float, end: Float, progress: Float): Float {
            val min = Math.min(start, end)
+16 −15
Original line number Diff line number Diff line
@@ -18,7 +18,8 @@ package com.android.systemui.ripple
/** A common utility functions that are used for computing [RippleShader]. */
class RippleShaderUtilLibrary {
    companion object {
        const val SHADER_LIB = """float triangleNoise(vec2 n) {
        const val SHADER_LIB = """
            float triangleNoise(vec2 n) {
                    n  = fract(n * vec2(5.3987, 5.4421));
                    n += dot(n.yx, n.xy + vec2(21.5351, 14.3137));
                    float xy = n.x * n.y;
+58 −24
Original line number Diff line number Diff line
@@ -23,22 +23,13 @@ class SdfShaderLibrary {
                return (length(p)-r) / r;
            }

            float softCircle(vec2 p, vec2 origin, float radius, float blur) {
                float d = sdCircle(p-origin, radius);
                float blurHalf = blur * 0.5;
                return smoothstep(-blurHalf, blurHalf, d);
            }

            float softRing(vec2 p, vec2 origin, float radius, float blur) {
            float circleRing(vec2 p, float radius) {
                float thicknessHalf = radius * 0.25;

                float outerCircle = sdCircle(p-origin, radius + thicknessHalf);
                float innerCircle = sdCircle(p-origin, radius);
                float outerCircle = sdCircle(p, radius + thicknessHalf);
                float innerCircle = sdCircle(p, radius);

                float d = max(outerCircle, -innerCircle);
                float blurHalf = blur * 0.5;

                return smoothstep(-blurHalf, blurHalf, d);
                return subtract(outerCircle, innerCircle);
            }
        """

@@ -54,24 +45,67 @@ class SdfShaderLibrary {
                return (outside+inside-cornerRadius)/size.y;
            }

            float softRoundedBox(vec2 p, vec2 origin, vec2 size, float cornerRadius, float blur) {
                float d = sdRoundedBox(p-origin, size, cornerRadius);
                float blurHalf = blur * 0.5;
                return smoothstep(-blurHalf, blurHalf, d);
            float roundedBoxRing(vec2 p, vec2 size, float cornerRadius,
                float borderThickness) {
                float outerRoundBox = sdRoundedBox(p, size, cornerRadius);
                float innerRoundBox = sdRoundedBox(p, size - vec2(borderThickness),
                    cornerRadius - borderThickness);
                return subtract(outerRoundBox, innerRoundBox);
            }
        """

            float softRoundedBoxRing(vec2 p, vec2 origin, vec2 size, float cornerRadius,
                float borderThickness, float blur) {
        // Used non-trigonometry parametrization and Halley's method (iterative) for root finding.
        // This is more expensive than the regular circle SDF, recommend to use the circle SDF if
        // possible.
        const val ELLIPSE_SDF = """float sdEllipse(vec2 p, vec2 wh) {
            wh *= 0.5;

                float outerRoundBox = sdRoundedBox(p-origin, size, cornerRadius);
                float innerRoundBox = sdRoundedBox(p-origin, size - vec2(borderThickness),
                    cornerRadius - borderThickness);
            // symmetry
            (wh.x > wh.y) ? wh = wh.yx, p = abs(p.yx) : p = abs(p);

                float d = max(outerRoundBox, -innerRoundBox);
                float blurHalf = blur * 0.5;
            vec2 u = wh*p, v = wh*wh;

            float U1 = u.y/2.0;  float U5 = 4.0*U1;
            float U2 = v.y-v.x;  float U6 = 6.0*U1;
            float U3 = u.x-U2;   float U7 = 3.0*U3;
            float U4 = u.x+U2;

            float t = 0.5;
            for (int i = 0; i < 3; i ++) {
                float F1 = t*(t*t*(U1*t+U3)+U4)-U1;
                float F2 = t*t*(U5*t+U7)+U4;
                float F3 = t*(U6*t+U7);

                t += (F1*F2)/(F1*F3-F2*F2);
            }

            t = clamp(t, 0.0, 1.0);

            float d = distance(p, wh*vec2(1.0-t*t,2.0*t)/(t*t+1.0));
            d /= wh.y;

            return (dot(p/wh,p/wh)>1.0) ? d : -d;
        }

        float ellipseRing(vec2 p, vec2 wh) {
            vec2 thicknessHalf = wh * 0.25;

            float outerEllipse = sdEllipse(p, wh + thicknessHalf);
            float innerEllipse = sdEllipse(p, wh);

            return subtract(outerEllipse, innerEllipse);
        }
        """

        const val SHADER_SDF_OPERATION_LIB = """
            float soften(float d, float blur) {
                float blurHalf = blur * 0.5;
                return smoothstep(-blurHalf, blurHalf, d);
            }

            float subtract(float outer, float inner) {
                return max(outer, -inner);
            }
        """
    }
}
+5 −0
Original line number Diff line number Diff line
@@ -43,4 +43,9 @@ class RippleViewTest : SysuiTestCase() {
    fun testSetupShader_compilesRoundedBox() {
        rippleView.setupShader(RippleShader.RippleShape.ROUNDED_BOX)
    }

    @Test
    fun testSetupShader_compilesEllipse() {
        rippleView.setupShader(RippleShader.RippleShape.ELLIPSE)
    }
}