I'm following the tutorial <here>, `load = { loadI...
# compose-desktop
e
I'm following the tutorial here,
load = { loadImageBitmap(File("sample.png")) },
fails because it cant find the image although I placed it under "commonMain/composeResources/drawable", and
build.gradle.kts
does have
implementation(compose.components.resources)
where am I wrong?
I guess I shall use
Painter
?
File
is just for absolute paths, isn't?
a
You’re mixing the old (desktop-only) resources API with the new (multiplatform) resources API.
That tutorial is for the old one
e
how is the new one?
a
Yes
e
how would you change
AsyncImage
?
e
shall it drop
load
and get directly
painter
?
Copy code
@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(<http://Dispatchers.IO|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)
}
a
If you’re showing an image from a local resource, there’s no need to async it
e
website
a
Then that doesn’t have anything do to with the resources API
e
If you’re showing an image from a local resource, there’s no need to async it
but for the sake of knowing how it shall be done?
I can provide a PR to update the tutorial later on
a
How what is done? Loading and showing an image asynchronously?
e
if you have heavy pics and tons of them, it might make sense to load them async
> How what is done? Loading and showing an image asynchronously? yes, from
composeResources
e
how would I go from
ByteArray
to
Painter
?
a
I’m not in front of the computer right now, but look inside loadImageBitmap to see how it does it.
Combine it with BitmapPainter(…)
e
yep, you are right, sorry
Image.makeFromEncoded(inputStream.readAllBytes()).toComposeImageBitmap()
Copy code
fun main() = singleWindowApplication {
    var bytes by remember { mutableStateOf(ByteArray(0)) }
    LaunchedEffect(Unit) { bytes = Res.readBytes("files/sample.png") }
    Column {
        if (sampleBytes.isNotEmpty())
            Image(painter = loadImageBitmap(bytes),
                  contentDescription = "Sample",
                  modifier = Modifier.width(200.dp))
    }
}
fun loadImageBitmap(bytes: ByteArray) = BitmapPainter(org.jetbrains.skia.Image.makeFromEncoded(bytes).toComposeImageBitmap())
> Exception in thread "main" java.lang.IllegalArgumentException: Failed to Image::makeFromEncoded > at org.jetbrains.skia.Image$Companion.makeFromEncoded(Image.kt:137) maybe when you get to your pc and have a moment can try to replicate this
"sample.png" is both in "drawable" and "files"
a
You’re trying to create an image from an empty byte array
e
ok, fixed with
if (bytes.isNotEmpty())
Exception in thread "AWT-EventQueue-0" org.jetbrains.compose.resources.MissingResourceException: Missing resource with path: sample.png
fixed, "files/sample.png"
thanks for the help, Alexander
a
Just set it to
null
initially
e
what,
bytes
?
a
yes
e
but I do need
remeber
, or?
a
Yes
and it’s better to use produceState here, rather than LaunchedEffect
e
Copy code
var sampleBytes by remember { mutableStateOf(null as ByteArray?) }
?
Copy code
var sampleBytes by remember { mutableStateOf(null as ByteArray?) }
produceState(Unit) { sampleBytes = Res.readBytes("files/sample.png") }
Column {
    sampleBytes?.let {
        Image(painter = loadImageBitmap(it),
              contentDescription = "Sample",
              modifier = Modifier.width(200.dp))
    }
it seems working
a
Yes, or more conventionally: var bytes: ByteArray? by remember { mutableStateOf(null) }
👍 1
No, that’s not how you use produceState
e
I'm not familiar with all of this, I can attempt but I'm afraid to consume any more of your time, shall I try?
a
it’s not hard
😬 1
That tutorial even uses image loading as its example
In your case it’ll be something like
Copy code
val painter by produceState<BitmapPainter?>(initialValue = null, filename) {
    val bytes = Res.readBytes(filename)
    value = loadImageBitmap(bytes)
}
e
Copy code
@OptIn(ExperimentalResourceApi::class)
fun main() = singleWindowApplication {
    val density = LocalDensity.current
    val bitmapPainter by produceState<BitmapPainter?>(initialValue = null) {
        value = loadImageBitmap(Res.readBytes("files/sample.png"))
    }
    val imageVector by produceState<ImageVector?>(initialValue = null) {
        value = loadXmlImageVector(Res.readBytes("files/compose-logo.xml"), density)
    }
    Column {
        bitmapPainter?.let {
            Image(painter = it,
                  contentDescription = "Sample",
                  contentScale = ContentScale.Fit,
                  modifier = Modifier.width(200.dp))
        }

        AsyncImage(load = { loadSvgPainter("<https://github.com/JetBrains/compose-multiplatform/raw/master/artwork/idea-logo.svg>", density) },
                   painterFor = { it },
                   contentDescription = "Idea logo",
                   contentScale = ContentScale.FillWidth,
                   modifier = Modifier.width(200.dp))

        imageVector?.let {
            Image(painter = rememberVectorPainter(it),
                  contentDescription = "Compose logo",
                  contentScale = ContentScale.FillWidth,
                  modifier = Modifier.width(200.dp))
        }
    }
}
I had to put
rememberVectorPainter
under
Column
since it's a suspend fun