Skip to content

Commit

Permalink
[Jetlagged] Migrate background AGSL modifier to use Modifier.Node (#1231
Browse files Browse the repository at this point in the history
)

Following our new guidance, the `Modifier.yellowBackground()` was
migrated to use `Modifier.Node`.
https://developer.android.com/jetpack/compose/custom-modifiers#animating_modifier
  • Loading branch information
riggaroo authored Dec 7, 2023
2 parents d8d341b + 73755dc commit ae9363c
Showing 1 changed file with 54 additions and 33 deletions.
87 changes: 54 additions & 33 deletions JetLagged/app/src/main/java/com/example/jetlagged/Background.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,58 +19,79 @@ package com.example.jetlagged
import android.graphics.Color
import android.graphics.RuntimeShader
import android.os.Build
import androidx.annotation.RequiresApi
import androidx.compose.animation.core.withInfiniteAnimationFrameMillis
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.ShaderBrush
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.node.DrawModifierNode
import androidx.compose.ui.node.ModifierNodeElement
import com.example.jetlagged.ui.theme.White
import com.example.jetlagged.ui.theme.Yellow
import com.example.jetlagged.ui.theme.YellowVariant
import kotlinx.coroutines.launch
import org.intellij.lang.annotations.Language

fun Modifier.yellowBackground(): Modifier = this.composed {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
// produce updating time in seconds variable to pass into shader
val time by produceState(0f) {
private data object YellowBackgroundElement : ModifierNodeElement<YellowBackgroundNode>() {
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun create() = YellowBackgroundNode()
override fun update(node: YellowBackgroundNode) {
}
}

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private class YellowBackgroundNode : DrawModifierNode, Modifier.Node() {

private val shader = RuntimeShader(SHADER)
private val shaderBrush = ShaderBrush(shader)
private val time = mutableFloatStateOf(0f)

init {
shader.setColorUniform(
"color",
Color.valueOf(Yellow.red, Yellow.green, Yellow.blue, Yellow.alpha)
)
}

override fun ContentDrawScope.draw() {
shader.setFloatUniform("resolution", size.width, size.height)
shader.setFloatUniform("time", time.floatValue)
drawRect(shaderBrush)
drawContent()
}

override fun onAttach() {
coroutineScope.launch {
while (true) {
withInfiniteAnimationFrameMillis {
value = it / 1000f
time.floatValue = it / 1000f
}
}
}
Modifier.drawWithCache {
val shader = RuntimeShader(SHADER)
val shaderBrush = ShaderBrush(shader)
shader.setFloatUniform("iResolution", size.width, size.height)
// Pass the color to support color space automatically
shader.setColorUniform(
"iColor",
Color.valueOf(Yellow.red, Yellow.green, Yellow.blue, Yellow.alpha)
)
onDrawBehind {
shader.setFloatUniform("iTime", time)
drawRect(shaderBrush)
}
}
}
}

fun Modifier.yellowBackground(): Modifier =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
this.then(YellowBackgroundElement)
} else {
Modifier.drawWithCache {
drawWithCache {

val gradientBrush = Brush.verticalGradient(listOf(Yellow, YellowVariant, White))
onDrawBehind {
drawRect(gradientBrush)
}
}
}
}

@Language("AGSL")
val SHADER = """
uniform float2 iResolution;
uniform float iTime;
layout(color) uniform half4 iColor;
uniform float2 resolution;
uniform float time;
layout(color) uniform half4 color;
float calculateColorMultiplier(float yCoord, float factor) {
return step(yCoord, 1.0 + factor * 2.0) - step(yCoord, factor - 0.1);
Expand All @@ -84,23 +105,23 @@ val SHADER = """
const float energy = 0.6;
// Calculated values
float2 uv = fragCoord / iResolution.xy;
float3 color = iColor.rgb;
float timeOffset = iTime * speedMultiplier;
float2 uv = fragCoord / resolution.xy;
float3 rgbColor = color.rgb;
float timeOffset = time * speedMultiplier;
float hAdjustment = uv.x * 4.3;
float3 loopColor = vec3(1.0 - color.r, 1.0 - color.g, 1.0 - color.b) / loops;
float3 loopColor = vec3(1.0 - rgbColor.r, 1.0 - rgbColor.g, 1.0 - rgbColor.b) / loops;
for (float i = 1.0; i <= loops; i += 1.0) {
float loopFactor = i * 0.1;
float sinInput = (timeOffset + hAdjustment) * energy;
float curve = sin(sinInput) * (1.0 - loopFactor) * 0.05;
float colorMultiplier = calculateColorMultiplier(uv.y, loopFactor);
color += loopColor * colorMultiplier;
rgbColor += loopColor * colorMultiplier;
// Offset for next loop
uv.y += curve;
}
return float4(color, 1.0);
return float4(rgbColor, 1.0);
}
""".trimIndent()

0 comments on commit ae9363c

Please sign in to comment.