I'm trying to build a custom shadow modifier that ...
# compose
r
I'm trying to build a custom shadow modifier that will follow the alpha channel of the content. I'd expect to achieve this by: 1. Drawing the content into a bitmap 2. Copying the bitmap, colorizing it and blurring it (then drawing it). 3. Drawing the original bitmap over the top. The issue I'm running into is that
BlurEffect
gets applied on the graphics layer, which I only have 1 of. So either both bitmaps are blurred, or none are. Is there a trick I'm missing here? Seems like I could solve this if I could have 2 graphics layers that are siblings
s
The
BlurEffect
works for API 32 and above, but there is a backward-compatible way for older Android versions. Although it is software-based, but should be fine for still images. Here's how you can achieve the blur effect on older Android versions: 1. Render the
GraphicsLayer
into a bitmap using the
.toImageBitmap()
method. 2. Use the
drawBehind
modifier to draw the bitmap using a
Paint
object with a
BlurMaskFilter
applied to it. If you want to proceed with the
BlurEffect
anyway, you need to draw the
GraphicsLayer
into two different `RenderNode`s: • One RenderNode for the content. • Another RenderNode for the blur effect. You can see Haze library as an example.
💯 2
r
@Sergey Y. interesting -- does this require the new GraphicsLayer APIs from 1.7.0?
s
You can use recording to android.graphics.Picture for Compose 1.6.0
instead of GraphicsLayer if you are using older version of compose
r
Ah, I'm building for desktop too, so android.graphics.Picture sadly isn't available, nor the new APIs 😞
Hopefully the new GraphicsLayer APIs make it into compose multiplatform soon
s
In that case, you'll need to work with platform-dependent logic. I'm not an expert in Compose for Desktop, but I do remember they had APIs for rendering content into bitmaps even before Android. It was called ComposeScene or something similar.
r
Makes sense, thank you. Looks like I can draw into an imageBitmap backed canvas, so that might be workable too. Still, feels like this overall functionality (shadowing based on alpha mask) should be part of the core lib
👍 1
s
I agree, this area definitely has room for improvement. At the very least, I'll try to address this specific case in my project(android only).
❤️ 1
r
2. Use the
drawBehind
modifier to draw the bitmap using a
Paint
object with a
BlurMaskFilter
applied to it.
I'm curious how this is done actually. I don't see a way to pass paint to
drawImage
when drawing the bitmap? The docs say "The image is composited into the canvas using the given
Paint
." -- but there is no paint parameter to specify?
s
> I'm curious how this is done actually. I don't see a way to pass paint to
drawImage
when drawing the bitmap? You need to use
drawIntoCanvas { canvas -> }
to access Canvas object directly.
r
Ack, thank you -- missed that
s
Maybe something like this
But this is Android target
r
I'm using this approach, but the BlurMaskFilter appears to only blur visible pixels that touch the edge of the canvas.
s
Could you share the image of the result? software blurring is clipped to bounds, this is downside of the technique.
to workaround it play with different blurring modes and bounds of the layout you are blurring.
r
(the blurred pixels are the ones touching the edge of my canvas)
🤔 1
If I draw a rectangle to fill the bounds completely it works fine.
round rect
s
interesting, never saw this before
r
Looks like it could be this: https://issuetracker.google.com/issues/238769745 I'm on Android 14 though
helpfully the screenshots are removed from the issue tracker 🤦
It seems like there's a bug here where: (1) Blurred pixels are calculated (2) The blurred pixels are drawn using the wrong alpha channel -- the alpha channel of the non-blurred image, rather than the blurred version
s
You can verify if this is indeed a bug by running the app on Android 12 or below. If everything works fine there, then it's likely a bug specific to the latest Android version. If that's the case, unfortunately, you'll need to apply blurring to the bitmap using the deprecated RenderScript or Google's highly optimized native library, which is suggested for migrating away from RenderScript. 😞 With Compose for Desktop, it should be much simpler as you can call directly to Skiko, which is shipped with it.