diff --git a/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt b/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt index 301ee3e..54fffb0 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/components/metaball/ProfileMetaballOverlay.kt @@ -260,6 +260,16 @@ fun ProfileMetaballOverlay( // Should we draw the connector? (when avatar gets close enough) val showConnector = collapseProgress > 0.3f && avatarState.showBlob && distance < maxDist * 2f + // Calculate blur intensity for avatar based on distance to notch (like Telegram) + // When avatar is far - no blur, when close - more blur + val avatarBlurRadius = if (showConnector) { + // More blur as avatar gets closer to notch + val nearProgress = (1f - (distance / maxDist).coerceIn(0f, 1f)) + (nearProgress * ProfileMetaballConstants.BLUR_RADIUS * 0.5f).coerceIn(0f, 10f) + } else { + 0f + } + Box(modifier = modifier.fillMaxSize()) { // LAYER 1: Metaball shapes with blur effect (BLACK shapes only) Canvas( @@ -267,18 +277,16 @@ fun ProfileMetaballOverlay( .fillMaxSize() .graphicsLayer { metaShader.setFloatUniform("cutoff", ProfileMetaballConstants.CUTOFF) - renderEffect = RenderEffect - .createBlurEffect( - ProfileMetaballConstants.BLUR_RADIUS, - ProfileMetaballConstants.BLUR_RADIUS, - Shader.TileMode.DECAL - ) - .let { blurEffect -> - RenderEffect.createChainEffect( - RenderEffect.createRuntimeShaderEffect(metaShader, "composable"), - blurEffect - ) - } + // IMPORTANT: First blur, THEN threshold shader + // createChainEffect(outer, inner) - inner is applied first + val blurEffect = RenderEffect.createBlurEffect( + ProfileMetaballConstants.BLUR_RADIUS, + ProfileMetaballConstants.BLUR_RADIUS, + Shader.TileMode.DECAL + ) + val thresholdEffect = RenderEffect.createRuntimeShaderEffect(metaShader, "composable") + // Chain: blur first (inner), then threshold (outer) + renderEffect = RenderEffect.createChainEffect(thresholdEffect, blurEffect) .asComposeRenderEffect() } ) { @@ -343,7 +351,7 @@ fun ProfileMetaballOverlay( } } - // LAYER 2: Actual avatar content (NO BLUR!) - rendered on top + // LAYER 2: Actual avatar content - with blur when close to notch if (avatarState.showBlob) { val avatarSizeDp = with(density) { (avatarState.radius * 2f).toDp() } val avatarOffsetX = with(density) { (avatarState.centerX - avatarState.radius).toDp() } @@ -357,6 +365,14 @@ fun ProfileMetaballOverlay( .clip(RoundedCornerShape(avatarSizeDp / 2)) .graphicsLayer { alpha = avatarState.opacity + // Apply blur to avatar when close to notch (like Telegram) + if (avatarBlurRadius > 0.5f) { + renderEffect = RenderEffect.createBlurEffect( + avatarBlurRadius, + avatarBlurRadius, + Shader.TileMode.DECAL + ).asComposeRenderEffect() + } }, contentAlignment = Alignment.Center, content = avatarContent