diff --git a/backdrop/src/main/java/com/kyant/backdrop/BackdropEffectScope.kt b/backdrop/src/main/java/com/kyant/backdrop/BackdropEffectScope.kt index 6ced457..c469588 100644 --- a/backdrop/src/main/java/com/kyant/backdrop/BackdropEffectScope.kt +++ b/backdrop/src/main/java/com/kyant/backdrop/BackdropEffectScope.kt @@ -8,9 +8,9 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.compose.ui.draw.CacheDrawScope import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.LayoutDirection @@ -43,7 +43,7 @@ internal abstract class BackdropEffectScopeImpl : BackdropEffectScope { return runtimeShaders.getOrPut(key) { RuntimeShader(string) } } - fun applyDrawScope(scope: CacheDrawScope) { + fun applyDrawScope(scope: DrawScope) { density = scope.density fontScale = scope.fontScale size = scope.size diff --git a/backdrop/src/main/java/com/kyant/backdrop/ContentBackdropModifier.kt b/backdrop/src/main/java/com/kyant/backdrop/ContentBackdropModifier.kt index 2e6c6b1..73d25d2 100644 --- a/backdrop/src/main/java/com/kyant/backdrop/ContentBackdropModifier.kt +++ b/backdrop/src/main/java/com/kyant/backdrop/ContentBackdropModifier.kt @@ -2,19 +2,22 @@ package com.kyant.backdrop import android.os.Build import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.CacheDrawModifierNode import androidx.compose.ui.graphics.GraphicsLayerScope import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.asComposeRenderEffect +import androidx.compose.ui.graphics.drawscope.ContentDrawScope import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.layer.GraphicsLayer import androidx.compose.ui.graphics.layer.drawLayer import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasureResult import androidx.compose.ui.layout.MeasureScope -import androidx.compose.ui.node.DelegatingNode +import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.node.LayoutModifierNode import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.node.ObserverModifierNode +import androidx.compose.ui.node.invalidateDraw +import androidx.compose.ui.node.observeReads import androidx.compose.ui.node.requireGraphicsContext import androidx.compose.ui.platform.InspectorInfo import androidx.compose.ui.unit.Constraints @@ -85,7 +88,7 @@ private class ContentBackdropElement( node.onDrawBehind = onDrawBehind node.onDrawSurface = onDrawSurface node.effects = effects - node.drawNode.invalidateDrawCache() + node.invalidateDrawCache() } override fun InspectorInfo.inspectableProperties() { @@ -122,7 +125,7 @@ private class ContentBackdropNode( var onDrawBehind: (DrawScope.() -> Unit)?, var onDrawSurface: (DrawScope.() -> Unit)?, var effects: BackdropEffectScope.() -> Unit -) : LayoutModifierNode, DelegatingNode() { +) : LayoutModifierNode, DrawModifierNode, ObserverModifierNode, Modifier.Node() { override val shouldAutoInvalidate: Boolean = false @@ -138,22 +141,14 @@ private class ContentBackdropNode( null } - val drawNode = delegate(CacheDrawModifierNode { + private var isCacheValid = false + private val cacheDrawBlock: DrawScope.() -> Unit = { if (effectScope != null) { effectScope.applyDrawScope(this) effects(effectScope) graphicsLayer?.renderEffect = effectScope.renderEffect?.asComposeRenderEffect() } - - onDrawWithContent { - onDrawBehind?.invoke(this) - graphicsLayer?.let { layer -> - layer.record { this@onDrawWithContent.drawContent() } - drawLayer(layer) - } - onDrawSurface?.invoke(this) - } - }) + } private val layerBlock: GraphicsLayerScope.() -> Unit = { clip = true @@ -171,6 +166,31 @@ private class ContentBackdropNode( } } + override fun ContentDrawScope.draw() { + if (!isCacheValid) { + observeReads { cacheDrawBlock() } + isCacheValid = true + } + + onDrawBehind?.invoke(this) + + graphicsLayer?.let { layer -> + layer.record { this@draw.drawContent() } + drawLayer(layer) + } + + onDrawSurface?.invoke(this) + } + + override fun onObservedReadsChanged() { + invalidateDrawCache() + } + + fun invalidateDrawCache() { + isCacheValid = false + invalidateDraw() + } + override fun onAttach() { val graphicsContext = requireGraphicsContext() graphicsLayer = graphicsContext.createGraphicsLayer() diff --git a/backdrop/src/main/java/com/kyant/backdrop/DrawBackdropModifier.kt b/backdrop/src/main/java/com/kyant/backdrop/DrawBackdropModifier.kt index d0dcdce..39700b1 100644 --- a/backdrop/src/main/java/com/kyant/backdrop/DrawBackdropModifier.kt +++ b/backdrop/src/main/java/com/kyant/backdrop/DrawBackdropModifier.kt @@ -5,10 +5,10 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.CacheDrawModifierNode import androidx.compose.ui.graphics.GraphicsLayerScope import androidx.compose.ui.graphics.Shape import androidx.compose.ui.graphics.asComposeRenderEffect +import androidx.compose.ui.graphics.drawscope.ContentDrawScope import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.layer.GraphicsLayer import androidx.compose.ui.graphics.layer.drawLayer @@ -16,10 +16,13 @@ import androidx.compose.ui.layout.LayoutCoordinates import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasureResult import androidx.compose.ui.layout.MeasureScope -import androidx.compose.ui.node.DelegatingNode +import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.node.GlobalPositionAwareModifierNode import androidx.compose.ui.node.LayoutModifierNode import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.node.ObserverModifierNode +import androidx.compose.ui.node.invalidateDraw +import androidx.compose.ui.node.observeReads import androidx.compose.ui.node.requireGraphicsContext import androidx.compose.ui.platform.InspectorInfo import androidx.compose.ui.unit.Constraints @@ -104,7 +107,7 @@ private class DrawBackdropElement( node.onDrawBackdrop = onDrawBackdrop node.onDrawSurface = onDrawSurface node.effects = effects - node.drawNode.invalidateDrawCache() + node.invalidateDrawCache() } override fun InspectorInfo.inspectableProperties() { @@ -149,7 +152,7 @@ private class DrawBackdropNode( var onDrawBackdrop: DrawScope.(drawBackdrop: DrawScope.() -> Unit) -> Unit, var onDrawSurface: (DrawScope.() -> Unit)?, var effects: BackdropEffectScope.() -> Unit -) : LayoutModifierNode, GlobalPositionAwareModifierNode, DelegatingNode() { +) : LayoutModifierNode, DrawModifierNode, GlobalPositionAwareModifierNode, ObserverModifierNode, Modifier.Node() { override val shouldAutoInvalidate: Boolean = false @@ -166,28 +169,14 @@ private class DrawBackdropNode( null } - val drawNode = delegate(CacheDrawModifierNode { + private var isCacheValid = false + private val cacheDrawBlock: DrawScope.() -> Unit = { if (effectScope != null) { effectScope.applyDrawScope(this) effects(effectScope) graphicsLayer?.renderEffect = effectScope.renderEffect?.asComposeRenderEffect() } - - onDrawWithContent { - onDrawBehind?.invoke(this) - - val layer = graphicsLayer - val recordBlock = recordBlock - if (layer != null && recordBlock != null) { - layer.record(block = recordBlock) - drawLayer(layer) - } - - onDrawSurface?.invoke(this) - - drawContent() - } - }) + } private val layerBlock: GraphicsLayerScope.() -> Unit = { clip = true @@ -205,12 +194,41 @@ private class DrawBackdropNode( } } + override fun ContentDrawScope.draw() { + if (!isCacheValid) { + observeReads { cacheDrawBlock() } + isCacheValid = true + } + + onDrawBehind?.invoke(this) + + val layer = graphicsLayer + val recordBlock = recordBlock + if (layer != null && recordBlock != null) { + layer.record(block = recordBlock) + drawLayer(layer) + } + + onDrawSurface?.invoke(this) + + drawContent() + } + override fun onGloballyPositioned(coordinates: LayoutCoordinates) { if (coordinates.isAttached) { recordBlock = { with(backdrop) { onDrawBackdrop { drawBackdrop(coordinates) } } } } } + override fun onObservedReadsChanged() { + invalidateDrawCache() + } + + fun invalidateDrawCache() { + isCacheValid = false + invalidateDraw() + } + override fun onAttach() { val graphicsContext = requireGraphicsContext() graphicsLayer = graphicsContext.createGraphicsLayer() diff --git a/backdrop/src/main/java/com/kyant/backdrop/highlight/HighlightModifier.kt b/backdrop/src/main/java/com/kyant/backdrop/highlight/HighlightModifier.kt index 7de97ce..88b5aa4 100644 --- a/backdrop/src/main/java/com/kyant/backdrop/highlight/HighlightModifier.kt +++ b/backdrop/src/main/java/com/kyant/backdrop/highlight/HighlightModifier.kt @@ -4,12 +4,14 @@ import android.graphics.BlurMaskFilter import android.graphics.Paint import android.graphics.RectF import android.os.Build -import androidx.compose.ui.draw.CacheDrawModifierNode +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.GraphicsLayerScope import androidx.compose.ui.graphics.Outline import androidx.compose.ui.graphics.asAndroidPath import androidx.compose.ui.graphics.asComposePaint import androidx.compose.ui.graphics.asComposeRenderEffect +import androidx.compose.ui.graphics.drawscope.ContentDrawScope +import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.isUnspecified import androidx.compose.ui.graphics.layer.GraphicsLayer import androidx.compose.ui.graphics.layer.drawLayer @@ -18,9 +20,12 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasureResult import androidx.compose.ui.layout.MeasureScope -import androidx.compose.ui.node.DelegatingNode +import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.node.LayoutModifierNode import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.node.ObserverModifierNode +import androidx.compose.ui.node.invalidateDraw +import androidx.compose.ui.node.observeReads import androidx.compose.ui.node.requireGraphicsContext import androidx.compose.ui.platform.InspectorInfo import androidx.compose.ui.unit.Constraints @@ -41,7 +46,7 @@ internal class HighlightElement( override fun update(node: HighlightNode) { node.shapeProvider = shapeProvider node.highlight = highlight - node.drawNode.invalidateDrawCache() + node.invalidateDrawCache() } override fun InspectorInfo.inspectableProperties() { @@ -70,7 +75,7 @@ internal class HighlightElement( internal class HighlightNode( var shapeProvider: BackdropShapeProvider, var highlight: () -> Highlight? -) : LayoutModifierNode, DelegatingNode() { +) : LayoutModifierNode, DrawModifierNode, ObserverModifierNode, Modifier.Node() { override val shouldAutoInvalidate: Boolean = false @@ -80,13 +85,16 @@ internal class HighlightNode( style = Paint.Style.STROKE } - val drawNode = delegate(CacheDrawModifierNode { - val highlight = highlight() + private var isCacheValid = false + private var _highlight: Highlight? = null + private val cacheDrawBlock: DrawScope.() -> Unit = cacheDrawBlock@{ + val highlight = highlight().also { _highlight = it } val graphicsLayer = graphicsLayer if (highlight == null || graphicsLayer == null || highlight.width.value <= 0f || highlight.color.isUnspecified ) { - return@CacheDrawModifierNode onDrawWithContent { drawContent() } + _highlight = null + return@cacheDrawBlock } val size = size @@ -129,16 +137,7 @@ internal class HighlightNode( } } } - - onDrawWithContent { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - graphicsLayer.renderEffect = - highlight.style().createRenderEffect(size, strokeWidth)?.asComposeRenderEffect() - } - drawContent() - drawLayer(graphicsLayer) - } - }) + } private val layerBlock: GraphicsLayerScope.() -> Unit = { clip = true @@ -156,6 +155,35 @@ internal class HighlightNode( } } + override fun ContentDrawScope.draw() { + if (!isCacheValid) { + observeReads { cacheDrawBlock() } + isCacheValid = true + } + + drawContent() + + val highlight = _highlight + val graphicsLayer = graphicsLayer + if (highlight != null && graphicsLayer != null) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val strokeWidth = ceil(highlight.width.toPx().fastCoerceAtMost(size.minDimension / 2f)) + graphicsLayer.renderEffect = + highlight.style().createRenderEffect(size, strokeWidth)?.asComposeRenderEffect() + } + drawLayer(graphicsLayer) + } + } + + override fun onObservedReadsChanged() { + invalidateDrawCache() + } + + fun invalidateDrawCache() { + isCacheValid = false + invalidateDraw() + } + override fun onAttach() { val graphicsContext = requireGraphicsContext() graphicsLayer = graphicsContext.createGraphicsLayer() diff --git a/backdrop/src/main/java/com/kyant/backdrop/shadow/ShadowModifier.kt b/backdrop/src/main/java/com/kyant/backdrop/shadow/ShadowModifier.kt index 7c60416..cd4825a 100644 --- a/backdrop/src/main/java/com/kyant/backdrop/shadow/ShadowModifier.kt +++ b/backdrop/src/main/java/com/kyant/backdrop/shadow/ShadowModifier.kt @@ -6,11 +6,13 @@ import android.graphics.PorterDuff import android.graphics.PorterDuffXfermode import android.graphics.RectF import android.os.Build -import androidx.compose.ui.draw.CacheDrawModifierNode +import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Outline import androidx.compose.ui.graphics.asAndroidPath import androidx.compose.ui.graphics.asComposePaint +import androidx.compose.ui.graphics.drawscope.ContentDrawScope +import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.graphics.isUnspecified import androidx.compose.ui.graphics.layer.CompositingStrategy @@ -18,8 +20,11 @@ import androidx.compose.ui.graphics.layer.GraphicsLayer import androidx.compose.ui.graphics.layer.drawLayer import androidx.compose.ui.graphics.nativeCanvas import androidx.compose.ui.graphics.toArgb -import androidx.compose.ui.node.DelegatingNode +import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.node.ObserverModifierNode +import androidx.compose.ui.node.invalidateDraw +import androidx.compose.ui.node.observeReads import androidx.compose.ui.node.requireGraphicsContext import androidx.compose.ui.platform.InspectorInfo import androidx.compose.ui.unit.IntSize @@ -38,7 +43,7 @@ internal class ShadowElement( override fun update(node: ShadowNode) { node.shapeProvider = shapeProvider node.shadow = shadow - node.drawNode.invalidateDrawCache() + node.invalidateDrawCache() } override fun InspectorInfo.inspectableProperties() { @@ -67,7 +72,7 @@ internal class ShadowElement( internal class ShadowNode( var shapeProvider: BackdropShapeProvider, var shadow: () -> Shadow? -) : DelegatingNode() { +) : DrawModifierNode, ObserverModifierNode, Modifier.Node() { override val shouldAutoInvalidate: Boolean = false @@ -82,13 +87,16 @@ internal class ShadowNode( } } - val drawNode = delegate(CacheDrawModifierNode { - val shadow = shadow() + private var _shadow: Shadow? = null + private var isCacheValid = false + private val cacheDrawBlock: DrawScope.() -> Unit = cacheDrawBlock@{ + val shadow = shadow().also { _shadow = it } val graphicsLayer = graphicsLayer if (shadow == null || graphicsLayer == null || shadow.elevation.value <= 0f || shadow.color.isUnspecified ) { - return@CacheDrawModifierNode onDrawWithContent { drawContent() } + _shadow = null + return@cacheDrawBlock } val size = size @@ -138,14 +146,34 @@ internal class ShadowNode( } } } + } - onDrawWithContent { - translate(-(elevation - offset.x), -(elevation - offset.y)) { + override fun ContentDrawScope.draw() { + if (!isCacheValid) { + observeReads { cacheDrawBlock() } + isCacheValid = true + } + + val shadow = _shadow + val graphicsLayer = graphicsLayer + if (shadow != null && graphicsLayer != null) { + val elevation = shadow.elevation.toPx() + translate(-(elevation - shadow.offset.x.toPx()), -(elevation - shadow.offset.y.toPx())) { drawLayer(graphicsLayer) } - drawContent() } - }) + + drawContent() + } + + override fun onObservedReadsChanged() { + invalidateDrawCache() + } + + fun invalidateDrawCache() { + isCacheValid = false + invalidateDraw() + } override fun onAttach() { val graphicsContext = requireGraphicsContext() diff --git a/catalog/release/baselineProfiles/0/catalog-release.dm b/catalog/release/baselineProfiles/0/catalog-release.dm index fe04be0..3e249ec 100644 Binary files a/catalog/release/baselineProfiles/0/catalog-release.dm and b/catalog/release/baselineProfiles/0/catalog-release.dm differ diff --git a/catalog/release/baselineProfiles/1/catalog-release.dm b/catalog/release/baselineProfiles/1/catalog-release.dm index 861788e..5f53105 100644 Binary files a/catalog/release/baselineProfiles/1/catalog-release.dm and b/catalog/release/baselineProfiles/1/catalog-release.dm differ diff --git a/catalog/release/catalog-release.apk b/catalog/release/catalog-release.apk index 083b64d..9b90218 100644 Binary files a/catalog/release/catalog-release.apk and b/catalog/release/catalog-release.apk differ