diff --git a/app/src/main/kotlin/com/skydoves/cloudydemo/Main.kt b/app/src/main/kotlin/com/skydoves/cloudydemo/Main.kt index 4f429df..eecdc4e 100644 --- a/app/src/main/kotlin/com/skydoves/cloudydemo/Main.kt +++ b/app/src/main/kotlin/com/skydoves/cloudydemo/Main.kt @@ -54,7 +54,7 @@ fun Main() { ) { var animationPlayed by remember { mutableStateOf(false) } val radius by animateIntAsState( - targetValue = if (animationPlayed) 15 else 0, + targetValue = if (animationPlayed) 45 else 0, animationSpec = tween( durationMillis = 1000, delayMillis = 500, diff --git a/app/src/main/kotlin/com/skydoves/cloudydemo/posters/HomePosters.kt b/app/src/main/kotlin/com/skydoves/cloudydemo/posters/HomePosters.kt index a86d5b1..c8e04c1 100644 --- a/app/src/main/kotlin/com/skydoves/cloudydemo/posters/HomePosters.kt +++ b/app/src/main/kotlin/com/skydoves/cloudydemo/posters/HomePosters.kt @@ -35,7 +35,7 @@ import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout -import com.skydoves.cloudy.Cloudy +import com.skydoves.cloudy.internals.cloudy import com.skydoves.cloudydemo.model.MockUtil import com.skydoves.cloudydemo.model.Poster import com.skydoves.cloudydemo.theme.PosterTheme @@ -53,16 +53,12 @@ fun HomePosters( applyBlur = true } - Cloudy( - radius = 20, - key1 = applyBlur + LazyVerticalGrid( + modifier = Modifier.cloudy(radius = 20), + columns = GridCells.Fixed(2) ) { - LazyVerticalGrid( - columns = GridCells.Fixed(2) - ) { - items(key = { it.id }, items = posters) { - HomePoster(poster = it) - } + items(key = { it.id }, items = posters) { + HomePoster(poster = it) } } } diff --git a/cloudy/src/main/kotlin/com/skydoves/cloudy/Cloudy.kt b/cloudy/src/main/kotlin/com/skydoves/cloudy/Cloudy.kt index a80650f..fa8aa02 100644 --- a/cloudy/src/main/kotlin/com/skydoves/cloudy/Cloudy.kt +++ b/cloudy/src/main/kotlin/com/skydoves/cloudy/Cloudy.kt @@ -42,7 +42,7 @@ import androidx.compose.ui.viewinterop.AndroidView import com.skydoves.cloudy.internals.CloudyModifier import com.skydoves.cloudy.internals.InternalLaunchedEffect import com.skydoves.cloudy.internals.LayoutInfo -import com.skydoves.cloudy.internals.render.RenderScriptToolkit +import com.skydoves.cloudy.internals.render.iterativeBlur import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -54,7 +54,7 @@ import kotlinx.coroutines.withContext * History: The [blur] modifier supports only Android 12 and higher, and [RenderScript] was also deprecated. * * @param modifier Adjust the drawing layout or drawing decoration of the content. - * @param radius Radius of the blur along both the x and y axis. It must be in 0 to 25. + * @param radius Radius of the blur along both the x and y axis. * @param key1 Key value for trigger recomposition. * @param key2 Key value for trigger recomposition. * @param onStateChanged Lambda function that will be invoked when the blur process has been updated. @@ -63,7 +63,7 @@ import kotlinx.coroutines.withContext @Composable public fun Cloudy( modifier: Modifier = Modifier, - @androidx.annotation.IntRange(from = 0, to = 25) radius: Int = 10, + radius: Int = 10, key1: Any? = null, key2: Any? = null, allowAccumulate: (CloudyState) -> Boolean = { false }, @@ -105,7 +105,7 @@ public fun Cloudy( */ private fun ComposeView.composeCloudy( modifier: Modifier, - @androidx.annotation.IntRange(from = 0, to = 25) radius: Int, + radius: Int, key1: Any? = null, key2: Any? = null, key3: Any? = null, @@ -173,7 +173,7 @@ private fun Modifier.cloudy( key3: Any? = null, initialBitmap: Bitmap? = null, graphicsLayer: GraphicsLayer, - @androidx.annotation.IntRange(from = 0, to = 25) radius: Int, + radius: Int, layoutInfo: LayoutInfo, onStateChanged: (CloudyState) -> Unit ): Modifier = composed( @@ -194,8 +194,8 @@ private fun Modifier.cloudy( val targetBitmap = blurredBitmap ?: graphicsLayer.toImageBitmap().asAndroidBitmap() .copy(Bitmap.Config.ARGB_8888, true) - blurredBitmap = RenderScriptToolkit.blur( - inputBitmap = targetBitmap, + blurredBitmap = iterativeBlur( + androidBitmap = targetBitmap, radius = radius ) } diff --git a/cloudy/src/main/kotlin/com/skydoves/cloudy/internals/CloudyModifierNode.kt b/cloudy/src/main/kotlin/com/skydoves/cloudy/internals/CloudyModifierNode.kt index e1c3d87..c1bf0fa 100644 --- a/cloudy/src/main/kotlin/com/skydoves/cloudy/internals/CloudyModifierNode.kt +++ b/cloudy/src/main/kotlin/com/skydoves/cloudy/internals/CloudyModifierNode.kt @@ -16,7 +16,6 @@ package com.skydoves.cloudy.internals import android.graphics.Bitmap -import androidx.annotation.IntRange import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.asAndroidBitmap @@ -37,12 +36,12 @@ import androidx.compose.ui.platform.InspectorInfo import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.offset import com.skydoves.cloudy.CloudyState -import com.skydoves.cloudy.internals.render.RenderScriptToolkit +import com.skydoves.cloudy.internals.render.iterativeBlur import kotlinx.coroutines.runBlocking @Composable public fun Modifier.cloudy( - @IntRange(from = 0, to = 25) radius: Int = 10, + radius: Int = 10, onStateChanged: (CloudyState) -> Unit = {} ): Modifier { return this then CloudyModifierNodeElement( @@ -54,7 +53,7 @@ public fun Modifier.cloudy( private data class CloudyModifierNodeElement( private val graphicsLayer: GraphicsLayer, - @IntRange(from = 0, to = 25) val radius: Int = 10, + val radius: Int = 10, val onStateChanged: (CloudyState) -> Unit = {} ) : ModifierNodeElement() { @@ -76,7 +75,7 @@ private data class CloudyModifierNodeElement( private class CloudyModifierNode( val graphicsLayer: GraphicsLayer, - @IntRange(from = 0, to = 25) var radius: Int = 10, + var radius: Int = 10, private val onStateChanged: (CloudyState) -> Unit = {} ) : LayoutModifierNode, GlobalPositionAwareModifierNode, DrawModifierNode, Modifier.Node() { @@ -111,13 +110,13 @@ private class CloudyModifierNode( onStateChanged.invoke(CloudyState.Loading) try { - val targetBitmap: Bitmap? = runBlocking { + val targetBitmap: Bitmap = runBlocking { graphicsLayer.toImageBitmap().asAndroidBitmap() .copy(Bitmap.Config.ARGB_8888, true) - } + } ?: throw RuntimeException("Couldn't capture a bitmap from the composable tree") - val blurredBitmap = RenderScriptToolkit.blur( - inputBitmap = targetBitmap, + val blurredBitmap = iterativeBlur( + androidBitmap = targetBitmap, radius = radius )?.apply { drawImage(this.asImageBitmap()) diff --git a/cloudy/src/main/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt b/cloudy/src/main/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt index f12ae36..b98d046 100644 --- a/cloudy/src/main/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt +++ b/cloudy/src/main/kotlin/com/skydoves/cloudy/internals/render/RenderScriptToolkit.kt @@ -314,3 +314,23 @@ internal fun vectorSize(bitmap: Bitmap): Int { ) } } + +internal fun iterativeBlur( + androidBitmap: Bitmap, + radius: Int +): Bitmap? { + val iterate = (radius + 1) / 25 + var bitmap: Bitmap? = RenderScriptToolkit.blur( + inputBitmap = androidBitmap, + radius = (radius + 1) % 25 + ) + + for (i in 0 until iterate) { + bitmap = RenderScriptToolkit.blur( + inputBitmap = bitmap, + radius = 25 + ) + } + + return bitmap +}