From 20de745bcdbd1c3b2353e4f9b957164adbd5a50f Mon Sep 17 00:00:00 2001 From: Colin White Date: Thu, 23 Jan 2025 21:07:42 -0800 Subject: [PATCH] Move several internal common Compose functions into utils. Minor cleanup. --- .../compose/AsyncImagePainter.android.kt | 11 +-- .../coil3/compose/internal/utils.android.kt | 9 +++ .../kotlin/coil3/compose/AsyncImage.kt | 47 +++++------- .../kotlin/coil3/compose/AsyncImagePainter.kt | 37 +--------- .../coil3/compose/ConstraintsSizeResolver.kt | 3 +- .../kotlin/coil3/compose/CrossfadePainter.kt | 3 +- .../coil3/compose/SubcomposeAsyncImage.kt | 22 +++--- .../internal/ContentPainterModifier.kt | 74 ++++++++++--------- .../kotlin/coil3/compose/internal/utils.kt | 43 +++++++++++ .../compose/AsyncImagePainter.nonAndroid.kt | 5 -- .../compose/internal/utils.nonAndroid.kt | 7 ++ 11 files changed, 137 insertions(+), 124 deletions(-) create mode 100644 coil-compose-core/src/androidMain/kotlin/coil3/compose/internal/utils.android.kt create mode 100644 coil-compose-core/src/nonAndroidMain/kotlin/coil3/compose/internal/utils.nonAndroid.kt diff --git a/coil-compose-core/src/androidMain/kotlin/coil3/compose/AsyncImagePainter.android.kt b/coil-compose-core/src/androidMain/kotlin/coil3/compose/AsyncImagePainter.android.kt index df0734cb80..6c41ed9bf7 100644 --- a/coil-compose-core/src/androidMain/kotlin/coil3/compose/AsyncImagePainter.android.kt +++ b/coil-compose-core/src/androidMain/kotlin/coil3/compose/AsyncImagePainter.android.kt @@ -2,19 +2,12 @@ package coil3.compose import android.graphics.drawable.Drawable import androidx.compose.ui.layout.ContentScale -import coil3.request.ImageRequest import coil3.request.SuccessResult -import coil3.request.lifecycle import coil3.request.transitionFactory import coil3.transition.CrossfadeTransition import coil3.transition.TransitionTarget import kotlin.time.Duration.Companion.milliseconds -internal actual fun validateRequestProperties(request: ImageRequest) { - require(request.target == null) { "request.target must be null." } - require(request.lifecycle == null) { "request.lifecycle must be null." } -} - internal actual fun maybeNewCrossfadePainter( previous: AsyncImagePainter.State, current: AsyncImagePainter.State, @@ -29,7 +22,7 @@ internal actual fun maybeNewCrossfadePainter( // Invoke the transition factory and wrap the painter in a `CrossfadePainter` if it returns // a `CrossfadeTransformation`. - val transition = result.request.transitionFactory.create(fakeTransitionTarget, result) + val transition = result.request.transitionFactory.create(FakeTransitionTarget, result) if (transition is CrossfadeTransition) { return CrossfadePainter( start = previous.painter.takeIf { previous is AsyncImagePainter.State.Loading }, @@ -44,7 +37,7 @@ internal actual fun maybeNewCrossfadePainter( } } -private val fakeTransitionTarget = object : TransitionTarget { +private val FakeTransitionTarget = object : TransitionTarget { override val view get() = throw UnsupportedOperationException() override val drawable: Drawable? get() = null } diff --git a/coil-compose-core/src/androidMain/kotlin/coil3/compose/internal/utils.android.kt b/coil-compose-core/src/androidMain/kotlin/coil3/compose/internal/utils.android.kt new file mode 100644 index 0000000000..7f911fb41c --- /dev/null +++ b/coil-compose-core/src/androidMain/kotlin/coil3/compose/internal/utils.android.kt @@ -0,0 +1,9 @@ +package coil3.compose.internal + +import coil3.request.ImageRequest +import coil3.request.lifecycle + +internal actual fun validateRequestProperties(request: ImageRequest) { + require(request.target == null) { "request.target must be null." } + require(request.lifecycle == null) { "request.lifecycle must be null." } +} diff --git a/coil-compose-core/src/commonMain/kotlin/coil3/compose/AsyncImage.kt b/coil-compose-core/src/commonMain/kotlin/coil3/compose/AsyncImage.kt index 4ec03533ed..bd4bbc0448 100644 --- a/coil-compose-core/src/commonMain/kotlin/coil3/compose/AsyncImage.kt +++ b/coil-compose-core/src/commonMain/kotlin/coil3/compose/AsyncImage.kt @@ -11,15 +11,17 @@ import androidx.compose.ui.graphics.drawscope.DrawScope.Companion.DefaultFilterQ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.Layout -import androidx.compose.ui.layout.MeasurePolicy import coil3.ImageLoader import coil3.compose.AsyncImagePainter.Companion.DefaultTransform import coil3.compose.AsyncImagePainter.State import coil3.compose.internal.AsyncImageState import coil3.compose.internal.ContentPainterElement +import coil3.compose.internal.UseMinConstraintsMeasurePolicy import coil3.compose.internal.onStateOf +import coil3.compose.internal.previewHandler import coil3.compose.internal.requestOfWithSizeResolver import coil3.compose.internal.transformOf +import coil3.compose.internal.validateRequest import coil3.request.ImageRequest /** @@ -157,32 +159,23 @@ private fun AsyncImage( validateRequest(request) Layout( - modifier = modifier - .then( - ContentPainterElement( - request = request, - imageLoader = state.imageLoader, - modelEqualityDelegate = state.modelEqualityDelegate, - transform = transform, - onState = onState, - contentScale = contentScale, - filterQuality = filterQuality, - alignment = alignment, - alpha = alpha, - colorFilter = colorFilter, - clipToBounds = clipToBounds, - previewHandler = previewHandler(), - contentDescription = contentDescription, - ), + modifier = modifier.then( + ContentPainterElement( + request = request, + imageLoader = state.imageLoader, + modelEqualityDelegate = state.modelEqualityDelegate, + transform = transform, + onState = onState, + contentScale = contentScale, + filterQuality = filterQuality, + alignment = alignment, + alpha = alpha, + colorFilter = colorFilter, + clipToBounds = clipToBounds, + previewHandler = previewHandler(), + contentDescription = contentDescription, ), - measurePolicy = UseMinConstraintsMeasurePolicy + ), + measurePolicy = UseMinConstraintsMeasurePolicy, ) } - -// Saving it into a field allows us to -// - not allocate it again for each usage of AsyncImage -// - have the same object when the AsyncImage is reused, which allows us to skip unnecessary -// remeasure as the policy didn't change -internal val UseMinConstraintsMeasurePolicy = MeasurePolicy { _, constraints -> - layout(constraints.minWidth, constraints.minHeight) {} -} diff --git a/coil-compose-core/src/commonMain/kotlin/coil3/compose/AsyncImagePainter.kt b/coil-compose-core/src/commonMain/kotlin/coil3/compose/AsyncImagePainter.kt index 6df7200727..fc56a42ceb 100644 --- a/coil-compose-core/src/commonMain/kotlin/coil3/compose/AsyncImagePainter.kt +++ b/coil-compose-core/src/commonMain/kotlin/coil3/compose/AsyncImagePainter.kt @@ -3,7 +3,6 @@ package coil3.compose import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.runtime.NonRestartableComposable -import androidx.compose.runtime.ReadOnlyComposable import androidx.compose.runtime.RememberObserver import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue @@ -15,13 +14,10 @@ import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.DefaultAlpha import androidx.compose.ui.graphics.FilterQuality -import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.drawscope.DrawScope import androidx.compose.ui.graphics.drawscope.DrawScope.Companion.DefaultFilterQuality import androidx.compose.ui.graphics.painter.Painter -import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale -import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.util.trace import coil3.Image import coil3.ImageLoader @@ -33,9 +29,11 @@ import coil3.compose.internal.AsyncImageState import coil3.compose.internal.DeferredDispatchCoroutineScope import coil3.compose.internal.launchUndispatched import coil3.compose.internal.onStateOf +import coil3.compose.internal.previewHandler import coil3.compose.internal.requestOf import coil3.compose.internal.toScale import coil3.compose.internal.transformOf +import coil3.compose.internal.validateRequest import coil3.request.ErrorResult import coil3.request.ImageRequest import coil3.request.ImageResult @@ -389,37 +387,6 @@ class AsyncImagePainter internal constructor( } } -@ReadOnlyComposable -@Composable -internal fun previewHandler(): AsyncImagePreviewHandler? { - return if (LocalInspectionMode.current) { - LocalAsyncImagePreviewHandler.current - } else { - null - } -} - -internal fun validateRequest(request: ImageRequest) { - when (request.data) { - is ImageRequest.Builder -> unsupportedData( - name = "ImageRequest.Builder", - description = "Did you forget to call ImageRequest.Builder.build()?", - ) - is ImageBitmap -> unsupportedData("ImageBitmap") - is ImageVector -> unsupportedData("ImageVector") - is Painter -> unsupportedData("Painter") - } - validateRequestProperties(request) -} - -private fun unsupportedData( - name: String, - description: String = "If you wish to display this $name, use androidx.compose.foundation.Image.", -): Nothing = throw IllegalArgumentException("Unsupported type: $name. $description") - -/** Validate platform-specific properties of an [ImageRequest]. */ -internal expect fun validateRequestProperties(request: ImageRequest) - /** Create and return a [CrossfadePainter] if requested. */ internal expect fun maybeNewCrossfadePainter( previous: State, diff --git a/coil-compose-core/src/commonMain/kotlin/coil3/compose/ConstraintsSizeResolver.kt b/coil-compose-core/src/commonMain/kotlin/coil3/compose/ConstraintsSizeResolver.kt index ed9130b7cb..8e45acf14b 100644 --- a/coil-compose-core/src/commonMain/kotlin/coil3/compose/ConstraintsSizeResolver.kt +++ b/coil-compose-core/src/commonMain/kotlin/coil3/compose/ConstraintsSizeResolver.kt @@ -8,6 +8,7 @@ import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasureResult import androidx.compose.ui.layout.MeasureScope import androidx.compose.ui.unit.Constraints +import coil3.compose.internal.ZeroConstraints import coil3.compose.internal.toSize import coil3.size.Size import coil3.size.SizeResolver @@ -29,7 +30,7 @@ fun rememberConstraintsSizeResolver(): ConstraintsSizeResolver { */ @Stable class ConstraintsSizeResolver : SizeResolver, LayoutModifier { - private var latestConstraints: Constraints = Constraints(maxWidth = 0, maxHeight = 0) + private var latestConstraints = ZeroConstraints private var continuation: Continuation? = null override suspend fun size(): Size { diff --git a/coil-compose-core/src/commonMain/kotlin/coil3/compose/CrossfadePainter.kt b/coil-compose-core/src/commonMain/kotlin/coil3/compose/CrossfadePainter.kt index 572b322857..f53efdb6d7 100644 --- a/coil-compose-core/src/commonMain/kotlin/coil3/compose/CrossfadePainter.kt +++ b/coil-compose-core/src/commonMain/kotlin/coil3/compose/CrossfadePainter.kt @@ -57,7 +57,8 @@ class CrossfadePainter( var start: Painter? = start private set - override val intrinsicSize get() = computeIntrinsicSize() + override val intrinsicSize: Size + get() = computeIntrinsicSize() override fun DrawScope.onDraw() { if (isDone) { diff --git a/coil-compose-core/src/commonMain/kotlin/coil3/compose/SubcomposeAsyncImage.kt b/coil-compose-core/src/commonMain/kotlin/coil3/compose/SubcomposeAsyncImage.kt index ad11416eac..41e00c7bc7 100644 --- a/coil-compose-core/src/commonMain/kotlin/coil3/compose/SubcomposeAsyncImage.kt +++ b/coil-compose-core/src/commonMain/kotlin/coil3/compose/SubcomposeAsyncImage.kt @@ -25,6 +25,7 @@ import coil3.compose.AsyncImagePainter.Companion.DefaultTransform import coil3.compose.AsyncImagePainter.State import coil3.compose.internal.AsyncImageState import coil3.compose.internal.SubcomposeContentPainterElement +import coil3.compose.internal.UseMinConstraintsMeasurePolicy import coil3.compose.internal.onStateOf import coil3.compose.internal.requestOfWithSizeResolver import coil3.request.ImageRequest @@ -275,18 +276,17 @@ fun SubcomposeAsyncImageScope.SubcomposeAsyncImageContent( colorFilter: ColorFilter? = this.colorFilter, clipToBounds: Boolean = this.clipToBounds, ) = Layout( - modifier = modifier - .then( - SubcomposeContentPainterElement( - painter = painter, - alignment = alignment, - contentScale = contentScale, - alpha = alpha, - colorFilter = colorFilter, - clipToBounds = clipToBounds, - contentDescription = contentDescription, - ), + modifier = modifier.then( + SubcomposeContentPainterElement( + painter = painter, + alignment = alignment, + contentScale = contentScale, + alpha = alpha, + colorFilter = colorFilter, + clipToBounds = clipToBounds, + contentDescription = contentDescription, ), + ), measurePolicy = UseMinConstraintsMeasurePolicy, ) diff --git a/coil-compose-core/src/commonMain/kotlin/coil3/compose/internal/ContentPainterModifier.kt b/coil-compose-core/src/commonMain/kotlin/coil3/compose/internal/ContentPainterModifier.kt index e7748e7843..b5086ca77f 100644 --- a/coil-compose-core/src/commonMain/kotlin/coil3/compose/internal/ContentPainterModifier.kt +++ b/coil-compose-core/src/commonMain/kotlin/coil3/compose/internal/ContentPainterModifier.kt @@ -41,6 +41,8 @@ import coil3.compose.AsyncImagePainter.Input import coil3.compose.AsyncImagePainter.State import coil3.compose.AsyncImagePreviewHandler import coil3.compose.ConstraintsSizeResolver +import coil3.compose.SubcomposeAsyncImage +import coil3.compose.SubcomposeAsyncImageContent import coil3.request.ImageRequest import kotlin.math.max import kotlin.math.roundToInt @@ -66,11 +68,9 @@ internal data class ContentPainterElement( override fun create(): ContentPainterNode { val input = Input(imageLoader, request, modelEqualityDelegate) - val constraintSizeResolver = request.sizeResolver as? ConstraintsSizeResolver - // we are creating painter during the modifier creation. as a result we reuse the same - // painter object when the modifier is being reused as part of lazy layouts reuse flow. - // it allows us to save on quite a lot of allocations during the reuse. + // Create the painter during modifier creation so we reuse the same painter object when the + // modifier is being reused as part of the lazy layouts reuse flow. val painter = AsyncImagePainter(input) painter.transform = transform painter.onState = onState @@ -81,7 +81,7 @@ internal data class ContentPainterElement( return ContentPainterNode( painter = painter, - constraintSizeResolver = constraintSizeResolver, + constraintSizeResolver = request.sizeResolver as? ConstraintsSizeResolver, alignment = alignment, contentScale = contentScale, alpha = alpha, @@ -157,13 +157,13 @@ internal class ContentPainterNode( contentDescription: String?, constraintSizeResolver: ConstraintsSizeResolver?, ) : AbstractContentPainterNode( - alignment, - contentScale, - alpha, - colorFilter, - clipToBounds, - contentDescription, - constraintSizeResolver, + alignment = alignment, + contentScale = contentScale, + alpha = alpha, + colorFilter = colorFilter, + clipToBounds = clipToBounds, + contentDescription = contentDescription, + constraintSizeResolver = constraintSizeResolver, ) { override fun onAttach() { @@ -176,10 +176,9 @@ internal class ContentPainterNode( } override fun onReset() { - // we reset the current input as once the modifier will be reused we will have a new - // modifier element update call, which is going to provide us a new up-to-date input. - // without doing so we might restart the request for the old input, as onAttach() is - // called before modifier element update. + // Clear the current input here as `ModifierNodeElement.update` will be called with the + // new input when it's reused. If we don't clear it here, we might restart the request for + // the old input, as `Modifier.Node.onAttach()` is called before modifier element update. painter._input = null } } @@ -187,9 +186,9 @@ internal class ContentPainterNode( /** * A custom [paint] modifier used by [SubcomposeAsyncImage]. * - * Ideally [SubcomposeAsyncImage] should use [ContentPainterElement] as well, however, - * [SubcomposeAsyncImageContent] exposing the fact we have to create a Painter during the - * composition as part of its api. + * Ideally [SubcomposeAsyncImage] should use [ContentPainterElement] as well, however + * [SubcomposeAsyncImageContent] exposes the fact that we have to create a painter during the + * composition as part of its API. */ internal data class SubcomposeContentPainterElement( private val painter: Painter, @@ -258,12 +257,13 @@ internal class SubcomposeContentPainterNode( clipToBounds: Boolean, contentDescription: String?, ) : AbstractContentPainterNode( - alignment, - contentScale, - alpha, - colorFilter, - clipToBounds, - contentDescription, + alignment = alignment, + contentScale = contentScale, + alpha = alpha, + colorFilter = colorFilter, + clipToBounds = clipToBounds, + contentDescription = contentDescription, + constraintSizeResolver = null, ) internal abstract class AbstractContentPainterNode( @@ -273,7 +273,7 @@ internal abstract class AbstractContentPainterNode( var colorFilter: ColorFilter?, var clipToBounds: Boolean, var contentDescription: String?, - var constraintSizeResolver: ConstraintsSizeResolver? = null, + var constraintSizeResolver: ConstraintsSizeResolver?, ) : Modifier.Node(), DrawModifierNode, LayoutModifierNode, SemanticsModifierNode { abstract val painter: Painter @@ -285,6 +285,7 @@ internal abstract class AbstractContentPainterNode( constraints: Constraints, ): MeasureResult { constraintSizeResolver?.setConstraints(constraints) + val placeable = measurable.measure(modifyConstraints(constraints)) return layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) @@ -297,10 +298,11 @@ internal abstract class AbstractContentPainterNode( ): Int { val constraints = Constraints(maxHeight = height) constraintSizeResolver?.setConstraints(constraints) + return if (painter.intrinsicSize.isSpecified) { - val constraints = modifyConstraints(constraints) + val modifiedConstraints = modifyConstraints(constraints) val layoutWidth = measurable.minIntrinsicWidth(height) - max(constraints.minWidth, layoutWidth) + max(modifiedConstraints.minWidth, layoutWidth) } else { measurable.minIntrinsicWidth(height) } @@ -312,10 +314,11 @@ internal abstract class AbstractContentPainterNode( ): Int { val constraints = Constraints(maxHeight = height) constraintSizeResolver?.setConstraints(constraints) + return if (painter.intrinsicSize.isSpecified) { - val constraints = modifyConstraints(constraints) + val modifiedConstraints = modifyConstraints(constraints) val layoutWidth = measurable.maxIntrinsicWidth(height) - max(constraints.minWidth, layoutWidth) + max(modifiedConstraints.minWidth, layoutWidth) } else { measurable.maxIntrinsicWidth(height) } @@ -327,10 +330,11 @@ internal abstract class AbstractContentPainterNode( ): Int { val constraints = Constraints(maxWidth = width) constraintSizeResolver?.setConstraints(constraints) + return if (painter.intrinsicSize.isSpecified) { - val constraints = modifyConstraints(constraints) + val modifiedConstraints = modifyConstraints(constraints) val layoutHeight = measurable.minIntrinsicHeight(width) - max(constraints.minHeight, layoutHeight) + max(modifiedConstraints.minHeight, layoutHeight) } else { measurable.minIntrinsicHeight(width) } @@ -342,10 +346,11 @@ internal abstract class AbstractContentPainterNode( ): Int { val constraints = Constraints(maxWidth = width) constraintSizeResolver?.setConstraints(constraints) + return if (painter.intrinsicSize.isSpecified) { - val constraints = modifyConstraints(constraints) + val modifiedConstraints = modifyConstraints(constraints) val layoutHeight = measurable.maxIntrinsicHeight(width) - max(constraints.minHeight, layoutHeight) + max(modifiedConstraints.minHeight, layoutHeight) } else { measurable.maxIntrinsicHeight(width) } @@ -442,7 +447,6 @@ internal abstract class AbstractContentPainterNode( } translate(dx.toFloat(), dy.toFloat()) }) { - // Draw the painter. with(painter) { draw(scaledSize, alpha, colorFilter) } diff --git a/coil-compose-core/src/commonMain/kotlin/coil3/compose/internal/utils.kt b/coil-compose-core/src/commonMain/kotlin/coil3/compose/internal/utils.kt index d26f2bfa75..ca8f783bc2 100644 --- a/coil-compose-core/src/commonMain/kotlin/coil3/compose/internal/utils.kt +++ b/coil-compose-core/src/commonMain/kotlin/coil3/compose/internal/utils.kt @@ -9,8 +9,12 @@ import androidx.compose.runtime.Stable import androidx.compose.runtime.remember import androidx.compose.ui.geometry.Size import androidx.compose.ui.geometry.isUnspecified +import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.layout.MeasurePolicy +import androidx.compose.ui.platform.LocalInspectionMode import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.IntSize import coil3.ImageLoader @@ -18,8 +22,10 @@ import coil3.compose.AsyncImage import coil3.compose.AsyncImageModelEqualityDelegate import coil3.compose.AsyncImagePainter.Companion.DefaultTransform import coil3.compose.AsyncImagePainter.State +import coil3.compose.AsyncImagePreviewHandler import coil3.compose.ConstraintsSizeResolver import coil3.compose.LocalAsyncImageModelEqualityDelegate +import coil3.compose.LocalAsyncImagePreviewHandler import coil3.compose.LocalPlatformContext import coil3.request.ImageRequest import coil3.request.NullRequestDataException @@ -203,3 +209,40 @@ internal val Size.isPositive get() = width >= 0.5 && height >= 0.5 @OptIn(ExperimentalStdlibApi::class) internal val CoroutineContext.dispatcher: CoroutineDispatcher? get() = get(CoroutineDispatcher) + +@ReadOnlyComposable +@Composable +internal fun previewHandler(): AsyncImagePreviewHandler? { + return if (LocalInspectionMode.current) { + LocalAsyncImagePreviewHandler.current + } else { + null + } +} + +internal val UseMinConstraintsMeasurePolicy = MeasurePolicy { _, constraints -> + layout(constraints.minWidth, constraints.minHeight) {} +} + +internal val ZeroConstraints = Constraints(maxWidth = 0, maxHeight = 0) + +internal fun validateRequest(request: ImageRequest) { + when (request.data) { + is ImageRequest.Builder -> unsupportedData( + name = "ImageRequest.Builder", + description = "Did you forget to call ImageRequest.Builder.build()?", + ) + is ImageBitmap -> unsupportedData("ImageBitmap") + is ImageVector -> unsupportedData("ImageVector") + is Painter -> unsupportedData("Painter") + } + validateRequestProperties(request) +} + +private fun unsupportedData( + name: String, + description: String = "If you wish to display this $name, use androidx.compose.foundation.Image.", +): Nothing = throw IllegalArgumentException("Unsupported type: $name. $description") + +/** Validate platform-specific properties of an [ImageRequest]. */ +internal expect fun validateRequestProperties(request: ImageRequest) diff --git a/coil-compose-core/src/nonAndroidMain/kotlin/coil3/compose/AsyncImagePainter.nonAndroid.kt b/coil-compose-core/src/nonAndroidMain/kotlin/coil3/compose/AsyncImagePainter.nonAndroid.kt index 446e18cdec..c8de794851 100644 --- a/coil-compose-core/src/nonAndroidMain/kotlin/coil3/compose/AsyncImagePainter.nonAndroid.kt +++ b/coil-compose-core/src/nonAndroidMain/kotlin/coil3/compose/AsyncImagePainter.nonAndroid.kt @@ -2,15 +2,10 @@ package coil3.compose import androidx.compose.ui.layout.ContentScale import coil3.decode.DataSource -import coil3.request.ImageRequest import coil3.request.SuccessResult import coil3.request.crossfadeMillis import kotlin.time.Duration.Companion.milliseconds -internal actual fun validateRequestProperties(request: ImageRequest) { - require(request.target == null) { "request.target must be null." } -} - internal actual fun maybeNewCrossfadePainter( previous: AsyncImagePainter.State, current: AsyncImagePainter.State, diff --git a/coil-compose-core/src/nonAndroidMain/kotlin/coil3/compose/internal/utils.nonAndroid.kt b/coil-compose-core/src/nonAndroidMain/kotlin/coil3/compose/internal/utils.nonAndroid.kt new file mode 100644 index 0000000000..0fa17d7a66 --- /dev/null +++ b/coil-compose-core/src/nonAndroidMain/kotlin/coil3/compose/internal/utils.nonAndroid.kt @@ -0,0 +1,7 @@ +package coil3.compose.internal + +import coil3.request.ImageRequest + +internal actual fun validateRequestProperties(request: ImageRequest) { + require(request.target == null) { "request.target must be null." } +}