[go: nahoru, domu]

blob: f2a118dc4055e294f15affd8851f7c15270aea43 [file] [log] [blame]
/*
* Copyright 2024 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.camera.viewfinder.compose.internal
import android.graphics.Matrix
import android.graphics.RectF
import android.util.Size
import androidx.camera.viewfinder.surface.TransformationInfo
/**
* A util class with methods that transform the input viewfinder surface so that its preview fits
* the given aspect ratio of its parent view.
*
* The goal is to transform it in a way so that the entire area of
* TransformationInfo's cropRect is 1) visible to end users, and 2)
* displayed as large as possible.
*
* The inputs for the calculation are 1) the dimension of the Surface, 2) the crop rect, 3) the
* dimension of the Viewfinder and 4) rotation degrees
*/
object SurfaceTransformationUtil {
fun getTextureViewCorrectionMatrix(
transformationInfo: TransformationInfo,
resolution: Size
): Matrix {
val surfaceRect =
RectF(0f, 0f, resolution.width.toFloat(), resolution.height.toFloat())
val rotationDegrees: Int = transformationInfo.sourceRotation
return TransformUtil.getRectToRect(surfaceRect, surfaceRect, rotationDegrees)
}
private fun getRotatedViewportSize(
transformationInfo: TransformationInfo
): Size {
return if (TransformUtil.is90or270(transformationInfo.sourceRotation)) {
Size(
transformationInfo.cropRectBottom - transformationInfo.cropRectTop,
transformationInfo.cropRectRight - transformationInfo.cropRectLeft)
} else {
Size(
transformationInfo.cropRectRight - transformationInfo.cropRectLeft,
transformationInfo.cropRectBottom - transformationInfo.cropRectTop)
}
}
private fun isViewportAspectRatioMatchViewFinder(
transformationInfo: TransformationInfo,
viewfinderSize: Size
): Boolean {
// Using viewport rect to check if the viewport is based on the view finder.
val rotatedViewportSize: Size = getRotatedViewportSize(transformationInfo)
return TransformUtil.isAspectRatioMatchingWithRoundingError(
viewfinderSize,
true,
rotatedViewportSize,
false
)
}
private fun setMatrixRectToRect(matrix: Matrix, source: RectF, destination: RectF) {
val matrixScaleType = Matrix.ScaleToFit.CENTER
// android.graphics.Matrix doesn't support fill scale types. The workaround is
// mapping inversely from destination to source, then invert the matrix.
matrix.setRectToRect(destination, source, matrixScaleType)
matrix.invert(matrix)
}
private fun getViewfinderViewportRectForMismatchedAspectRatios(
transformationInfo: TransformationInfo,
viewfinderSize: Size
): RectF {
val viewfinderRect =
RectF(
0f,
0f,
viewfinderSize.width.toFloat(),
viewfinderSize.height.toFloat()
)
val rotatedViewportSize = getRotatedViewportSize(transformationInfo)
val rotatedViewportRect =
RectF(
0f,
0f,
rotatedViewportSize.width.toFloat(),
rotatedViewportSize.height.toFloat()
)
val matrix = Matrix()
setMatrixRectToRect(
matrix,
rotatedViewportRect,
viewfinderRect
)
matrix.mapRect(rotatedViewportRect)
return rotatedViewportRect
}
private fun getSurfaceToViewFinderMatrix(
viewfinderSize: Size,
transformationInfo: TransformationInfo,
): Matrix {
// Get the target of the mapping, the coordinates of the crop rect in view finder.
val viewfinderCropRect: RectF =
if (isViewportAspectRatioMatchViewFinder(transformationInfo, viewfinderSize)) {
// If crop rect has the same aspect ratio as view finder, scale the crop rect to
// fill the entire view finder. This happens if the scale type is FILL_* AND a
// view-finder-based viewport is used.
RectF(
0f,
0f,
viewfinderSize.width.toFloat(),
viewfinderSize.height.toFloat()
)
} else {
// If the aspect ratios don't match, it could be 1) scale type is FIT_*, 2) the
// Viewport is not based on the view finder or 3) both.
getViewfinderViewportRectForMismatchedAspectRatios(
transformationInfo,
viewfinderSize
)
}
val surfaceCropRect = RectF(transformationInfo.cropRectLeft.toFloat(),
transformationInfo.cropRectTop.toFloat(),
transformationInfo.cropRectRight.toFloat(),
transformationInfo.cropRectBottom.toFloat())
val matrix =
TransformUtil.getRectToRect(
surfaceCropRect,
viewfinderCropRect,
transformationInfo.sourceRotation
)
if (transformationInfo.shouldMirror) {
if (TransformUtil.is90or270(transformationInfo.sourceRotation)) {
// If the rotation is 90/270, the Surface should be flipped vertically.
// +---+ 90 +---+ 270 +---+
// | ^ | --> | < | | > |
// +---+ +---+ +---+
matrix.preScale(
1f,
-1f,
surfaceCropRect.centerX(),
surfaceCropRect.centerY()
)
} else {
// If the rotation is 0/180, the Surface should be flipped horizontally.
// +---+ 0 +---+ 180 +---+
// | ^ | --> | ^ | | v |
// +---+ +---+ +---+
matrix.preScale(
-1f,
1f,
surfaceCropRect.centerX(),
surfaceCropRect.centerY()
)
}
}
return matrix
}
fun getTransformedSurfaceRect(
resolution: Size,
transformationInfo: TransformationInfo,
viewfinderSize: Size,
): RectF {
val surfaceToViewFinder: Matrix =
getSurfaceToViewFinderMatrix(
viewfinderSize,
transformationInfo,
)
val rect = RectF(0f, 0f, resolution.width.toFloat(), resolution.height.toFloat())
surfaceToViewFinder.mapRect(rect)
return rect
}
}