Compose 1.7.0 introduces `rememberGraphicsLayer()`...
# compose
r
Compose 1.7.0 introduces
rememberGraphicsLayer()
, which looks like a fantastic way to render a composable into a bitmap. What's the best way to do this without displaying the composable? e.g. outside my normal composable UI code?
s
Yes, GraphicsLayer is a fantastic API! I'm using it a lot in my project. You can record canvas commands into it, just like drawing on a canvas. However, I'm afraid it's not possible to draw a Composable widget outside of composition. When recording a composable widget into the GraphicsLayer, it's not necessary to display it on the screen, but the widget must still be included in the composition tree.
👍 1
r
@Sergey Y. is there a straightforward way to create a "headless" composition tree, e.g. what ImageComposeScene does on compose desktop?
s
Unfortunately, I'm not aware of this.
You could try something like this if it aligns with your goals:
However, this approach requires you to call the function only from within the composition scope, which you may have wanted to avoid. 🤷
r
Ah yes, I don't have a composition scope available where I need to do this!
Thanks for looking though 🙂
👍 1
c
You could put your composition in a VirtualDisplay to draw offscreen https://developer.android.com/reference/android/hardware/display/VirtualDisplay
s
Could you provide more details on this topic? It would be great to see a brief code sample demonstrating how this approach could be useful in this particular situation. It seems that there might be a misunderstanding about the core problem.
c
My goal was to combine camera video with Compose UI in a single GL texture, so I have a utility class which uses the following: •
ComposeView
, which acts as the content view for a… •
Presentation
, which is displayed on a… •
VirtualDisplay
, which draws to a… •
Surface
, which is backed by a… •
SurfaceTexture
Then I call
composeView.setContent(...)
, and the texture is updated. The performance is good for my use case, video still comes through at 30+ fps on older devices, though perhaps there’s a better approach, I don’t know.
There are a bunch of things you need to wire up to get it to work:
Copy code
private val savedStateRegistryOwner = EmptySavedStateRegistryOwner()

  private val presentation = Presentation(appContext, virtualDisplay.display).apply {
    window?.decorView?.let {
      it.setViewTreeLifecycleOwner(ProcessLifecycleOwner.get())
      it.setViewTreeSavedStateRegistryOwner(savedStateRegistryOwner)
    }
  }
  private val composeView = ComposeView(appContext).apply {
    layoutParams = virtualLayoutParams
    setViewTreeLifecycleOwner(ProcessLifecycleOwner.get())
    setViewTreeSavedStateRegistryOwner(savedStateRegistryOwner)
    presentation.addContentView(this, virtualLayoutParams)
  }
Dummy SavedStateRegistryOwner
Copy code
private class EmptySavedStateRegistryOwner : SavedStateRegistryOwner {
    private val controller = SavedStateRegistryController.create(this).apply {
      performRestore(null)
    }

    private val lifecycleOwner: LifecycleOwner? = ProcessLifecycleOwner.get()

    override fun getLifecycle(): Lifecycle {
      return object : Lifecycle() {
        override fun addObserver(observer: LifecycleObserver) {
          lifecycleOwner?.lifecycle?.addObserver(observer)
        }

        override fun removeObserver(observer: LifecycleObserver) {
          lifecycleOwner?.lifecycle?.removeObserver(observer)
        }

        override val currentState = State.INITIALIZED
      }
    }

    override val savedStateRegistry: SavedStateRegistry
      get() = controller.savedStateRegistry
  }
💯 1
I don’t think I can share my whole utility class code, but if you attempt this and run into a problem, feel free to msg me
(Obviously of course this is Android-only)
s
Interesting, I've never used android.app.Presentation before.
c
You can then copy the content of the texture into a Bitmap
Yeah it’s mostly for showing something on an external display afaik
s
TIL: android.app.Presentation Thanks
👍 1
BTW, starting from Compose 1.7.0, you can render Compose UI into a texture using GraphicsLayer. If you're using Compose 1.6.0, you can use a Picture instead. I'm actually using this approach for my hardware-accelerated blurring library.
c
Thanks for the tip! Sadly the app I built has been decommissioned due to a strategic pivot, but I actually think I could bring this portion of it back
😞 1
Is your blurring library available somewhere? I like checking out graphics code
s
The library is still under development, and the repository is currently private. I plan to make it public later, but I need to clean up the code a bit first. 😄 To be honest, I'm not very experienced with graphics programming, so there might be some silly mistakes in there. But if you think it would be helpful, I'd be happy to add you to the repository. Here how it is working now: https://x.com/desugar_64/status/1789795911217950794
c
Cool! Yeah I’m not a graphics guy either, but we needed a bit of graphics work done a few years back so I just picked up what I could
s
You know what? I think I'll make the repo public later today. I just need to add a readme file first. It's probably better to receive feedback and make changes during development rather than wait until the abstraction tower gets too high and potentially topples over because of a silly mistake. 😊
🙌 2
r
I have the VirtualDisplay / Presentation / Surface combo set up, but I'm failing to get my composable to actually...compose. This isthe same issue I was running into with alternative approaches too. The ComposeView fails to compose anything because it thinks it's not attached to a window, even though the ComposeView is passed to
presentation.addContentView(...)
c
Can you share your code and any errors you’re getting?
I can’t remember if this is required, you may need to call
presentation.show()
👍 1
r
Ack, presentation.show() fixes that issue -- the view does get attached to a window. But still, the composable content is never run
👍 1
Ended up solving this thanks to your hints @Chris Fillmore: https://gist.github.com/iamcalledrob/871568679ad58e64959b097d4ef30738 For my purposes, I can probably get away without using a VirtualDisplay, since it's possible to use the default display with alpha=0f
y
Not sure if I should use this. But this was quite impressive. Worked nicely with graphics layer
368 Views