coil-kt / coil

Image loading for Android and Compose Multiplatform.

Home Page:https://coil-kt.github.io/coil/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Load a large image from java.io.File doesn't work anymore in 2.0.0-rc01

sirambd opened this issue · comments

Describe the bug
When loading a large image from storage, with the load data(File) method, it doesn't work anymore knowing that it works well with the io.coil-kt:coil:1.4.0 version, but if you load it from the remote it works

With the same 56MB image on 1.4.0 it works but not on 2.0.0-rc01.

To Reproduce

  • Call ImageRequest.Builder(context).data(File)with java.io.File > 56Mo

This bug happens on all phones and emulators that I could test

Logs/Screenshots

I/RealImageLoader: 🚨 Failed - /storage/emulated/0/Android/media/com.infomaniak.drive/offline_storage/1085402/420132/test/RapidEye_RapidEye_5m_RGB_Altotting_Germany_Agriculture_and_Forestry_2009MAY17_8bits_sub_r_2 copie.jpg - java.lang.IllegalStateException: BitmapFactory returned a null bitmap. Often this means BitmapFactory could not decode the image data read from the input source (e.g. network, disk, or memory) as it's not encoded as a valid image format.

stacktrace

W/System.err: java.lang.IllegalStateException: BitmapFactory returned a null bitmap. Often this means BitmapFactory could not decode the image data read from the input source (e.g. network, disk, or memory) as it's not encoded as a valid image format.
W/System.err:     at coil.decode.BitmapFactoryDecoder.decode(BitmapFactoryDecoder.kt:64)
W/System.err:     at coil.decode.BitmapFactoryDecoder.access$decode(BitmapFactoryDecoder.kt:24)
W/System.err:     at coil.decode.BitmapFactoryDecoder$decode$2$1.invoke(BitmapFactoryDecoder.kt:31)
W/System.err:     at coil.decode.BitmapFactoryDecoder$decode$2$1.invoke(BitmapFactoryDecoder.kt:31)
W/System.err:     at kotlinx.coroutines.InterruptibleKt.runInterruptibleInExpectedContext(Interruptible.kt:46)
W/System.err:     at kotlinx.coroutines.InterruptibleKt.access$runInterruptibleInExpectedContext(Interruptible.kt:1)
W/System.err:     at kotlinx.coroutines.InterruptibleKt$runInterruptible$2.invokeSuspend(Interruptible.kt:38)
W/System.err:     at kotlinx.coroutines.InterruptibleKt$runInterruptible$2.invoke(Unknown Source:8)
W/System.err:     at kotlinx.coroutines.InterruptibleKt$runInterruptible$2.invoke(Unknown Source:4)
W/System.err:     at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:89)
W/System.err:     at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:157)
W/System.err:     at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
W/System.err:     at kotlinx.coroutines.InterruptibleKt.runInterruptible(Interruptible.kt:37)
W/System.err:     at kotlinx.coroutines.InterruptibleKt.runInterruptible$default(Interruptible.kt:34)
W/System.err:     at coil.decode.BitmapFactoryDecoder.decode(BitmapFactoryDecoder.kt:31)
W/System.err:     at coil.intercept.EngineInterceptor.decode(EngineInterceptor.kt:199)
W/System.err:     at coil.intercept.EngineInterceptor.access$decode(EngineInterceptor.kt:41)
W/System.err:     at coil.intercept.EngineInterceptor$execute$executeResult$1.invokeSuspend(EngineInterceptor.kt:127)
W/System.err:     at coil.intercept.EngineInterceptor$execute$executeResult$1.invoke(Unknown Source:8)
W/System.err:     at coil.intercept.EngineInterceptor$execute$executeResult$1.invoke(Unknown Source:4)
W/System.err:     at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:89)
W/System.err:     at kotlinx.coroutines.BuildersKt__Builders_commonKt.withContext(Builders.common.kt:165)
W/System.err:     at kotlinx.coroutines.BuildersKt.withContext(Unknown Source:1)
W/System.err:     at coil.intercept.EngineInterceptor.execute(EngineInterceptor.kt:126)
W/System.err:     at coil.intercept.EngineInterceptor.access$execute(EngineInterceptor.kt:41)
W/System.err:     at coil.intercept.EngineInterceptor$intercept$2.invokeSuspend(EngineInterceptor.kt:75)
W/System.err:     at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
W/System.err:     at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
W/System.err:     at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:39)
W/System.err:     at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
W/System.err:     at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
W/System.err:     at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
W/System.err:     at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
W/System.err:     at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
I/System.out: (HTTPLog)-Static: isSBSettingEnabled false
I/System.out: (HTTPLog)-Static: isSBSettingEnabled false

call

        val timer = createRefreshTimer(milliseconds = 400) { noThumbnailLayout?.isVisible = true }.start()
        val offlineFile = if (file.isOffline) getOfflineFile() else null // a `java.io.File` picture

        return ImageRequest.Builder(requireContext())
            .data(offlineFile ?: file.imagePreview())
            .listener(
                onError = { _, _ ->
                    fileName?.text = file.name
                    previewDescription?.isVisible = true
                    bigOpenWithButton?.isVisible = true
                    imageView?.isGone = true
                },
                onSuccess = { _, _ ->
                    timer.cancel()
                    noThumbnailLayout?.isGone = true
                },
            )
            .build()

imageLoader

    override fun newImageLoader(): ImageLoader {
        return ImageLoader.Builder(applicationContext)
            .crossfade(true)
            .components {
                add(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) ImageDecoderDecoder.Factory() else GifDecoder.Factory())
            }
            .okHttpClient {
                OkHttpClient.Builder().apply {
                    addInterceptor(Interceptor { chain ->
                        var request = chain.request()
                        if (request.url.toString().contains(DRIVE_API) || request.url.toString().contains(INFOMANIAK_API)) {
                            request = request.newBuilder().headers(HttpUtils.getHeaders()).removeHeader("Cache-Control").build()
                        }
                        chain.proceed(request)
                    })
                    addInterceptor(TokenInterceptor(tokenInterceptorListener()))
                    authenticator(TokenAuthenticator(tokenInterceptorListener()))
                    if (com.infomaniak.lib.core.BuildConfig.DEBUG) {
                        addNetworkInterceptor(StethoInterceptor())
                    }
                }.build()
            }
            .memoryCache {
                MemoryCache.Builder(applicationContext)
                    .maxSizePercent(MEMORY_CACHE_PERCENT)
                    .build()
            }
            .diskCache {
                DiskCache.Builder()
                    .directory(applicationContext.cacheDir.resolve("coil_cache"))
                    .maxSizePercent(DISK_CACHE_PERCENT)
                    .build()
            }
            .build()
    }

Version
io.coil-kt:coil:2.0.0-rc01

Thanks for the report. Could you post the failing image in this thread? You can also email me at colin<at>colinwhite.me if you'd like to keep the image private.

@sirambd Thanks for the sample image! I believe this is related to this change in 2.x. Setting a size for the request (e.g. ImageRequest.Builder.size(2000)) will ensure that you don't load the image at 2000x2000 pixels instead of full-size. This image is ~9000x5000, which is too large for Android to decode without resizing (~4000x4000 is the max).

EDIT: In hindsight this change seems unexpected and could cause issues when upgrading. Keep an eye out for 2.0.0-rc02 for a fix.