Skip to content

Commit 1a657aa

Browse files
add frosted glass sample with RenderEffect
1 parent 41267a8 commit 1a657aa

File tree

1 file changed

+170
-30
lines changed

1 file changed

+170
-30
lines changed

Tutorial1-1Basics/src/main/java/com/smarttoolfactory/tutorial1_1basics/chapter6_graphics/Tutorial40_2RenderEffect1.kt

+170-30
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ import androidx.compose.foundation.Canvas
1111
import androidx.compose.foundation.Image
1212
import androidx.compose.foundation.background
1313
import androidx.compose.foundation.border
14+
import androidx.compose.foundation.clickable
1415
import androidx.compose.foundation.layout.Arrangement
1516
import androidx.compose.foundation.layout.Box
1617
import androidx.compose.foundation.layout.Column
18+
import androidx.compose.foundation.layout.PaddingValues
1719
import androidx.compose.foundation.layout.Spacer
1820
import androidx.compose.foundation.layout.aspectRatio
1921
import androidx.compose.foundation.layout.fillMaxSize
@@ -22,21 +24,22 @@ import androidx.compose.foundation.layout.height
2224
import androidx.compose.foundation.layout.padding
2325
import androidx.compose.foundation.layout.width
2426
import androidx.compose.foundation.lazy.LazyColumn
27+
import androidx.compose.foundation.lazy.LazyRow
28+
import androidx.compose.foundation.lazy.rememberLazyListState
2529
import androidx.compose.foundation.rememberScrollState
2630
import androidx.compose.foundation.shape.RoundedCornerShape
2731
import androidx.compose.foundation.verticalScroll
2832
import androidx.compose.material3.Slider
2933
import androidx.compose.material3.Text
3034
import androidx.compose.runtime.Composable
31-
import androidx.compose.runtime.LaunchedEffect
3235
import androidx.compose.runtime.getValue
3336
import androidx.compose.runtime.mutableFloatStateOf
34-
import androidx.compose.runtime.mutableIntStateOf
3537
import androidx.compose.runtime.remember
3638
import androidx.compose.runtime.setValue
3739
import androidx.compose.ui.Alignment
3840
import androidx.compose.ui.Modifier
3941
import androidx.compose.ui.draw.clipToBounds
42+
import androidx.compose.ui.draw.drawWithCache
4043
import androidx.compose.ui.draw.drawWithContent
4144
import androidx.compose.ui.geometry.Offset
4245
import androidx.compose.ui.graphics.Brush
@@ -59,15 +62,16 @@ import androidx.compose.ui.platform.LocalDensity
5962
import androidx.compose.ui.res.imageResource
6063
import androidx.compose.ui.res.painterResource
6164
import androidx.compose.ui.tooling.preview.Preview
65+
import androidx.compose.ui.unit.Dp
6266
import androidx.compose.ui.unit.IntSize
6367
import androidx.compose.ui.unit.dp
68+
import androidx.compose.ui.unit.roundToIntSize
6469
import androidx.compose.ui.unit.sp
6570
import com.smarttoolfactory.tutorial1_1basics.R
6671
import com.smarttoolfactory.tutorial1_1basics.ui.backgroundColor
6772
import com.smarttoolfactory.tutorial1_1basics.ui.components.StyleableTutorialText
6873
import com.smarttoolfactory.tutorial1_1basics.ui.components.TutorialHeader
6974
import com.smarttoolfactory.tutorial1_1basics.ui.components.TutorialText2
70-
import kotlinx.coroutines.delay
7175
import org.intellij.lang.annotations.Language
7276
import kotlin.math.roundToInt
7377

@@ -77,7 +81,6 @@ fun Tutorial6_40Screen2() {
7781
TutorialContent()
7882
}
7983

80-
8184
@Composable
8285
private fun TutorialContent() {
8386
Column(modifier = Modifier.padding(8.dp)) {
@@ -507,19 +510,11 @@ fun RenderNodeSample() {
507510

508511
val graphicsLayer = rememberGraphicsLayer()
509512

510-
var counter by remember {
511-
mutableIntStateOf(0)
512-
}
513-
514-
LaunchedEffect(Unit) {
515-
while (true) {
516-
delay(60)
517-
counter++
518-
}
519-
}
513+
val state = rememberLazyListState()
520514

521515
Box {
522516
LazyColumn(
517+
state = state,
523518
modifier = Modifier
524519
.background(backgroundColor)
525520
.fillMaxSize()
@@ -536,20 +531,6 @@ fun RenderNodeSample() {
536531
) {
537532
this@drawWithContent.drawContent()
538533
}
539-
540-
// val recordingCanvas = contentNode.beginRecording()
541-
// canvasHolder.drawInto(recordingCanvas) {
542-
// drawContext.also {
543-
// it.layoutDirection = layoutDirection
544-
// it.size = size
545-
// it.canvas = this
546-
// }
547-
548-
// drawLayer(graphicsLayer)
549-
// }
550-
//
551-
// contentNode.endRecording()
552-
553534
},
554535
verticalArrangement = Arrangement.spacedBy(8.dp)
555536
) {
@@ -600,8 +581,6 @@ fun RenderNodeSample() {
600581
)
601582
)
602583

603-
println("Drawing...$counter")
604-
605584
drawIntoCanvas { canvas: Canvas ->
606585
val recordingCanvas = contentNode.beginRecording()
607586
canvasHolder.drawInto(recordingCanvas) {
@@ -626,4 +605,165 @@ fun RenderNodeSample() {
626605
)
627606
}
628607
}
608+
}
609+
610+
fun Modifier.frostedGlass(height: Dp) = this.then(
611+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
612+
Modifier.drawWithCache {
613+
val topBarHeightPx = height.toPx()
614+
615+
val blurLayer = obtainGraphicsLayer().apply {
616+
renderEffect = RenderEffect
617+
.createBlurEffect(16f, 16f, Shader.TileMode.DECAL)
618+
.asComposeRenderEffect()
619+
}
620+
621+
onDrawWithContent {
622+
blurLayer.record(size.copy(height = topBarHeightPx).roundToIntSize()) {
623+
this@onDrawWithContent.drawContent()
624+
}
625+
626+
drawLayer(blurLayer)
627+
clipRect(top = topBarHeightPx) {
628+
this@onDrawWithContent.drawContent()
629+
}
630+
}
631+
}
632+
} else {
633+
Modifier
634+
}
635+
)
636+
637+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
638+
@Preview(showBackground = true)
639+
@Composable
640+
fun PartialBlurTest() {
641+
642+
val scrollState = rememberLazyListState()
643+
val topBarHeight = 180.dp
644+
645+
LazyColumn(
646+
state = scrollState,
647+
modifier = Modifier
648+
.fillMaxSize()
649+
.frostedGlass(height = topBarHeight),
650+
verticalArrangement = Arrangement.spacedBy(8.dp)
651+
) {
652+
653+
item {
654+
Spacer(modifier = Modifier.height(topBarHeight))
655+
}
656+
657+
item {
658+
LazyRow(
659+
modifier = Modifier.fillMaxWidth().aspectRatio(2f),
660+
contentPadding = PaddingValues(horizontal = 16.dp),
661+
horizontalArrangement = Arrangement.spacedBy(8.dp)
662+
) {
663+
items(5) {
664+
Image(
665+
modifier = Modifier
666+
.fillParentMaxWidth()
667+
.aspectRatio(2f),
668+
contentScale = ContentScale.Crop,
669+
painter = painterResource(R.drawable.landscape11), contentDescription = null
670+
)
671+
}
672+
}
673+
}
674+
items(100) {
675+
if (it == 5) {
676+
Image(
677+
modifier = Modifier
678+
.fillMaxWidth()
679+
.aspectRatio(.5f),
680+
painter = painterResource(R.drawable.landscape11),
681+
contentScale = ContentScale.Crop,
682+
contentDescription = null
683+
)
684+
} else {
685+
Box(
686+
modifier = Modifier
687+
.clickable {
688+
689+
}
690+
.fillMaxWidth()
691+
.background(Color.White, RoundedCornerShape(16.dp))
692+
.padding(16.dp)
693+
) {
694+
Text("Row $it", fontSize = 22.sp)
695+
}
696+
}
697+
}
698+
}
699+
}
700+
701+
702+
@Language("GLSL")
703+
val CheapFrostedGlassTopBarAGSL =
704+
"""
705+
const vec4 white = vec4(1.0);
706+
707+
uniform shader inputShader;
708+
uniform float barHeight;
709+
710+
vec4 main(vec2 coord) {
711+
if (coord.y > barHeight) {
712+
return inputShader.eval(coord);
713+
} else {
714+
vec2 factor = vec2(3.0);
715+
716+
vec4 color = vec4(0.0);
717+
color += inputShader.eval(coord - 3.0 * factor) * 0.0540540541;
718+
color += inputShader.eval(coord - 2.0 * factor) * 0.1216216216;
719+
color += inputShader.eval(coord - factor) * 0.1945945946;
720+
color += inputShader.eval(coord) * 0.2270270270;
721+
color += inputShader.eval(coord + factor) * 0.1945945946;
722+
color += inputShader.eval(coord + 2.0 * factor) * 0.1216216216;
723+
color += inputShader.eval(coord + 3.0 * factor) * 0.0540540541;
724+
725+
return mix(color, white, 0.7);
726+
}
727+
}
728+
""".trimIndent()
729+
730+
val TopAppBarHeight = 180.dp
731+
732+
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
733+
@Preview(showBackground = true)
734+
@Composable
735+
fun FrostedGlassTopBarTest() {
736+
LazyColumn(
737+
modifier = Modifier
738+
.fillMaxSize()
739+
.graphicsLayer {
740+
val shader = RuntimeShader(CheapFrostedGlassTopBarAGSL)
741+
shader.setFloatUniform("barHeight", TopAppBarHeight.toPx())
742+
val androidRenderEffect = android.graphics.RenderEffect
743+
.createRuntimeShaderEffect(shader, "inputShader")
744+
renderEffect = androidRenderEffect.asComposeRenderEffect()
745+
}
746+
) {
747+
items(100) {
748+
if (it == 5) {
749+
Image(
750+
modifier = Modifier
751+
.fillMaxWidth()
752+
.aspectRatio(2f),
753+
painter = painterResource(R.drawable.landscape11),
754+
contentScale = ContentScale.Crop,
755+
contentDescription = null
756+
)
757+
} else {
758+
Box(
759+
modifier = Modifier
760+
.fillMaxWidth()
761+
.background(Color.White, RoundedCornerShape(16.dp))
762+
.padding(16.dp)
763+
) {
764+
Text("Row $it", fontSize = 22.sp)
765+
}
766+
}
767+
}
768+
}
629769
}

0 commit comments

Comments
 (0)