Is there a way to take the compose window and rend...
# compose-desktop
r
Is there a way to take the compose window and render it to an image file instead of displaying on screen?
d
The entire window, or just the compose content?
r
I’m looking for just the compose content, but I could probably make do with either
d
I was going to suggest using the
ComposePanel
and existing tools to render Swing UI to an image, since that's a thing.
r
Ok I’ll give that a go. Thanks.
I haven’t been able to get this to work. I’m trying to do something like this:
Copy code
val panel = ComposePanel()
    panel.setContent {
        Surface(color = Color.White, modifier = Modifier.fillMaxSize()) {
            Text("Hello from Compose", fontSize = 100.sp)
        }
    }

    val window = JFrame()
    window.contentPane.add(panel, BorderLayout.CENTER)
//    window.contentPane.add(JTextPane().apply { text = "Hello from Swing" }, BorderLayout.CENTER)
    window.setSize(3300, 2550)
    window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
    window.isUndecorated = true
    window.isVisible = true

    val image = BufferedImage(window.width, window.height, BufferedImage.TYPE_INT_RGB)
    val graphics = image.createGraphics()
    window.printAll(graphics)
    graphics.dispose()

    val file = File(filename)
    ImageIO.write(image, "png", file)

    window.dispose()
I see the compose output rendered on screen but it doesn’t get printed to the file. If I uncomment the commented line adding a JTextPane I do see that rendered in file. I think the culprit is that
ComposePanel
overrides
paint()
and ignores the supplied
Graphics
object
Copy code
override fun paint(g: Graphics?) {
        needRedrawLayer()
    }
so it never draws to the
BufferedImage
.
d
That's quite unfortunate. Ugh. This looks ticket worthy.
r
I still should write up a ticket on this but here’s a hacky workaround by grabbing the internal skia layer
Copy code
val panel = ComposePanel()
    panel.setContent {
        Surface(color = Color.White, modifier = Modifier.fillMaxSize()) {
            Text("Hello from Compose", fontSize = 100.sp)
        }
    }

    val window = JFrame()
    window.contentPane.add(panel, BorderLayout.CENTER)
    window.setSize(3300, 2550)
    window.defaultCloseOperation = WindowConstants.DISPOSE_ON_CLOSE
    window.isUndecorated = true
    window.isVisible = true

    panel.requestFocus()
    // Wait for panel to request focus and then pass it to its internal SkiaPanel
    var focus: Component? = null
    while (focus !is SkiaLayer) {
        focus = window.focusOwner
        Thread.sleep(100)
    }

    val skiaLayer = window.focusOwner as SkiaLayer
    val bitmap = ImageBitmap(window.width, window.height)
    val canvas = Canvas(bitmap)
    skiaLayer.renderer?.onRender(canvas.nativeCanvas, window.width, window.height)

    val file = File("hello.png")
    val image = BufferedImage(window.width, window.height, BufferedImage.TYPE_INT_RGB)
    val pixelMap = bitmap.toPixelMap()
    for (x in 0 until bitmap.width) {
        for (y in 0 until bitmap.height) {
            image.setRGB(x, y, pixelMap[x, y].toArgb())
        }
    }
    ImageIO.write(image, "png", file)

    window.dispose()