Update highlight APIs
This commit is contained in:
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
|
||||
+8
-10
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user