Skip to content
7 changes: 7 additions & 0 deletions coil-compose-core/api/android/coil-compose-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,14 @@ public final class coil3/compose/CrossfadePainter : androidx/compose/ui/graphics
public static final field $stable I
public synthetic fun <init> (Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/layout/ContentScale;JLkotlin/time/TimeSource;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/layout/ContentScale;JLkotlin/time/TimeSource;ZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/layout/ContentScale;JLkotlin/time/TimeSource;ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/layout/ContentScale;JLkotlin/time/TimeSource;ZZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getContentScale ()Landroidx/compose/ui/layout/ContentScale;
public final fun getDuration-UwyO8pc ()J
public final fun getEnd ()Landroidx/compose/ui/graphics/painter/Painter;
public final fun getFadeStart ()Z
public fun getIntrinsicSize-NH-jbRc ()J
public final fun getPreferEndFirstIntrinsicSize ()Z
public final fun getPreferExactIntrinsicSize ()Z
public final fun getStart ()Landroidx/compose/ui/graphics/painter/Painter;
public final fun getTimeSource ()Lkotlin/time/TimeSource;
Expand Down Expand Up @@ -162,8 +165,12 @@ public final class coil3/compose/ImagePainter_androidKt {
}

public final class coil3/compose/ImageRequestsKt {
public static final fun getPreferEndFirstIntrinsicSize (Lcoil3/Extras$Key$Companion;)Lcoil3/Extras$Key;
public static final fun getPreferEndFirstIntrinsicSize (Lcoil3/request/ImageRequest;)Z
public static final fun getUseExistingImageAsPlaceholder (Lcoil3/Extras$Key$Companion;)Lcoil3/Extras$Key;
public static final fun getUseExistingImageAsPlaceholder (Lcoil3/request/ImageRequest;)Z
public static final fun preferEndFirstIntrinsicSize (Lcoil3/ImageLoader$Builder;Z)Lcoil3/ImageLoader$Builder;
public static final fun preferEndFirstIntrinsicSize (Lcoil3/request/ImageRequest$Builder;Z)Lcoil3/request/ImageRequest$Builder;
public static final fun useExistingImageAsPlaceholder (Lcoil3/ImageLoader$Builder;Z)Lcoil3/ImageLoader$Builder;
public static final fun useExistingImageAsPlaceholder (Lcoil3/request/ImageRequest$Builder;Z)Lcoil3/request/ImageRequest$Builder;
}
Expand Down
9 changes: 9 additions & 0 deletions coil-compose-core/api/coil-compose-core.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,7 @@ final class coil3.compose/ConstraintsSizeResolver : androidx.compose.ui.layout/L

final class coil3.compose/CrossfadePainter : androidx.compose.ui.graphics.painter/Painter { // coil3.compose/CrossfadePainter|null[0]
constructor <init>(androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.layout/ContentScale = ..., kotlin.time/Duration = ..., kotlin.time/TimeSource = ..., kotlin/Boolean = ..., kotlin/Boolean = ...) // coil3.compose/CrossfadePainter.<init>|<init>(androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.layout.ContentScale;kotlin.time.Duration;kotlin.time.TimeSource;kotlin.Boolean;kotlin.Boolean){}[0]
constructor <init>(androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.layout/ContentScale = ..., kotlin.time/Duration = ..., kotlin.time/TimeSource = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin/Boolean = ...) // coil3.compose/CrossfadePainter.<init>|<init>(androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.layout.ContentScale;kotlin.time.Duration;kotlin.time.TimeSource;kotlin.Boolean;kotlin.Boolean;kotlin.Boolean){}[0]

final val contentScale // coil3.compose/CrossfadePainter.contentScale|{}contentScale[0]
final fun <get-contentScale>(): androidx.compose.ui.layout/ContentScale // coil3.compose/CrossfadePainter.contentScale.<get-contentScale>|<get-contentScale>(){}[0]
Expand All @@ -162,6 +163,8 @@ final class coil3.compose/CrossfadePainter : androidx.compose.ui.graphics.painte
final fun <get-fadeStart>(): kotlin/Boolean // coil3.compose/CrossfadePainter.fadeStart.<get-fadeStart>|<get-fadeStart>(){}[0]
final val intrinsicSize // coil3.compose/CrossfadePainter.intrinsicSize|{}intrinsicSize[0]
final fun <get-intrinsicSize>(): androidx.compose.ui.geometry/Size // coil3.compose/CrossfadePainter.intrinsicSize.<get-intrinsicSize>|<get-intrinsicSize>(){}[0]
final val preferEndFirstIntrinsicSize // coil3.compose/CrossfadePainter.preferEndFirstIntrinsicSize|{}preferEndFirstIntrinsicSize[0]
final fun <get-preferEndFirstIntrinsicSize>(): kotlin/Boolean // coil3.compose/CrossfadePainter.preferEndFirstIntrinsicSize.<get-preferEndFirstIntrinsicSize>|<get-preferEndFirstIntrinsicSize>(){}[0]
final val preferExactIntrinsicSize // coil3.compose/CrossfadePainter.preferExactIntrinsicSize|{}preferExactIntrinsicSize[0]
final fun <get-preferExactIntrinsicSize>(): kotlin/Boolean // coil3.compose/CrossfadePainter.preferExactIntrinsicSize.<get-preferExactIntrinsicSize>|<get-preferExactIntrinsicSize>(){}[0]
final val timeSource // coil3.compose/CrossfadePainter.timeSource|{}timeSource[0]
Expand Down Expand Up @@ -195,14 +198,20 @@ final val coil3.compose/coil3_compose_AsyncImagePainter_State_Success$stableprop
final val coil3.compose/coil3_compose_ConstraintsSizeResolver$stableprop // coil3.compose/coil3_compose_ConstraintsSizeResolver$stableprop|#static{}coil3_compose_ConstraintsSizeResolver$stableprop[0]
final val coil3.compose/coil3_compose_CrossfadePainter$stableprop // coil3.compose/coil3_compose_CrossfadePainter$stableprop|#static{}coil3_compose_CrossfadePainter$stableprop[0]
final val coil3.compose/coil3_compose_ImagePainter$stableprop // coil3.compose/coil3_compose_ImagePainter$stableprop|#static{}coil3_compose_ImagePainter$stableprop[0]
final val coil3.compose/preferEndFirstIntrinsicSize // coil3.compose/preferEndFirstIntrinsicSize|@coil3.Extras.Key.Companion{}preferEndFirstIntrinsicSize[0]
final fun (coil3/Extras.Key.Companion).<get-preferEndFirstIntrinsicSize>(): coil3/Extras.Key<kotlin/Boolean> // coil3.compose/preferEndFirstIntrinsicSize.<get-preferEndFirstIntrinsicSize>|<get-preferEndFirstIntrinsicSize>@coil3.Extras.Key.Companion(){}[0]
final val coil3.compose/preferEndFirstIntrinsicSize // coil3.compose/preferEndFirstIntrinsicSize|@coil3.request.ImageRequest{}preferEndFirstIntrinsicSize[0]
final fun (coil3.request/ImageRequest).<get-preferEndFirstIntrinsicSize>(): kotlin/Boolean // coil3.compose/preferEndFirstIntrinsicSize.<get-preferEndFirstIntrinsicSize>|<get-preferEndFirstIntrinsicSize>@coil3.request.ImageRequest(){}[0]
final val coil3.compose/useExistingImageAsPlaceholder // coil3.compose/useExistingImageAsPlaceholder|@coil3.Extras.Key.Companion{}useExistingImageAsPlaceholder[0]
final fun (coil3/Extras.Key.Companion).<get-useExistingImageAsPlaceholder>(): coil3/Extras.Key<kotlin/Boolean> // coil3.compose/useExistingImageAsPlaceholder.<get-useExistingImageAsPlaceholder>|<get-useExistingImageAsPlaceholder>@coil3.Extras.Key.Companion(){}[0]
final val coil3.compose/useExistingImageAsPlaceholder // coil3.compose/useExistingImageAsPlaceholder|@coil3.request.ImageRequest{}useExistingImageAsPlaceholder[0]
final fun (coil3.request/ImageRequest).<get-useExistingImageAsPlaceholder>(): kotlin/Boolean // coil3.compose/useExistingImageAsPlaceholder.<get-useExistingImageAsPlaceholder>|<get-useExistingImageAsPlaceholder>@coil3.request.ImageRequest(){}[0]

final fun (coil3.compose/SubcomposeAsyncImageScope).coil3.compose/SubcomposeAsyncImageContent(androidx.compose.ui/Modifier?, androidx.compose.ui.graphics.painter/Painter?, kotlin/String?, androidx.compose.ui/Alignment?, androidx.compose.ui.layout/ContentScale?, kotlin/Float, androidx.compose.ui.graphics/ColorFilter?, kotlin/Boolean, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int) // coil3.compose/SubcomposeAsyncImageContent|SubcomposeAsyncImageContent@coil3.compose.SubcomposeAsyncImageScope(androidx.compose.ui.Modifier?;androidx.compose.ui.graphics.painter.Painter?;kotlin.String?;androidx.compose.ui.Alignment?;androidx.compose.ui.layout.ContentScale?;kotlin.Float;androidx.compose.ui.graphics.ColorFilter?;kotlin.Boolean;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int){}[0]
final fun (coil3.request/ImageRequest.Builder).coil3.compose/preferEndFirstIntrinsicSize(kotlin/Boolean): coil3.request/ImageRequest.Builder // coil3.compose/preferEndFirstIntrinsicSize|preferEndFirstIntrinsicSize@coil3.request.ImageRequest.Builder(kotlin.Boolean){}[0]
final fun (coil3.request/ImageRequest.Builder).coil3.compose/useExistingImageAsPlaceholder(kotlin/Boolean): coil3.request/ImageRequest.Builder // coil3.compose/useExistingImageAsPlaceholder|useExistingImageAsPlaceholder@coil3.request.ImageRequest.Builder(kotlin.Boolean){}[0]
final fun (coil3/Image).coil3.compose/asPainter(coil3/PlatformContext, androidx.compose.ui.graphics/FilterQuality = ...): androidx.compose.ui.graphics.painter/Painter // coil3.compose/asPainter|asPainter@coil3.Image(coil3.PlatformContext;androidx.compose.ui.graphics.FilterQuality){}[0]
final fun (coil3/ImageLoader.Builder).coil3.compose/preferEndFirstIntrinsicSize(kotlin/Boolean): coil3/ImageLoader.Builder // coil3.compose/preferEndFirstIntrinsicSize|preferEndFirstIntrinsicSize@coil3.ImageLoader.Builder(kotlin.Boolean){}[0]
final fun (coil3/ImageLoader.Builder).coil3.compose/useExistingImageAsPlaceholder(kotlin/Boolean): coil3/ImageLoader.Builder // coil3.compose/useExistingImageAsPlaceholder|useExistingImageAsPlaceholder@coil3.ImageLoader.Builder(kotlin.Boolean){}[0]
final fun coil3.compose/AsyncImage(kotlin/Any?, kotlin/String?, coil3/ImageLoader, androidx.compose.ui/Modifier?, androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.graphics.painter/Painter?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Loading, kotlin/Unit>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Success, kotlin/Unit>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Error, kotlin/Unit>?, androidx.compose.ui/Alignment?, androidx.compose.ui.layout/ContentScale?, kotlin/Float, androidx.compose.ui.graphics/ColorFilter?, androidx.compose.ui.graphics/FilterQuality, kotlin/Boolean, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int, kotlin/Int) // coil3.compose/AsyncImage|AsyncImage(kotlin.Any?;kotlin.String?;coil3.ImageLoader;androidx.compose.ui.Modifier?;androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.graphics.painter.Painter?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Loading,kotlin.Unit>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Success,kotlin.Unit>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Error,kotlin.Unit>?;androidx.compose.ui.Alignment?;androidx.compose.ui.layout.ContentScale?;kotlin.Float;androidx.compose.ui.graphics.ColorFilter?;androidx.compose.ui.graphics.FilterQuality;kotlin.Boolean;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
final fun coil3.compose/AsyncImage(kotlin/Any?, kotlin/String?, coil3/ImageLoader, androidx.compose.ui/Modifier?, androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.graphics.painter/Painter?, androidx.compose.ui.graphics.painter/Painter?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Loading, kotlin/Unit>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Success, kotlin/Unit>?, kotlin/Function1<coil3.compose/AsyncImagePainter.State.Error, kotlin/Unit>?, androidx.compose.ui/Alignment?, androidx.compose.ui.layout/ContentScale?, kotlin/Float, androidx.compose.ui.graphics/ColorFilter?, androidx.compose.ui.graphics/FilterQuality?, kotlin/Boolean, androidx.compose.runtime/Composer?, kotlin/Int, kotlin/Int, kotlin/Int) // coil3.compose/AsyncImage|AsyncImage(kotlin.Any?;kotlin.String?;coil3.ImageLoader;androidx.compose.ui.Modifier?;androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.graphics.painter.Painter?;androidx.compose.ui.graphics.painter.Painter?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Loading,kotlin.Unit>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Success,kotlin.Unit>?;kotlin.Function1<coil3.compose.AsyncImagePainter.State.Error,kotlin.Unit>?;androidx.compose.ui.Alignment?;androidx.compose.ui.layout.ContentScale?;kotlin.Float;androidx.compose.ui.graphics.ColorFilter?;androidx.compose.ui.graphics.FilterQuality?;kotlin.Boolean;androidx.compose.runtime.Composer?;kotlin.Int;kotlin.Int;kotlin.Int){}[0]
Expand Down
7 changes: 7 additions & 0 deletions coil-compose-core/api/jvm/coil-compose-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -130,11 +130,14 @@ public final class coil3/compose/CrossfadePainter : androidx/compose/ui/graphics
public static final field $stable I
public synthetic fun <init> (Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/layout/ContentScale;JLkotlin/time/TimeSource;ZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/layout/ContentScale;JLkotlin/time/TimeSource;ZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/layout/ContentScale;JLkotlin/time/TimeSource;ZZZILkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/graphics/painter/Painter;Landroidx/compose/ui/layout/ContentScale;JLkotlin/time/TimeSource;ZZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getContentScale ()Landroidx/compose/ui/layout/ContentScale;
public final fun getDuration-UwyO8pc ()J
public final fun getEnd ()Landroidx/compose/ui/graphics/painter/Painter;
public final fun getFadeStart ()Z
public fun getIntrinsicSize-NH-jbRc ()J
public final fun getPreferEndFirstIntrinsicSize ()Z
public final fun getPreferExactIntrinsicSize ()Z
public final fun getStart ()Landroidx/compose/ui/graphics/painter/Painter;
public final fun getTimeSource ()Lkotlin/time/TimeSource;
Expand Down Expand Up @@ -162,8 +165,12 @@ public final class coil3/compose/ImagePainter_nonAndroidKt {
}

public final class coil3/compose/ImageRequestsKt {
public static final fun getPreferEndFirstIntrinsicSize (Lcoil3/Extras$Key$Companion;)Lcoil3/Extras$Key;
public static final fun getPreferEndFirstIntrinsicSize (Lcoil3/request/ImageRequest;)Z
public static final fun getUseExistingImageAsPlaceholder (Lcoil3/Extras$Key$Companion;)Lcoil3/Extras$Key;
public static final fun getUseExistingImageAsPlaceholder (Lcoil3/request/ImageRequest;)Z
public static final fun preferEndFirstIntrinsicSize (Lcoil3/ImageLoader$Builder;Z)Lcoil3/ImageLoader$Builder;
public static final fun preferEndFirstIntrinsicSize (Lcoil3/request/ImageRequest$Builder;Z)Lcoil3/request/ImageRequest$Builder;
public static final fun useExistingImageAsPlaceholder (Lcoil3/ImageLoader$Builder;Z)Lcoil3/ImageLoader$Builder;
public static final fun useExistingImageAsPlaceholder (Lcoil3/request/ImageRequest$Builder;Z)Lcoil3/request/ImageRequest$Builder;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ internal actual fun maybeNewCrossfadePainter(
duration = transition.durationMillis.milliseconds,
fadeStart = result !is SuccessResult || !result.isPlaceholderCached,
preferExactIntrinsicSize = transition.preferExactIntrinsicSize,
preferEndFirstIntrinsicSize = result.request.preferEndFirstIntrinsicSize
)
} else {
return null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,34 @@ class CrossfadePainterTest : RobolectricTest() {
assertEquals(Size.Unspecified, preferUnspecified.intrinsicSize)
}

@Test
fun intrinsicSize_prefersEndFirstIntrinsicSize() {
val start = SolidColorPainter(Color.Red, Size(40f, 30f))
val end = SolidColorPainter(Color.Blue, Size(50f, 20f))

val painterEndPreferred = CrossfadePainter(
start = start,
end = end,
preferEndFirstIntrinsicSize = true,
)
assertEquals(Size(50f, 20f), painterEndPreferred.intrinsicSize)

val endUnspecified = SolidColorPainter(Color.Blue, Size.Unspecified)
val painterEndUnspecified = CrossfadePainter(
start = start,
end = endUnspecified,
preferEndFirstIntrinsicSize = true,
)
assertEquals(Size(40f, 30f), painterEndUnspecified.intrinsicSize)

val painterMaxPreferred = CrossfadePainter(
start = start,
end = end,
preferEndFirstIntrinsicSize = false,
)
assertEquals(Size(50f, 30f), painterMaxPreferred.intrinsicSize)
}

@Test
fun intrinsicSize_remainsStableAfterCrossfadeCompletes() {
val start = SolidColorPainter(Color.Red, Size(24f, 24f))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ import kotlin.time.TimeSource
* if [start] **and** [end] return -1 for that dimension. If false, the intrinsic width/height will
* be -1 if [start] **or** [end] return -1 for that dimension. This is useful for views that
* require an exact intrinsic size to scale the drawable.
* @param preferEndFirstIntrinsicSize Returns `true` if this request prefers the end painter's intrinsic size
* when calculating the `CrossfadePainter`'s intrinsic size.
* When enabled, the end painter's intrinsic size takes precedence.
*/
@Stable
class CrossfadePainter(
Expand All @@ -45,8 +48,20 @@ class CrossfadePainter(
val timeSource: TimeSource = TimeSource.Monotonic,
val fadeStart: Boolean = true,
val preferExactIntrinsicSize: Boolean = false,
val preferEndFirstIntrinsicSize: Boolean = false
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to add a @Deprecated("Kept for binary compatibility.", level = DeprecationLevel.HIDDEN) version of this constructor without the preferEndFirstIntrinsicSize argument to preserve binary compatibility. Check out NetworkClient for an example .

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

50ab610
please check this

) : Painter() {

@Deprecated("Kept for binary compatibility.", level = DeprecationLevel.HIDDEN)
constructor(
start: Painter?,
end: Painter?,
contentScale: ContentScale = ContentScale.Fit,
duration: Duration = 200.milliseconds,
timeSource: TimeSource = TimeSource.Monotonic,
fadeStart: Boolean = true,
preferExactIntrinsicSize: Boolean = false,
) : this(start, end, contentScale, duration, timeSource, fadeStart, preferExactIntrinsicSize)

private var invalidateTick by mutableIntStateOf(0)
private var startTime: TimeMark? = null
private var isDone = false
Expand Down Expand Up @@ -99,12 +114,19 @@ class CrossfadePainter(

val isStartSpecified = startSize.isSpecified
val isEndSpecified = endSize.isSpecified

if (preferEndFirstIntrinsicSize) {
if (isEndSpecified) return endSize
if (isStartSpecified) return startSize
}
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion, we could simply set the intrinsicSize based on the endSize. What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be a significant behaviour change and I'm not sure about the ramifications. How would crossfading from a painter with an intrinsic size to a painter with no intrinsic size (e.g. ColorPainter) inside an AsyncImage with no constraints look? Would it have 0 size?


if (isStartSpecified && isEndSpecified) {
return Size(
width = maxOf(startSize.width, endSize.width),
height = maxOf(startSize.height, endSize.height),
)
}

if (preferExactIntrinsicSize) {
if (isStartSpecified) return startSize
if (isEndSpecified) return endSize
Expand Down
Loading