Merge "[CameraPipe] Fix ZoomControl linearZoom conversions and re-organize zoom conversion related code" into androidx-main
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt
index 9f5dbc4..25c172d 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraControlAdapter.kt
@@ -117,12 +117,10 @@
}
override fun setZoomRatio(ratio: Float): ListenableFuture<Void> =
- zoomControl.setZoomRatioAsync(ratio)
+ zoomControl.setZoomRatio(ratio)
- override fun setLinearZoom(linearZoom: Float): ListenableFuture<Void> {
- val ratio = zoomControl.toZoomRatio(linearZoom)
- return setZoomRatio(ratio)
- }
+ override fun setLinearZoom(linearZoom: Float): ListenableFuture<Void> =
+ zoomControl.setLinearZoom(linearZoom)
override fun getFlashMode(): Int {
return flashControl.flashMode
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZoomValue.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZoomValue.kt
index 5cc5969..8d19d82 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZoomValue.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/ZoomValue.kt
@@ -17,6 +17,8 @@
package androidx.camera.camera2.pipe.integration.adapter
import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.integration.internal.ZoomMath.getLinearZoomFromZoomRatio
+import androidx.camera.camera2.pipe.integration.internal.ZoomMath.getZoomRatioFromLinearZoom
import androidx.camera.core.ZoomState
/**
@@ -26,16 +28,39 @@
data class ZoomValue(
private val zoomRatio: Float,
private val minZoomRatio: Float,
- private val maxZoomRatio: Float
+ private val maxZoomRatio: Float,
) : ZoomState {
+ private var linearZoom: Float? = null
+
+ /**
+ * ZoomValue should be created with either zoomRatio or linearZoom and the other value should
+ * be calculated. If both are allowed to be set from outside, it becomes confusing regarding
+ * which value to use if the values don't align with conversion values.
+ * Secondary constructor with a LinearZoom value wrapper class is used for this purpose.
+ */
+ data class LinearZoom(val value: Float)
+ constructor(
+ linearZoom: LinearZoom,
+ minZoomRatio: Float,
+ maxZoomRatio: Float,
+ ) : this(
+ getZoomRatioFromLinearZoom(
+ linearZoom = linearZoom.value,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ ),
+ minZoomRatio,
+ maxZoomRatio
+ ) {
+ this.linearZoom = linearZoom.value
+ }
+
override fun getZoomRatio(): Float = zoomRatio
override fun getMaxZoomRatio(): Float = maxZoomRatio
override fun getMinZoomRatio(): Float = minZoomRatio
- override fun getLinearZoom(): Float {
- val range = maxZoomRatio - minZoomRatio
- if (range > 0) {
- return (zoomRatio - minZoomRatio) / range
- }
- return 1.0f
- }
-}
\ No newline at end of file
+ override fun getLinearZoom() = linearZoom ?: getLinearZoomFromZoomRatio(
+ zoomRatio = zoomRatio,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ )
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt
index 38d92ef..87ef64b 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/compat/ZoomCompat.kt
@@ -30,8 +30,8 @@
import dagger.Provides
interface ZoomCompat {
- val minZoom: Float
- val maxZoom: Float
+ val minZoomRatio: Float
+ val maxZoomRatio: Float
fun apply(
zoomRatio: Float,
@@ -60,11 +60,11 @@
}
class CropRegionZoomCompat(private val cameraProperties: CameraProperties) : ZoomCompat {
- override val minZoom: Float
+ override val minZoomRatio: Float
get() = 1.0f
- override val maxZoom: Float
+ override val maxZoomRatio: Float
get() = cameraProperties.metadata.getOrDefault(
- CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, minZoom
+ CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM, minZoomRatio
)
override fun apply(
@@ -93,16 +93,16 @@
@RequiresApi(Build.VERSION_CODES.R)
class AndroidRZoomCompat(private val range: Range<Float>) : ZoomCompat {
- override val minZoom: Float
+ override val minZoomRatio: Float
get() = range.lower
- override val maxZoom: Float
+ override val maxZoomRatio: Float
get() = range.upper
override fun apply(
zoomRatio: Float,
camera: UseCaseCamera
) {
- require(zoomRatio in minZoom..maxZoom)
+ require(zoomRatio in minZoomRatio..maxZoomRatio)
camera.setParameterAsync(CaptureRequest.CONTROL_ZOOM_RATIO, zoomRatio)
}
-}
\ No newline at end of file
+}
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
index 698da1b..3c6df78 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
@@ -21,6 +21,8 @@
import androidx.camera.camera2.pipe.integration.adapter.asListenableFuture
import androidx.camera.camera2.pipe.integration.compat.ZoomCompat
import androidx.camera.camera2.pipe.integration.config.CameraScope
+import androidx.camera.camera2.pipe.integration.internal.ZoomMath.getLinearZoomFromZoomRatio
+import androidx.camera.camera2.pipe.integration.internal.ZoomMath.getZoomRatioFromLinearZoom
import androidx.camera.core.CameraControl
import androidx.camera.core.ZoomState
import androidx.camera.core.impl.utils.futures.Futures
@@ -31,7 +33,6 @@
import dagger.Module
import dagger.multibindings.IntoSet
import javax.inject.Inject
-import kotlin.math.abs
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
@@ -47,11 +48,11 @@
) : UseCaseCameraControl {
// NOTE: minZoom may be lower than 1.0
// NOTE: Default zoom ratio is 1.0 (DEFAULT_ZOOM_RATIO)
- val minZoom: Float = zoomCompat.minZoom
- val maxZoom: Float = zoomCompat.maxZoom
+ val minZoomRatio: Float = zoomCompat.minZoomRatio
+ val maxZoomRatio: Float = zoomCompat.maxZoomRatio
val defaultZoomState by lazy {
- ZoomValue(DEFAULT_ZOOM_RATIO, minZoom, maxZoom)
+ ZoomValue(DEFAULT_ZOOM_RATIO, minZoomRatio, maxZoomRatio)
}
private val _zoomState by lazy {
@@ -62,28 +63,18 @@
get() = _zoomState
/** Linear zoom is between 0.0f and 1.0f */
- fun toLinearZoom(zoomRatio: Float): Float {
- val range = zoomCompat.maxZoom - zoomCompat.minZoom
- if (range > 0) {
- return (zoomRatio - zoomCompat.minZoom) / range
- }
- return 0.0f
- }
+ fun toLinearZoom(zoomRatio: Float) = getLinearZoomFromZoomRatio(
+ zoomRatio = zoomRatio,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ )
/** Zoom ratio is commonly used as the "1x, 2x, 5x" zoom ratio. Zoom ratio may be less than 1 */
- fun toZoomRatio(linearZoom: Float): Float {
- val range = zoomCompat.maxZoom - zoomCompat.minZoom
- if (range > 0) {
- return linearZoom * range + zoomCompat.minZoom
- }
-
- // if minZoom = maxZoom = 2.0f, 2.0f should be returned instead of default 1.0f
- if (nearZero(range)) {
- return zoomCompat.minZoom
- }
-
- return DEFAULT_ZOOM_RATIO
- }
+ private fun toZoomRatio(linearZoom: Float) = getZoomRatioFromLinearZoom(
+ linearZoom = linearZoom,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ )
private var _useCaseCamera: UseCaseCamera? = null
override var useCaseCamera: UseCaseCamera?
@@ -117,16 +108,29 @@
}
}
- fun setZoomRatioAsync(ratio: Float): ListenableFuture<Void> {
+ fun setLinearZoom(linearZoom: Float): ListenableFuture<Void> {
+ val zoomValue = ZoomValue(
+ ZoomValue.LinearZoom(linearZoom),
+ minZoomRatio,
+ maxZoomRatio,
+ )
+ return setZoomValue(zoomValue)
+ }
+
+ fun setZoomRatio(zoomRatio: Float): ListenableFuture<Void> {
+ val zoomValue = ZoomValue(
+ zoomRatio,
+ minZoomRatio,
+ maxZoomRatio,
+ )
+ return setZoomValue(zoomValue)
+ }
+
+ fun setZoomValue(zoomValue: ZoomValue): ListenableFuture<Void> {
// TODO: report IllegalArgumentException if ratio not in range
return Futures.nonCancellationPropagating(
useCaseCamera?.let {
threads.scope.launch(start = CoroutineStart.UNDISPATCHED) {
- val zoomValue = ZoomValue(
- ratio,
- minZoom,
- maxZoom
- )
setZoomState(zoomValue)
update()
}.asListenableFuture()
@@ -136,10 +140,6 @@
)
}
- private fun nearZero(num: Float): Boolean {
- return abs(num) < 2.0 * Math.ulp(abs(num))
- }
-
@Module
abstract class Bindings {
@Binds
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/ZoomMath.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/ZoomMath.kt
new file mode 100644
index 0000000..0114d24
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/internal/ZoomMath.kt
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2023 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.camera2.pipe.integration.internal
+
+import androidx.core.math.MathUtils
+import kotlin.math.abs
+
+/**
+ * This class is used for containing the mathematical calculations for ZoomControl, mainly the
+ * conversions between zoomRatio and linearZoom.
+ *
+ * The linearZoom is the percentage of zoom amount i.e. how much cropWidth is being used,
+ * so linearZoom = 0.5 should represent the middle point of
+ * [minZoomCropWidth, maxZoomCropWidth] range. But that does not mean it should be the same as
+ * (minZoom + maxZoom) / 2. For example, consider the case where
+ * original cropWidth = 10000 for zoomRatio = 1.0f,
+ * minZoomRatio = 1.0f, maxZoomRatio = 10.0f,
+ * zoomRatio = 5.5f may not represent linearZoom = 0.5 i.e. the half zoom amount. Here,
+ * zoomRatio = 1.0f, cropWidth = 10000,
+ * zoomRatio = 5.5f, cropWidth = 1818.18
+ * zoomRatio = 10.0f, cropWidth = 1000
+ * As observed, zoomRatio = 5.5f does not yield cropWidth = 5500 which would be the actual
+ * zooming amount middle point.
+ */
+object ZoomMath {
+ fun getLinearZoomFromZoomRatio(
+ zoomRatio: Float,
+ minZoomRatio: Float,
+ maxZoomRatio: Float
+ ): Float {
+ // if zoom is not supported i.e. minZoomRatio = maxZoomRatio, return 0
+ if (areFloatsEqual(minZoomRatio, maxZoomRatio)) {
+ return 0f
+ }
+
+ if (areFloatsEqual(zoomRatio, maxZoomRatio)) {
+ return 1f
+ } else if (areFloatsEqual(zoomRatio, minZoomRatio)) {
+ return 0f
+ }
+
+ /**
+ * linearZoom should represent the percentage of zoom amount based on how much cropWidth
+ * is visible.
+ *
+ * The original sensor region width is considered as 1.0f here as we only need the
+ * linearZoom ratio, not the actual crop width.
+ */
+ val relativeCropWidth = 1.0f / zoomRatio
+ val relativeCropWidthInMaxZoom = 1.0f / maxZoomRatio
+ val relativeCropWidthInMinZoom = 1.0f / minZoomRatio
+
+ val linearZoom = (relativeCropWidthInMinZoom - relativeCropWidth) /
+ (relativeCropWidthInMinZoom - relativeCropWidthInMaxZoom)
+
+ return MathUtils.clamp(linearZoom, 0f, 1.0f)
+ }
+
+ fun getZoomRatioFromLinearZoom(
+ linearZoom: Float,
+ minZoomRatio: Float,
+ maxZoomRatio: Float
+ ): Float {
+ if (areFloatsEqual(linearZoom, 1.0f)) {
+ return maxZoomRatio
+ } else if (areFloatsEqual(linearZoom, 0f)) {
+ return minZoomRatio
+ }
+
+ /**
+ * This crop width is proportional to the real crop width.
+ * The real crop with = sensorWidth/ zoomRatio, but we need the ratio only so we can
+ * assume sensorWidth as 1.0f.
+ */
+ val relativeCropWidthInMaxZoom = 1.0f / maxZoomRatio
+ val relativeCropWidthInMinZoom = 1.0f / minZoomRatio
+
+ val cropWidth = relativeCropWidthInMinZoom -
+ (relativeCropWidthInMinZoom - relativeCropWidthInMaxZoom) * linearZoom
+
+ val ratio = 1.0f / cropWidth
+
+ return MathUtils.clamp(ratio, minZoomRatio, maxZoomRatio)
+ }
+
+ private fun areFloatsEqual(num1: Float, num2: Float): Boolean {
+ return nearZero(num1 - num2)
+ }
+
+ private fun nearZero(num: Float): Boolean {
+ return abs(num) < 2.0 * Math.ulp(abs(num))
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
index fe14c55..685eb31 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/adapter/CameraInfoAdapterTest.kt
@@ -98,12 +98,12 @@
// if useCaseCamera is null, zoom setting operation will be cancelled
zoomControl.useCaseCamera = FakeUseCaseCamera()
- zoomControl.setZoomRatioAsync(3.0f)[3, TimeUnit.SECONDS]
+ val expectedZoomState = ZoomValue(3.0f, 1.0f, 10.0f)
+ zoomControl.setZoomValue(expectedZoomState)[3, TimeUnit.SECONDS]
- // minZoom and maxZoom will be set as 0 due to FakeZoomCompat using those values
- assertWithMessage("zoomState did not return default zoom ratio successfully")
+ assertWithMessage("zoomState did not return the correct zoom state successfully")
.that(currentZoomState)
- .isEqualTo(ZoomValue(3.0f, zoomControl.minZoom, zoomControl.maxZoom))
+ .isEqualTo(expectedZoomState)
}
@Test
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/ZoomControlTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/ZoomControlTest.kt
index 600f8ab..f1b057a 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/ZoomControlTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/ZoomControlTest.kt
@@ -66,9 +66,9 @@
@Test
fun canUpdateZoomRatioInCompat() {
- zoomControl.setZoomRatioAsync(3.0f)[3, TimeUnit.SECONDS]
+ zoomControl.setZoomRatio(3.0f)[3, TimeUnit.SECONDS]
- Truth.assertWithMessage("zoomState did not return default zoom state successfully")
+ Truth.assertWithMessage("zoomCompat not updated with correct zoom ratio")
.that(zoomCompat.zoomRatio)
.isEqualTo(3.0f)
}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/internal/ZoomMathTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/internal/ZoomMathTest.kt
new file mode 100644
index 0000000..f2fe667
--- /dev/null
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/internal/ZoomMathTest.kt
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2023 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.camera2.pipe.integration.internal
+
+import androidx.camera.camera2.pipe.integration.internal.ZoomMath.getLinearZoomFromZoomRatio
+import androidx.camera.camera2.pipe.integration.internal.ZoomMath.getZoomRatioFromLinearZoom
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+
+private const val CROP_REGION_TOLERANCE = 5f
+class ZoomMathTest {
+ private val minZoomRatio = 0.6f
+ private val maxZoomRatio = 8f
+
+ @Test
+ fun getLinearZoomFromZoomRatio_zoomRatioIsMin_linearZoomIs0() {
+ val linearZoom = getLinearZoomFromZoomRatio(
+ zoomRatio = minZoomRatio,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ )
+
+ assertThat(linearZoom).isEqualTo(0f)
+ }
+
+ @Test
+ fun getLinearZoomFromZoomRatio_zoomRatioIsMax_linearZoomIs1() {
+ val linearZoom = getLinearZoomFromZoomRatio(
+ zoomRatio = maxZoomRatio,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ )
+
+ assertThat(linearZoom).isEqualTo(1f)
+ }
+
+ @Test
+ fun getLinearZoomFromZoomRatio_zoomUnsupported_linearZoomIs0() {
+ // zoom unsupported means minZoomRatio = maxZoomRatio
+ val linearZoom = getLinearZoomFromZoomRatio(
+ zoomRatio = 1.0f,
+ minZoomRatio = 1.0f,
+ maxZoomRatio = 1.0f
+ )
+
+ assertThat(linearZoom).isEqualTo(0f)
+ }
+
+ @Test
+ fun getZoomRatioFromLinearZoom_linearZoomIs0_zoomRatioIsMin() {
+ val zoomRatio = getZoomRatioFromLinearZoom(
+ linearZoom = 0f,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ )
+
+ assertThat(zoomRatio).isEqualTo(minZoomRatio)
+ }
+
+ @Test
+ fun getZoomRatioFromLinearZoom_linearZoomIs1_zoomRatioIsMax() {
+ val zoomRatio = getZoomRatioFromLinearZoom(
+ linearZoom = 1.0f,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ )
+
+ assertThat(zoomRatio).isEqualTo(maxZoomRatio)
+ }
+
+ @Test
+ fun getZoomRatioFromLinearZoom_zoomUnsupportedAndLinearZoom0_zoomRatioIsTheAllowedValue() {
+ // zoom unsupported means minZoomRatio = maxZoomRatio
+ val zoomRatio = getZoomRatioFromLinearZoom(
+ linearZoom = 0f,
+ minZoomRatio = 1.0f,
+ maxZoomRatio = 1.0f
+ )
+
+ assertThat(zoomRatio).isEqualTo(1.0f)
+ }
+
+ @Test
+ fun getZoomRatioFromLinearZoom_zoomUnsupportedAndLinearZoom0_5f_zoomRatioIsTheAllowedValue() {
+ // zoom unsupported means minZoomRatio = maxZoomRatio
+ val zoomRatio = getZoomRatioFromLinearZoom(
+ linearZoom = 0.5f,
+ minZoomRatio = 1.0f,
+ maxZoomRatio = 1.0f
+ )
+
+ assertThat(zoomRatio).isEqualTo(1.0f)
+ }
+
+ @Test
+ fun getZoomRatioFromLinearZoom_zoomUnsupportedAndLinearZoom1_zoomRatioIsTheAllowedValue() {
+ // zoom unsupported means minZoomRatio = maxZoomRatio
+ val zoomRatio = getZoomRatioFromLinearZoom(
+ linearZoom = 1.0f,
+ minZoomRatio = 1.0f,
+ maxZoomRatio = 1.0f
+ )
+
+ assertThat(zoomRatio).isEqualTo(1.0f)
+ }
+
+ @Test
+ fun getLinearZoomFromZoomRatio_getZoomRatioFromLinearZoomReturnsSameRatio() {
+ val linearZoom = getLinearZoomFromZoomRatio(
+ zoomRatio = 2f,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ )
+
+ val zoomRatio = getZoomRatioFromLinearZoom(
+ linearZoom = linearZoom,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ )
+
+ assertThat(zoomRatio).isEqualTo(2f)
+ }
+
+ @Test
+ fun linearZoomIs0_5f_cropWidthIsHalf() {
+ val sensorRegionWidth = 10000f
+ val minZoomCropWidth = sensorRegionWidth / minZoomRatio
+ val maxZoomCropWidth = sensorRegionWidth / maxZoomRatio
+
+ val zoomRatio = getZoomRatioFromLinearZoom(
+ linearZoom = 0.5f,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ )
+ val cropWidth = sensorRegionWidth / zoomRatio
+
+ assertThat(cropWidth)
+ .isWithin(CROP_REGION_TOLERANCE)
+ .of((minZoomCropWidth + maxZoomCropWidth) / 2)
+ }
+
+ @Test
+ fun linearZoomIsIncreasedProgressively_cropWidthIsChangedLinearly() {
+ val sensorRegionWidth = 10000f
+ var previousCropWidth = sensorRegionWidth / minZoomRatio
+ var previousCropWidthDiff = Float.NaN
+
+ var linearZoom = 0.1f
+
+ while (linearZoom < 1f) {
+ val zoomRatio = getZoomRatioFromLinearZoom(
+ linearZoom = linearZoom,
+ minZoomRatio = minZoomRatio,
+ maxZoomRatio = maxZoomRatio
+ )
+
+ val cropWidth = sensorRegionWidth / zoomRatio
+ val cropWidthDiff = previousCropWidth - cropWidth
+
+ if (!previousCropWidthDiff.isNaN()) {
+ assertThat(cropWidthDiff)
+ .isWithin(CROP_REGION_TOLERANCE)
+ .of(previousCropWidthDiff)
+ }
+
+ previousCropWidthDiff = cropWidthDiff
+ previousCropWidth = cropWidth
+ linearZoom += 0.1f
+ }
+ }
+}
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeZoomCompat.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeZoomCompat.kt
index cabac1a..6feabbd 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeZoomCompat.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeZoomCompat.kt
@@ -20,8 +20,8 @@
import androidx.camera.camera2.pipe.integration.impl.UseCaseCamera
class FakeZoomCompat constructor(
- override val minZoom: Float = 0f,
- override val maxZoom: Float = 0f,
+ override val minZoomRatio: Float = 0f,
+ override val maxZoomRatio: Float = 0f,
) : ZoomCompat {
var zoomRatio = 0f
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ZoomControlDeviceTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ZoomControlDeviceTest.kt
index d41a8ce..0d7512c 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ZoomControlDeviceTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/ZoomControlDeviceTest.kt
@@ -278,7 +278,7 @@
@Test
fun setZoomRatioBy2_0_cropRegionIsSetCorrectly() = runBlocking {
- assumeTrue(getMaxDigitalZoom() != null && getMaxDigitalZoom()!! <= 2.0f + DELTA)
+ assumeTrue(getMaxDigitalZoom() != null && getMaxDigitalZoom()!! > 2.0f + DELTA)
checkTestPreconditions(isAndroidRZoom = false)
@@ -342,11 +342,6 @@
@Test
fun setLinearZoomBy0_5_isHalfCropWidth() = runBlocking {
- assumeFalse(
- "b/267665704: CameraPipe linear zoom conversion to zoom ratio is not correct",
- implName == "CameraPipeConfig"
- )
-
checkTestPreconditions(isAndroidRZoom = false)
// crop region in percentage == 0 may be null, need to use sensor rect instead.
@@ -363,11 +358,6 @@
@Test
@SdkSuppress(minSdkVersion = 30)
fun setLinearZoomBy0_5_androidRZoomRatioUpdatedCorrectly() = runBlocking {
- assumeFalse(
- "b/267665704: CameraPipe linear zoom conversion to zoom ratio is not correct",
- implName == "CameraPipeConfig"
- )
-
checkTestPreconditions(isAndroidRZoom = true)
val cropWidth = 10000f
@@ -388,11 +378,6 @@
@Test
fun setLinearZoom_cropWidthChangedLinearly() = runBlocking {
- assumeFalse(
- "b/267665704: CameraPipe linear zoom conversion to zoom ratio is not correct",
- implName == "CameraPipeConfig"
- )
-
checkTestPreconditions(isAndroidRZoom = false)
// crop region in percentage == 0 may be null, need to use sensor rect instead.
@@ -421,11 +406,6 @@
@RequiresApi(Build.VERSION_CODES.R)
@Test
fun setLinearZoom_androidRZoomRatio_cropWidthChangedLinearly() = runBlocking {
- assumeFalse(
- "b/267665704: CameraPipe linear zoom conversion to zoom ratio is not correct",
- implName == "CameraPipeConfig"
- )
-
checkTestPreconditions(isAndroidRZoom = true)
val cropWidth = 10000f
@@ -527,7 +507,7 @@
@Test
fun getZoomRatioLiveData_observerIsCalledWhenZoomRatioIsSet() = runBlocking {
- assumeTrue(getMaxDigitalZoom() != null && getMaxDigitalZoom()!! <= 2.0f + DELTA)
+ assumeTrue(getMaxDigitalZoom() != null && getMaxDigitalZoom()!! > 2.0f + DELTA)
val latch1 = CountDownLatch(1)
val latch2 = CountDownLatch(1)
@@ -579,11 +559,6 @@
@Test
fun getZoomPercentageLiveData_observerIsCalledWhenZoomPercentageIsSet() = runBlocking {
- assumeFalse(
- "b/267665704: CameraPipe linear zoom conversion to zoom ratio is not correct",
- implName == "CameraPipeConfig"
- )
-
val latch1 = CountDownLatch(1)
val latch2 = CountDownLatch(1)
val latch3 = CountDownLatch(1)
@@ -611,7 +586,7 @@
@Test
fun getZoomPercentageLiveData_observerIsCalledWhenZoomRatioIsSet() = runBlocking {
- assumeTrue(getMaxDigitalZoom() != null && getMaxDigitalZoom()!! <= 2.0f + DELTA)
+ assumeTrue(getMaxDigitalZoom() != null && getMaxDigitalZoom()!! > 2.0f + DELTA)
val latch = CountDownLatch(3)
withContext(Dispatchers.Main) {