0

I have a BLURRED background Bitmap and a horizontal scrollable row. What I want to achieve is that the background image must be visible only in the row item area...

I've tried to apply DST_IN with blendMode but no success. Any ideas how to do it in jetpack compose?

What I'm trying to achieve is this

enter image description here

I want to have a blurred version of the background Image only visible though the row items, so that it will seem like a gloss mirror background on row items when scrolled

MJakhongir
  • 2,101
  • 2
  • 16
  • 27

1 Answers1

1

You either need to set alpha something less than 1f

    Canvas(
        modifier = Modifier
            .fillMaxSize()
            // Provide a slight opacity to for compositing into an
            // offscreen buffer to ensure blend modes are applied to empty pixel information
            // By default any alpha != 1.0f will use a compositing layer by default
            .graphicsLayer(alpha = 0.99f)
    ) {


        val dimension = (size.height.coerceAtMost(size.width) / 2f).toInt()

        drawImage(
            image = imageBitmapDst,
            dstSize = IntSize(dimension, dimension)
        )
        drawImage(
            image = imageBitmapSrc,
            dstSize = IntSize(dimension, dimension),
            blendMode = BlendMode.DstIn
        )
    }
}

or use a layer inside canvas

 with(drawContext.canvas.nativeCanvas) {

    val checkPoint = saveLayer(null, null)

// Destination
    drawImage(
        image = dstImage,
        srcSize = IntSize(canvasWidth / 2, canvasHeight / 2),
        dstSize = IntSize(canvasWidth, canvasHeight),
    )

    // Source
    drawImage(
        image = srcImage,
        srcSize = IntSize(canvasWidth / 2, canvasHeight / 2),
        dstSize = IntSize(canvasWidth, canvasHeight),
        blendMode = BlendMode.DstIn

    )
    restoreToCount(checkPoint)
}

You can check this tutorial for more about Blend modes.

Setting alpha less than 1f might look like a hack but Image's Painter source code uses it too to decide whether to create layer or not

private fun configureAlpha(alpha: Float) {
    if (this.alpha != alpha) {
        val consumed = applyAlpha(alpha)
        if (!consumed) {
            if (alpha == DefaultAlpha) {
                // Only update the paint parameter if we had it allocated before
                layerPaint?.alpha = alpha
                useLayer = false
            } else {
                obtainPaint().alpha = alpha
                useLayer = true
            }
        }
        this.alpha = alpha
    }
}

And creates a layer as for Image

    fun DrawScope.draw(
        size: Size,
        alpha: Float = DefaultAlpha,
        colorFilter: ColorFilter? = null
    ) {
        configureAlpha(alpha)
        configureColorFilter(colorFilter)
        configureLayoutDirection(layoutDirection)

        // b/156512437 to expose saveLayer on DrawScope
        inset(
            left = 0.0f,
            top = 0.0f,
            right = this.size.width - size.width,
            bottom = this.size.height - size.height
        ) {

            if (alpha > 0.0f && size.width > 0 && size.height > 0) {
                if (useLayer) {
                    val layerRect = Rect(Offset.Zero, Size(size.width, size.height))
                    // TODO (b/154550724) njawad replace with RenderNode/Layer API usage
                    drawIntoCanvas { canvas ->
                        canvas.withSaveLayer(layerRect, obtainPaint()) {
                            onDraw()
                        }
                    }
                } else {
                    onDraw()
                }
            }
        }
    }
}
Thracian
  • 43,021
  • 16
  • 133
  • 222
  • how to draw into the same canvas for Row background and items background inside Row composable ...? – MJakhongir May 10 '22 at 08:38
  • 1
    Do you have an image what you wish to build? Creating a Canvas and drawing with blend modes using a layer or setting alpha will let you create pixel manipulation you desire. anything you draw on that canvas and setting destination blend mode will help you achieve it. – Thracian May 10 '22 at 08:43
  • My answer is about how to use BlendMode correctly with Jetpack Compose Canvas. In the link in the answer you can find many samples how to use blendmodes with Images, Paths and drawings. – Thracian May 10 '22 at 08:44
  • I've added an image of what I want to achieve – MJakhongir May 10 '22 at 08:54
  • I don't think you will be able to achieve this with BlendMode. You can use fun `Modifier.blur( radius: Dp, edgeTreatment: BlurredEdgeTreatment = BlurredEdgeTreatment.Rectangle ) = blur(radius, radius, edgeTreatment)` but it's available starting from Android 12 as document states. You should either check for a library or `RenderScript ` to create your own modifier below Android 12 if needed – Thracian May 10 '22 at 08:58
  • blur is not the point here, as I said, I have the blurred image already... the hack I wan't to do is that the blurred image will be visible only through row items background... – MJakhongir May 10 '22 at 09:08
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/244631/discussion-between-thracian-and-mjakhongir). – Thracian May 10 '22 at 09:09
  • 1
    Instead of using saveLayer(), you can use Modifier.graphicsLayer(compositingStrategy = CompositingStrategy.Offscreen). I also tried to achieve some BlendMode effects and found this modifier helpful. Also, notice that saveLayer() is very expensive. @Thracian. – kosher Jun 22 '23 at 13:18