blob: 979fe33fb25b30f93023006aa32f4b09237dee03 [file] [log] [blame]
/*
* Copyright (C) 2021 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.biometrics
import android.graphics.PointF
import android.graphics.RuntimeShader
import android.util.MathUtils
/**
* Shader class that renders a distorted ripple for the UDFPS dwell effect.
* Adjustable shader parameters:
* - progress
* - origin
* - color
* - time
* - maxRadius
* - distortionStrength.
* See per field documentation for more details.
*
* Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
*/
class DwellRippleShader internal constructor() : RuntimeShader(SHADER) {
companion object {
private const val SHADER_UNIFORMS = """uniform vec2 in_origin;
uniform float in_time;
uniform float in_radius;
uniform float in_blur;
layout(color) uniform vec4 in_color;
uniform float in_phase1;
uniform float in_phase2;
uniform float in_distortion_strength;"""
private const val SHADER_LIB = """
float softCircle(vec2 uv, vec2 xy, float radius, float blur) {
float blurHalf = blur * 0.5;
float d = distance(uv, xy);
return 1. - smoothstep(1. - blurHalf, 1. + blurHalf, d / radius);
}
float softRing(vec2 uv, vec2 xy, float radius, float blur) {
float thickness_half = radius * 0.25;
float circle_outer = softCircle(uv, xy, radius + thickness_half, blur);
float circle_inner = softCircle(uv, xy, radius - thickness_half, blur);
return circle_outer - circle_inner;
}
vec2 distort(vec2 p, float time, float distort_amount_xy, float frequency) {
return p + vec2(sin(p.y * frequency + in_phase1),
cos(p.x * frequency * -1.23 + in_phase2)) * distort_amount_xy;
}
vec4 ripple(vec2 p, float distort_xy, float frequency) {
vec2 p_distorted = distort(p, in_time, distort_xy, frequency);
float circle = softCircle(p_distorted, in_origin, in_radius * 1.2, in_blur);
float rippleAlpha = max(circle,
softRing(p_distorted, in_origin, in_radius, in_blur)) * 0.25;
return in_color * rippleAlpha;
}
"""
private const val SHADER_MAIN = """vec4 main(vec2 p) {
vec4 color1 = ripple(p,
34 * in_distortion_strength, // distort_xy
0.012 // frequency
);
vec4 color2 = ripple(p,
49 * in_distortion_strength, // distort_xy
0.018 // frequency
);
// Alpha blend between two layers.
return vec4(color1.xyz + color2.xyz
* (1 - color1.w), color1.w + color2.w * (1-color1.w));
}"""
private const val SHADER = SHADER_UNIFORMS + SHADER_LIB + SHADER_MAIN
}
/**
* Maximum radius of the ripple.
*/
var maxRadius: Float = 0.0f
/**
* Origin coordinate of the ripple.
*/
var origin: PointF = PointF()
set(value) {
field = value
setFloatUniform("in_origin", value.x, value.y)
}
/**
* Progress of the ripple. Float value between [0, 1].
*/
var progress: Float = 0.0f
set(value) {
field = value
setFloatUniform("in_radius",
(1 - (1 - value) * (1 - value) * (1 - value))* maxRadius)
setFloatUniform("in_blur", MathUtils.lerp(1f, 0.7f, value))
}
/**
* Distortion strength between [0, 1], with 0 being no distortion and 1 being full distortion.
*/
var distortionStrength: Float = 0.0f
set(value) {
field = value
setFloatUniform("in_distortion_strength", value)
}
/**
* Play time since the start of the effect in seconds.
*/
var time: Float = 0.0f
set(value) {
field = value * 0.001f
setFloatUniform("in_time", field)
setFloatUniform("in_phase1", field * 3f + 0.367f)
setFloatUniform("in_phase2", field * 7.2f * 1.531f)
}
/**
* A hex value representing the ripple color, in the format of ARGB
*/
var color: Int = 0xffffff.toInt()
set(value) {
field = value
setColorUniform("in_color", value)
}
}