diff --git a/coil-core/api/android/coil-core.api b/coil-core/api/android/coil-core.api index a2d70df59b..35f7708a16 100644 --- a/coil-core/api/android/coil-core.api +++ b/coil-core/api/android/coil-core.api @@ -272,6 +272,24 @@ public final class coil3/decode/BitmapFactoryDecoder$Factory : coil3/decode/Deco public fun create (Lcoil3/fetch/SourceFetchResult;Lcoil3/request/Options;Lcoil3/ImageLoader;)Lcoil3/decode/Decoder; } +public final class coil3/decode/BlackholeDecoder : coil3/decode/Decoder { + public fun (Lkotlin/jvm/functions/Function0;)V + public fun decode (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class coil3/decode/BlackholeDecoder$Factory : coil3/decode/Decoder$Factory { + public static final field Companion Lcoil3/decode/BlackholeDecoder$Factory$Companion; + public static final field EMPTY_IMAGE Lcoil3/Image; + public fun ()V + public fun (Lkotlin/jvm/functions/Function0;)V + public synthetic fun (Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun create (Lcoil3/fetch/SourceFetchResult;Lcoil3/request/Options;Lcoil3/ImageLoader;)Lcoil3/decode/BlackholeDecoder; + public synthetic fun create (Lcoil3/fetch/SourceFetchResult;Lcoil3/request/Options;Lcoil3/ImageLoader;)Lcoil3/decode/Decoder; +} + +public final class coil3/decode/BlackholeDecoder$Factory$Companion { +} + public final class coil3/decode/ByteBufferMetadata : coil3/decode/ImageSource$Metadata { public fun (Ljava/nio/ByteBuffer;)V public final fun getByteBuffer ()Ljava/nio/ByteBuffer; diff --git a/coil-core/api/coil-core.klib.api b/coil-core/api/coil-core.klib.api index d5e74db6d9..80f5eec9ec 100644 --- a/coil-core/api/coil-core.klib.api +++ b/coil-core/api/coil-core.klib.api @@ -422,6 +422,23 @@ abstract class coil3/PlatformContext { // coil3/PlatformContext|null[0] } } +final class coil3.decode/BlackholeDecoder : coil3.decode/Decoder { // coil3.decode/BlackholeDecoder|null[0] + constructor (kotlin/Function0) // coil3.decode/BlackholeDecoder.|(kotlin.Function0){}[0] + + final suspend fun decode(): coil3.decode/DecodeResult // coil3.decode/BlackholeDecoder.decode|decode(){}[0] + + final class Factory : coil3.decode/Decoder.Factory { // coil3.decode/BlackholeDecoder.Factory|null[0] + constructor (kotlin/Function0 = ...) // coil3.decode/BlackholeDecoder.Factory.|(kotlin.Function0){}[0] + + final fun create(coil3.fetch/SourceFetchResult, coil3.request/Options, coil3/ImageLoader): coil3.decode/BlackholeDecoder // coil3.decode/BlackholeDecoder.Factory.create|create(coil3.fetch.SourceFetchResult;coil3.request.Options;coil3.ImageLoader){}[0] + + final object Companion { // coil3.decode/BlackholeDecoder.Factory.Companion|null[0] + final val EMPTY_IMAGE // coil3.decode/BlackholeDecoder.Factory.Companion.EMPTY_IMAGE|{}EMPTY_IMAGE[0] + final fun (): coil3/Image // coil3.decode/BlackholeDecoder.Factory.Companion.EMPTY_IMAGE.|(){}[0] + } + } +} + final class coil3.decode/DecodeResult { // coil3.decode/DecodeResult|null[0] constructor (coil3/Image, kotlin/Boolean) // coil3.decode/DecodeResult.|(coil3.Image;kotlin.Boolean){}[0] diff --git a/coil-core/api/jvm/coil-core.api b/coil-core/api/jvm/coil-core.api index d625e83079..67da5142cd 100644 --- a/coil-core/api/jvm/coil-core.api +++ b/coil-core/api/jvm/coil-core.api @@ -221,6 +221,24 @@ public abstract interface annotation class coil3/annotation/ExperimentalCoilApi public abstract interface annotation class coil3/annotation/InternalCoilApi : java/lang/annotation/Annotation { } +public final class coil3/decode/BlackholeDecoder : coil3/decode/Decoder { + public fun (Lkotlin/jvm/functions/Function0;)V + public fun decode (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; +} + +public final class coil3/decode/BlackholeDecoder$Factory : coil3/decode/Decoder$Factory { + public static final field Companion Lcoil3/decode/BlackholeDecoder$Factory$Companion; + public static final field EMPTY_IMAGE Lcoil3/Image; + public fun ()V + public fun (Lkotlin/jvm/functions/Function0;)V + public synthetic fun (Lkotlin/jvm/functions/Function0;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun create (Lcoil3/fetch/SourceFetchResult;Lcoil3/request/Options;Lcoil3/ImageLoader;)Lcoil3/decode/BlackholeDecoder; + public synthetic fun create (Lcoil3/fetch/SourceFetchResult;Lcoil3/request/Options;Lcoil3/ImageLoader;)Lcoil3/decode/Decoder; +} + +public final class coil3/decode/BlackholeDecoder$Factory$Companion { +} + public final class coil3/decode/ByteBufferMetadata : coil3/decode/ImageSource$Metadata { public fun (Ljava/nio/ByteBuffer;)V public final fun getByteBuffer ()Ljava/nio/ByteBuffer; diff --git a/coil-core/src/commonMain/kotlin/coil3/decode/BlackholeDecoder.kt b/coil-core/src/commonMain/kotlin/coil3/decode/BlackholeDecoder.kt new file mode 100644 index 0000000000..c49fbecf7c --- /dev/null +++ b/coil-core/src/commonMain/kotlin/coil3/decode/BlackholeDecoder.kt @@ -0,0 +1,50 @@ +package coil3.decode + +import coil3.Canvas +import coil3.Image +import coil3.ImageLoader +import coil3.annotation.ExperimentalCoilApi +import coil3.fetch.SourceFetchResult +import coil3.request.Options +import kotlin.jvm.JvmField + +/** + * A [Decoder] that ignores the [SourceFetchResult] and always returns the [Image] returned by + * [imageFactory]. + * + * This is useful for skipping the decoding step, for instance when you only want to preload to disk + * and do not want to decode the image into memory. + */ +@ExperimentalCoilApi +class BlackholeDecoder( + private val imageFactory: () -> Image, +) : Decoder { + + override suspend fun decode(): DecodeResult { + return DecodeResult( + image = imageFactory(), + isSampled = false, + ) + } + + class Factory( + private val imageFactory: () -> Image = { EMPTY_IMAGE }, + ) : Decoder.Factory { + + override fun create( + result: SourceFetchResult, + options: Options, + imageLoader: ImageLoader, + ) = BlackholeDecoder(imageFactory) + + companion object { + @JvmField val EMPTY_IMAGE = object : Image { + override val size get() = 0L + override val width get() = -1 + override val height get() = -1 + override val shareable get() = true + override fun draw(canvas: Canvas) { /* Draw nothing. */ } + } + } + } +} diff --git a/coil-core/src/commonTest/kotlin/coil3/decode/BlackholeDecoderTest.kt b/coil-core/src/commonTest/kotlin/coil3/decode/BlackholeDecoderTest.kt new file mode 100644 index 0000000000..dc8d54f770 --- /dev/null +++ b/coil-core/src/commonTest/kotlin/coil3/decode/BlackholeDecoderTest.kt @@ -0,0 +1,34 @@ +package coil3.decode + +import coil3.ImageLoader +import coil3.fetch.SourceFetchResult +import coil3.request.Options +import coil3.test.utils.RobolectricTest +import coil3.test.utils.context +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlinx.coroutines.test.runTest +import okio.Buffer +import okio.fakefilesystem.FakeFileSystem + +class BlackholeDecoderTest : RobolectricTest() { + + @Test + fun basic() = runTest { + val decoderFactory = BlackholeDecoder.Factory() + val bufferSize = 1024 + val buffer = Buffer().apply { write(ByteArray(bufferSize)) } + val fetchResult = SourceFetchResult( + source = ImageSource( + source = buffer, + fileSystem = FakeFileSystem(), + ), + mimeType = null, + dataSource = DataSource.MEMORY, + ) + val decoder = decoderFactory.create(fetchResult, Options(context), ImageLoader(context)) + + assertEquals(BlackholeDecoder.Factory.EMPTY_IMAGE, decoder.decode().image) + assertEquals(bufferSize.toLong(), buffer.size) + } +} diff --git a/docs/faq.md b/docs/faq.md index 8129e142f3..a6a51da581 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -8,11 +8,33 @@ Yes! [Read here](java_compatibility.md). ## How do I preload an image? -[Read here](getting_started.md#preloading). +Launch an image request with no target: + +```kotlin +val request = ImageRequest.Builder(context) + .data("https://www.example.com/image.jpg") + .build() +imageLoader.enqueue(request) +``` + +That will preload the image and save it to the disk and memory caches. + +If you only want to preload to the disk cache you can skip decoding and saving to the memory cache like so: + +```kotlin +val request = ImageRequest.Builder(context) + .data("https://www.example.com/image.jpg") + // Disables writing to the memory cache. + .memoryCachePolicy(CachePolicy.DISABLED) + // Skips the decode step so we don't waste time/memory decoding the image into memory. + .decoderFactory(BlackholeDecoder.Factory()) + .build() +imageLoader.enqueue(request) +``` ## How do I enable logging? -Set `logger(DebugLogger())` when [constructing your `ImageLoader`](getting_started.md#image-loaders). +Set `logger(DebugLogger())` when [constructing your `ImageLoader`](getting_started.md#configuring-the-singleton-imageloader). !!! Note `DebugLogger` should only be used in debug builds.