How to load image in Kotlin Compose desktop?
Asked Answered
P

7

12

How to load images from the hard disk when using Kotlin compose on the desktop?

Paracelsus answered 8/11, 2020 at 20:0 Comment(1)
I'm trying to figuring out how to load from a url...Globin
P
10

You can get ImageAsset with this function

fun imageFromFile(file: File): ImageAsset {
    return org.jetbrains.skia.Image.makeFromEncoded(file.readBytes()).asImageAsset()
}

Full example:

import androidx.compose.desktop.Window
import androidx.compose.foundation.Image
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.ImageAsset
import androidx.compose.ui.graphics.asImageAsset
import java.io.File

fun main() = Window {
   val file = File("D:\\images\\my_image.PNG")
   val image = remember { imageFromFile(file) }

   Image(asset = image)
}

fun imageFromFile(file: File): ImageAsset {
   return org.jetbrains.skia.Image.makeFromEncoded(file.readBytes()).asImageAsset()
}
Paracelsus answered 8/11, 2020 at 20:0 Comment(1)
It's skia, not skijaQuartziferous
R
15

other answers are outdated, as per Compose 1.0.0-beta5 you should do the following:

Image(painterResource("image.jpg"))

if you want to load a bitmap only

val bitmap = useResource("image.jpg") { loadImageBitmap(it) }

Only the file name is required (not full path), but make sure that your resources are in src/main/resources/image.jpg

Ramunni answered 14/11, 2021 at 3:53 Comment(0)
P
10

You can get ImageAsset with this function

fun imageFromFile(file: File): ImageAsset {
    return org.jetbrains.skia.Image.makeFromEncoded(file.readBytes()).asImageAsset()
}

Full example:

import androidx.compose.desktop.Window
import androidx.compose.foundation.Image
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.ImageAsset
import androidx.compose.ui.graphics.asImageAsset
import java.io.File

fun main() = Window {
   val file = File("D:\\images\\my_image.PNG")
   val image = remember { imageFromFile(file) }

   Image(asset = image)
}

fun imageFromFile(file: File): ImageAsset {
   return org.jetbrains.skia.Image.makeFromEncoded(file.readBytes()).asImageAsset()
}
Paracelsus answered 8/11, 2020 at 20:0 Comment(1)
It's skia, not skijaQuartziferous
G
5

Try this one:

import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.res.loadImageBitmap
import java.io.File


val file = File(path)
val imageBitmap: ImageBitmap = remember(file) {
    loadImageBitmap(file.inputStream())
}

Image(
    painter = BitmapPainter(image = imageBitmap),
    contentDescription = null
)
Gaylor answered 2/1, 2022 at 20:37 Comment(1)
This is the simplest and best answer. Other answers rely on 3rd part libraries or only work for images defined as resources.Rogers
D
4

This worked for me.

 Image(bitmap = imageFromResource("image.png"),
       "image",
 )

contentDescription is necessary, but can be anything you'd like. You can also add modifiers such as

val imageModifier = Modifier
   .height(240.dp)
   .fillMaxWidth()
   .clip(RoundedCornerShape(12.dp))

Image(bitmap = imageFromResource("header.png"),
      "image",
      imageModifier,
      contentScale = ContentScale.Fit
    )
Donnettedonni answered 25/2, 2021 at 18:4 Comment(0)
A
3

Image.asImageBitmap() is deprecated. Use new Image.toComposeImageBitmap(). For now (01.04.2022) it's not deprecated yet:

@Composable
fun CanvasArea2() {
    val image = remember { imageFromFile(File("C:/Users/Admin/Desktop/banana.png")) }

    Canvas(modifier = Modifier.background(color = Color(0xFFFFFFFF))) {
        drawImage(image)
    }
}

fun imageFromFile(file: File): ImageBitmap {
    return org.jetbrains.skia.Image.makeFromEncoded(file.readBytes()).toComposeImageBitmap()
}
Astrogation answered 1/4, 2022 at 6:20 Comment(0)
G
2

from the docs : Loading images from device storage or network asynchronously

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.width
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.loadImageBitmap
import androidx.compose.ui.res.loadSvgPainter
import androidx.compose.ui.res.loadXmlImageVector
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.singleWindowApplication
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.xml.sax.InputSource
import java.io.File
import java.io.IOException
import java.net.URL

fun main() = singleWindowApplication {
    val density = LocalDensity.current
    Column {
        AsyncImage(
            load = { loadImageBitmap(File("sample.png")) },
            painterFor = { remember { BitmapPainter(it) } },
            contentDescription = "Sample",
            modifier = Modifier.width(200.dp)
        )
        AsyncImage(
            load = { loadSvgPainter("https://github.com/JetBrains/compose-jb/raw/master/artwork/idea-logo.svg", density) },
            painterFor = { it },
            contentDescription = "Idea logo",
            contentScale = ContentScale.FillWidth,
            modifier = Modifier.width(200.dp)
        )
        AsyncImage(
            load = { loadXmlImageVector(File("compose-logo.xml"), density) },
            painterFor = { rememberVectorPainter(it) },
            contentDescription = "Compose logo",
            contentScale = ContentScale.FillWidth,
            modifier = Modifier.width(200.dp)
        )
    }
}

@Composable
fun <T> AsyncImage(
    load: suspend () -> T,
    painterFor: @Composable (T) -> Painter,
    contentDescription: String,
    modifier: Modifier = Modifier,
    contentScale: ContentScale = ContentScale.Fit,
) {
    val image: T? by produceState<T?>(null) {
        value = withContext(Dispatchers.IO) {
            try {
                load()
            } catch (e: IOException) {
                // instead of printing to console, you can also write this to log,
                // or show some error placeholder
                e.printStackTrace()
                null
            }
        }
    }

    if (image != null) {
        Image(
            painter = painterFor(image!!),
            contentDescription = contentDescription,
            contentScale = contentScale,
            modifier = modifier
        )
    }
}

/* Loading from file with java.io API */

fun loadImageBitmap(file: File): ImageBitmap =
    file.inputStream().buffered().use(::loadImageBitmap)

fun loadSvgPainter(file: File, density: Density): Painter =
    file.inputStream().buffered().use { loadSvgPainter(it, density) }

fun loadXmlImageVector(file: File, density: Density): ImageVector =
    file.inputStream().buffered().use { loadXmlImageVector(InputSource(it), density) }

/* Loading from network with java.net API */

fun loadImageBitmap(url: String): ImageBitmap =
    URL(url).openStream().buffered().use(::loadImageBitmap)

fun loadSvgPainter(url: String, density: Density): Painter =
    URL(url).openStream().buffered().use { loadSvgPainter(it, density) }

fun loadXmlImageVector(url: String, density: Density): ImageVector =
    URL(url).openStream().buffered().use { loadXmlImageVector(InputSource(it), density) }

/* Loading from network with Ktor client API (https://ktor.io/docs/client.html). */

/*

suspend fun loadImageBitmap(url: String): ImageBitmap =
    urlStream(url).use(::loadImageBitmap)

suspend fun loadSvgPainter(url: String, density: Density): Painter =
    urlStream(url).use { loadSvgPainter(it, density) }

suspend fun loadXmlImageVector(url: String, density: Density): ImageVector =
    urlStream(url).use { loadXmlImageVector(InputSource(it), density) }

@OptIn(KtorExperimentalAPI::class)
private suspend fun urlStream(url: String) = HttpClient(CIO).use {
    ByteArrayInputStream(it.get(url))
}

 */
Garnishee answered 25/11, 2022 at 2:49 Comment(0)
L
0

Using compose-1.1.0, this works for me.

import org.jetbrains.skia.Image
// ...
Image(
    contentDescription = "image",
    bitmap = Image.Companion.makeFromEncoded(imageBytes).toComposeImageBitmap()
)
Loeffler answered 24/6, 2022 at 14:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.