Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 329bba57 authored by Fabián Kozynski's avatar Fabián Kozynski
Browse files

Rewrite scrim clip modifier

This uses Offscreen composition to have more performant draw phase.

Test: manual, scrim works
Test: performance suites show some improvement
Test: perfetto trace shows significantly shorter draw times during
animation
Flag: com.android.systemui.qs_ui_refactor_compose_fragment
Bug: 383085298

Change-Id: I3ca543ae166dace56ac0e240e6d6a5a06e09fde9
parent 1fcff7e0
Loading
Loading
Loading
Loading
+32 −65
Original line number Diff line number Diff line
@@ -17,14 +17,16 @@
package com.android.systemui.qs.composefragment.ui

import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.ClipOp
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.asAndroidPath
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.clipPath
import androidx.compose.ui.node.DrawModifierNode
import androidx.compose.ui.node.ModifierNodeElement
import androidx.compose.ui.platform.InspectorInfo
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.clipRect
import androidx.compose.ui.graphics.layer.CompositingStrategy
import androidx.compose.ui.graphics.layer.drawLayer

/**
 * Clipping modifier for clipping out the notification scrim as it slides over QS. It will clip out
@@ -32,63 +34,28 @@ import androidx.compose.ui.platform.InspectorInfo
 * from the QS container.
 */
fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams): Modifier {
    return this then NotificationScrimClipElement(clipParams)
}

private class NotificationScrimClipNode(var clipParams: () -> NotificationScrimClipParams) :
    DrawModifierNode, Modifier.Node() {
    private val path = Path()

    private var lastClipParams = NotificationScrimClipParams()

    override fun ContentDrawScope.draw() {
        val newClipParams = clipParams()
        if (newClipParams != lastClipParams) {
            lastClipParams = newClipParams
            applyClipParams(path, lastClipParams)
        }
        clipPath(path, ClipOp.Difference) { this@draw.drawContent() }
    }

    private fun ContentDrawScope.applyClipParams(
        path: Path,
        clipParams: NotificationScrimClipParams,
    ) {
        with(clipParams) {
            path.rewind()
            path
                .asAndroidPath()
                .addRoundRect(
                    -leftInset.toFloat(),
                    top.toFloat(),
                    size.width + rightInset,
                    bottom.toFloat(),
                    radius.toFloat(),
                    radius.toFloat(),
                    android.graphics.Path.Direction.CW,
    return this.drawWithCache {
            val params = clipParams()
            val left = -params.leftInset.toFloat()
            val right = size.width + params.rightInset.toFloat()
            val top = params.top.toFloat()
            val bottom = params.bottom.toFloat()
            val graphicsLayer = obtainGraphicsLayer()
            graphicsLayer.compositingStrategy = CompositingStrategy.Offscreen
            graphicsLayer.record {
                drawContent()
                clipRect {
                    drawRoundRect(
                        color = Color.Black,
                        cornerRadius = CornerRadius(params.radius.toFloat()),
                        blendMode = BlendMode.Clear,
                        topLeft = Offset(left, top),
                        size = Size(right - left, bottom - top),
                    )
                }
            }
}

private data class NotificationScrimClipElement(val clipParams: () -> NotificationScrimClipParams) :
    ModifierNodeElement<NotificationScrimClipNode>() {
    override fun create(): NotificationScrimClipNode {
        return NotificationScrimClipNode(clipParams)
    }

    override fun update(node: NotificationScrimClipNode) {
        node.clipParams = clipParams
    }

    override fun InspectorInfo.inspectableProperties() {
        name = "notificationScrimClip"
        with(clipParams()) {
            properties["leftInset"] = leftInset
            properties["top"] = top
            properties["rightInset"] = rightInset
            properties["bottom"] = bottom
            properties["radius"] = radius
            onDrawWithContent {
                drawLayer(graphicsLayer)
            }
        }
}