Update highlight APIs

This commit is contained in:
Kyant0 committed 2025-09-12 14:21:11 +08:00
1 parent d07e755fd6
commit 2815d8d7ed
5 files changed
+150 -189

No files matched your search

@@ -17,6 +17,6 @@ data class GlassStyle(
val innerRefraction: InnerRefraction = InnerRefraction.Default,
val dispersion: Dispersion = Dispersion.None,
val material: GlassMaterial = GlassMaterial.Default,
val highlight: GlassHighlight = GlassHighlight.Default,
val highlight: GlassHighlight? = GlassHighlight.Default,
val shadow: GlassShadow? = GlassShadow.Default
)
@@ -1,193 +1,38 @@
package com.kyant.liquidglass.highlight
import android.graphics.RenderEffect
import android.graphics.RuntimeShader
import android.graphics.Shader
import android.os.Build
import androidx.annotation.FloatRange
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.kyant.liquidglass.utils.GlassShaders
import kotlin.math.PI
/**
* The highlight effect applied to the liquid glass.
*
* @param width
* The width of the highlight.
*
* @param color
* The color of the highlight.
*
* @param blendMode
* The blend mode of the highlight.
*
* @param style
* The style of the highlight.
*/
@Immutable
sealed interface GlassHighlight {
val width: Dp
val color: Color
val blendMode: BlendMode
@Stable
fun createRenderEffect(size: Size, density: Density, cornerRadius: Float): RenderEffect? {
return null
}
/**
* No highlight effect.
*/
@Immutable
data object None : GlassHighlight {
override val width: Dp = Dp.Unspecified
override val color: Color = Color.Unspecified
override val blendMode: BlendMode = DrawScope.DefaultBlendMode
}
/**
* Solid highlight effect.
*
* @param width
* The width of the highlight.
*
* @param color
* The color of the highlight.
*
* @param blendMode
* The blend mode of the highlight.
*/
@Immutable
data class Solid(
override val width: Dp = DefaultHighlightWidth,
override val color: Color = DefaultHighlightColor,
override val blendMode: BlendMode = DefaultHighlightBlendMode
) : GlassHighlight
/**
* Soft highlight effect with blur.
*
* @param width
* The width of the highlight.
*
* @param color
* The color of the highlight.
*
* @param blendMode
* The blend mode of the highlight.
*/
@Immutable
data class Soft(
override val width: Dp = DefaultHighlightWidth,
override val color: Color = DefaultHighlightColor,
override val blendMode: BlendMode = DefaultHighlightBlendMode
) : GlassHighlight {
override fun createRenderEffect(size: Size, density: Density, cornerRadius: Float): RenderEffect? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val blurRadiusPx = with(density) { (width / 2f).toPx() }
if (blurRadiusPx > 0f) {
RenderEffect.createBlurEffect(
blurRadiusPx,
blurRadiusPx,
Shader.TileMode.DECAL
)
} else {
null
}
} else {
null
}
}
}
/**
* Dynamic highlight effect with shimmering effect.
*
* @param width
* The width of the highlight.
*
* @param color
* The color of the highlight.
*
* @param blendMode
* The blend mode of the highlight.
*
* @param angle
* The angle of the highlight in degrees.
*
* @param falloff
* The falloff of the highlight. Higher value results in a sharper highlight.
*/
@Immutable
data class Dynamic(
override val width: Dp = DefaultHighlightWidth,
override val color: Color = DefaultHighlightColor,
override val blendMode: BlendMode = DefaultHighlightBlendMode,
val angle: Float = 45f,
@param:FloatRange(from = 0.0) val falloff: Float = 1f
) : GlassHighlight {
private var highlightShader: RuntimeShader? = null
override fun createRenderEffect(size: Size, density: Density, cornerRadius: Float): RenderEffect? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val blurRadiusPx = with(density) { (width / 2f).toPx() }
val blurRenderEffect =
if (blurRadiusPx > 0f) {
RenderEffect.createBlurEffect(
blurRadiusPx,
blurRadiusPx,
Shader.TileMode.DECAL
)
} else {
null
}
val shader = highlightShader
?: RuntimeShader(GlassShaders.highlightShaderString)
.also { highlightShader = it }
val highlightRenderEffect =
RenderEffect.createRuntimeShaderEffect(
shader.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
}
} else {
null
}
}
}
data class GlassHighlight(
val width: Dp = 1f.dp,
val color: Color = Color.White.copy(alpha = 0.3f),
val blendMode: BlendMode = BlendMode.Plus,
val style: GlassHighlightStyle = GlassHighlightStyle.Default
) {
companion object {
@Stable
val Default: Dynamic = Dynamic()
@Stable
val Solid: Solid = Solid()
@Stable
val Soft: Soft = Soft()
val Default: GlassHighlight = GlassHighlight()
}
}
private val DefaultHighlightWidth = 1f.dp
private val DefaultHighlightColor = Color.White.copy(alpha = 0.3f)
private val DefaultHighlightBlendMode = BlendMode.Plus
@@ -95,7 +95,7 @@ internal class SimpleGlassHighlightNode(
override val shouldAutoInvalidate: Boolean = false
private var highlight: GlassHighlight = style.highlight
private var highlight: GlassHighlight? = style.highlight
set(value) {
if (field != value) {
field = value
@@ -114,7 +114,8 @@ internal class SimpleGlassHighlightNode(
private var graphicsLayer: GraphicsLayer? = null
private val drawNode = delegate(CacheDrawModifierNode {
if (highlight == GlassHighlight.None) {
val highlight = highlight
if (highlight == null) {
return@CacheDrawModifierNode onDrawWithContent { drawContent() }
}
@@ -138,9 +139,7 @@ internal class SimpleGlassHighlightNode(
if (outline != null) {
graphicsLayer?.let { layer ->
val cornerRadiusPx = shape.topStart.toPx(size, this)
val borderRenderEffect = highlight.createRenderEffect(size, this, cornerRadiusPx)
val borderRenderEffect = highlight.style.createRenderEffect(size, strokeWidthPx)
layer.renderEffect = borderRenderEffect?.asComposeRenderEffect()
layer.blendMode = highlight.blendMode
@@ -206,7 +205,7 @@ internal class GlassHighlightNode(
override val shouldAutoInvalidate: Boolean = false
private var highlight: GlassHighlight = style().highlight
private var highlight: GlassHighlight? = style().highlight
set(value) {
if (field != value) {
field = value
@@ -225,7 +224,8 @@ internal class GlassHighlightNode(
private var graphicsLayer: GraphicsLayer? = null
private val drawNode = delegate(CacheDrawModifierNode {
if (highlight == GlassHighlight.None) {
val highlight = highlight
if (highlight == null) {
return@CacheDrawModifierNode onDrawWithContent { drawContent() }
}
@@ -249,9 +249,7 @@ internal class GlassHighlightNode(
if (outline != null) {
graphicsLayer?.let { layer ->
val cornerRadiusPx = shape.topStart.toPx(size, this)
val borderRenderEffect = highlight.createRenderEffect(size, this, cornerRadiusPx)
val borderRenderEffect = highlight.style.createRenderEffect(size, strokeWidthPx)
layer.renderEffect = borderRenderEffect?.asComposeRenderEffect()
layer.blendMode = highlight.blendMode
@@ -0,0 +1,117 @@
package com.kyant.liquidglass.highlight
import android.graphics.RenderEffect
import android.graphics.RuntimeShader
import android.graphics.Shader
import android.os.Build
import androidx.annotation.FloatRange
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import androidx.compose.ui.geometry.Size
import com.kyant.liquidglass.utils.GlassShaders
import kotlin.math.PI
@Immutable
interface GlassHighlightStyle {
@Stable
fun createRenderEffect(size: Size, width: Float): RenderEffect? {
return null
}
/**
* Solid highlight effect.
*/
@Immutable
data object Solid : GlassHighlightStyle
/**
* Soft highlight effect with blur.
*/
@Immutable
data object Soft : GlassHighlightStyle {
override fun createRenderEffect(size: Size, width: Float): RenderEffect? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val blurRadiusPx = width / 2f
if (blurRadiusPx > 0f) {
RenderEffect.createBlurEffect(
blurRadiusPx,
blurRadiusPx,
Shader.TileMode.DECAL
)
} else {
null
}
} else {
null
}
}
}
/**
* Dynamic highlight effect with shimmering effect.
*
* @param angle
* The angle of the highlight in degrees.
*
* @param falloff
* The falloff of the highlight. Higher value results in a sharper highlight.
*/
@Immutable
data class Dynamic(
val angle: Float = 45f,
@param:FloatRange(from = 0.0) val falloff: Float = 1f
) : GlassHighlightStyle {
private var highlightShader: RuntimeShader? = null
override fun createRenderEffect(size: Size, width: Float): RenderEffect? {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val blurRadiusPx = width / 2f
val blurRenderEffect =
if (blurRadiusPx > 0f) {
RenderEffect.createBlurEffect(
blurRadiusPx,
blurRadiusPx,
Shader.TileMode.DECAL
)
} else {
null
}
val shader = highlightShader
?: RuntimeShader(GlassShaders.dynamicHighlightStyleShaderString)
.also { highlightShader = it }
val highlightRenderEffect =
RenderEffect.createRuntimeShaderEffect(
shader.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
}
} else {
null
}
}
}
companion object {
@Stable
val Default: Dynamic = Dynamic()
}
}
@@ -2,10 +2,11 @@ package com.kyant.liquidglass.utils
import org.intellij.lang.annotations.Language
@Suppress("ConstPropertyName")
internal object GlassShaders {
@Language("AGSL")
internal val sdRectangleShaderUtils = """
private const val sdRectangleShaderUtils = """
float sdRectangle(float2 coord, float2 halfSize) {
float2 d = abs(coord) - halfSize;
float outside = length(max(d, 0.0));
@@ -30,7 +31,7 @@ float2 gradSdRoundedRectangle(float2 coord, float2 halfSize, float cornerRadius)
}"""
@Language("AGSL")
val refractionShaderString = """
const val refractionShaderString = """
uniform shader image;
uniform float2 size;
@@ -67,7 +68,7 @@ half4 main(float2 coord) {
}"""
@Language("AGSL")
val dispersionShaderString = """
const val dispersionShaderString = """
uniform shader image;
uniform float2 size;
@@ -127,7 +128,7 @@ half4 main(float2 coord) {
}"""
@Language("AGSL")
val highlightShaderString = """
const val dynamicHighlightStyleShaderString = """
uniform shader image;
uniform float2 size;