Max
03/28/2024, 9:41 PMMatthew Feinberg
03/28/2024, 11:15 PMexpect fun ImageBitmap.Companion.fromPlatformFile(fileReference: String) : Result<ImageBitmap>
Here, fileReference
could be a path, URI, whatever you need.
For better type safety, you could encapsulate this in another class like PlatformFile
or something (which could be returned by your contact importer). Used something like...
val contact = getContact() // whatever your expect/actual function is.
val bitmap = ImageBitmap.fromPlatformFile(contact.imageFile).getOrNull()
In my case, what I actually did was to make an expect/actual ImageBitmap extension that makes an image from a ByteArray:
expect fun ImageBitmap.Companion.fromByteArray(data: ByteArray) : ImageBitmap?
// Apple
actual fun ImageBitmap.Companion.fromByteArray(data: ByteArray): ImageBitmap? =
Image.makeFromEncoded(data).toComposeImageBitmap()
// Android
actual fun ImageBitmap.Companion.fromByteArray(data: ByteArray): ImageBitmap? = BitmapFactory.decodeByteArray(data,0,data.size)?.asImageBitmap()
And I have a PlatformFile
class that wraps a platform-dependent reference to a file (URI, path, whatever) and provides an expect/actual .readBytes() : Result<ByteArray>
method.
So you end up with something like:
getContact().imageFile.readBytes()
.map { ImageBitmap.fromByteArray(it) }
.getOrNull()
Matthew Feinberg
03/28/2024, 11:17 PMMichael Paus
03/29/2024, 9:54 AMMatthew Feinberg
03/29/2024, 9:59 AMOkio’s...but on Android, you quite often getclass supports Windows-style (likePath
) and UNIX-style paths (likeC:\autoexec.bat
)./etc/passwd
content://
URIs that need to be read via ContentResolver
....Michael Paus
03/29/2024, 6:09 PMfun getSource(uri: Uri): Source {
val contentResolver = activity.getContentResolver()
val inputStream = contentResolver.openInputStream(uri)
if (inputStream == null) throw FileNotFoundException("Can't open input stream, uri: $uri")
return inputStream.source()
}
I haven’t tested it though.Matthew Feinberg
03/29/2024, 7:51 PMMatthew Feinberg
03/29/2024, 7:53 PMUri
is not multiplatform... so in either case you're still going to need to use a string or have your own encapsulation for a platform type...Michael Paus
03/30/2024, 6:42 AMMax
03/30/2024, 8:01 AMMatthew Feinberg
03/31/2024, 12:40 AMkotlinx.io.files
will have moved beyond the experimental stage. If not, Okio looks like a good backup choice!Matthew Feinberg
03/31/2024, 12:48 AMLaunchedEffect
and <http://Dispatchers.IO|Dispatchers.IO>
, something like:
var contactImageBitmap : ImageBitmap? by remember { mutableStateOf(null) }
contactImageBitmap?.let{ Image( it, "Contact Photo" ) }
LaunchedEffect(contactImageRef) {
contactImageBitmap =
withContext(<http://Dispatchers.IO|Dispatchers.IO>) {contactImageRef.readBytes()}
.map { ImageBitmap.fromByteArray(it) }
.getOrNull()
}
Max
04/14/2024, 10:12 AMData
to a `KotlinByteArray`:
private func dataToKotlinByteArray(data: Data) -> KotlinByteArray {
let swiftByteArray = [UInt8](data)
return swiftByteArray
.map(Int8.init(bitPattern:))
.enumerated()
.reduce(into: KotlinByteArray(size: Int32(swiftByteArray.count))) { result, row in
result.set(index: Int32(row.offset), value: row.element)
}
}
2. In CMP i display the image via:
Image(
bitmap = imageLoader.loadImage(it),
contentDescription = "image",
modifier = Modifier.size(140.dp).clip(RoundedCornerShape(percent = 50))
)
3. Now comes the tricky part, took me a while to find out: You can only access skia’s ByteArray to Image in iOS source set, otherwise you can while compiling “Could not find skia..“:
class IOSImageLoader: ImageLoader {
override fun loadImage(bytes: ByteArray): ImageBitmap {
return Image.makeFromEncoded(bytes).toComposeImageBitmap()
}
}
And … the image is displayed:)