Update highlight implementation to use BlurMaskFilter

This commit is contained in:
Kyant0 committed 2025-09-17 11:20:29 +08:00
1 parent 9dcfa0aa8c
commit 1810bc64f3
4 files changed
+42 -74

No files matched your search

@@ -25,7 +25,7 @@ private class BackdropElement(val backdrop: Backdrop) : ModifierNodeElement<Back
}
override fun InspectorInfo.inspectableProperties() {
name = "layerBackdrop"
name = "backdrop"
properties["backdrop"] = backdrop
}
@@ -9,8 +9,9 @@ import androidx.compose.ui.unit.dp
@Immutable
data class Highlight(
val width: Dp = 1f.dp,
val color: Color = Color.White.copy(alpha = 0.38f),
val color: Color = Color.White.copy(alpha = 0.25f),
val blendMode: BlendMode = BlendMode.Plus,
val blurRadius: Dp = width / 3f,
val style: () -> HighlightStyle = DefaultHighlightStyle
)
@@ -1,16 +1,15 @@
package com.kyant.backdrop.highlight
import android.graphics.BlurMaskFilter
import android.graphics.Paint
import android.graphics.RectF
import android.os.Build
import androidx.compose.ui.draw.CacheDrawModifierNode
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.GraphicsLayerScope
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.asAndroidPath
import androidx.compose.ui.graphics.asComposePaint
import androidx.compose.ui.graphics.asComposeRenderEffect
import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.isUnspecified
import androidx.compose.ui.graphics.layer.GraphicsLayer
import androidx.compose.ui.graphics.layer.drawLayer
@@ -91,42 +90,43 @@ internal class HighlightNode(
}
val size = size
val strokeWidth = ceil(highlight.width.toPx().fastCoerceAtMost(size.minDimension / 2f))
val borderSize = Size(size.width - strokeWidth, size.height - strokeWidth)
val outline = shapeProvider.shape.createOutline(borderSize, layoutDirection, this)
val outline = shapeProvider.shape.createOutline(size, layoutDirection, this)
paint.color = highlight.color.toArgb()
paint.strokeWidth = strokeWidth
val strokeWidth = ceil(highlight.width.toPx().fastCoerceAtMost(size.minDimension / 2f))
paint.strokeWidth = strokeWidth * 2f
val blurRadius = highlight.blurRadius.toPx()
if (blurRadius > 0f) {
paint.maskFilter = BlurMaskFilter(blurRadius, BlurMaskFilter.Blur.NORMAL)
}
paint.asComposePaint().blendMode = highlight.blendMode
graphicsLayer.blendMode = highlight.blendMode
graphicsLayer.record {
translate(strokeWidth / 2f, strokeWidth / 2f) {
val canvas = drawContext.canvas.nativeCanvas
val canvas = drawContext.canvas.nativeCanvas
when (outline) {
is Outline.Rectangle -> {
val rect = RectF(0f, 0f, borderSize.width, borderSize.height)
canvas.drawRect(rect, paint)
}
when (outline) {
is Outline.Rectangle -> {
val rect = RectF(0f, 0f, size.width, size.height)
canvas.drawRect(rect, paint)
}
is Outline.Rounded -> {
@Suppress("INVISIBLE_REFERENCE")
val path = outline.roundRectPath?.asAndroidPath()
if (path != null) {
canvas.drawPath(path, paint)
} else {
val rect = with(outline.roundRect) { RectF(left, top, right, bottom) }
val radius = outline.roundRect.topLeftCornerRadius.x
canvas.drawRoundRect(rect, radius, radius, paint)
}
}
is Outline.Generic -> {
val path = outline.path.asAndroidPath()
is Outline.Rounded -> {
@Suppress("INVISIBLE_REFERENCE")
val path = outline.roundRectPath?.asAndroidPath()
if (path != null) {
canvas.drawPath(path, paint)
} else {
val rect = with(outline.roundRect) { RectF(left, top, right, bottom) }
val radius = outline.roundRect.topLeftCornerRadius.x
canvas.drawRoundRect(rect, radius, radius, paint)
}
}
is Outline.Generic -> {
val path = outline.path.asAndroidPath()
canvas.drawPath(path, paint)
}
}
}
@@ -141,6 +141,8 @@ internal class HighlightNode(
})
private val layerBlock: GraphicsLayerScope.() -> Unit = {
clip = true
shape = shapeProvider.shape
compositingStrategy = androidx.compose.ui.graphics.CompositingStrategy.Offscreen
}
@@ -2,7 +2,6 @@ package com.kyant.backdrop.highlight
import android.graphics.RenderEffect
import android.graphics.RuntimeShader
import android.graphics.Shader
import android.os.Build
import androidx.annotation.FloatRange
import androidx.annotation.RequiresApi
@@ -24,22 +23,7 @@ interface HighlightStyle {
data object Solid : HighlightStyle
@Immutable
data object Soft : HighlightStyle {
@RequiresApi(Build.VERSION_CODES.S)
override fun createRenderEffect(size: Size, width: Float): RenderEffect? {
val blurRadiusPx = width / 2f
return if (blurRadiusPx > 0f) {
RenderEffect.createBlurEffect(
blurRadiusPx,
blurRadiusPx,
Shader.TileMode.DECAL
)
} else {
null
}
}
}
data object Soft : HighlightStyle
@Immutable
data class Dynamic(
@@ -49,36 +33,17 @@ interface HighlightStyle {
@RequiresApi(Build.VERSION_CODES.S)
override fun createRenderEffect(size: Size, width: Float): RenderEffect? {
val blurRadiusPx = width / 2f
val blurRenderEffect =
if (blurRadiusPx > 0f) {
RenderEffect.createBlurEffect(
blurRadiusPx,
blurRadiusPx,
Shader.TileMode.DECAL
)
} else {
null
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val highlightRenderEffect =
RenderEffect.createRuntimeShaderEffect(
RuntimeShader(DynamicHighlightShaderString).apply {
setFloatUniform("size", size.width, size.height)
setFloatUniform("angle", angle * (PI / 180f).toFloat())
setFloatUniform("falloff", falloff)
},
"image"
)
if (blurRenderEffect != null) {
RenderEffect.createChainEffect(highlightRenderEffect, blurRenderEffect)
} else {
highlightRenderEffect
}
RenderEffect.createRuntimeShaderEffect(
RuntimeShader(DynamicHighlightShaderString).apply {
setFloatUniform("size", size.width, size.height)
setFloatUniform("angle", angle * (PI / 180f).toFloat())
setFloatUniform("falloff", falloff)
},
"image"
)
} else {
blurRenderEffect
null
}
}
}