Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve internal performance of GlideImage and CoilImage by passing lambda image model #171

Merged
merged 3 commits into from
Sep 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package com.github.skydoves.landscapistdemo.model

import androidx.compose.runtime.Immutable
import java.util.UUID

@Immutable
data class Poster(
Expand All @@ -24,5 +25,6 @@ data class Poster(
val playtime: String,
val description: String,
val image: String?,
val gif: String?
val gif: String?,
val id: String = UUID.randomUUID().toString()
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package com.github.skydoves.landscapistdemo.ui

import android.os.Build.VERSION.SDK_INT
import androidx.compose.animation.Crossfade
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
Expand Down Expand Up @@ -48,29 +47,21 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.palette.graphics.Palette
import coil.ImageLoader
import coil.decode.GifDecoder
import coil.decode.ImageDecoderDecoder
import com.github.skydoves.landscapistdemo.R
import com.github.skydoves.landscapistdemo.model.MockUtil
import com.github.skydoves.landscapistdemo.model.Poster
import com.github.skydoves.landscapistdemo.theme.DisneyComposeTheme
import com.github.skydoves.landscapistdemo.theme.background800
import com.github.skydoves.landscapistdemo.theme.shimmerHighLight
import com.skydoves.landscapist.ImageOptions
import com.skydoves.landscapist.animation.circular.CircularRevealPlugin
import com.skydoves.landscapist.animation.crossfade.CrossfadePlugin
import com.skydoves.landscapist.coil.CoilImage
import com.skydoves.landscapist.components.rememberImageComponent
import com.skydoves.landscapist.fresco.FrescoImage
import com.skydoves.landscapist.glide.GlideImage
import com.skydoves.landscapist.palette.PalettePlugin
import com.skydoves.landscapist.placeholder.shimmer.ShimmerPlugin

@Composable
fun DisneyPosters(
Expand Down Expand Up @@ -103,7 +94,7 @@ fun DisneyPosters(
)
}
}
items(items = posters) { poster ->
items(items = posters, key = { it.id }) { poster ->
PosterItem(poster, vm)
}
}
Expand Down Expand Up @@ -138,9 +129,8 @@ private fun SelectedPoster(
var palette by remember { mutableStateOf<Palette?>(null) }

GlideImage(
imageModel = poster.image,
modifier = Modifier
.aspectRatio(0.8f),
imageModel = { poster.image },
modifier = Modifier.aspectRatio(0.8f),
component = rememberImageComponent {
+CircularRevealPlugin()
+PalettePlugin { palette = it }
Expand Down Expand Up @@ -171,26 +161,8 @@ private fun SelectedPoster(
modifier = Modifier.padding(8.dp)
)

val context = LocalContext.current
val imageLoader = ImageLoader.Builder(context)
.components {
if (SDK_INT >= 28) {
add(ImageDecoderDecoder.Factory())
} else {
add(GifDecoder.Factory())
}
}
.build()

CoilImage(
imageModel = poster.gif,
imageLoader = { imageLoader },
component = rememberImageComponent {
+ShimmerPlugin(
baseColor = background800,
highlightColor = shimmerHighLight
)
},
GlideImage(
imageModel = { poster.gif },
modifier = Modifier
.height(500.dp)
.padding(8.dp)
Expand Down
16 changes: 16 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,19 @@ plugins {
}

apply(from = "${rootDir}/scripts/publish-root.gradle")

subprojects {
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().all {
kotlinOptions.freeCompilerArgs += listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:reportsDestination=" +
project.buildDir.absolutePath + "/compose_metrics"
)
kotlinOptions.freeCompilerArgs += listOf(
"-P",
"plugin:androidx.compose.compiler.plugins.kotlin:metricsDestination=" +
project.buildDir.absolutePath + "/compose_metrics"
)
kotlinOptions.jvmTarget = JavaVersion.VERSION_1_8.toString()
}
}
10 changes: 3 additions & 7 deletions coil/api/coil.api
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
public final class com/skydoves/landscapist/coil/CoilImage {
public static final fun CoilImage (Lcoil/request/ImageRequest;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;Lcom/skydoves/landscapist/components/ImageComponent;Lcom/skydoves/landscapist/ImageOptions;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V
public static final fun CoilImage (Ljava/lang/Object;Landroidx/compose/ui/Modifier;Landroid/content/Context;Landroidx/lifecycle/LifecycleOwner;Lkotlin/jvm/functions/Function2;Lcom/skydoves/landscapist/components/ImageComponent;Lcoil/request/ImageRequest$Listener;Lcom/skydoves/landscapist/ImageOptions;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;III)V
public static final fun CoilImage (Ljava/lang/Object;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;Lcom/skydoves/landscapist/components/ImageComponent;Lkotlin/jvm/functions/Function0;Lcom/skydoves/landscapist/ImageOptions;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;III)V
public static final fun CoilImage (Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;Lcom/skydoves/landscapist/components/ImageComponent;Lcom/skydoves/landscapist/ImageOptions;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;II)V
public static final fun CoilImage (Lkotlin/jvm/functions/Function0;Landroidx/compose/ui/Modifier;Lkotlin/jvm/functions/Function2;Lcom/skydoves/landscapist/components/ImageComponent;Lkotlin/jvm/functions/Function0;Lcom/skydoves/landscapist/ImageOptions;Lkotlin/jvm/functions/Function1;ILkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Lkotlin/jvm/functions/Function4;Landroidx/compose/runtime/Composer;III)V
}

public abstract class com/skydoves/landscapist/coil/CoilImageState : com/skydoves/landscapist/ImageState {
public static final field $stable I
}

public final class com/skydoves/landscapist/coil/CoilImageState$Failure : com/skydoves/landscapist/coil/CoilImageState {
public static final field $stable I
public fun <init> (Landroid/graphics/drawable/Drawable;Ljava/lang/Throwable;)V
public final fun component1 ()Landroid/graphics/drawable/Drawable;
public final fun component2 ()Ljava/lang/Throwable;
Expand All @@ -22,17 +21,14 @@ public final class com/skydoves/landscapist/coil/CoilImageState$Failure : com/sk
}

public final class com/skydoves/landscapist/coil/CoilImageState$Loading : com/skydoves/landscapist/coil/CoilImageState {
public static final field $stable I
public static final field INSTANCE Lcom/skydoves/landscapist/coil/CoilImageState$Loading;
}

public final class com/skydoves/landscapist/coil/CoilImageState$None : com/skydoves/landscapist/coil/CoilImageState {
public static final field $stable I
public static final field INSTANCE Lcom/skydoves/landscapist/coil/CoilImageState$None;
}

public final class com/skydoves/landscapist/coil/CoilImageState$Success : com/skydoves/landscapist/coil/CoilImageState {
public static final field $stable I
public fun <init> (Landroid/graphics/drawable/Drawable;Lcom/skydoves/landscapist/DataSource;)V
public final fun component1 ()Landroid/graphics/drawable/Drawable;
public final fun component2 ()Lcom/skydoves/landscapist/DataSource;
Expand Down
112 changes: 88 additions & 24 deletions coil/src/main/kotlin/com/skydoves/landscapist/coil/CoilImage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

package com.skydoves.landscapist.coil

import android.content.Context
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.net.Uri
Expand All @@ -40,7 +39,6 @@ import androidx.compose.ui.platform.LocalInspectionMode
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.painterResource
import androidx.core.graphics.drawable.toBitmap
import androidx.lifecycle.LifecycleOwner
import coil.ImageLoader
import coil.request.ImageRequest
import coil.request.ImageResult
Expand All @@ -49,6 +47,7 @@ import com.skydoves.landscapist.ImageLoad
import com.skydoves.landscapist.ImageLoadState
import com.skydoves.landscapist.ImageOptions
import com.skydoves.landscapist.LandscapistImage
import com.skydoves.landscapist.StableHolder
import com.skydoves.landscapist.components.ComposeFailureStatePlugins
import com.skydoves.landscapist.components.ComposeLoadingStatePlugins
import com.skydoves.landscapist.components.ComposeSuccessStatePlugins
Expand All @@ -63,6 +62,71 @@ import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.flow.channelFlow
import okhttp3.HttpUrl

/**
* Load and render an image with the given [imageModel] from the network or local storage.
*
*
* @param imageModel The data model to request image. See [ImageRequest.Builder.data] for types allowed.
* @param modifier [Modifier] used to adjust the layout or drawing content.
* @param imageLoader The [ImageLoader] to use when requesting the image.
* @param component An image component that conjuncts pluggable [ImagePlugin]s.
* @param requestListener A class for monitoring the status of a request while images load.
* @param imageOptions Represents parameters to load generic [Image] Composable.
* @param onImageStateChanged An image state change listener will be triggered whenever the image state is changed.
* @param previewPlaceholder Drawable resource ID which will be displayed when this function is ran in preview mode.
* @param loading Content to be displayed when the request is in progress.
* @param success Content to be displayed when the request is succeeded.
* @param failure Content to be displayed when the request is failed.
*/
@Composable
@Deprecated(
message = "Use CoilImage(imageModel = { imageModel }..) " +
"for improving recomposition performance.",
replaceWith = ReplaceWith(
"" +
"CoilImage(\n" +
" imageModel = { imageModel },\n" +
" modifier = modifier,\n" +
" imageLoader = imageLoader,\n" +
" component = component,\n" +
" requestListener = requestListener,\n" +
" imageOptions = imageOptions,\n" +
" onImageStateChanged = onImageStateChanged,\n" +
" previewPlaceholder = previewPlaceholder,\n" +
" loading = loading,\n" +
" success = success,\n" +
" failure = failure\n" +
" )"
)
)
public fun CoilImage(
imageModel: Any?,
modifier: Modifier = Modifier,
imageLoader: @Composable () -> ImageLoader = { LocalCoilProvider.getCoilImageLoader() },
component: ImageComponent = rememberImageComponent {},
requestListener: (() -> ImageRequest.Listener)? = null,
imageOptions: ImageOptions = ImageOptions(),
onImageStateChanged: (CoilImageState) -> Unit = {},
@DrawableRes previewPlaceholder: Int = 0,
loading: @Composable (BoxScope.(imageState: CoilImageState.Loading) -> Unit)? = null,
success: @Composable (BoxScope.(imageState: CoilImageState.Success) -> Unit)? = null,
failure: @Composable (BoxScope.(imageState: CoilImageState.Failure) -> Unit)? = null
) {
CoilImage(
imageModel = { imageModel },
modifier = modifier,
imageLoader = imageLoader,
component = component,
requestListener = requestListener,
imageOptions = imageOptions,
onImageStateChanged = onImageStateChanged,
previewPlaceholder = previewPlaceholder,
loading = loading,
success = success,
failure = failure
)
}

/**
* Load and render an image with the given [imageModel] from the network or local storage.
*
Expand All @@ -71,7 +135,7 @@ import okhttp3.HttpUrl
*
* ```
* CoilImage(
* imageModel = imageModel,
* imageModel = { imageModel },
* modifier = modifier,
* imageOptions = ImageOptions(contentScale = ContentScale.Crop),
* loading = {
Expand All @@ -88,8 +152,6 @@ import okhttp3.HttpUrl
*
* @param imageModel The data model to request image. See [ImageRequest.Builder.data] for types allowed.
* @param modifier [Modifier] used to adjust the layout or drawing content.
* @param context The context for creating the [ImageRequest.Builder].
* @param lifecycleOwner The [LifecycleOwner] for constructing the [ImageRequest.Builder].
* @param imageLoader The [ImageLoader] to use when requesting the image.
* @param component An image component that conjuncts pluggable [ImagePlugin]s.
* @param requestListener A class for monitoring the status of a request while images load.
Expand All @@ -102,26 +164,28 @@ import okhttp3.HttpUrl
*/
@Composable
public fun CoilImage(
imageModel: Any?,
imageModel: () -> Any?,
modifier: Modifier = Modifier,
context: Context = LocalContext.current,
lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
imageLoader: @Composable () -> ImageLoader = { LocalCoilProvider.getCoilImageLoader() },
component: ImageComponent = rememberImageComponent {},
requestListener: ImageRequest.Listener? = null,
requestListener: (() -> ImageRequest.Listener)? = null,
imageOptions: ImageOptions = ImageOptions(),
onImageStateChanged: (CoilImageState) -> Unit = {},
@DrawableRes previewPlaceholder: Int = 0,
loading: @Composable (BoxScope.(imageState: CoilImageState.Loading) -> Unit)? = null,
success: @Composable (BoxScope.(imageState: CoilImageState.Success) -> Unit)? = null,
failure: @Composable (BoxScope.(imageState: CoilImageState.Failure) -> Unit)? = null
) {
val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current
CoilImage(
imageRequest = ImageRequest.Builder(context)
.data(imageModel)
.listener(requestListener)
.lifecycle(lifecycleOwner)
.build(),
imageRequest = {
ImageRequest.Builder(context)
.data(imageModel.invoke())
.listener(requestListener?.invoke())
.lifecycle(lifecycleOwner)
.build()
},
imageLoader = imageLoader,
component = component,
modifier = modifier,
Expand Down Expand Up @@ -169,7 +233,7 @@ public fun CoilImage(
*/
@Composable
public fun CoilImage(
imageRequest: ImageRequest,
imageRequest: () -> ImageRequest,
modifier: Modifier = Modifier,
imageLoader: @Composable () -> ImageLoader = { LocalCoilProvider.getCoilImageLoader() },
component: ImageComponent = rememberImageComponent {},
Expand Down Expand Up @@ -200,8 +264,8 @@ public fun CoilImage(
}

CoilImage(
recomposeKey = imageRequest,
imageLoader = imageLoader.invoke(),
recomposeKey = StableHolder(imageRequest.invoke()),
imageLoader = StableHolder(imageLoader.invoke()),
modifier = modifier
) ImageRequest@{ imageState ->
when (val coilImageState = imageState.toCoilImageState().apply { internalState = this }) {
Expand All @@ -224,7 +288,7 @@ public fun CoilImage(
is CoilImageState.Success -> {
component.ComposeSuccessStatePlugins(
modifier = modifier,
imageModel = imageRequest.data,
imageModel = imageRequest.invoke().data,
imageOptions = imageOptions,
imageBitmap = coilImageState.drawable?.toBitmap()
?.copy(Bitmap.Config.ARGB_8888, true)?.asImageBitmap()
Expand All @@ -251,7 +315,7 @@ public fun CoilImage(
*
* ```
* CoilImage(
* imageRequest = ImageRequest.Builder(context)
* recomposeKey = ImageRequest.Builder(context)
* .data(imageModel)
* .lifecycle(lifecycleOwner)
* .build(),
Expand All @@ -273,22 +337,22 @@ public fun CoilImage(
*/
@Composable
private fun CoilImage(
recomposeKey: ImageRequest,
recomposeKey: StableHolder<ImageRequest>,
modifier: Modifier = Modifier,
imageLoader: ImageLoader = LocalCoilProvider.getCoilImageLoader(),
imageLoader: StableHolder<ImageLoader> = StableHolder(LocalCoilProvider.getCoilImageLoader()),
content: @Composable BoxScope.(imageState: ImageLoadState) -> Unit
) {
val context = LocalContext.current

ImageLoad(
recomposeKey = recomposeKey,
recomposeKey = recomposeKey.value,
executeImageRequest = {
channelFlow {
recomposeKey.newBuilder(context).target(
recomposeKey.value.newBuilder(context).target(
onStart = { trySendBlocking(ImageLoadState.Loading) }
).build()

val result = imageLoader.execute(recomposeKey).toResult()
val result = imageLoader.value.execute(recomposeKey.value).toResult()
send(result)
}
},
Expand Down
Loading