Skip to main content

Shader Transitions

AsyncPic V3.0.0 introduces AGSL (Android Graphics Shading Language) cinematic reveal effects. These are GPU-accelerated transitions that play when the image finishes loading.

Android 13+ Only

Shader transitions require Android 13 (API 33 / Tiramisu). On older devices, AsyncPic silently falls back to the standard crossfade — no crash, no extra code needed.


AsyncPicTransition​

sealed class AsyncPicTransition {
object Standard : AsyncPicTransition() // default crossfade
data class ShaderReveal(
val type: RevealType = RevealType.DISSOLVE,
val durationMillis: Int = 1000
) : AsyncPicTransition()
}

RevealType​

Three GPU shader effects are available:

DISSOLVE (default)​

Noise-based particle dissolve. Image materializes from random pixels.

AsyncPic(
source = ImageSource.Url("https://example.com/photo.jpg"),
modifier = Modifier.fillMaxWidth().height(250.dp),
transition = AsyncPicTransition.ShaderReveal(
type = RevealType.DISSOLVE,
durationMillis = 1200
)
)

PIXELATE​

Starts as large pixel blocks that shrink into full resolution.

AsyncPic(
source = ImageSource.Url("https://example.com/photo.jpg"),
modifier = Modifier.fillMaxWidth().height(250.dp),
transition = AsyncPicTransition.ShaderReveal(
type = RevealType.PIXELATE,
durationMillis = 800
)
)

WIPE​

Clean left-to-right wipe reveal.

AsyncPic(
source = ImageSource.Url("https://example.com/photo.jpg"),
modifier = Modifier.fillMaxWidth().height(250.dp),
transition = AsyncPicTransition.ShaderReveal(
type = RevealType.WIPE,
durationMillis = 600
)
)

Standard (No Shader)​

Use the default Coil crossfade with no shader:

AsyncPic(
source = ImageSource.Url("..."),
modifier = Modifier.size(200.dp),
transition = AsyncPicTransition.Standard // default
)

Duration​

Control animation speed via durationMillis (milliseconds):

// Fast: 400ms
transition = AsyncPicTransition.ShaderReveal(durationMillis = 400)

// Slow cinematic: 2000ms
transition = AsyncPicTransition.ShaderReveal(durationMillis = 2000)

How It Works​

AsyncPic uses android.graphics.RuntimeShader and RenderEffect.createRuntimeShaderEffect to apply the AGSL shader to the composable's graphicsLayer. A progress uniform (0.0 → 1.0) is animated via animateFloatAsState and passed into the shader each frame. On API < 33, the shader block is skipped entirely.