Use Shader instead of RenderEffect for highlight

This commit is contained in:
Kyant0 committed 2025-10-16 00:14:46 +08:00
1 parent e2571c1d67
commit 781ff16df8
3 files changed
+34 -61

No files matched your search

@@ -166,8 +166,6 @@ half4 main(float2 coord) {
@Language("AGSL") @Language("AGSL")
internal const val DefaultHighlightShaderString = """ internal const val DefaultHighlightShaderString = """
uniform shader content;
uniform float2 size; uniform float2 size;
uniform float4 cornerRadii; uniform float4 cornerRadii;
uniform float angle; uniform float angle;
@@ -184,13 +182,11 @@ half4 main(float2 coord) {
float2 grad = gradSdRoundedRectangle(centeredCoord, halfSize, gradRadius); float2 grad = gradSdRoundedRectangle(centeredCoord, halfSize, gradRadius);
float2 normal = float2(-cos(angle), -sin(angle)); float2 normal = float2(-cos(angle), -sin(angle));
float intensity = pow(abs(dot(normal, grad)), falloff); float intensity = pow(abs(dot(normal, grad)), falloff);
return content.eval(coord) * intensity; return half4(intensity);
}""" }"""
@Language("AGSL") @Language("AGSL")
internal const val AmbientHighlightShaderString = """ internal const val AmbientHighlightShaderString = """
uniform shader content;
uniform float2 size; uniform float2 size;
uniform float4 cornerRadii; uniform float4 cornerRadii;
uniform float angle; uniform float angle;
@@ -207,16 +203,13 @@ half4 main(float2 coord) {
float2 grad = gradSdRoundedRectangle(centeredCoord, halfSize, gradRadius); float2 grad = gradSdRoundedRectangle(centeredCoord, halfSize, gradRadius);
float2 normal = float2(-cos(angle), -sin(angle)); float2 normal = float2(-cos(angle), -sin(angle));
float d = dot(normal, grad); float d = dot(normal, grad);
float alpha = content.eval(coord).a;
float intensity = pow(abs(d), falloff); float intensity = pow(abs(d), falloff);
if (d > 0.0) { if (d > 0.0) {
return half4(0.0, 0.0, 0.0, 1.0) * intensity * alpha; return half4(0.0, 0.0, 0.0, 1.0) * intensity;
} else {
return half4(1.0) * intensity;
} }
if (d < 0.0) {
return half4(1.0) * intensity * alpha;
}
return half4(0.0);
}""" }"""
@Language("AGSL") @Language("AGSL")
@@ -11,7 +11,6 @@ import androidx.compose.ui.graphics.drawOutline
import androidx.compose.ui.graphics.drawscope.ContentDrawScope import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.layer.CompositingStrategy
import androidx.compose.ui.graphics.layer.GraphicsLayer import androidx.compose.ui.graphics.layer.GraphicsLayer
import androidx.compose.ui.graphics.layer.drawLayer import androidx.compose.ui.graphics.layer.drawLayer
import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.node.DrawModifierNode
@@ -73,7 +72,6 @@ internal class HighlightNode(
override val shouldAutoInvalidate: Boolean = false override val shouldAutoInvalidate: Boolean = false
private var highlightLayer: GraphicsLayer? = null private var highlightLayer: GraphicsLayer? = null
private var maskLayer: GraphicsLayer? = null
private val paint = private val paint =
Paint().apply { Paint().apply {
@@ -94,8 +92,7 @@ internal class HighlightNode(
drawContent() drawContent()
val highlightLayer = highlightLayer val highlightLayer = highlightLayer
val maskLayer = maskLayer if (highlightLayer != null) {
if (highlightLayer != null && maskLayer != null) {
val size = size val size = size
val density: Density = this val density: Density = this
val layoutDirection = layoutDirection val layoutDirection = layoutDirection
@@ -116,18 +113,8 @@ internal class HighlightNode(
configurePaint(highlight) configurePaint(highlight)
val style = highlight.style highlightLayer.alpha = highlight.alpha
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { highlightLayer.blendMode = highlight.style.blendMode
if (prevStyle != style) {
highlightLayer.renderEffect = with(style) {
createRenderEffect(
shape = shapeProvider.shape,
runtimeShaderCache = runtimeShaderCache
)
}
prevStyle = style
}
}
highlightLayer.record(safeSize) { highlightLayer.record(safeSize) {
translate(1f, 1f) { translate(1f, 1f) {
val canvas = drawContext.canvas val canvas = drawContext.canvas
@@ -138,14 +125,8 @@ internal class HighlightNode(
} }
} }
maskLayer.alpha = highlight.alpha
maskLayer.blendMode = style.blendMode
maskLayer.record(safeSize) {
drawLayer(highlightLayer)
}
translate(-1f, -1f) { translate(-1f, -1f) {
drawLayer(maskLayer) drawLayer(highlightLayer)
} }
} }
} }
@@ -153,10 +134,6 @@ internal class HighlightNode(
override fun onAttach() { override fun onAttach() {
val graphicsContext = requireGraphicsContext() val graphicsContext = requireGraphicsContext()
highlightLayer = graphicsContext.createGraphicsLayer() highlightLayer = graphicsContext.createGraphicsLayer()
maskLayer =
graphicsContext.createGraphicsLayer().apply {
compositingStrategy = CompositingStrategy.Offscreen
}
} }
override fun onDetach() { override fun onDetach() {
@@ -165,10 +142,6 @@ internal class HighlightNode(
graphicsContext.releaseGraphicsLayer(layer) graphicsContext.releaseGraphicsLayer(layer)
highlightLayer = null highlightLayer = null
} }
maskLayer?.let { layer ->
graphicsContext.releaseGraphicsLayer(layer)
maskLayer = null
}
clipPath = null clipPath = null
runtimeShaderCache.clear() runtimeShaderCache.clear()
prevStyle = null prevStyle = null
@@ -185,5 +158,13 @@ internal class HighlightNode(
} else { } else {
null null
} }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
paint.shader = with(highlight.style) {
createShader(
shape = shapeProvider.shape,
runtimeShaderCache = runtimeShaderCache
)
}
}
} }
} }
@@ -8,9 +8,8 @@ import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import androidx.compose.ui.graphics.BlendMode import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.RenderEffect import androidx.compose.ui.graphics.Shader
import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.asComposeRenderEffect
import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.util.fastCoerceAtMost import androidx.compose.ui.util.fastCoerceAtMost
@@ -27,10 +26,10 @@ interface HighlightStyle {
val blendMode: BlendMode val blendMode: BlendMode
@RequiresApi(Build.VERSION_CODES.S) @RequiresApi(Build.VERSION_CODES.S)
fun DrawScope.createRenderEffect( fun DrawScope.createShader(
shape: Shape, shape: Shape,
runtimeShaderCache: RuntimeShaderCache runtimeShaderCache: RuntimeShaderCache
): RenderEffect? ): Shader?
@Immutable @Immutable
data class Plain( data class Plain(
@@ -39,10 +38,10 @@ interface HighlightStyle {
) : HighlightStyle { ) : HighlightStyle {
@RequiresApi(Build.VERSION_CODES.S) @RequiresApi(Build.VERSION_CODES.S)
override fun DrawScope.createRenderEffect( override fun DrawScope.createShader(
shape: Shape, shape: Shape,
runtimeShaderCache: RuntimeShaderCache runtimeShaderCache: RuntimeShaderCache
): RenderEffect? = null ): Shader? = null
} }
@Immutable @Immutable
@@ -57,20 +56,20 @@ interface HighlightStyle {
override val blendMode: BlendMode = BlendMode.Plus override val blendMode: BlendMode = BlendMode.Plus
@RequiresApi(Build.VERSION_CODES.S) @RequiresApi(Build.VERSION_CODES.S)
override fun DrawScope.createRenderEffect( override fun DrawScope.createShader(
shape: Shape, shape: Shape,
runtimeShaderCache: RuntimeShaderCache runtimeShaderCache: RuntimeShaderCache
): RenderEffect? { ): Shader? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val shader = runtimeShaderCache.obtainRuntimeShader("Default", DefaultHighlightShaderString) runtimeShaderCache.obtainRuntimeShader(
shader.apply { "Default",
DefaultHighlightShaderString
).apply {
setFloatUniform("size", size.width, size.height) setFloatUniform("size", size.width, size.height)
setFloatUniform("cornerRadii", getCornerRadii(shape)) setFloatUniform("cornerRadii", getCornerRadii(shape))
setFloatUniform("angle", angle * (PI / 180f).toFloat()) setFloatUniform("angle", angle * (PI / 180f).toFloat())
setFloatUniform("falloff", falloff) setFloatUniform("falloff", falloff)
} }
android.graphics.RenderEffect.createRuntimeShaderEffect(shader, "content")
.asComposeRenderEffect()
} else { } else {
null null
} }
@@ -87,20 +86,20 @@ interface HighlightStyle {
override val blendMode: BlendMode = DrawScope.DefaultBlendMode override val blendMode: BlendMode = DrawScope.DefaultBlendMode
@RequiresApi(Build.VERSION_CODES.S) @RequiresApi(Build.VERSION_CODES.S)
override fun DrawScope.createRenderEffect( override fun DrawScope.createShader(
shape: Shape, shape: Shape,
runtimeShaderCache: RuntimeShaderCache runtimeShaderCache: RuntimeShaderCache
): RenderEffect? { ): Shader? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val shader = runtimeShaderCache.obtainRuntimeShader("Ambient", AmbientHighlightShaderString) runtimeShaderCache.obtainRuntimeShader(
shader.apply { "Ambient",
AmbientHighlightShaderString
).apply {
setFloatUniform("size", size.width, size.height) setFloatUniform("size", size.width, size.height)
setFloatUniform("cornerRadii", getCornerRadii(shape)) setFloatUniform("cornerRadii", getCornerRadii(shape))
setFloatUniform("angle", 45f * (PI / 180f).toFloat()) setFloatUniform("angle", 45f * (PI / 180f).toFloat())
setFloatUniform("falloff", 1f) setFloatUniform("falloff", 1f)
} }
android.graphics.RenderEffect.createRuntimeShaderEffect(shader, "content")
.asComposeRenderEffect()
} else { } else {
null null
} }
@@ -121,9 +120,9 @@ interface HighlightStyle {
} }
private fun DrawScope.getCornerRadii(shape: Shape): FloatArray { private fun DrawScope.getCornerRadii(shape: Shape): FloatArray {
val shape = shape as? CornerBasedShape ?: return FloatArray(4) { size.minDimension / 2f }
val size = size val size = size
val maxRadius = size.minDimension / 2f val maxRadius = size.minDimension / 2f
val shape = shape as? CornerBasedShape ?: return FloatArray(4) { maxRadius }
val isLtr = layoutDirection == LayoutDirection.Ltr val isLtr = layoutDirection == LayoutDirection.Ltr
val topLeft = val topLeft =
if (isLtr) shape.topStart.toPx(size, this) if (isLtr) shape.topStart.toPx(size, this)