bumptech / bumptech/glide

Consider implementing contentScale in Compose without using Transformations

Open
#5,391 0 comments 1 reaction 0 assignees View on GitHub
Dominant language
Java
Stars
35k
Forks
6.2k
Avg merge
1d 11h
Merged PRs (30d)
8

Description

Glide compose version used: `1.0.0-beta01`.

In `GlideImage`, the implementation of `ContentScale` is with a `contentScaleTransform`.

This causes a few issues:

* The same image with different content scales applied will have multiple memory cache entries.
* It is currently not possible to interpolate a content scale change.

Ideally the application of a content scale should not require the creation of separate images. The same image should be properly displayed in the bounds of the container and not have any extra processing applied to it for different content scales.

The bug is best demonstrated visually:

[glide-bug.webm](https://github.com/bumptech/glide/assets/8147224/f97e4318-d178-4be2-b8de-e5863069b9ce)

It is possible to interpolate `ContentScale` as described in this [blog post](https://www.tunjid.com/articles/animating-contentscale-during-image-shared-element-transitions-65fba03537c67f8df0161c31) with the following code:

```kotlin
@Composable
fun ContentScale.interpolate(): ContentScale {
var interpolation by remember {
mutableFloatStateOf(1f)
}
var previousScale by remember {
mutableStateOf(this)
}

val currentScale by remember {
mutableStateOf(this)
}.apply {
if (value != this@interpolate) previousScale = when {
interpolation == 1f -> value
else -> CapturedContentScale(
capturedInterpolation = interpolation,
previousScale = previousScale,
currentScale = value
)
}.also { interpolation = 0f }
value = this@interpolate
}

LaunchedEffect(currentScale) {
animate(
initialValue = 0f,
targetValue = 1f,
animationSpec = spring(
stiffness = 10f

),
block = { progress, _ ->
interpolation = progress
},
)
}

return remember {
object : ContentScale {
override fun computeScaleFactor(
srcSize: Size,
dstSize: Size
): ScaleFactor {
val start = previousScale.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize
)
val stop = currentScale.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize
)

return if (start == stop) stop
else lerp(
start = start,
stop = stop,
fraction = interpolation
)
}
}
}
}

private class CapturedContentScale(
private val capturedInterpolation: Float,
private val previousScale: ContentScale,
private val currentScale: ContentScale,

) : ContentScale {
override fun computeScaleFactor(
srcSize: Size,
dstSize: Size
): ScaleFactor = lerp(
start = previousScale.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize
),
stop = currentScale.computeScaleFactor(
srcSize = srcSize,
dstSize = dstSize
),
fraction = capturedInterpolation
)
}
```

In a comparison with Coil and a manual painter, Glide is unable to actually apply this interpolation. Consider the following:

```kotlin
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
var contentScale by remember { mutableStateOf(ContentScale.Crop) }
ImageComparison(
imageUrl = IMAGE_URL,
contentDescription = null,
center = Alignment.Center,
crop = contentScale.interpolate(),
modifier = Modifier.size(200.dp),
)

val scales = remember {
listOf(
ContentScale.Crop to "Crop",
ContentScale.Fit to "Fit",
ContentScale.None to "None",
ContentScale.FillHeight to "FillHeight",
ContentScale.FillWidth to "FillWidth",
ContentScale.FillBounds to "Fill",
)
}
Row {
scales.forEach { (scale, label) ->
Text(
text = label,
textDecoration = if (contentScale == scale) TextDecoration.Underline else TextDecoration.None,
modifier = Modifier
.padding(8.dp)
.clickable {
contentScale = scale
}
)
}
}
}
```

Where `ImageComparison` is:

```kotlin
@OptIn(ExperimentalGlideComposeApi::class)
@Composable
private fun ImageComparison(
imageUrl: String,
contentDescription: String?,
center: Alignment,
crop: ContentScale,
modifier: Modifier
) {
AsyncImage(
modifier = modifier,
model = imageUrl,
contentDescription = contentDescription,
alignment = center,
contentScale = crop,
)
Text(text = "Coil")

GlideImage(
modifier = modifier,
model = imageUrl,
contentDescription = contentDescription,
alignment = center,
contentScale = crop,
)
Text(text = "Glide")

ImageByUrl(
modifier = modifier,
imageUrl = imageUrl,
contentDescription = contentDescription,
alignment = center,
contentScale = crop,
)
Text(text = "Manual Painter")
}
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.