I have an image encoded as 2D array of colors like this:
val pixels = arrayOf(
arrayOf(color1, color2,... colorN),
...
)
I would like to draw it pixel-by-pixel on Compose Canvas (desktop application, not Android):
Canvas(
modifier = Modifier
.size(200.dp)
.background(Color.White)
) {
drawPixels(pixels) //need function like this
}
The only way I found is to use drawPoints
but it required list of points for every color. Not the best way if I have hundreds of different colors.
How can I draw an array of pixels?
Your array is effectively a bitmap, a list of colors for each pixel. Bitmaps can be drawn into a Canvas with drawImage
.
The only thing to do is to convert your array of arrays into the proper bitmap format:
fun ImageBitmap(colors: Array<Array<Color>>): ImageBitmap {
val bytesPerPixel = 4 // Alpha, Red, Green, Blue
val width = colors.firstOrNull()?.size ?: 0
val height = colors.size
val bytes = ByteArray(width * height * bytesPerPixel)
colors.forEachIndexed { x, row ->
row.forEachIndexed { y, color ->
with(color.convert(ColorSpaces.Srgb).value) {
repeat(bytesPerPixel) {
bytes[x * width * bytesPerPixel + y * bytesPerPixel + it] =
shr(32 + it * 8).toByte()
}
}
}
}
val image: Image = Image.makeRaster(
imageInfo = ImageInfo.makeN32Premul(width, height),
bytes = bytes,
rowBytes = width * 4,
)
return image.toComposeImageBitmap()
}
This assumes your array contains colors of type androidx.compose.ui.graphics.Color
.
You can now simply draw this bitmap into the Canvas:
val image = remember(pixels) { ImageBitmap(pixels) }
Canvas(
modifier = Modifier
.size(200.dp)
.background(Color.White)
) {
drawImage(image)
}
On Android this would be a lot easier because we can create a android.graphics.Bitmap
:
import android.graphics.Bitmap
fun <T> createBitmap(
colors: Array<Array<T>>,
transform: (T) -> Int,
config: Bitmap.Config = Bitmap.Config.ARGB_8888,
): Bitmap {
val intColors = colors
.flatten()
.map(transform)
.toIntArray()
val width = colors.firstOrNull()?.size ?: 0
val height = colors.size
return Bitmap.createBitmap(
intColors,
width,
height,
config,
)
}
It can also use other color types as input. For an array of androidx.compose.ui.graphics.Color
s the bitmap would be created like this:
val bitmap = createBitmap(pixels, Color::toArgb)
The config
parameter is used to specify the type of the bitmap. The default is ARGB_8888
(simple 8 bit sRGB values with alpha) which should be a good fit for a Canvas.
You can now simply draw this bitmap into the Canvas:
Canvas(
modifier = Modifier
.size(200.dp)
.background(Color.White)
) {
drawImage(
image = bitmap.asImageBitmap(),
)
}
If you call createBitmap
from a composable make sure you remember
the result so it isn't recreated on every recomposition.