Use the old highlight shader, update highlight API
This commit is contained in:
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)
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user