Use the old highlight shader, update highlight API

This commit is contained in:
Kyant0 committed 2025-09-26 23:04:56 +08:00
1 parent 9a3a2294da
commit 869e7715c7
9 files changed
+88 -45

No files matched your search

@@ -147,26 +147,19 @@ internal const val DynamicHighlightShaderString = """
uniform shader image;
uniform float2 size;
uniform float4 cornerRadii;
uniform float angle;
uniform float falloff;
float2 gradSdCapsule(float2 coord, float2 halfSize) {
float cornerRadius = min(halfSize.x, halfSize.y);
float2 innerHalfSize = halfSize - float2(cornerRadius);
float2 cornerCoord = abs(coord) - innerHalfSize;
float insideCorner = step(0.0, min(cornerCoord.x, cornerCoord.y)); // 1 if in corner
float xMajor = step(cornerCoord.y, cornerCoord.x); // 1 if x is major
float2 gradEdge = float2(xMajor, 1.0 - xMajor);
float2 gradCorner = normalize(cornerCoord);
return sign(coord) * mix(gradEdge, gradCorner, insideCorner);
}
$RoundedRectSDF
half4 main(float2 coord) {
float2 halfSize = size * 0.5;
float2 centeredCoord = coord - halfSize;
float2 grad = gradSdCapsule(centeredCoord, halfSize);
float4 maxGradRadius = float4(min(halfSize.x, halfSize.y));
float4 gradRadius = min(cornerRadii * 1.5, maxGradRadius);
float2 grad = gradSdRoundedRectangle(centeredCoord, halfSize, gradRadius);
float2 normal = float2(-cos(angle), -sin(angle));
float intensity = pow(abs(dot(normal, grad)), falloff);
return image.eval(coord) * intensity;
@@ -177,26 +170,19 @@ internal const val AmbientHighlightShaderString = """
uniform shader image;
uniform float2 size;
uniform float4 cornerRadii;
uniform float angle;
uniform float falloff;
float2 gradSdCapsule(float2 coord, float2 halfSize) {
float cornerRadius = min(halfSize.x, halfSize.y);
float2 innerHalfSize = halfSize - float2(cornerRadius);
float2 cornerCoord = abs(coord) - innerHalfSize;
float insideCorner = step(0.0, min(cornerCoord.x, cornerCoord.y)); // 1 if in corner
float xMajor = step(cornerCoord.y, cornerCoord.x); // 1 if x is major
float2 gradEdge = float2(xMajor, 1.0 - xMajor);
float2 gradCorner = normalize(cornerCoord);
return sign(coord) * mix(gradEdge, gradCorner, insideCorner);
}
$RoundedRectSDF
half4 main(float2 coord) {
float2 halfSize = size * 0.5;
float2 centeredCoord = coord - halfSize;
float2 grad = gradSdCapsule(centeredCoord, halfSize);
float4 maxGradRadius = float4(min(halfSize.x, halfSize.y));
float4 gradRadius = min(cornerRadii * 1.5, maxGradRadius);
float2 grad = gradSdRoundedRectangle(centeredCoord, halfSize, gradRadius);
float2 normal = float2(-cos(angle), -sin(angle));
float d = dot(normal, grad);
float alpha = image.eval(coord).a;
@@ -208,7 +194,7 @@ half4 main(float2 coord) {
if (d < 0.0) {
return half4(1.0) * intensity * alpha;
}
return half4(0.0) * intensity * alpha;
return half4(0.0);
}"""
@Language("AGSL")
@@ -17,9 +17,26 @@ data class Highlight(
val color: Color = Color.White.copy(alpha = 0.38f),
val alpha: Float = 1f,
val blendMode: BlendMode = BlendMode.Plus,
val style: () -> HighlightStyle = DefaultHighlightStyle
val style: HighlightStyle = HighlightStyle.Dynamic.Default
) {
@Deprecated(message = "Use the non-lambda version of style parameter")
constructor(
width: Dp = (2f / 3f).dp,
blurRadius: Dp = width / 2f,
color: Color = Color.White.copy(alpha = 0.38f),
alpha: Float = 1f,
blendMode: BlendMode = BlendMode.Plus,
style: () -> HighlightStyle
) : this(
width = width,
blurRadius = blurRadius,
color = color,
alpha = alpha,
blendMode = blendMode,
style = style()
)
companion object {
@Stable
@@ -30,13 +47,13 @@ data class Highlight(
Highlight(
width = (1f / 3f).dp,
blurRadius = (1f / 3f).dp,
color = Color.Transparent.copy(0.5f),
color = Color.Transparent.copy(alpha = 0.5f),
blendMode = BlendMode.SrcOver,
style = { HighlightStyle.Dynamic.AmbientDefault }
style = HighlightStyle.Dynamic.AmbientDefault
)
@Stable
val SolidDefault: Highlight = Highlight { HighlightStyle.Solid }
val SolidDefault: Highlight = Highlight(style = HighlightStyle.Solid)
}
}
@@ -48,8 +65,6 @@ fun lerp(start: Highlight, stop: Highlight, fraction: Float): Highlight {
alpha = lerp(start.alpha, stop.alpha, fraction),
blendMode = if (fraction < 0.5f) start.blendMode else stop.blendMode,
blurRadius = lerp(start.blurRadius, stop.blurRadius, fraction),
style = { lerp(start.style(), stop.style(), fraction) }
style = lerp(start.style, stop.style, fraction)
)
}
private val DefaultHighlightStyle = { HighlightStyle.Dynamic.Default }
@@ -98,10 +98,14 @@ internal class HighlightNode(
configurePaint(highlight)
val style = highlight.style()
val style = highlight.style
if (prevStyle != style) {
highlightLayer.renderEffect =
with(highlight.style()) { runtimeShaderCacheScope.createRenderEffect(size) }
highlightLayer.renderEffect = with(style) {
createRenderEffect(
shape = shapeProvider.shape,
shaderCache = runtimeShaderCacheScope
)
}
prevStyle = style
}
highlightLayer.record { drawHighlight(outline) }
@@ -3,11 +3,15 @@ package com.kyant.backdrop.highlight
import android.os.Build
import androidx.annotation.FloatRange
import androidx.annotation.RequiresApi
import androidx.compose.foundation.shape.CornerBasedShape
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.RenderEffect
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.asComposeRenderEffect
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.util.fastCoerceAtMost
import androidx.compose.ui.util.lerp
import com.kyant.backdrop.AmbientHighlightShaderString
import com.kyant.backdrop.DynamicHighlightShaderString
@@ -15,16 +19,22 @@ import com.kyant.backdrop.RuntimeShaderCacheScope
import kotlin.math.PI
@Immutable
sealed interface HighlightStyle {
interface HighlightStyle {
@RequiresApi(Build.VERSION_CODES.S)
fun RuntimeShaderCacheScope.createRenderEffect(size: Size): RenderEffect?
fun DrawScope.createRenderEffect(
shape: Shape,
shaderCache: RuntimeShaderCacheScope
): RenderEffect?
@Immutable
data object Solid : HighlightStyle {
@RequiresApi(Build.VERSION_CODES.S)
override fun RuntimeShaderCacheScope.createRenderEffect(size: Size): RenderEffect? = null
override fun DrawScope.createRenderEffect(
shape: Shape,
shaderCache: RuntimeShaderCacheScope
): RenderEffect? = null
}
@Immutable
@@ -35,16 +45,20 @@ sealed interface HighlightStyle {
) : HighlightStyle {
@RequiresApi(Build.VERSION_CODES.S)
override fun RuntimeShaderCacheScope.createRenderEffect(size: Size): RenderEffect? {
override fun DrawScope.createRenderEffect(
shape: Shape,
shaderCache: RuntimeShaderCacheScope
): RenderEffect? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val shader =
if (isAmbient) {
obtainRuntimeShader("Ambient", AmbientHighlightShaderString)
shaderCache.obtainRuntimeShader("Ambient", AmbientHighlightShaderString)
} else {
obtainRuntimeShader("Dynamic", DynamicHighlightShaderString)
shaderCache.obtainRuntimeShader("Dynamic", DynamicHighlightShaderString)
}
shader.apply {
setFloatUniform("size", size.width, size.height)
setFloatUniform("cornerRadii", getCornerRadii(shape))
setFloatUniform("angle", angle * (PI / 180f).toFloat())
setFloatUniform("falloff", falloff)
}
@@ -80,3 +94,28 @@ fun lerp(start: HighlightStyle, stop: HighlightStyle, fraction: Float): Highligh
}
return if (fraction < 0.5f) start else stop
}
private fun DrawScope.getCornerRadii(shape: Shape): FloatArray {
val shape = shape as? CornerBasedShape ?: return FloatArray(4) { size.minDimension / 2f }
val size = size
val maxRadius = size.minDimension / 2f
val isLtr = layoutDirection == LayoutDirection.Ltr
val topLeft =
if (isLtr) shape.topStart.toPx(size, this)
else shape.topEnd.toPx(size, this)
val topRight =
if (isLtr) shape.topEnd.toPx(size, this)
else shape.topStart.toPx(size, this)
val bottomRight =
if (isLtr) shape.bottomEnd.toPx(size, this)
else shape.bottomStart.toPx(size, this)
val bottomLeft =
if (isLtr) shape.bottomStart.toPx(size, this)
else shape.bottomEnd.toPx(size, this)
return floatArrayOf(
topLeft.fastCoerceAtMost(maxRadius),
topRight.fastCoerceAtMost(maxRadius),
bottomRight.fastCoerceAtMost(maxRadius),
bottomLeft.fastCoerceAtMost(maxRadius)
)
}