`loadImageBitmap(InputStream)` is deprecated. The ...
# compose-desktop
t
loadImageBitmap(InputStream)
is deprecated. The replacement is the resource API, which is fine for resources, but what if I want to load a user-provided image?
e
consider using a library like Coil or Glide?
t
that might be the way
a black box which displays an equally black image, but giving no error while doing so
Coil, at least
it claims it can't load file: URLs
literally the first type of URL I would have implemented, but OK
e
in preview or on device? it's expected to fail in preview https://developer.android.com/develop/ui/compose/tooling/previews#preview-limitations
m
Sure, you can use an external library for that but why? I store images in a database for example and load them like this:
Copy code
//-- ImageLoader.kt
import androidx.compose.ui.graphics.ImageBitmap

expect fun imageBitmapFromBytes(encodedImageData: ByteArray): ImageBitmap

//-- ImageLoader.skikoCommon.kt
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.toComposeImageBitmap
import org.jetbrains.skia.Image
import org.jetbrains.skia.impl.use

actual fun imageBitmapFromBytes(encodedImageData: ByteArray): ImageBitmap {
    return Image.makeFromEncoded(encodedImageData).use { it.toComposeImageBitmap() } // 'use' closes intermediate image
}

//-- ImageLoader.android.kt
import android.graphics.BitmapFactory
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap

actual fun imageBitmapFromBytes(encodedImageData: ByteArray): ImageBitmap {
    return BitmapFactory.decodeByteArray(encodedImageData, 0, encodedImageData.size).asImageBitmap()
}
You only have to distinguish Android from all other targets. Skiko can do that out of the box and you only need some different code for Android (which is also available out of the box on Android). Now your image bytes can come from anywhere and with a single function in common code you can convert them to a Compose image.
🙌 1
t
somehow I got it working by passing the File itself instead of a URI
but yes I expect to support drag and drop of all sorts of stuff so I will have to get a network handler at some point
skikoCommon is a new source set which parents jvm and something else?
m
You have to define that yourself in the build script.
Copy code
@OptIn(ExperimentalKotlinGradlePluginApi::class)
applyDefaultHierarchyTemplate {
    common {
        group("skikoCommon") {
            withJvm()
            withIos()
            withJs()
            withWasmJs()
        }
    }
}
e
yeah you can use skiko.Image, but if you're starting from an external resource, you'll need to do I/O, which should not be done on the main thread, or the composition thread (we don't have threaded composition yet). so you'll need to dispatch work and put some placeholder there until it's done, and then you've basically done what async image loading libraries do
m
Well, I just remember a painter which gets initialized with a default (placeholder) painter and then swap that out once the real painter is created. What’s the problem? If you are already using Coil for other purposes you can of course use it but I wouldn’t add it to simply load and show an image from some internal data source.
e
OP's
what if I want to load a user-provided image?
doesn't sound like an internal data source
yeah you can
Copy code
var image by remember(file) { mutableStateOf<Image?>(null) }
LaunchedEffect(file) {
    withContext(<http://Dispatchers.IO|Dispatchers.IO>) {
        image = Image.makeFromEncoded(file.readBytes()).use { it.toComposeImageBitmap() }
    }
}
val painter = image?.let { BitmapPainter(it) } ?: placeholder…
or whatever you want to do, but this is a solved problem already
m
Just to clarify. I meant “internal data source” in the sense of something that you do not have to download from the web. A local (user provided) file or something in a local database (e.g. SQLite) like in my case. Otherwise, everybody has to decide on his own how much external code he is willing to pull in to solve a trivial task. If things get more complicated than this, I also use Coil.