Loading packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt +36 −12 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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; Loading @@ -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; Loading @@ -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) Loading packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt +16 −15 Original line number Diff line number Diff line Loading @@ -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; Loading packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt +58 −24 Original line number Diff line number Diff line Loading @@ -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); } """ Loading @@ -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); } """ } } packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt +5 −0 Original line number Diff line number Diff line Loading @@ -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) } } Loading
packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/ReceiverChipRippleView.kt +1 −0 Original line number Diff line number Diff line Loading @@ -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 Loading
packages/SystemUI/src/com/android/systemui/ripple/RippleShader.kt +36 −12 Original line number Diff line number Diff line Loading @@ -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 { Loading @@ -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; Loading @@ -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; Loading @@ -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) Loading
packages/SystemUI/src/com/android/systemui/ripple/RippleShaderUtilLibrary.kt +16 −15 Original line number Diff line number Diff line Loading @@ -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; Loading
packages/SystemUI/src/com/android/systemui/ripple/SdfShaderLibrary.kt +58 −24 Original line number Diff line number Diff line Loading @@ -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); } """ Loading @@ -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); } """ } }
packages/SystemUI/tests/src/com/android/systemui/ripple/RippleViewTest.kt +5 −0 Original line number Diff line number Diff line Loading @@ -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) } }