Android Developers
Published in

Android Developers

AGSL: Made in the Shade(r)

RenderEffects #2: Pixel shaders in Android 13

The video version of these two articles, which also includes information on Pen & Stylus, and SurfaceView vs TextureView. It’s a lot to cover in a 16 minute video. We talk fast.

Blurry Reasoning

Having the background blurred allows the viewer to focus on the larger image that is shown on top.

Fragment Shaders in Android!

Shader code runs on every fragment to determine the resulting colors. (shader source: @XorDev https://twitter.com/XorDev/status/1475524322785640455)
uniform shader input;vec4 main(vec2 coords) {
vec4 red = vec4(1, 0, 0, 1);
return mix(input.eval(coords), red, .5);
}
The result of running the shader above on every fragment of the image on the left to produce the red-tinted image on the right.

AGSL: Fragment Shaders for Android

shaders.skia.org has several example SkSL shaders and tools for editing and debugging shaders

Shaders vs Shaders

val effect = RenderEffect.createShaderEffect(shader)
myView.setEffect(effect)

Back to the Demo

The image caption pops the text out with a shader which blurs and ‘frosts’ the underlying image

Frosted Caption Shader

Relevant view hierarchy for the overall effect of a focused, captioned picture over a blurred background
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
val FROSTED_GLASS_SHADER = RuntimeShader("""
uniform shader inputShader;
uniform float height;
uniform float width;

vec4 main(vec2 coords) {
vec4 currValue = inputShader.eval(coords);
float top = height - 100;
if (coords.y < top) {
return currValue;
} else {
// Avoid blurring edges
if (coords.x > 1 && coords.y > 1 &&
coords.x < (width - 1) &&
coords.y < (height - 1)) {
// simple box blur - average 5x5 grid around pixel
vec4 boxSum =
inputShader.eval(coords + vec2(-2, -2)) +
// ...
currValue +
// ...
inputShader.eval(coords + vec2(2, 2));
currValue = boxSum / 25;
}

const vec4 white = vec4(1);
// top-left corner of label area
vec2 lefttop = vec2(0, top);
float lightenFactor = min(1.0, .6 *
length(coords - lefttop) /
(0.85 * length(vec2(width, 100))));
// White in upper-left, blended increasingly
// toward lower-right
return mix(currValue, white, 1 - lightenFactor);
}
}
""")
The final effect, where the caption is printed on top of a blurred, frosted-glass background.

RuntimeShader + RenderEffect

val effect = RenderEffect.createRuntimeShaderEffect(
FROSTED_GLASS_SHADER, "inputShader")
FROSTED_GLASS_SHADER.setFloatUniform("height", h.toFloat())
FROSTED_GLASS_SHADER.setFloatUniform("width", w.toFloat())
setRenderEffect(effect)

Worth Noting

Bugs, bugs bugs

Shader vs LinearGradient

A Faster Way to Blur

Shader Resources

Skia shaders

AGSL

GLSL

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store