Norbi
10/20/2023, 9:50 AMCsaba Kozák
10/20/2023, 10:53 AMMichael Paus
10/20/2023, 11:12 AMCsaba Kozák
10/20/2023, 11:21 AMandroid.graphics.Picture
class.Michael Paus
10/20/2023, 11:26 AMMichael Paus
10/20/2023, 11:41 AMimport androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.*
import java.io.File
import javax.imageio.ImageIO
import kotlin.test.Test
import kotlin.test.assertTrue
class ImageCanvasTest {
@Test
fun test() {
val image = ImageBitmap(400, 400)
val canvas = Canvas(image)
canvas.drawLine(Offset.Zero, Offset(image.width.toFloat(), image.height.toFloat()), Paint().apply { color = Color.Red })
val file = File("canvasimage.png")
if (file.exists()) file.delete()
ImageIO.write(image.toAwtImage(), "png", file)
assertTrue(file.exists())
}
}
Marcin Wisniowski
10/23/2023, 2:06 PMBox { Image(); Text("Watermark")... }
to use a Compose layout to add a watermark to an image (not that it’s a great use case, but a simple example).
Not using Canvas, just composable layouts that would then be rendered to an image, instead of to a screen.matsem
07/28/2024, 6:25 PMMichael Paus
07/29/2024, 9:37 AMmatsem
07/29/2024, 9:41 AMSkaldebane
07/29/2024, 4:23 PMSkaldebane
07/29/2024, 4:31 PMImageCaptureScene
API to do just that (it doesn't create a window or call any code that would raise an AWT HeadlessException
, so it's perfect for this usecase).Skaldebane
07/29/2024, 4:33 PMimport androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.ui.Alignment
import androidx.compose.ui.ImageComposeScene
import androidx.compose.ui.Modifier
fun main() {
System.setProperty("java.awt.headless", "true")
val scene = ImageComposeScene(
width = 1000,
height = 1000
) {
MaterialTheme {
Surface(color = MaterialTheme.colors.background) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize()
) {
Text("Hello, World!")
}
}
}
}
val image = scene.render(0)
// do something with the image;
// e.g. send back as a response through Ktor
}
Skaldebane
07/29/2024, 4:39 PMImageComposeScene
to generate video as well (e.g from an animated Composable) by moving time forward and rendering multiple images.Marcin Wisniowski
07/29/2024, 4:44 PMmatsem
07/29/2024, 5:20 PMSkaldebane
07/29/2024, 5:26 PMSkaldebane
07/29/2024, 5:32 PMImageComposeScene
is much simpler to use. (SingleLayerComposeScene
is an internal API as well).matsem
08/02/2024, 8:59 PMStylianos Gakis
08/02/2024, 9:07 PMmatsem
08/02/2024, 9:14 PMSkaldebane
08/02/2024, 10:05 PMSingleLayerComposeScene
(or MultiLayerComposeScene
), they're only on skikoMain.
So Android is excluded from this unfortunately. One of its biggest benefits is the ability to control time, and I'm not sure if there's a way to replicate that using the classic GraphicsLayer.toBitmap()
solution on Android or some other way.Skaldebane
08/02/2024, 10:20 PMSystem.setProperty("java.awt.headless", "true")
, since ImageComposeScene
skips AWT completely and renders the image through Skia and returns the rendered result.
It's apparently only useful if you're creating an actual AWT window (that includes Compose's Window
and maybe application
too), since it causes AWT to not try contacting the OS to get a display/keyboard/mouse device when it's initialized, and causes all functionalities that rely on them to throw HeadlessException
if they're used. I'm not sure Compose's Window
supports this mode at all, but it doesn't matter when using ImageComposeScene
AFAIK.
More details about headless mode: https://www.oracle.com/technical-resources/articles/javase/headless.htmlmatsem
08/02/2024, 10:35 PMStylianos Gakis
08/02/2024, 11:25 PM