Merge "Allow Preview to select 1080p resolution when device display has one shorter edge but larger area size" into androidx-main
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
index 416b0e4..4d083da 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/adapter/CameraUseCaseAdapter.kt
@@ -35,7 +35,6 @@
import androidx.camera.core.impl.Config
import androidx.camera.core.impl.ImageCaptureConfig
import androidx.camera.core.impl.ImageOutputConfig
-import androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR
import androidx.camera.core.impl.MutableOptionsBundle
import androidx.camera.core.impl.OptionsBundle
import androidx.camera.core.impl.PreviewConfig
@@ -43,8 +42,6 @@
import androidx.camera.core.impl.UseCaseConfig
import androidx.camera.core.impl.UseCaseConfigFactory
import androidx.camera.core.impl.UseCaseConfigFactory.CaptureType
-import androidx.camera.core.resolutionselector.ResolutionSelector
-import androidx.camera.core.resolutionselector.ResolutionStrategy
/**
* This class builds [Config] objects for a given [UseCaseConfigFactory.CaptureType].
@@ -136,15 +133,6 @@
ImageOutputConfig.OPTION_MAX_RESOLUTION,
previewSize
)
- mutableConfig.insertOption(
- OPTION_RESOLUTION_SELECTOR,
- ResolutionSelector.Builder().setResolutionStrategy(
- ResolutionStrategy(
- previewSize,
- ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER
- )
- ).build()
- )
}
mutableConfig.insertOption(
diff --git a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
index 3d271cd..0010019 100644
--- a/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
+++ b/camera/camera-camera2/src/main/java/androidx/camera/camera2/internal/Camera2UseCaseConfigFactory.java
@@ -17,7 +17,6 @@
package androidx.camera.camera2.internal;
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_MAX_RESOLUTION;
-import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ROTATION;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_CAPTURE_CONFIG_UNPACKER;
import static androidx.camera.core.impl.UseCaseConfig.OPTION_DEFAULT_CAPTURE_CONFIG;
@@ -37,8 +36,6 @@
import androidx.camera.core.impl.OptionsBundle;
import androidx.camera.core.impl.SessionConfig;
import androidx.camera.core.impl.UseCaseConfigFactory;
-import androidx.camera.core.resolutionselector.ResolutionSelector;
-import androidx.camera.core.resolutionselector.ResolutionStrategy;
/**
* Implementation of UseCaseConfigFactory to provide the default camera2 configurations for use
@@ -86,11 +83,6 @@
if (captureType == CaptureType.PREVIEW) {
Size previewSize = mDisplayInfoManager.getPreviewSize();
mutableConfig.insertOption(OPTION_MAX_RESOLUTION, previewSize);
- ResolutionStrategy resolutionStrategy = new ResolutionStrategy(previewSize,
- ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER);
- mutableConfig.insertOption(OPTION_RESOLUTION_SELECTOR,
- new ResolutionSelector.Builder().setResolutionStrategy(
- resolutionStrategy).build());
}
// The default rotation value should be determined by the max non-state-off display.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
index 6661ecb..e36507c 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/Preview.java
@@ -1080,10 +1080,8 @@
* size match to the device's screen resolution, or to 1080p (1920x1080), whichever is
* smaller. See the
* <a href="https://developer.android.com/reference/android/hardware/camera2/CameraDevice#regular-capture">Regular capture</a>
- * section in {@link android.hardware.camera2.CameraDevice}'. {@link Preview} has a
- * default {@link ResolutionStrategy} with the {@code PREVIEW} bound size and
- * {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_LOWER} to achieve this. Applications
- * can override this default strategy with a different resolution strategy.
+ * section in {@link android.hardware.camera2.CameraDevice}'. Applications can set any
+ * {@link ResolutionStrategy} to override it.
*
* <p>Note that due to compatibility reasons, CameraX may select a resolution that is
* larger than the default screen resolution on certain devices.
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
index 396e646..a002de7 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/UseCase.java
@@ -19,6 +19,7 @@
import static androidx.camera.core.MirrorMode.MIRROR_MODE_OFF;
import static androidx.camera.core.MirrorMode.MIRROR_MODE_ON;
import static androidx.camera.core.MirrorMode.MIRROR_MODE_ON_FRONT_ONLY;
+import static androidx.camera.core.impl.ImageOutputConfig.OPTION_MAX_RESOLUTION;
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_RESOLUTION_SELECTOR;
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_ASPECT_RATIO;
import static androidx.camera.core.impl.ImageOutputConfig.OPTION_TARGET_RESOLUTION;
@@ -226,6 +227,17 @@
}
}
+ // Removes the default max resolution setting if application sets any ResolutionStrategy
+ // to override it.
+ if (mUseCaseConfig.containsOption(OPTION_RESOLUTION_SELECTOR)
+ && mergedConfig.containsOption(OPTION_MAX_RESOLUTION)) {
+ ResolutionSelector resolutionSelector =
+ mUseCaseConfig.retrieveOption(OPTION_RESOLUTION_SELECTOR);
+ if (resolutionSelector.getResolutionStrategy() != null) {
+ mergedConfig.removeOption(OPTION_MAX_RESOLUTION);
+ }
+ }
+
// If any options need special handling, this is the place to do it. For now we'll just copy
// over all options.
for (Option<?> opt : mUseCaseConfig.listOptions()) {
diff --git a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
index 7bbe75e..be65059 100644
--- a/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
+++ b/camera/camera-core/src/main/java/androidx/camera/core/internal/SupportedOutputSizesSorter.java
@@ -40,6 +40,7 @@
import androidx.camera.core.impl.utils.AspectRatioUtil;
import androidx.camera.core.impl.utils.CameraOrientationUtil;
import androidx.camera.core.impl.utils.CompareSizesByArea;
+import androidx.camera.core.internal.utils.SizeUtil;
import androidx.camera.core.resolutionselector.AspectRatioStrategy;
import androidx.camera.core.resolutionselector.ResolutionFilter;
import androidx.camera.core.resolutionselector.ResolutionSelector;
@@ -216,6 +217,13 @@
applyAspectRatioStrategy(resolutionCandidateList,
resolutionSelector.getAspectRatioStrategy());
+
+ // Applies the max resolution setting
+ Size maxResolution = ((ImageOutputConfig) useCaseConfig).getMaxResolution(null);
+ if (maxResolution != null) {
+ applyMaxResolutionRestriction(aspectRatioSizeListMap, maxResolution);
+ }
+
// Applies the resolution strategy onto the resolution candidate list.
applyResolutionStrategy(aspectRatioSizeListMap, resolutionSelector.getResolutionStrategy());
@@ -436,6 +444,32 @@
}
/**
+ * Applies the max resolution restriction.
+ *
+ * <p>Filters out the output sizes that exceed the max resolution in area size.
+ *
+ * @param sortedAspectRatioSizeListMap the aspect ratio to size list linked hash map. The
+ * entries order should not be changed.
+ * @param maxResolution the max resolution size.
+ */
+ private static void applyMaxResolutionRestriction(
+ @NonNull LinkedHashMap<Rational, List<Size>> sortedAspectRatioSizeListMap,
+ @NonNull Size maxResolution) {
+ int maxResolutionAreaSize = SizeUtil.getArea(maxResolution);
+ for (Rational key : sortedAspectRatioSizeListMap.keySet()) {
+ List<Size> supportedSizesList = sortedAspectRatioSizeListMap.get(key);
+ List<Size> filteredResultList = new ArrayList<>();
+ for (Size size : supportedSizesList) {
+ if (SizeUtil.getArea(size) <= maxResolutionAreaSize) {
+ filteredResultList.add(size);
+ }
+ }
+ supportedSizesList.clear();
+ supportedSizesList.addAll(filteredResultList);
+ }
+ }
+
+ /**
* Applies the resolution filtered to the sorted output size list.
*
* @param sizeList the supported size list which has been filtered and sorted by the
diff --git a/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt
index 3cb7587..e79c482 100644
--- a/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt
+++ b/camera/camera-core/src/test/java/androidx/camera/core/internal/SupportedOutputSizesSorterTest.kt
@@ -540,6 +540,28 @@
supportedOutputSizesSorter.getSortedSupportedOutputSizes(useCaseConfig)
}
+ @Test
+ fun canKeepFhdResolution_whenMaxResolutionHasShorterEdgeButLargerArea() {
+ verifySupportedOutputSizesWithResolutionSelectorSettings(
+ maxResolution = Size(2244, 1008),
+ expectedList = listOf(
+ // Matched default preferred AspectRatio items, sorted by area size.
+ Size(1280, 960),
+ Size(640, 480),
+ Size(320, 240),
+ // Mismatched default preferred AspectRatio items, sorted by FOV and area size.
+ Size(960, 960), // 1:1
+ Size(1920, 1080), // 16:9, this can be kept even the max resolution
+ // setting has a shorter edge of 1008
+ Size(1280, 720),
+ Size(960, 544),
+ Size(800, 450),
+ Size(320, 180),
+ Size(256, 144),
+ )
+ )
+ }
+
private fun verifySupportedOutputSizesWithResolutionSelectorSettings(
outputSizesSorter: SupportedOutputSizesSorter = supportedOutputSizesSorter,
captureType: CaptureType = CaptureType.IMAGE_CAPTURE,
@@ -551,6 +573,7 @@
resolutionFilter: ResolutionFilter? = null,
allowedResolutionMode: Int = ResolutionSelector.PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION,
highResolutionForceDisabled: Boolean = false,
+ maxResolution: Size? = null,
expectedList: List<Size> = Collections.emptyList(),
) {
val useCaseConfig = createUseCaseConfig(
@@ -562,7 +585,8 @@
resolutionFallbackRule,
resolutionFilter,
allowedResolutionMode,
- highResolutionForceDisabled
+ highResolutionForceDisabled,
+ maxResolution,
)
val resultList = outputSizesSorter.getSortedSupportedOutputSizes(useCaseConfig)
assertThat(resultList).containsExactlyElementsIn(expectedList).inOrder()
@@ -578,6 +602,7 @@
resolutionFilter: ResolutionFilter? = null,
allowedResolutionMode: Int = ResolutionSelector.PREFER_CAPTURE_RATE_OVER_HIGHER_RESOLUTION,
highResolutionForceDisabled: Boolean = false,
+ maxResolution: Size? = null,
): UseCaseConfig<*> {
val useCaseConfigBuilder = FakeUseCaseConfig.Builder(captureType, ImageFormat.JPEG)
val resolutionSelectorBuilder = ResolutionSelector.Builder()
@@ -616,6 +641,9 @@
// Sets the high resolution force disabled setting
useCaseConfigBuilder.setHighResolutionDisabled(highResolutionForceDisabled)
+ // Sets the max resolution setting
+ maxResolution?.let { useCaseConfigBuilder.setMaxResolution(it) }
+
return useCaseConfigBuilder.useCaseConfig
}
}
diff --git a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
index 00a4ae3..569a333 100644
--- a/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
+++ b/camera/integration-tests/coretestapp/src/androidTest/java/androidx/camera/integration/core/camera2/PreviewTest.kt
@@ -35,6 +35,7 @@
import androidx.camera.core.internal.CameraUseCaseAdapter
import androidx.camera.core.resolutionselector.ResolutionSelector
import androidx.camera.core.resolutionselector.ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE
+import androidx.camera.core.resolutionselector.ResolutionStrategy
import androidx.camera.testing.CameraPipeConfigTestRule
import androidx.camera.testing.CameraUtil
import androidx.camera.testing.CameraUtil.PreTestCameraIdList
@@ -583,6 +584,39 @@
Truth.assertThat(surfaceFutureSemaphore!!.tryAcquire(10, TimeUnit.SECONDS)).isTrue()
}
+ @Test
+ fun defaultMaxResolutionCanBeKept_whenResolutionStrategyIsNotSet() {
+ assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK))
+ val useCase = Preview.Builder().build()
+ camera = CameraUtil.createCameraAndAttachUseCase(
+ context!!,
+ CameraSelector.DEFAULT_BACK_CAMERA, useCase
+ )
+ Truth.assertThat(
+ useCase.currentConfig.containsOption(
+ ImageOutputConfig.OPTION_MAX_RESOLUTION
+ )
+ ).isTrue()
+ }
+
+ @Test
+ fun defaultMaxResolutionCanBeRemoved_whenResolutionStrategyIsSet() {
+ assumeTrue(CameraUtil.hasCameraWithLensFacing(CameraSelector.LENS_FACING_BACK))
+ val useCase = Preview.Builder().setResolutionSelector(
+ ResolutionSelector.Builder()
+ .setResolutionStrategy(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY).build()
+ ).build()
+ camera = CameraUtil.createCameraAndAttachUseCase(
+ context!!,
+ CameraSelector.DEFAULT_BACK_CAMERA, useCase
+ )
+ Truth.assertThat(
+ useCase.currentConfig.containsOption(
+ ImageOutputConfig.OPTION_MAX_RESOLUTION
+ )
+ ).isFalse()
+ }
+
private val workExecutorWithNamedThread: Executor
get() {
val threadFactory =