[go: nahoru, domu]

blob: 4e6b22a9548a437eb5ed43947f70c39b79d6015c [file] [log] [blame]
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.ui.core
import androidx.compose.Immutable
import androidx.compose.Stable
import androidx.ui.geometry.Size
import kotlin.math.max
import kotlin.math.min
private const val OriginalScale = 1.0f
/**
* Represents a rule to apply to scale a source rectangle to be inscribed into a destination
*/
@Stable
interface ContentScale {
/**
* Computes the scale factor to apply to both dimensions in order to fit the source
* appropriately with the given destination size
*/
fun scale(srcSize: Size, dstSize: Size): Float
/**
* Companion object containing commonly used [ContentScale] implementations
*/
companion object {
/**
* Scale the source uniformly (maintaining the source's aspect ratio) so that both
* dimensions (width and height) of the source will be equal to or larger than the
* corresponding dimension of the destination.
*
* This [ContentScale] implementation in combination with usage of [Alignment.Center]
* provides similar behavior to [android.widget.ImageView.ScaleType.CENTER_CROP]
*/
@Stable
val Crop = object : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float =
computeFillMaxDimension(srcSize, dstSize)
}
/**
* Scale the source uniformly (maintaining the source's aspect ratio) so that both
* dimensions (width and height) of the source will be equal to or less than the
* corresponding dimension of the destination
*
* This [ContentScale] implementation in combination with usage of [Alignment.Center]
* provides similar behavior to [android.widget.ImageView.ScaleType.FIT_CENTER]
*/
@Stable
val Fit = object : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float =
computeFillMinDimension(srcSize, dstSize)
}
/**
* Scale the source maintaining the aspect ratio so that the bounds match the destination
* height. This can cover a larger area than the destination if the height is larger than
* the width.
*/
@Stable
val FillHeight = object : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float =
computeFillHeight(srcSize, dstSize)
}
/**
* Scale the source maintaining the aspect ratio so that the bounds match the
* destination width. This can cover a larger area than the destination if the width is
* larger than the height.
*/
@Stable
val FillWidth = object : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float =
computeFillWidth(srcSize, dstSize)
}
/**
* Scale the source to maintain the aspect ratio to be inside the destination bounds
* if the source is larger than the destination. If the source is smaller than or equal
* to the destination in both dimensions, this behaves similarly to [None]. This will
* always be contained within the bounds of the destination.
*
* This [ContentScale] implementation in combination with usage of [Alignment.Center]
* provides similar behavior to [android.widget.ImageView.ScaleType.CENTER_INSIDE]
*/
@Stable
val Inside = object : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float =
if (srcSize.width <= dstSize.width && srcSize.height <= dstSize.height) {
OriginalScale
} else {
computeFillMinDimension(srcSize, dstSize)
}
}
/**
* Do not apply any scaling to the source
*/
@Stable
val None = FixedScale(OriginalScale)
}
}
/**
* [ContentScale] implementation that always scales the dimension by the provided
* fixed floating point value
*/
@Immutable
data class FixedScale(val value: Float) : ContentScale {
override fun scale(srcSize: Size, dstSize: Size): Float = value
}
private fun computeFillMaxDimension(srcSize: Size, dstSize: Size): Float {
val widthScale = computeFillWidth(srcSize, dstSize)
val heightScale = computeFillHeight(srcSize, dstSize)
return max(widthScale, heightScale)
}
private fun computeFillMinDimension(srcSize: Size, dstSize: Size): Float {
val widthScale = computeFillWidth(srcSize, dstSize)
val heightScale = computeFillHeight(srcSize, dstSize)
return min(widthScale, heightScale)
}
private fun computeFillWidth(srcSize: Size, dstSize: Size): Float =
dstSize.width / srcSize.width
private fun computeFillHeight(srcSize: Size, dstSize: Size): Float =
dstSize.height / srcSize.height