coil-kt / coil

Image loading for Android and Compose Multiplatform.
https://coil-kt.github.io/coil/
Apache License 2.0
10.84k stars 665 forks source link

Compose Multiplatform support #842

Closed tiwiz closed 1 month ago

tiwiz commented 3 years ago

Is your feature request related to a problem? Please describe. As an Android Developer, I would love to use Coil with Compose Desktop in the same way I use Coil for Compose on Android.

Describe the solution you'd like Ideally, it would be the best to have the same approach we have on Android, so that the knowledge can be reused.

Additional context I think it would be OK to link the image download scope to either a LaunchedEffect in a Composable, or just link it to the Application lifecycle.

colinrtwhite commented 3 years ago

~I agree this would be cool to have. That said, I think this is very very unlikely to be implemented as Coil heavily relies on a number of Android framework classes (Bitmap, BitmapFactory, ImageDecoder) and integrates closely with Views and Lifecycles. Also, Coil depends on a number of AndroidX libraries and OkHttp which aren't (and likely won't) be multiplatform. Overall, it's a lot of work and I don't have plans to work on it. Going to leave this open as a catch-all for multiplatform questions.~

Check here for an update. TLDR: This is now planned!

jershell commented 3 years ago

On compose I wrapped coil and wrote two actual functions. On desktop its look as

package components

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material.CircularProgressIndicator
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale
import com.tradernet.data.source.getHttpClient
import io.ktor.client.request.*
import org.jetbrains.skija.Image
import androidx.compose.foundation.Image as ComposeImage

suspend fun loadPicture(url: String): Result<ImageBitmap> {
    val client = getHttpClient()
    return try {
        val image = client.get<ByteArray>(url)
        Result.success(Image.makeFromEncoded(image).asImageBitmap())
    } catch (e: Exception) {
        Result.failure(e)
    }
}

@Composable
actual fun ExternalImage(url: String, modifier: Modifier, OnFail: @Composable () -> Unit) {
    var isLoading by remember { mutableStateOf(false) }
    var hasFail by remember { mutableStateOf(false) }
    var imageBitmap: ImageBitmap? by remember { mutableStateOf(null) }

    LaunchedEffect(url) {
        isLoading = true
        loadPicture(url)
            .onSuccess {
                imageBitmap = it
            }
            .onFailure {
                hasFail = true
            }
        isLoading = false
    }

    when {
        isLoading -> {
            Column(
                modifier = Modifier.fillMaxSize(),
                horizontalAlignment = Alignment.CenterHorizontally,
                verticalArrangement = Arrangement.Center
            ) {
                CircularProgressIndicator()
            }
        }
        hasFail -> {
            OnFail()
        }
        else -> {
            imageBitmap?.let { bitmap ->

                ComposeImage(
                    bitmap = bitmap,
                    contentDescription = "",
                    contentScale = ContentScale.FillWidth,
                    alpha = DefaultAlpha,
                    colorFilter = null,
                )

            } ?: OnFail()
        }
    }
}

it's a variant I took from https://stackoverflow.com/questions/66002696/how-to-load-image-from-remote-url-in-kotlin-compose-desktop. What's missing is caching mechanism.

apkelly commented 3 years ago

I too would love to see a coil-desktop implementation, even if it was just using JVM calls under the hood, similar to this post https://dev.to/gerardpaligot/sharing-compose-components-between-android-and-desktop-17kg

glureau commented 3 years ago

Thanks @jershell . Just a note for those who will try to reuse your snippet: it was easier for me to use skiko v0.5.3 instead of skija, and then just swap the line in the try catch with:

Result.success(Image.makeFromEncoded(image).toComposeImageBitmap())

Works but yikes for the caching indeed, so +1 for Coil-desktop. Not sure if there is multiplatform alternatives or even work-in-progress on this topic, please share if you know something.

yschimke commented 2 years ago

In case it's relevant to the feasibility - https://github.com/square/okhttp/issues/6963

Libraries like Square Wire (gRPC) are also blocked on a multiplatform OkHttp Call.Factory. I hope in 2022, we get a good suite of KMP libraries for IO, Networking, RPC, Images, so hope this becomes feasible at some point.

DevSrSouza commented 2 years ago

For multiplatform projects there is the Kamel, based on watch I saw of the source code, almost all the source is based on Ktor Client, Skiko and Androidx Compose, this meas that even now that is working on Desktop and don't see how hard would be to make it support on iOS in the future.

egorikftp commented 2 years ago

Hi, since coil 2.0 do you have any plans to implement this feature?

dragossusi commented 2 years ago

Porting the library from okhttp to ktor(which can still use okhttp) will be a great start to make this library multiplatform.

Coroutines, ktor and okio are all multiplatform libraries.

glureau commented 2 years ago

Adding ktor means adding more code in Android apps with no purpose. Either Coil stick to Android and it should not try to move to ktor (and keep a lightweight performant library), or it's decided that Coil wants to go full KMP. I think a lot of Android projects will not move to KMP right now, so Coil is still a very good option as is.

Since Kamel is going multi-platform from start, maybe better to consider adding missing things to Kamel instead of trying to port a lot of Android specific code to KMP?

evant commented 2 years ago

This issue is for compose desktop which works on the jvm, not KMP, which I do think would be an easier ask.

ln-12 commented 2 years ago

I am using this library for now which works great: https://github.com/alialbaali/Kamel/pull/14#issuecomment-1150598198

ursusursus commented 1 year ago

Yes desktop (jvm) version would be great, all of infrastructure as far as coroutines, okhttp etc should be there, so "only" Bitmap & friends would need to be wrapped

Compose has a ImageBitmap , maybe that is a way?

Note that if you want to write a proper multiplatform (mac, win, linux) desktop app, youre option basically is just Electron (VS Code, Spotify, Slack, etc), which is ..not great performance.

I could see a world where compose desktop could be very popular.

GabrielLasso commented 1 year ago

You can use libs like https://github.com/alialbaali/Kamel or https://github.com/ltttttttttttt/load-the-image and use @jershell's idea to wrap this libs with expect/actual.

kazemcodes commented 1 year ago

there is already an image loader that use coil as foundation to load images https://github.com/qdsfdhvh/compose-imageloader

colinrtwhite commented 1 year ago

Hi folks, big update! There are now plans to add Kotlin Multiplatform support to Coil and this will be the main feature of 3.x. I can't offer a timeline, but this is top of mind.

The plan is to leave Coil's public API largely the same (except for Android-specific classes) for an initial alpha then iterate on other public API changes we want to make for 3.x. Some of the main initial changes we'll need to make will be to abstract away usages of Bitmap and Drawable and replace BitmapFactory with a multiplatform solution. Fortunately, Compose Multiplatform should make migrating the coil-compose artifact fairly straightforward. I'm heading on holiday until May 7th, but will follow up with more details afterwards.

mxalbert1996 commented 1 year ago

To be clear, by Kotlin Multiplatform support do you mean support for Compose Multiplatform or some UI framework independent solution (which will be much more complicated I think)? Also what platforms do you plan to support in addition to Android?

eygraber commented 1 year ago

@colinrtwhite have you considered applying for a grant for this - http://kotlinfoundation.org/grants

colinrtwhite commented 1 year ago

@mxalbert1996 The goal is to support Kotlin Multiplatform and Compose Multiplatform with as many targets as possible. The Android target will have the most functionality by supporting lifecycles, views, and other Android concepts. Other targets will be focused on supporting fetching + decoding images with integration with coroutines and less platform-specific support.

I think we can accomplish this by replacing BitmapFactory with skiko, which is the image decoder used by Compose Multiplatform, for non-Android targets. Migrating to Compose Multiplatform should be pretty straightforward once coil-base has been converted to multiplatform. We'll just need to swap AndroidX Compose with Jetbrains Compose.

The biggest challenge I see at the moment is refactoring Context references so ImageLoader and other classes aren't coupled with Context. Currently, we pass a Context object deep into ImageLoader's classes.

glureau commented 1 year ago

Good news! Any idea if skiko will be more or less performant than BitmapFactory on Android?

For the Context I tend to use a ContentProvider that hold a static reference to the application Context, so that it can be used in androidMain code without being declared in commonMain. Do you see any issue with this approach? Is there a limitation with theme / image loading requiring the Activity/fragment context instead?

MohamedRejeb commented 1 year ago

@colinrtwhite Don't you think about making a development branch for working on this feature so we can contribute and make this process faster

colinrtwhite commented 1 year ago

@glureau My guess is it would be slightly slower given BitmapFactory is an Android framework class, which is why we should continue to use BitmapFactory on Android and skiko only on non-Android targets. For Context I'd prefer to avoid content providers and have the user pass a Context when building the ImageLoader.

@MohamedRejeb Will do! I'm going to create a 3.x branch that folks can contribute to once I've handled some of the initial multiplatform migration.

terrakok commented 1 year ago

To use BitmapFactory on Android is the right decision because Skiko is not a lightweight library 😉

sonatard commented 1 year ago

@colinrtwhite Is there a plan to migrate from OkHttp to Ktor for sending HTTP requests to support for Kotlin Multiplatform Mobile?

Or are you planning to use different HTTP clients depending on the environment?

colinrtwhite commented 1 year ago

@sonatard That's still up for discussion. I think it's likely we migrate to Ktor and make it an optional dependency to support https://github.com/coil-kt/coil/issues/1648.

sonatard commented 1 year ago

I'm relieved that Ktor has become the standard.

As many libraries are advancing their support for KMM using Ktor, it's pleasing to be able to reduce dependencies on other libraries like okHttp.

ursusursus commented 1 year ago

Most will driver ktor with okhttp

On Wed, 17 May 2023, 03:49 sonatard, @.***> wrote:

I'm relieved that Ktor has become the standard.

As many libraries are advancing their support for KMM using Ktor, it's pleasing to be able to reduce dependencies on other libraries like okHttp.

— Reply to this email directly, view it on GitHub https://github.com/coil-kt/coil/issues/842#issuecomment-1550570657, or unsubscribe https://github.com/notifications/unsubscribe-auth/AALPBKTY3Y3RPIRNP6EDSODXGQVBZANCNFSM5BOPLKOA . You are receiving this because you commented.Message ID: @.***>

ursusursus commented 1 year ago

Hmm, is it? Stable?

On Sat, 20 May 2023, 16:41 Natan Lifshitz, @.***> wrote:

Why migrate to ktor if okhttp already supports Kotlin Multiplatform?

— Reply to this email directly, view it on GitHub https://github.com/coil-kt/coil/issues/842#issuecomment-1555926634, or unsubscribe https://github.com/notifications/unsubscribe-auth/AALPBKRMNRD4AMGN5XRUFR3XHDJYPANCNFSM5BOPLKOA . You are receiving this because you commented.Message ID: @.***>

lucasmeneghin commented 1 year ago

I'm relieved that Ktor has become the standard.

As many libraries are advancing their support for KMM using Ktor, it's pleasing to be able to reduce dependencies on other libraries like okHttp.

+1 for using ktor

mahramane commented 1 year ago

Hi Please pace yourself

FunkyMuse commented 1 year ago

I've been lurking this thread, just wanted to say congratulations https://kotlinfoundation.org/news/grants-program-winners-23/

colinrtwhite commented 1 year ago

@FunkyMuse Thanks! Huge thanks to the Kotlin team for selecting Coil for the grant. Also thanks to @eygraber for recommending I submit a proposal.

Quick update on multiplatform support: I've been hacking on support in the 3.x branch and it's going fairly well. Core components like DiskCache and MemoryCache have already been migrated, but there's still plenty of work to do to integrate Skiko and refactor out references to Android classes. It looks like by and large the API should remain similar to 2.x.

If folks are interested in helping out, I'd hold off for the moment until I can get it into a compilable state, though feel free to drop suggestions in this thread. I'll post again when it's ready.

aaronbond commented 1 year ago

great work dude :) really looking forward to it.

DatL4g commented 1 year ago

https://bugs.chromium.org/p/skia/issues/detail?id=12251

This probably needs to be addressed / fixed to support SVG on Desktop properly.

yuroyami commented 1 year ago

It's worth mentioning that Compose has a built-in platform-agnosticImageBitmap, which is used as a parameter for the standard Image composable. Coil can make use of the open-source Compose code to see how this ImageBitmap is drawn. Compose API also exposes asImageBitmap extension function to convert a Android Bitmap to a Compose ImageBitmap.

eygraber commented 1 year ago

@colinrtwhite what's the status of the 3.x branch? Is it compilable and/or usable? Have a couple of KMP side projects that are still in the POC stage that I'd like to use Coil with, and they don't need stability anytime soon (honestly don't even need it to really work as long as it compiles and doesn't crash).

colinrtwhite commented 1 year ago

@eygraber Hope to have it publishing snapshots soon-ish. The branch currently doesn't compile, but it's getting close. Tests are still mostly Android-only and will need to be updated. I'll start publishing snapshots as soon as it compiles + can load a basic network image. Will post here as soon as that's ready.

colinrtwhite commented 11 months ago

Hi folks, I just enabled snapshot publishing for Coil 3.0.0 to maven's snapshot repository:

allprojects {
    repositories {
        maven("https://oss.sonatype.org/content/repositories/snapshots")
    }
}

dependencies {
    implementation("io.coil-kt.coil3:coil:3.0.0-SNAPSHOT")
    implementation("io.coil-kt.coil3:coil-network:3.0.0-SNAPSHOT")
}

Documentation is still a WIP at the moment so here are some quick notes:

Will likely cut a proper alpha01 in a few weeks after the snapshots are smoke tested and documentation is added.

shibasis0801 commented 11 months ago

@colinrtwhite I wish to help with the WASM support. What are the pending tasks ? How can I help ?

DRSchlaubi commented 11 months ago

Probably ktor for once

colinrtwhite commented 11 months ago

@shibasis0801 Yep we're waiting on updates from Ktor, kotlinx-immutable-collections, Poko, ~and possibly others~ to support wasm.

Fixing https://github.com/drewhamilton/Poko/issues/235 is up for grabs and would unblock Coil.

EDIT: I think it's just those three. okio-fakefilesystem doesn't have WASM support, but it's only a dependency for Coil's tests.

DRSchlaubi commented 11 months ago

Here a list of issues

nowiwr01w commented 11 months ago

@DRSchlaubi

So we're only waiting for Poko?

FunkyMuse commented 11 months ago

Looks like kotlinx.collections.immutable for K/Wasm was implemented here

there still isn't a release of that PR

colinrtwhite commented 11 months ago

WIP wasmJs support is here, but runs into a compiler issue:

> Task :coil:wasmJsBrowserTest FAILED
Module not found: Error: Can't resolve './skiko.mjs'

If anyone knows how to work-around this feel free to add to that branch.

EDIT: Artifacts are now published with wasmJs support, though Coil's test suite fails to run for wasm.

inveders commented 11 months ago

Hello @colinrtwhite maybe I made a mistake adding the snapshot but after adding the dependencies and the line for maven, I have the good coil3 package displaying, but inside there is no compose folder. So I can't reach AsyncImage and others things as in the sample. Is it normal?

Kamil-H commented 11 months ago

@inveders I think I faced the same problem. I had to change the JetBrains Compose version to 1.6.0-alpha01, and it worked. In fact, I faced another issue:

Could not find org.jetbrains.skiko:skiko:0.7.90

but this one, I fixed by strictly declaring skiko version:

commonMainImplementation("org.jetbrains.skiko:skiko") {
    version {
        strictly("0.7.89.1")
    }
}
colinrtwhite commented 11 months ago

@inveders You need to add a dependency on Coil's Compose artifact:

implementation("io.coil-kt.coil3:coil-compose:3.0.0-SNAPSHOT")
rsktash commented 11 months ago

@colinrtwhite Is there a plan to support svg artifact for multiplatform (android/ios)? I'd like to test coil3 in my projects but loading svg images is very crucial for it

colinrtwhite commented 11 months ago

@rustamsmax No plans yet as I'm focused improving on Coil's core and Compose integration.

I think it's possible to make coil-svg multiplatform using org.jetbrains.skia.svg.SVGDOM on non-Android platforms. I'd be open to a PR that adds support for this!

coil-gif and coil-video are unlikely to be multiplatform as I don't think Skiko provides animated image support or video frame support.

DRSchlaubi commented 11 months ago

@rustamsmax No plans yet as I'm focused improving on Coil's core and Compose integration.

I think it's possible to make coil-svg multiplatform using org.jetbrains.skia.svg.SVGDOM on non-Android platforms. I'd be open to a PR that adds support for this!

coil-gif and coil-video are unlikely to be multiplatform as I don't think Skiko provides animated image support or video frame support.

There is GIF support in this awsome lib so there seems to be a way