Checks if the cropped aspect ratio matches the output size
Mismatched aspect ratio leads to stretched output, which is undesirable.
Bug: 259308680
Test: manual test and ./gradlew bOS
Change-Id: Iad5099d9662f614def9c4696985fc6632aa718f2
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/TransformUtils.java b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/TransformUtils.java
index 87c1c95..b0ddbab 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/TransformUtils.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/impl/utils/TransformUtils.java
@@ -207,6 +207,17 @@
/**
* Checks if aspect ratio matches while tolerating rounding error.
*
+ * @see #isAspectRatioMatchingWithRoundingError(Size, boolean, Size, boolean)
+ */
+ public static boolean isAspectRatioMatchingWithRoundingError(
+ @NonNull Size size1, @NonNull Size size2) {
+ return isAspectRatioMatchingWithRoundingError(
+ size1, /*isAccurate1=*/ false, size2, /*isAccurate2=*/ false);
+ }
+
+ /**
+ * Checks if aspect ratio matches while tolerating rounding error.
+ *
* <p> One example of the usage is comparing the viewport-based crop rect from different use
* cases. The crop rect is rounded because pixels are integers, which may introduce an error
* when we check if the aspect ratio matches. For example, when
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorNode.java b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorNode.java
index d00f151..a72db62 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorNode.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/processing/SurfaceProcessorNode.java
@@ -18,10 +18,12 @@
import static androidx.camera.core.impl.utils.TransformUtils.getRectToRect;
import static androidx.camera.core.impl.utils.TransformUtils.getRotatedSize;
+import static androidx.camera.core.impl.utils.TransformUtils.isAspectRatioMatchingWithRoundingError;
import static androidx.camera.core.impl.utils.TransformUtils.sizeToRect;
import static androidx.camera.core.impl.utils.TransformUtils.sizeToRectF;
import static androidx.camera.core.impl.utils.TransformUtils.within360;
import static androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor;
+import static androidx.core.util.Preconditions.checkArgument;
import android.graphics.Rect;
import android.util.Size;
@@ -132,8 +134,11 @@
sizeToRectF(outConfig.getSize()), rotationDegrees, mirroring);
sensorToBufferTransform.postConcat(imageTransform);
- // TODO(b/259308680): Checks that the aspect ratio of the rotated crop rect matches the
- // output size.
+ // The aspect ratio of the output must match the aspect ratio of the crop rect. Otherwise
+ // the output will be stretched.
+ Size rotatedCropSize = getRotatedSize(outConfig.getCropRect(), rotationDegrees);
+ checkArgument(isAspectRatioMatchingWithRoundingError(rotatedCropSize, outConfig.getSize()));
+
outputSurface = new SettableSurface(
outConfig.getTargets(),
outConfig.getSize(),
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceProcessorNodeTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceProcessorNodeTest.kt
index 9139c14..a5a6cde 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceProcessorNodeTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/processing/SurfaceProcessorNodeTest.kt
@@ -29,6 +29,7 @@
import androidx.camera.core.SurfaceRequest.TransformationInfo
import androidx.camera.core.impl.utils.TransformUtils.is90or270
import androidx.camera.core.impl.utils.TransformUtils.rectToSize
+import androidx.camera.core.impl.utils.TransformUtils.rotateSize
import androidx.camera.core.impl.utils.TransformUtils.sizeToRect
import androidx.camera.core.impl.utils.executor.CameraXExecutors.mainThreadExecutor
import androidx.camera.core.processing.SurfaceProcessorNode.OutConfig
@@ -111,8 +112,10 @@
for (rotationDegrees in arrayOf(0, 90, 180, 270)) {
// Arrange.
createSurfaceProcessorNode()
+ val videoOutputSize = rotateSize(VIDEO_SIZE, rotationDegrees - ROTATION_DEGREES)
createInputEdge(
- previewRotationDegrees = rotationDegrees
+ previewRotationDegrees = rotationDegrees,
+ videoOutputSize = videoOutputSize
)
// The result cropRect should have zero left and top.
val expectedCropRect = if (is90or270(rotationDegrees))
@@ -130,8 +133,8 @@
assertThat(previewOutput.cropRect).isEqualTo(expectedCropRect)
assertThat(previewOutput.rotationDegrees).isEqualTo(0)
val videoOutput = nodeOutput[videoOutConfig]!!
- assertThat(videoOutput.size).isEqualTo(VIDEO_SIZE)
- assertThat(videoOutput.cropRect).isEqualTo(sizeToRect(VIDEO_SIZE))
+ assertThat(videoOutput.size).isEqualTo(videoOutputSize)
+ assertThat(videoOutput.cropRect).isEqualTo(sizeToRect(videoOutputSize))
assertThat(videoOutput.rotationDegrees).isEqualTo(0)
// Clean up.
@@ -141,6 +144,15 @@
}
}
+ @Test(expected = IllegalArgumentException::class)
+ fun cropSizeMismatchesOutputSize_throwsException() {
+ createSurfaceProcessorNode()
+ createInputEdge(
+ videoOutputSize = Size(VIDEO_SIZE.width - 2, VIDEO_SIZE.height + 2)
+ )
+ node.transform(nodeInput)
+ }
+
@Test
fun transformInput_applyCropRotateAndMirroring_outputHasNoMirroring() {
for (mirroring in arrayOf(false, true)) {
@@ -267,6 +279,7 @@
previewCropRect: Rect = PREVIEW_CROP_RECT,
previewRotationDegrees: Int = ROTATION_DEGREES,
mirroring: Boolean = MIRRORING,
+ videoOutputSize: Size = VIDEO_SIZE
) {
val surface = SettableSurface(
previewTarget,
@@ -281,7 +294,7 @@
videoOutConfig = OutConfig.of(
VIDEO_CAPTURE,
VIDEO_CROP_RECT,
- VIDEO_SIZE
+ videoOutputSize
)
previewOutConfig = OutConfig.of(surface)
nodeInput = SurfaceProcessorNode.In.of(
diff --git a/camera/camera-view/src/main/java/androidx/camera/view/transform/CoordinateTransform.java b/camera/camera-view/src/main/java/androidx/camera/view/transform/CoordinateTransform.java
index 9c441dc..6994283 100644
--- a/camera/camera-view/src/main/java/androidx/camera/view/transform/CoordinateTransform.java
+++ b/camera/camera-view/src/main/java/androidx/camera/view/transform/CoordinateTransform.java
@@ -83,9 +83,8 @@
// the transform from sensor to surface. But it will require the view artifact to
// depend on a new internal API in the core artifact, which we can't do at the
// moment because of the version mismatch between view and core.
- if (!isAspectRatioMatchingWithRoundingError(
- source.getViewPortSize(), /* isAccurate1= */ false,
- target.getViewPortSize(), /* isAccurate2= */ false)) {
+ if (!isAspectRatioMatchingWithRoundingError(source.getViewPortSize(),
+ target.getViewPortSize())) {
// Mismatched aspect ratio means the outputs are not associated with the same Viewport.
Logger.w(TAG, String.format(MISMATCH_MSG, source.getViewPortSize(),
target.getViewPortSize()));