Skip to content
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
9 changes: 6 additions & 3 deletions coil-core/api/android/coil-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -360,9 +360,12 @@ public final class coil3/decode/DecodeUtils {
public static final field INSTANCE Lcoil3/decode/DecodeUtils;
public static final fun calculateInSampleSize (IIIILcoil3/size/Scale;)I
public static final fun computeDstSize-sEdh43o (IILcoil3/size/Size;Lcoil3/size/Scale;Lcoil3/size/Size;)J
public static final fun computeSizeMultiplier (DDDDLcoil3/size/Scale;)D
public static final fun computeSizeMultiplier (FFFFLcoil3/size/Scale;)F
public static final fun computeSizeMultiplier (IIIILcoil3/size/Scale;)D
public static final fun computeSizeMultiplier (DDDDLcoil3/size/Scale;Lcoil3/size/Size;)D
public static final fun computeSizeMultiplier (FFFFLcoil3/size/Scale;Lcoil3/size/Size;)F
public static final fun computeSizeMultiplier (IIIILcoil3/size/Scale;Lcoil3/size/Size;)D
public static synthetic fun computeSizeMultiplier$default (DDDDLcoil3/size/Scale;Lcoil3/size/Size;ILjava/lang/Object;)D
public static synthetic fun computeSizeMultiplier$default (FFFFLcoil3/size/Scale;Lcoil3/size/Size;ILjava/lang/Object;)F
public static synthetic fun computeSizeMultiplier$default (IIIILcoil3/size/Scale;Lcoil3/size/Size;ILjava/lang/Object;)D
}

public abstract interface class coil3/decode/Decoder {
Expand Down
6 changes: 3 additions & 3 deletions coil-core/api/coil-core.klib.api
Original file line number Diff line number Diff line change
Expand Up @@ -972,9 +972,9 @@ final value class coil3.util/IntPair { // coil3.util/IntPair|null[0]
final object coil3.decode/DecodeUtils { // coil3.decode/DecodeUtils|null[0]
final fun calculateInSampleSize(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, coil3.size/Scale): kotlin/Int // coil3.decode/DecodeUtils.calculateInSampleSize|calculateInSampleSize(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;coil3.size.Scale){}[0]
final fun computeDstSize(kotlin/Int, kotlin/Int, coil3.size/Size, coil3.size/Scale, coil3.size/Size): coil3.util/IntPair // coil3.decode/DecodeUtils.computeDstSize|computeDstSize(kotlin.Int;kotlin.Int;coil3.size.Size;coil3.size.Scale;coil3.size.Size){}[0]
final fun computeSizeMultiplier(kotlin/Double, kotlin/Double, kotlin/Double, kotlin/Double, coil3.size/Scale): kotlin/Double // coil3.decode/DecodeUtils.computeSizeMultiplier|computeSizeMultiplier(kotlin.Double;kotlin.Double;kotlin.Double;kotlin.Double;coil3.size.Scale){}[0]
final fun computeSizeMultiplier(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, coil3.size/Scale): kotlin/Float // coil3.decode/DecodeUtils.computeSizeMultiplier|computeSizeMultiplier(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;coil3.size.Scale){}[0]
final fun computeSizeMultiplier(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, coil3.size/Scale): kotlin/Double // coil3.decode/DecodeUtils.computeSizeMultiplier|computeSizeMultiplier(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;coil3.size.Scale){}[0]
final fun computeSizeMultiplier(kotlin/Double, kotlin/Double, kotlin/Double, kotlin/Double, coil3.size/Scale, coil3.size/Size = ...): kotlin/Double // coil3.decode/DecodeUtils.computeSizeMultiplier|computeSizeMultiplier(kotlin.Double;kotlin.Double;kotlin.Double;kotlin.Double;coil3.size.Scale;coil3.size.Size){}[0]
final fun computeSizeMultiplier(kotlin/Float, kotlin/Float, kotlin/Float, kotlin/Float, coil3.size/Scale, coil3.size/Size = ...): kotlin/Float // coil3.decode/DecodeUtils.computeSizeMultiplier|computeSizeMultiplier(kotlin.Float;kotlin.Float;kotlin.Float;kotlin.Float;coil3.size.Scale;coil3.size.Size){}[0]
final fun computeSizeMultiplier(kotlin/Int, kotlin/Int, kotlin/Int, kotlin/Int, coil3.size/Scale, coil3.size/Size = ...): kotlin/Double // coil3.decode/DecodeUtils.computeSizeMultiplier|computeSizeMultiplier(kotlin.Int;kotlin.Int;kotlin.Int;kotlin.Int;coil3.size.Scale;coil3.size.Size){}[0]
}

final object coil3.request/NullRequestData { // coil3.request/NullRequestData|null[0]
Expand Down
9 changes: 6 additions & 3 deletions coil-core/api/jvm/coil-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -303,9 +303,12 @@ public final class coil3/decode/DecodeUtils {
public static final field INSTANCE Lcoil3/decode/DecodeUtils;
public static final fun calculateInSampleSize (IIIILcoil3/size/Scale;)I
public static final fun computeDstSize-sEdh43o (IILcoil3/size/Size;Lcoil3/size/Scale;Lcoil3/size/Size;)J
public static final fun computeSizeMultiplier (DDDDLcoil3/size/Scale;)D
public static final fun computeSizeMultiplier (FFFFLcoil3/size/Scale;)F
public static final fun computeSizeMultiplier (IIIILcoil3/size/Scale;)D
public static final fun computeSizeMultiplier (DDDDLcoil3/size/Scale;Lcoil3/size/Size;)D
public static final fun computeSizeMultiplier (FFFFLcoil3/size/Scale;Lcoil3/size/Size;)F
public static final fun computeSizeMultiplier (IIIILcoil3/size/Scale;Lcoil3/size/Size;)D
public static synthetic fun computeSizeMultiplier$default (DDDDLcoil3/size/Scale;Lcoil3/size/Size;ILjava/lang/Object;)D
public static synthetic fun computeSizeMultiplier$default (FFFFLcoil3/size/Scale;Lcoil3/size/Size;ILjava/lang/Object;)F
public static synthetic fun computeSizeMultiplier$default (IIIILcoil3/size/Scale;Lcoil3/size/Size;ILjava/lang/Object;)D
}

public abstract interface class coil3/decode/Decoder {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,34 @@ class RealImageLoaderAndroidTest {
srcHeight = 4965,
dstWidth = maxBitmapSize.width.pxOrElse { throw IllegalStateException() },
dstHeight = maxBitmapSize.height.pxOrElse { throw IllegalStateException() },
scale = Scale.FIT
scale = Scale.FIT,
maxSize = maxBitmapSize,
)
val expectedWidth = (multiplier * 9052).roundToInt()
val expectedHeight = (multiplier * 4965).roundToInt()
assertTrue(image.bitmap.width in expectedWidth - 1..expectedWidth + 1)
assertTrue(image.bitmap.height in expectedHeight - 1..expectedHeight + 1)
}

@Test
fun veryLargeImageWithFill() = runTest {
val request = ImageRequest.Builder(context)
.data(R.drawable.very_large)
.scale(Scale.FILL)
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.

The test with FILL scaling.

.build()
val result = imageLoader.execute(request)
if (result is ErrorResult) throw result.throwable

assertIs<SuccessResult>(result)
val image = assertIs<BitmapImage>(result.image)
val maxBitmapSize = Extras.Key.maxBitmapSize.default
val multiplier = DecodeUtils.computeSizeMultiplier(
srcWidth = 9052,
srcHeight = 4965,
dstWidth = maxBitmapSize.width.pxOrElse { throw IllegalStateException() },
dstHeight = maxBitmapSize.height.pxOrElse { throw IllegalStateException() },
scale = Scale.FILL,
maxSize = maxBitmapSize,
)
val expectedWidth = (multiplier * 9052).roundToInt()
val expectedHeight = (multiplier * 4965).roundToInt()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ class BitmapFactoryDecoder(
dstWidth = dstWidth.toDouble(),
dstHeight = dstHeight.toDouble(),
scale = options.scale,
maxSize = options.maxBitmapSize,
)

// Only upscale the image if the options require an exact size.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class StaticImageDecoder(
dstWidth = dstWidth,
dstHeight = dstHeight,
scale = options.scale,
maxSize = options.maxBitmapSize,
)

// Set the target size if the image is larger than the requested dimensions
Expand Down
48 changes: 43 additions & 5 deletions coil-core/src/commonMain/kotlin/coil3/decode/DecodeUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,27 @@ object DecodeUtils {
dstWidth: Int,
dstHeight: Int,
scale: Scale,
maxSize: Size = Size.ORIGINAL,
): Double {
val widthPercent = dstWidth / srcWidth.toDouble()
val heightPercent = dstHeight / srcHeight.toDouble()
return when (scale) {
val srcWidthDouble = srcWidth.toDouble()
val srcHeightDouble = srcHeight.toDouble()
val widthPercent = dstWidth / srcWidthDouble
val heightPercent = dstHeight / srcHeightDouble
var percent = when (scale) {
Scale.FILL -> maxOf(widthPercent, heightPercent)
Scale.FIT -> minOf(widthPercent, heightPercent)
}
if (maxSize.width is Dimension.Pixels) {
val maxWidth = maxSize.width.px
val maxWidthPercent = maxWidth / srcWidthDouble
percent = percent.coerceAtMost(maxWidthPercent)
}
if (maxSize.height is Dimension.Pixels) {
val maxHeight = maxSize.height.px
val maxHeightPercent = maxHeight / srcHeightDouble
percent = percent.coerceAtMost(maxHeightPercent)
}
return percent
}

/** @see computeSizeMultiplier */
Expand All @@ -60,13 +74,25 @@ object DecodeUtils {
dstWidth: Float,
dstHeight: Float,
scale: Scale,
maxSize: Size = Size.ORIGINAL,
): Float {
val widthPercent = dstWidth / srcWidth
val heightPercent = dstHeight / srcHeight
return when (scale) {
var percent = when (scale) {
Scale.FILL -> maxOf(widthPercent, heightPercent)
Scale.FIT -> minOf(widthPercent, heightPercent)
}
if (maxSize.width is Dimension.Pixels) {
val maxWidth = maxSize.width.px
val maxWidthPercent = maxWidth / srcWidth
percent = percent.coerceAtMost(maxWidthPercent)
}
if (maxSize.height is Dimension.Pixels) {
val maxHeight = maxSize.height.px
val maxHeightPercent = maxHeight / srcHeight
percent = percent.coerceAtMost(maxHeightPercent)
}
return percent
}

/** @see computeSizeMultiplier */
Expand All @@ -77,13 +103,25 @@ object DecodeUtils {
dstWidth: Double,
dstHeight: Double,
scale: Scale,
maxSize: Size = Size.ORIGINAL,
): Double {
val widthPercent = dstWidth / srcWidth
val heightPercent = dstHeight / srcHeight
return when (scale) {
var percent = when (scale) {
Scale.FILL -> maxOf(widthPercent, heightPercent)
Scale.FIT -> minOf(widthPercent, heightPercent)
}
if (maxSize.width is Dimension.Pixels) {
val maxWidth = maxSize.width.px
val maxWidthPercent = maxWidth / srcWidth
percent = percent.coerceAtMost(maxWidthPercent)
}
if (maxSize.height is Dimension.Pixels) {
val maxHeight = maxSize.height.px
val maxHeightPercent = maxHeight / srcHeight
percent = percent.coerceAtMost(maxHeightPercent)
}
return percent
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ internal fun Bitmap.Companion.makeFromImage(
dstWidth = dstWidth,
dstHeight = dstHeight,
scale = options.scale,
maxSize = options.maxBitmapSize,
)

// Only upscale the image if the options require an exact size.
Expand Down
1 change: 1 addition & 0 deletions coil-gif/src/main/java/coil3/gif/AnimatedImageDecoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class AnimatedImageDecoder(
dstWidth = dstWidth,
dstHeight = dstHeight,
scale = options.scale,
maxSize = options.maxBitmapSize,
)

// Set the target size if the image is larger than the requested dimensions
Expand Down
1 change: 1 addition & 0 deletions coil-svg/src/commonMain/kotlin/coil3/svg/SvgDecoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class SvgDecoder(
dstWidth = dstWidth.toFloat(),
dstHeight = dstHeight.toFloat(),
scale = options.scale,
maxSize = options.maxBitmapSize,
)
bitmapWidth = (multiplier * svgWidth).toInt()
bitmapHeight = (multiplier * svgHeight).toInt()
Expand Down
1 change: 1 addition & 0 deletions coil-video/src/main/java/coil3/video/VideoFrameDecoder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ class VideoFrameDecoder(
dstWidth = dstWidth,
dstHeight = dstHeight,
scale = options.scale,
maxSize = options.maxBitmapSize,
)
val scale = if (options.precision == Precision.INEXACT) {
rawScale.coerceAtMost(1.0)
Expand Down