Skip to content

Instantly share code, notes, and snippets.

@alexfacciorusso
Last active September 26, 2023 11:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexfacciorusso/95f813fd552df1091053b2fce2d28fa7 to your computer and use it in GitHub Desktop.
Save alexfacciorusso/95f813fd552df1091053b2fce2d28fa7 to your computer and use it in GitHub Desktop.
Start recording Skia frames in SKP files from Kotlin Compose Desktop
fun main() = application {
Window(onCloseRequest = ::exitApplication) {
LaunchedEffect(Unit) {
window.attachSkiaDebugger()
}
MaterialTheme {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.background(Color.LightGray).height(IntrinsicSize.Min)
) {
Text(
"Text 1",
modifier = Modifier.background(Color.Green),
)
Text(
"Text 2\nMultiline",
modifier = Modifier.background(Color.Yellow),
)
Text(
"Text 3",
modifier = Modifier.background(Color.Red),
)
}
}
}
}
import androidx.compose.ui.awt.ComposeWindow
import org.jetbrains.skia.Canvas
import org.jetbrains.skia.PictureRecorder
import org.jetbrains.skia.Rect
import org.jetbrains.skiko.SkiaLayer
import org.jetbrains.skiko.SkikoView
import java.awt.Container
import java.io.Closeable
import java.io.File
import javax.swing.JComponent
private fun <T : JComponent> findComponent(
container: Container,
klass: Class<T>,
): T? {
val componentSequence = container.components.asSequence()
return componentSequence.filter { klass.isInstance(it) }.ifEmpty {
componentSequence.filterIsInstance<Container>()
.mapNotNull { findComponent(it, klass) }
}.map { klass.cast(it) }.firstOrNull()
}
private inline fun <reified T : JComponent> Container.findComponent() = findComponent(this, T::class.java)
fun ComposeWindow.findSkiaLayer(): SkiaLayer? = findComponent<SkiaLayer>()
class RecordingSkikoView(private val original: SkikoView) : SkikoView, Closeable {
private val recorder = PictureRecorder()
init {
recorder.beginRecording(Rect(0f, 0f, 1024f, 768f))
}
override fun onRender(canvas: Canvas, width: Int, height: Int, nanoTime: Long) {
original.onRender(recorder.recordingCanvas!!, width, height, nanoTime)
original.onRender(canvas, width, height, nanoTime)
save()
}
private fun save() {
val pic = recorder.finishRecordingAsPicture()
val picBytes = pic.serializeToData().bytes
// save bytes to file
File("skia_debugger.skp").writeBytes(picBytes)
recorder.beginRecording(Rect(0f, 0f, 1024f, 768f))
}
override fun close() {
recorder.close()
}
}
fun ComposeWindow.attachSkiaDebugger() {
val skiaLayer = findSkiaLayer() ?: error("SkiaLayer not found")
val originalSkikoView = skiaLayer.skikoView ?: error("SkikoView not present")
skiaLayer.skikoView = RecordingSkikoView(originalSkikoView)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment