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

Convert Dimension.Original to be Dimension.Undefined. #1250

Merged
merged 11 commits into from Apr 26, 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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 5 additions & 5 deletions coil-base/api/coil-base.api
Expand Up @@ -764,11 +764,6 @@ public final class coil/size/-Sizes {
public abstract class coil/size/Dimension {
}

public final class coil/size/Dimension$Original : coil/size/Dimension {
public static final field INSTANCE Lcoil/size/Dimension$Original;
public fun toString ()Ljava/lang/String;
}

public final class coil/size/Dimension$Pixels : coil/size/Dimension {
public final field px I
public fun <init> (I)V
Expand All @@ -777,6 +772,11 @@ public final class coil/size/Dimension$Pixels : coil/size/Dimension {
public fun toString ()Ljava/lang/String;
}

public final class coil/size/Dimension$Undefined : coil/size/Dimension {
public static final field INSTANCE Lcoil/size/Dimension$Undefined;
public fun toString ()Ljava/lang/String;
}

public final class coil/size/Precision : java/lang/Enum {
public static final field AUTOMATIC Lcoil/size/Precision;
public static final field EXACT Lcoil/size/Precision;
Expand Down
Expand Up @@ -57,7 +57,7 @@ class BitmapFactoryDecoderTest {
fun undefinedWidth() = runTest {
val result = decode(
assetName = "normal.jpg",
size = Size(Dimension.Original, 100),
size = Size(Dimension.Undefined, 100),
scale = Scale.FIT
)

Expand All @@ -71,7 +71,7 @@ class BitmapFactoryDecoderTest {
fun undefinedHeight() = runTest {
val result = decode(
assetName = "normal.jpg",
size = Size(100, Dimension.Original),
size = Size(100, Dimension.Undefined),
scale = Scale.FIT
)

Expand Down
8 changes: 4 additions & 4 deletions coil-base/src/main/java/coil/decode/BitmapFactoryDecoder.kt
Expand Up @@ -7,10 +7,11 @@ import coil.ImageLoader
import coil.fetch.SourceResult
import coil.request.Options
import coil.size.isOriginal
import coil.size.pxOrElse
import coil.util.MIME_TYPE_JPEG
import coil.util.heightPx
import coil.util.toDrawable
import coil.util.toSoftware
import coil.util.widthPx
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
Expand Down Expand Up @@ -126,9 +127,8 @@ class BitmapFactoryDecoder @JvmOverloads constructor(
val srcWidth = if (exifData.isSwapped) outHeight else outWidth
val srcHeight = if (exifData.isSwapped) outWidth else outHeight

val dstSize = options.size
val dstWidth = dstSize.width.pxOrElse { srcWidth }
val dstHeight = dstSize.height.pxOrElse { srcHeight }
val dstWidth = options.size.widthPx(options.scale) { srcWidth }
val dstHeight = options.size.heightPx(options.scale) { srcHeight }

// Calculate the image's sample size.
inSampleSize = DecodeUtils.calculateInSampleSize(
Expand Down
2 changes: 1 addition & 1 deletion coil-base/src/main/java/coil/request/RequestService.kt
Expand Up @@ -74,7 +74,7 @@ internal class RequestService(
config != Bitmap.Config.ALPHA_8

// Use `Scale.FIT` if either dimension is undefined.
val scale = if (size.width == Dimension.Original || size.height == Dimension.Original) {
val scale = if (size.width == Dimension.Undefined || size.height == Dimension.Undefined) {
Scale.FIT
} else {
request.scale
Expand Down
12 changes: 6 additions & 6 deletions coil-base/src/main/java/coil/size/Dimension.kt
Expand Up @@ -31,18 +31,18 @@ sealed class Dimension {
}

/**
* Represents the original pixel value of the source image.
* Represents an undefined pixel value.
*
* i.e. if the image's original dimensions are 400x600 and this is used as the width, this
* should be treated as 400 pixels.
* E.g. given `Size(400, Dimension.Undefined)`, the image should be loaded to fit/fill a width
* of 400 pixels irrespective of the image's height.
*
* This value is typically used in cases where a dimension is unbounded (e.g. [WRAP_CONTENT],
* `Constraints.Infinity`).
*
* NOTE: If at least one dimension is [Original], [Options.scale] is always [Scale.FIT].
* NOTE: If either dimension is [Undefined], [Options.scale] is always [Scale.FIT].
*/
object Original : Dimension() {
override fun toString() = "Dimension.Original"
object Undefined : Dimension() {
override fun toString() = "Dimension.Undefined"
}
}

Expand Down
15 changes: 13 additions & 2 deletions coil-base/src/main/java/coil/size/Size.kt
Expand Up @@ -8,6 +8,17 @@ import coil.request.ImageRequest
/**
* Represents the target size of an image request.
*
* Each [Size] is composed of two [Dimension]s, [width] and [height]. Each dimension determines
* by how much the source image should be scaled. A [Dimension] can either be a fixed pixel
* value or [Dimension.Undefined]. Examples:
*
* - Given `Size(400, 600)`, the image should be loaded to fit/fill a width of 400 pixels and a
* height of 600 pixels.
* - Given `Size(400, Dimension.Undefined)`, the image should be loaded to fit/fill a width of 400
* pixels.
* - Given `Size(Dimension.Undefined, Dimension.Undefined)`, the image should not be scaled to
* fit/fill either width or height. i.e. it will be loaded at its original width/height.
*
* @see ImageRequest.Builder.size
* @see SizeResolver.size
*/
Expand All @@ -18,9 +29,9 @@ data class Size(

companion object {
/**
* A [Size] whose width and height are equal to the original dimensions of the source image.
* A [Size] whose width and height are not scaled.
*/
@JvmField val ORIGINAL = Size(Dimension.Original, Dimension.Original)
@JvmField val ORIGINAL = Size(Dimension.Undefined, Dimension.Undefined)
}
}

Expand Down
4 changes: 2 additions & 2 deletions coil-base/src/main/java/coil/size/ViewSizeResolver.kt
Expand Up @@ -85,9 +85,9 @@ interface ViewSizeResolver<T : View> : SizeResolver {
)

private fun getDimension(paramSize: Int, viewSize: Int, paddingSize: Int): Dimension? {
// If the dimension is set to WRAP_CONTENT, use `Dimension.Original`.
// If the dimension is set to WRAP_CONTENT, then the dimension is undefined.
if (paramSize == ViewGroup.LayoutParams.WRAP_CONTENT) {
return Dimension.Original
return Dimension.Undefined
}

// Assume the dimension will match the value in the view's layout params.
Expand Down
Expand Up @@ -15,8 +15,9 @@ import androidx.core.graphics.createBitmap
import coil.decode.DecodeUtils
import coil.size.Scale
import coil.size.Size
import coil.size.pxOrElse
import coil.util.heightPx
import coil.util.safeConfig
import coil.util.widthPx
import kotlin.math.roundToInt

/**
Expand Down Expand Up @@ -51,8 +52,8 @@ class RoundedCornersTransformation(
override suspend fun transform(input: Bitmap, size: Size): Bitmap {
val paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)

val dstWidth = size.width.pxOrElse { input.width }
val dstHeight = size.height.pxOrElse { input.height }
val dstWidth = size.widthPx(Scale.FILL) { input.width }
val dstHeight = size.heightPx(Scale.FILL) { input.height }
val multiplier = DecodeUtils.computeSizeMultiplier(
srcWidth = input.width,
srcHeight = input.height,
Expand Down
9 changes: 4 additions & 5 deletions coil-base/src/main/java/coil/util/DrawableUtils.kt
Expand Up @@ -13,7 +13,6 @@ import androidx.core.graphics.createBitmap
import coil.decode.DecodeUtils
import coil.size.Scale
import coil.size.Size
import coil.size.pxOrElse
import kotlin.math.roundToInt

internal object DrawableUtils {
Expand Down Expand Up @@ -52,8 +51,8 @@ internal object DrawableUtils {
val multiplier = DecodeUtils.computeSizeMultiplier(
srcWidth = srcWidth,
srcHeight = srcHeight,
dstWidth = size.width.pxOrElse { srcWidth },
dstHeight = size.height.pxOrElse { srcHeight },
dstWidth = size.widthPx(scale) { srcWidth },
dstHeight = size.heightPx(scale) { srcHeight },
scale = scale
)
val bitmapWidth = (multiplier * srcWidth).roundToInt()
Expand Down Expand Up @@ -87,8 +86,8 @@ internal object DrawableUtils {
return DecodeUtils.computeSizeMultiplier(
srcWidth = bitmap.width,
srcHeight = bitmap.height,
dstWidth = size.width.pxOrElse { bitmap.width },
dstHeight = size.height.pxOrElse { bitmap.height },
dstWidth = size.widthPx(scale) { bitmap.width },
dstHeight = size.heightPx(scale) { bitmap.height },
scale = scale
) == 1.0
}
Expand Down
19 changes: 19 additions & 0 deletions coil-base/src/main/java/coil/util/Utils.kt
Expand Up @@ -38,7 +38,11 @@ import coil.memory.MemoryCache
import coil.request.Parameters
import coil.request.Tags
import coil.request.ViewTargetRequestManager
import coil.size.Dimension
import coil.size.Scale
import coil.size.Size
import coil.size.isOriginal
import coil.size.pxOrElse
import coil.transform.Transformation
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand Down Expand Up @@ -205,6 +209,21 @@ internal val Interceptor.Chain.eventListener: EventListener

internal fun Int.isMinOrMax() = this == Int.MIN_VALUE || this == Int.MAX_VALUE

internal inline fun Size.widthPx(scale: Scale, original: () -> Int): Int {
return if (isOriginal) original() else width.toPx(scale)
}

internal inline fun Size.heightPx(scale: Scale, original: () -> Int): Int {
return if (isOriginal) original() else height.toPx(scale)
}

internal fun Dimension.toPx(scale: Scale) = pxOrElse {
when (scale) {
Scale.FILL -> Int.MIN_VALUE
Scale.FIT -> Int.MAX_VALUE
}
}

internal fun unsupported(): Nothing = error("Unsupported")

internal const val ASSET_FILE_PATH_ROOT = "android_asset"
Expand Down
12 changes: 6 additions & 6 deletions coil-base/src/test/java/coil/memory/MemoryCacheServiceTest.kt
Expand Up @@ -442,37 +442,37 @@ class MemoryCacheServiceTest {
request = request,
cached = createBitmap(width = 400, height = 200),
isSampled = true,
size = Size(400, Dimension.Original)
size = Size(400, Dimension.Undefined)
))
assertTrue(service.isCacheValueValid(
request = request,
cached = createBitmap(width = 400, height = 200),
isSampled = true,
size = Size(Dimension.Original, 200)
size = Size(Dimension.Undefined, 200)
))
assertFalse(service.isCacheValueValid(
request = request,
cached = createBitmap(width = 400, height = 200),
isSampled = true,
size = Size(450, Dimension.Original)
size = Size(450, Dimension.Undefined)
))
assertFalse(service.isCacheValueValid(
request = request,
cached = createBitmap(width = 400, height = 200),
isSampled = true,
size = Size(Dimension.Original, 250)
size = Size(Dimension.Undefined, 250)
))
assertTrue(service.isCacheValueValid(
request = request,
cached = createBitmap(width = 400, height = 200),
isSampled = false,
size = Size(450, Dimension.Original)
size = Size(450, Dimension.Undefined)
))
assertTrue(service.isCacheValueValid(
request = request,
cached = createBitmap(width = 400, height = 200),
isSampled = false,
size = Size(Dimension.Original, 250)
size = Size(Dimension.Undefined, 250)
))
}

Expand Down
2 changes: 1 addition & 1 deletion coil-base/src/test/java/coil/size/ViewSizeResolverTest.kt
Expand Up @@ -96,6 +96,6 @@ class ViewSizeResolverTest {
view.viewTreeObserver.dispatchOnPreDraw()

val size = deferred.await()
assertEquals(Size(Dimension.Original, 100), size)
assertEquals(Size(Dimension.Undefined, 100), size)
}
}
Expand Up @@ -673,8 +673,7 @@ class AsyncImageTest {

waitForRequestComplete()

// Equal to the source dimensions of 'sample.jpg'.
assertSampleLoadedBitmapSize(1024.0, 1326.0)
assertLoadedBitmapSize(SampleWidth, SampleHeight)
}

private fun waitForRequestComplete(finishedRequests: Int = 1) {
Expand Down
4 changes: 2 additions & 2 deletions coil-compose-base/src/main/java/coil/compose/AsyncImage.kt
Expand Up @@ -234,7 +234,7 @@ private fun Modifier.contentDescription(contentDescription: String?): Modifier {
private fun Constraints.toSizeOrNull() = when {
isZero -> null
else -> CoilSize(
width = if (hasBoundedWidth) Dimension(maxWidth) else Dimension.Original,
height = if (hasBoundedHeight) Dimension(maxHeight) else Dimension.Original
width = if (hasBoundedWidth) Dimension(maxWidth) else Dimension.Undefined,
height = if (hasBoundedHeight) Dimension(maxHeight) else Dimension.Undefined
)
}
Expand Up @@ -401,8 +401,8 @@ private val Size.isPositive get() = width >= 0.5 && height >= 0.5
private fun Size.toSizeOrNull() = when {
isUnspecified -> CoilSize.ORIGINAL
isPositive -> CoilSize(
width = if (width.isFinite()) Dimension(width.roundToInt()) else Dimension.Original,
height = if (height.isFinite()) Dimension(height.roundToInt()) else Dimension.Original
width = if (width.isFinite()) Dimension(width.roundToInt()) else Dimension.Undefined,
height = if (height.isFinite()) Dimension(height.roundToInt()) else Dimension.Undefined
)
else -> null
}
Expand Down
7 changes: 4 additions & 3 deletions coil-gif/src/main/java/coil/decode/ImageDecoderDecoder.kt
Expand Up @@ -17,10 +17,11 @@ import coil.request.animatedTransformation
import coil.request.animationEndCallback
import coil.request.animationStartCallback
import coil.request.repeatCount
import coil.size.pxOrElse
import coil.util.animatable2CallbackOf
import coil.util.asPostProcessor
import coil.util.heightPx
import coil.util.isHardware
import coil.util.widthPx
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runInterruptible
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -56,8 +57,8 @@ class ImageDecoderDecoder @JvmOverloads constructor(

// Configure the output image's size.
val (srcWidth, srcHeight) = info.size
val dstWidth = options.size.width.pxOrElse { srcWidth }
val dstHeight = options.size.height.pxOrElse { srcHeight }
val dstWidth = options.size.widthPx(options.scale) { srcWidth }
val dstHeight = options.size.heightPx(options.scale) { srcHeight }
if (srcWidth > 0 && srcHeight > 0 &&
(srcWidth != dstWidth || srcHeight != dstHeight)) {
val multiplier = DecodeUtils.computeSizeMultiplier(
Expand Down
20 changes: 20 additions & 0 deletions coil-gif/src/main/java/coil/util/Utils.kt
Expand Up @@ -10,6 +10,11 @@ import android.graphics.drawable.Drawable
import android.os.Build.VERSION.SDK_INT
import androidx.annotation.RequiresApi
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
import coil.size.Dimension
import coil.size.Scale
import coil.size.Size
import coil.size.isOriginal
import coil.size.pxOrElse
import coil.transform.AnimatedTransformation
import coil.transform.PixelOpacity

Expand Down Expand Up @@ -49,3 +54,18 @@ internal inline fun <T> List<T>.forEachIndices(action: (T) -> Unit) {

internal val Bitmap.Config.isHardware: Boolean
get() = SDK_INT >= 26 && this == Bitmap.Config.HARDWARE

internal inline fun Size.widthPx(scale: Scale, original: () -> Int): Int {
return if (isOriginal) original() else width.toPx(scale)
}

internal inline fun Size.heightPx(scale: Scale, original: () -> Int): Int {
return if (isOriginal) original() else height.toPx(scale)
}

internal fun Dimension.toPx(scale: Scale) = pxOrElse {
when (scale) {
Scale.FILL -> Int.MIN_VALUE
Scale.FIT -> Int.MAX_VALUE
}
}