From 7f75c601b0994903e205848f32ba0c50bd2ea7bb Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 28 Dec 2024 21:02:37 -0500 Subject: [PATCH 1/2] Added shader --- .../animite/features/home/HomeScreen.kt | 77 ++++++++++++++++--- .../animite/core/ui/shaders/Ethereal.kt | 51 ++++++++++++ 2 files changed, 116 insertions(+), 12 deletions(-) create mode 100644 core/src/main/kotlin/com/imashnake/animite/core/ui/shaders/Ethereal.kt diff --git a/app/src/main/kotlin/com/imashnake/animite/features/home/HomeScreen.kt b/app/src/main/kotlin/com/imashnake/animite/features/home/HomeScreen.kt index ded1079c..82cd3e6e 100644 --- a/app/src/main/kotlin/com/imashnake/animite/features/home/HomeScreen.kt +++ b/app/src/main/kotlin/com/imashnake/animite/features/home/HomeScreen.kt @@ -1,5 +1,8 @@ package com.imashnake.animite.features.home +import android.graphics.RuntimeShader +import android.os.Build +import androidx.annotation.RequiresApi import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.ExperimentalSharedTransitionApi @@ -34,16 +37,25 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState +import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.withFrameMillis import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.CacheDrawScope +import androidx.compose.ui.draw.DrawResult import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.ShaderBrush +import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.dimensionResource @@ -67,16 +79,18 @@ import com.imashnake.animite.core.ui.MediaSmallRow import com.imashnake.animite.core.ui.ProgressIndicator import com.imashnake.animite.core.ui.layouts.BannerLayout import com.imashnake.animite.core.ui.layouts.TranslucentStatusBarLayout +import com.imashnake.animite.core.ui.shaders.etherealShader +import com.imashnake.animite.features.media.MediaPage import com.imashnake.animite.navigation.SharedContentKey import com.imashnake.animite.navigation.SharedContentKey.Component.Card import com.imashnake.animite.navigation.SharedContentKey.Component.Image import com.imashnake.animite.navigation.SharedContentKey.Component.Page import com.imashnake.animite.navigation.SharedContentKey.Component.Text -import com.imashnake.animite.features.media.MediaPage import com.materialkolor.ktx.hasEnoughContrast import com.imashnake.animite.core.R as coreR import com.imashnake.animite.navigation.R as navigationR +@RequiresApi(Build.VERSION_CODES.TIRAMISU) @OptIn(ExperimentalSharedTransitionApi::class) @Composable @Suppress("LongMethod") @@ -121,18 +135,12 @@ fun HomeScreen( alignment = Alignment.TopCenter ) - Box( - modifier = bannerModifier.background( - Brush.verticalGradient( - listOf( - Color.Transparent, - MaterialTheme.colorScheme.secondaryContainer - .copy(alpha = 0.2f) - ) - ) - ) + Ethereal( + Color(0xFF6C408D), + Color(0x00000000), ) + // TODO: We can probably draw the shader behind this directly. Row( modifier = Modifier .fillMaxWidth() @@ -350,3 +358,48 @@ private fun MediaTypeSelector( } } } + +@RequiresApi(Build.VERSION_CODES.TIRAMISU) +@Composable +fun Ethereal( + color1: Color, + color2: Color, +) { + val shader = remember { RuntimeShader(etherealShader) } + with(shader) { + setColorUniform("color1", color1.toArgb()) + setColorUniform("color2", color2.toArgb()) + } + SimpleSketchWithCache( + modifier = Modifier + .fillMaxWidth() + .height(168.dp) + ) { timeState -> + with(shader) { + setFloatUniform("resolution", size.width, size.height) + setFloatUniform("time", timeState.value) + } + onDrawBehind { + drawRect(ShaderBrush(shader)) + } + } +} + +@Composable +fun SimpleSketchWithCache( + modifier: Modifier = Modifier, + speed: Float = 0.01f, + onBuildDrawCache: CacheDrawScope.(time: State) -> DrawResult +) { + val time = remember { mutableFloatStateOf(0f) } + + LaunchedEffect(Unit) { + do { + withFrameMillis { + time.value += speed + } + } while (true) + } + + Box(modifier.drawWithCache { onBuildDrawCache(time) }) +} diff --git a/core/src/main/kotlin/com/imashnake/animite/core/ui/shaders/Ethereal.kt b/core/src/main/kotlin/com/imashnake/animite/core/ui/shaders/Ethereal.kt new file mode 100644 index 00000000..a5d74be1 --- /dev/null +++ b/core/src/main/kotlin/com/imashnake/animite/core/ui/shaders/Ethereal.kt @@ -0,0 +1,51 @@ +package com.imashnake.animite.core.ui.shaders + +import org.intellij.lang.annotations.Language + + +@Language("AGSL") +val Util = """ + float pi = 3.1415926536; + float twopi = 6.28318530718; + + float random(float2 st) { + return fract(sin(dot(st.xy, float2(12.9898,78.233))) * 43758.5453123); + } + + float plot(vec2 st, float pct){ + return smoothstep(pct - 0.01, pct, st.y) - smoothstep(pct, pct + 0.01, st.y); + } +""".trimIndent() + +@Language("AGSL") +val etherealShader = """ + $Util + + uniform float2 resolution; + uniform float time; + + layout(color) uniform half4 color1; + layout(color) uniform half4 color2; + + half4 main(float2 coord) { + float2 uv = coord.xy / resolution; + +// // Show path +// if ( +// uv.y < -sin(6 * uv.x)/4 + 0.5 && +// uv.y > -sin(6 * uv.x)/4 + 0.47 +// ) color = 0.0; + + // Moving center + vec2 center = vec2(pow(sin(time * 0.2), 2.0) * resolution.x, (-sin(6 * pow(sin(time * 0.2), 2.0))/4 + 0.47) * resolution.y); + + // Radius and size of circular gradient + float radius = 350.0 + (pow(sin(time), 2.0) * 50.0); + float amount = 400.0; + + float d = length(coord - center) - (radius - amount); + float3 pct = float3(smoothstep(0.0, amount, d)); + float3 color = mix(color1.rgb, color2.rgb, pct); + return half4(color, 0.2); + } +""".trimIndent() From 4a0f7b087898d9f14869dda5fa09167867ac9262 Mon Sep 17 00:00:00 2001 From: imashnake0 Date: Sat, 28 Dec 2024 21:37:48 -0500 Subject: [PATCH 2/2] Cleanup --- .../animite/features/home/HomeScreen.kt | 80 +++++-------------- .../animite/core/ui/shaders/Ethereal.kt | 6 +- 2 files changed, 25 insertions(+), 61 deletions(-) diff --git a/app/src/main/kotlin/com/imashnake/animite/features/home/HomeScreen.kt b/app/src/main/kotlin/com/imashnake/animite/features/home/HomeScreen.kt index 82cd3e6e..c70d5f9c 100644 --- a/app/src/main/kotlin/com/imashnake/animite/features/home/HomeScreen.kt +++ b/app/src/main/kotlin/com/imashnake/animite/features/home/HomeScreen.kt @@ -39,7 +39,6 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.MutableState -import androidx.compose.runtime.State import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf @@ -49,8 +48,6 @@ import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.withFrameMillis import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.CacheDrawScope -import androidx.compose.ui.draw.DrawResult import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawWithCache import androidx.compose.ui.graphics.Color @@ -115,6 +112,16 @@ fun HomeScreen( allTimePopularList, ) + val time = remember { mutableFloatStateOf(0f) } + LaunchedEffect(Unit) { + do { + withFrameMillis { + time.value += 0.01f + } + } while (true) + } + val shader = remember { RuntimeShader(etherealShader) } + when { rows.all { it is Resource.Success } -> { val scrollState = rememberScrollState() @@ -135,18 +142,20 @@ fun HomeScreen( alignment = Alignment.TopCenter ) - Ethereal( - Color(0xFF6C408D), - Color(0x00000000), - ) - - // TODO: We can probably draw the shader behind this directly. Row( - modifier = Modifier - .fillMaxWidth() - .align(Alignment.BottomCenter), + modifier = bannerModifier.drawWithCache { + with(shader) { + setFloatUniform("resolution", size.width, size.height) + setFloatUniform("time", time.floatValue) + setColorUniform("orb", Color(0xFF6C408D).toArgb()) + setColorUniform("bg", android.graphics.Color.TRANSPARENT) + } + onDrawBehind { + drawRect(ShaderBrush(shader)) + } + }, horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.Bottom, ) { Text( text = stringResource(R.string.okaeri), @@ -358,48 +367,3 @@ private fun MediaTypeSelector( } } } - -@RequiresApi(Build.VERSION_CODES.TIRAMISU) -@Composable -fun Ethereal( - color1: Color, - color2: Color, -) { - val shader = remember { RuntimeShader(etherealShader) } - with(shader) { - setColorUniform("color1", color1.toArgb()) - setColorUniform("color2", color2.toArgb()) - } - SimpleSketchWithCache( - modifier = Modifier - .fillMaxWidth() - .height(168.dp) - ) { timeState -> - with(shader) { - setFloatUniform("resolution", size.width, size.height) - setFloatUniform("time", timeState.value) - } - onDrawBehind { - drawRect(ShaderBrush(shader)) - } - } -} - -@Composable -fun SimpleSketchWithCache( - modifier: Modifier = Modifier, - speed: Float = 0.01f, - onBuildDrawCache: CacheDrawScope.(time: State) -> DrawResult -) { - val time = remember { mutableFloatStateOf(0f) } - - LaunchedEffect(Unit) { - do { - withFrameMillis { - time.value += speed - } - } while (true) - } - - Box(modifier.drawWithCache { onBuildDrawCache(time) }) -} diff --git a/core/src/main/kotlin/com/imashnake/animite/core/ui/shaders/Ethereal.kt b/core/src/main/kotlin/com/imashnake/animite/core/ui/shaders/Ethereal.kt index a5d74be1..acd0f466 100644 --- a/core/src/main/kotlin/com/imashnake/animite/core/ui/shaders/Ethereal.kt +++ b/core/src/main/kotlin/com/imashnake/animite/core/ui/shaders/Ethereal.kt @@ -24,8 +24,8 @@ val etherealShader = """ uniform float2 resolution; uniform float time; - layout(color) uniform half4 color1; - layout(color) uniform half4 color2; + layout(color) uniform half4 orb; + layout(color) uniform half4 bg; half4 main(float2 coord) { float2 uv = coord.xy / resolution; @@ -45,7 +45,7 @@ val etherealShader = """ float d = length(coord - center) - (radius - amount); float3 pct = float3(smoothstep(0.0, amount, d)); - float3 color = mix(color1.rgb, color2.rgb, pct); + float3 color = mix(orb.rgb, bg.rgb, pct); return half4(color, 0.2); } """.trimIndent()