I use `ImageComposeScene` with this code to take screenshots of my Composables which works fine for...
s
I use
ImageComposeScene
with this code to take screenshots of my Composables which works fine for most things. But now I got a Composable which shows an
Image
Composable after a
produceState()
loaded the
BitmapPainter
. As long as the bitmapPainter has the initialValue of null is just displays an empty
Box
as placeholder. My question now is how I can let
ImageComposeScene
wait until the
produceState()
executed and re-composed until taking the screenshot. Now the screenshot is taken immediately after the first composition which leaves me with an empty
Box
.
Copy code
val bitmapPainter = produceState<BitmapPainter?>(initialValue = null, thumbnailFileName) {

    value = withContext(Dispatchers.Default) {

        val image = imageLoader.loadThumbnailImage(thumbnailFileName)
            ?: return@withContext null

        return@withContext BitmapPainter(image = image)
    }
}
Copy code
@OptIn(ExperimentalComposeUiApi::class)
fun takeScreenshot(content: @Composable () -> Unit): ByteArray {

    ImageComposeScene(
        width = 1024,
        height = 768,
        density = Density(1f),
        content = content
    ).use {

        return@takeScreenshot it.render().encodeToData(EncodedImageFormat.PNG)!!.bytes
    }
}
I guess I do it wrong, but if I call render() two times the images are there. I can't use
use()
because the test will run endless then. Hopefully someone tells me that I do it all wrong and how I must actually do this. 🙈
Copy code
@OptIn(ExperimentalComposeUiApi::class)
fun takeScreenshot(content: @Composable () -> Unit): ByteArray {

    val scene = ImageComposeScene(
        width = TestAppStates.DEFAULT_SCREEN_WIDTH,
        height = TestAppStates.DEFAULT_SCREEN_HEIGHT,
        density = density,
        content = content
    )

    /* First call to render() starts the animations and executes produceState() blocks. */
    scene.render()

    /*
     * The second call to render() produces the final screenshot.
     * See <https://github.com/JetBrains/compose-jb/issues/1395>
     *
     * We are supposed to close the ImageComposeScene now, but
     * if we do so the test will run endless.
     */
    return scene.render().encodeToPng()
}
k
I use something similar in Aurora, but with an additional delay between the two `render`s. I found that delay was needed for my custom runtime shaders to be updated with the content size, so that the second
render
gets me the right visuals.
s
Thank you. I guess it's supposed to be done that way then. 👍
@Kirill Grouchnikov Did you have the issue that the rendered screenshots look different on another machine / CI? I found my tests fail because the font is thicker on my local machine.
k
That would depend on what is the default font CfD / Skiko is using on that particular machine. You can’t rely on any particular font being the default, unless you’re loading from your own font files.
s
Of course I load it from my own TTF. 😉 On another thread Igor Demin hinted me that it's known that SKIA uses system dependend font rendering which differs. I did not expect that for software rendering.
133 Views