[go: nahoru, domu]

blob: 45efcbd309e86856156822c8f80f8a7c8a7c65b7 [file] [log] [blame]
/*
* 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.core.internal;
import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_16_9;
import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_3_4;
import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_4_3;
import static androidx.camera.core.impl.utils.AspectRatioUtil.ASPECT_RATIO_9_16;
import static androidx.camera.core.impl.utils.AspectRatioUtil.hasMatchingAspectRatio;
import android.graphics.ImageFormat;
import android.util.Pair;
import android.util.Rational;
import android.util.Size;
import android.view.Surface;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.Logger;
import androidx.camera.core.impl.CameraInfoInternal;
import androidx.camera.core.impl.ImageOutputConfig;
import androidx.camera.core.impl.UseCaseConfig;
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;
import androidx.camera.core.resolutionselector.ResolutionStrategy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* The supported output sizes collector to help collect the available resolution candidate list
* according to the use case config and the following settings in {@link ResolutionSelector}:
*
* <ul>
* <li>Aspect ratio strategy
* <li>Resolution strategy
* <li>Custom resolution filter
* <li>High resolution enabled flags
* </ul>
*/
@RequiresApi(21)
public class SupportedOutputSizesSorter {
private static final String TAG = "SupportedOutputSizesCollector";
private final CameraInfoInternal mCameraInfoInternal;
private final int mSensorOrientation;
private final int mLensFacing;
private final Rational mFullFovRatio;
private final SupportedOutputSizesSorterLegacy mSupportedOutputSizesSorterLegacy;
SupportedOutputSizesSorter(@NonNull CameraInfoInternal cameraInfoInternal,
@Nullable Size activeArraySize) {
mCameraInfoInternal = cameraInfoInternal;
mSensorOrientation = mCameraInfoInternal.getSensorRotationDegrees();
mLensFacing = mCameraInfoInternal.getLensFacing();
mFullFovRatio = activeArraySize != null ? calculateFullFovRatioFromActiveArraySize(
activeArraySize) : calculateFullFovRatioFromSupportedOutputSizes(
mCameraInfoInternal);
mSupportedOutputSizesSorterLegacy =
new SupportedOutputSizesSorterLegacy(cameraInfoInternal, mFullFovRatio);
}
/**
* Calculates the full FOV ratio by the active array size.
*/
@NonNull
private Rational calculateFullFovRatioFromActiveArraySize(@NonNull Size activeArraySize) {
return new Rational(activeArraySize.getWidth(), activeArraySize.getHeight());
}
/**
* Calculates the full FOV ratio by the output sizes retrieved from CameraInfoInternal.
*
* <p>For most devices, the full FOV ratio should match the aspect ratio of the max supported
* output sizes. The active pixel array info is not used because it may cause robolectric
* test to fail if it is not set in the test environment.
*/
@Nullable
private Rational calculateFullFovRatioFromSupportedOutputSizes(
@NonNull CameraInfoInternal cameraInfoInternal) {
List<Size> jpegOutputSizes = cameraInfoInternal.getSupportedResolutions(ImageFormat.JPEG);
if (jpegOutputSizes.isEmpty()) {
return null;
}
Size maxSize = Collections.max(jpegOutputSizes, new CompareSizesByArea());
return new Rational(maxSize.getWidth(), maxSize.getHeight());
}
/**
* Returns the sorted output sizes according to the use case config.
*
* <p>If ResolutionSelector is specified in the use case config, the output sizes will be
* sorted according to the ResolutionSelector setting and logic. Otherwise, the output sizes
* will be sorted according to the legacy resolution API settings and logic.
*/
@NonNull
List<Size> getSortedSupportedOutputSizes(@NonNull UseCaseConfig<?> useCaseConfig) {
ImageOutputConfig imageOutputConfig = (ImageOutputConfig) useCaseConfig;
List<Size> customOrderedResolutions = imageOutputConfig.getCustomOrderedResolutions(null);
// Directly returns the custom ordered resolutions list if it is set.
if (customOrderedResolutions != null) {
return customOrderedResolutions;
}
ResolutionSelector resolutionSelector = imageOutputConfig.getResolutionSelector(null);
List<Pair<Integer, Size[]>> customResolutions =
imageOutputConfig.getSupportedResolutions(null);
List<Size> candidateSizes = getResolutionCandidateList(customResolutions,
useCaseConfig.getInputFormat());
if (resolutionSelector == null) {
return mSupportedOutputSizesSorterLegacy.sortSupportedOutputSizes(
candidateSizes, useCaseConfig);
} else {
Size maxResolution = ((ImageOutputConfig) useCaseConfig).getMaxResolution(null);
int targetRotation = imageOutputConfig.getTargetRotation(Surface.ROTATION_0);
// Applies the high resolution settings onto the resolution candidate list.
if (!useCaseConfig.isHigResolutionDisabled(false)) {
candidateSizes = applyHighResolutionSettings(candidateSizes,
resolutionSelector, useCaseConfig.getInputFormat());
}
return sortSupportedOutputSizesByResolutionSelector(
imageOutputConfig.getResolutionSelector(),
candidateSizes,
maxResolution,
targetRotation,
mFullFovRatio,
mSensorOrientation,
mLensFacing);
}
}
@Nullable
private List<Size> getSizeListByFormat(
@Nullable List<Pair<Integer, Size[]>> resolutionsPairList,
int imageFormat) {
Size[] outputSizes = null;
if (resolutionsPairList != null) {
for (Pair<Integer, Size[]> formatResolutionPair : resolutionsPairList) {
if (formatResolutionPair.first == imageFormat) {
outputSizes = formatResolutionPair.second;
break;
}
}
}
return outputSizes == null ? null : Arrays.asList(outputSizes);
}
/**
* Sorts the resolution candidate list according to the ResolutionSelector API logic.
*
* <ol>
* <li>Applies the aspect ratio strategy
* <ul>
* <li>Applies the aspect ratio strategy fallback rule
* </ul>
* <li>Applies the resolution strategy
* <ul>
* <li>Applies the resolution strategy fallback rule
* </ul>
* <li>Applies the resolution filter
* </ol>
* @param resolutionSelector the ResolutionSelector used to sort the candidate
* sizes.
* @param candidateSizes the candidate sizes after the high resolution processing, which
* will be sorted by the rule of ResolutionSelector.
* @param maxResolution the max resolutions which sizes larger than it will be removed
* from candidate sizes.
* @param targetRotation the target rotation to calculate the rotation degrees to the
* {@link ResolutionFilter}.
* @param fullFovRatio the full FOV's aspect ratio.
* @param sensorOrientation the sensor orientation of the current camera.
* @param lensFacing the lens facing of the current camera
* @return a size list which has been filtered and sorted by the specified resolution
* selector settings.
* @throws IllegalArgumentException if the specified resolution filter returns any size which
* is not included in the provided supported size list.
*/
@NonNull
public static List<Size> sortSupportedOutputSizesByResolutionSelector(
@NonNull ResolutionSelector resolutionSelector,
@NonNull List<Size> candidateSizes,
@Nullable Size maxResolution,
int targetRotation,
@NonNull Rational fullFovRatio,
int sensorOrientation,
int lensFacing) {
// Applies the aspect ratio strategy onto the resolution candidate list.
LinkedHashMap<Rational, List<Size>> aspectRatioSizeListMap =
applyAspectRatioStrategy(candidateSizes,
resolutionSelector.getAspectRatioStrategy(), fullFovRatio);
// Applies the max resolution setting
if (maxResolution != null) {
applyMaxResolutionRestriction(aspectRatioSizeListMap, maxResolution);
}
// Applies the resolution strategy onto the resolution candidate list.
applyResolutionStrategy(aspectRatioSizeListMap, resolutionSelector.getResolutionStrategy());
// Collects all sizes from the sorted aspect ratio size groups into the final sorted list.
List<Size> resultList = new ArrayList<>();
for (List<Size> sortedSizes : aspectRatioSizeListMap.values()) {
for (Size size : sortedSizes) {
// A size may exist in multiple groups in mod16 condition. Keep only one in
// the final list.
if (!resultList.contains(size)) {
resultList.add(size);
}
}
}
// Applies the resolution filter onto the resolution candidate list.
return applyResolutionFilter(resultList, resolutionSelector.getResolutionFilter(),
targetRotation, sensorOrientation, lensFacing);
}
/**
* Returns the normal supported output sizes.
*
* <p>When using camera-camera2 implementation, the output sizes are retrieved via
* StreamConfigurationMap#getOutputSizes().
*
* @return the resolution candidate list sorted in descending order.
*/
@NonNull
private List<Size> getResolutionCandidateList(
@Nullable List<Pair<Integer, Size[]>> customResolutions, int imageFormat) {
// Tries to get the custom supported resolutions list if it is set
List<Size> resolutionCandidateList = getSizeListByFormat(customResolutions, imageFormat);
// Tries to get the supported output sizes from the CameraInfoInternal if both custom
// ordered and supported resolutions lists are not set.
if (resolutionCandidateList == null) {
resolutionCandidateList = mCameraInfoInternal.getSupportedResolutions(imageFormat);
}
// CameraInfoInternal.getSupportedResolutions is not guaranteed to return a modifiable list
// needed by Collections.sort(), so it is converted to a modifiable list here
resolutionCandidateList = new ArrayList<>(resolutionCandidateList);
Collections.sort(resolutionCandidateList, new CompareSizesByArea(true));
if (resolutionCandidateList.isEmpty()) {
Logger.w(TAG, "The retrieved supported resolutions from camera info internal is empty"
+ ". Format is " + imageFormat + ".");
}
return resolutionCandidateList;
}
/**
* Appends the high resolution supported output sizes according to the high resolution settings.
*
* <p>When using camera-camera2 implementation, the output sizes are retrieved via
* StreamConfigurationMap#getHighResolutionOutputSizes().
*
* @param resolutionCandidateList the supported size list which contains only normal output
* sizes.
* @param resolutionSelector the specified resolution selector.
* @param imageFormat the desired image format for the target use case.
* @return the resolution candidate list including the high resolution output sizes sorted in
* descending order.
*/
@NonNull
private List<Size> applyHighResolutionSettings(@NonNull List<Size> resolutionCandidateList,
@NonNull ResolutionSelector resolutionSelector, int imageFormat) {
// Appends high resolution output sizes if high resolution is enabled by ResolutionSelector
if (resolutionSelector.getAllowedResolutionMode()
== ResolutionSelector.PREFER_HIGHER_RESOLUTION_OVER_CAPTURE_RATE) {
List<Size> allSizesList = new ArrayList<>();
allSizesList.addAll(resolutionCandidateList);
allSizesList.addAll(mCameraInfoInternal.getSupportedHighResolutions(imageFormat));
Collections.sort(allSizesList, new CompareSizesByArea(true));
return allSizesList;
}
return resolutionCandidateList;
}
/**
* Applies the aspect ratio strategy onto the input resolution candidate list.
*
* @param resolutionCandidateList the supported sizes list which has been sorted in
* descending order.
* @param aspectRatioStrategy the specified aspect ratio strategy.
* @return an aspect ratio to size list linked hash map which the aspect ratio fallback rule
* is applied and is sorted against the preferred aspect ratio.
*/
@NonNull
private static LinkedHashMap<Rational, List<Size>> applyAspectRatioStrategy(
@NonNull List<Size> resolutionCandidateList,
@NonNull AspectRatioStrategy aspectRatioStrategy,
Rational fullFovRatio) {
// Group output sizes by aspect ratio.
Map<Rational, List<Size>> aspectRatioSizeListMap =
groupSizesByAspectRatio(resolutionCandidateList);
// Applies the aspect ratio fallback rule
return applyAspectRatioStrategyFallbackRule(
aspectRatioSizeListMap, aspectRatioStrategy, fullFovRatio);
}
/**
* Applies the aspect ratio strategy fallback rule to the aspect ratio to size list map.
*
* @param sizeGroupsMap the aspect ratio to size list map. The size list should have been
* sorted in descending order.
* @param aspectRatioStrategy the specified aspect ratio strategy.
* @return an aspect ratio to size list linked hash map which the aspect ratio fallback rule
* is applied and is sorted against the preferred aspect ratio.
*/
private static LinkedHashMap<Rational, List<Size>> applyAspectRatioStrategyFallbackRule(
@NonNull Map<Rational, List<Size>> sizeGroupsMap,
@NonNull AspectRatioStrategy aspectRatioStrategy,
Rational fullFovRatio) {
// Determines the sensor resolution orientation info by the full FOV ratio.
boolean isSensorLandscapeResolution = fullFovRatio != null ? fullFovRatio.getNumerator()
>= fullFovRatio.getDenominator() : true;
Rational aspectRatio = getTargetAspectRatioRationalValue(
aspectRatioStrategy.getPreferredAspectRatio(), isSensorLandscapeResolution);
// Remove items of all other aspect ratios if the fallback rule is AspectRatioStrategy
// .FALLBACK_RULE_NONE
if (aspectRatioStrategy.getFallbackRule() == AspectRatioStrategy.FALLBACK_RULE_NONE) {
Rational preferredAspectRatio = getTargetAspectRatioRationalValue(
aspectRatioStrategy.getPreferredAspectRatio(), isSensorLandscapeResolution);
for (Rational ratio : new ArrayList<>(sizeGroupsMap.keySet())) {
if (!ratio.equals(preferredAspectRatio)) {
sizeGroupsMap.remove(ratio);
}
}
}
// Sorts the aspect ratio key set by the preferred aspect ratio.
List<Rational> aspectRatios = new ArrayList<>(sizeGroupsMap.keySet());
Collections.sort(aspectRatios,
new AspectRatioUtil.CompareAspectRatiosByMappingAreaInFullFovAspectRatioSpace(
aspectRatio, fullFovRatio));
// Stores the size groups into LinkedHashMap to keep the order
LinkedHashMap<Rational, List<Size>> sortedAspectRatioSizeListMap = new LinkedHashMap<>();
for (Rational ratio : aspectRatios) {
sortedAspectRatioSizeListMap.put(ratio, sizeGroupsMap.get(ratio));
}
return sortedAspectRatioSizeListMap;
}
/**
* Applies the resolution strategy onto the aspect ratio to size list linked hash map.
*
* <p>The resolution fallback rule is applied to filter out and sort the sizes in the
* underlying size list.
*
* @param sortedAspectRatioSizeListMap the aspect ratio to size list linked hash map. The
* entries order should not be changed.
* @param resolutionStrategy the resolution strategy to sort the candidate
* resolutions.
*/
private static void applyResolutionStrategy(
@NonNull LinkedHashMap<Rational, List<Size>> sortedAspectRatioSizeListMap,
@Nullable ResolutionStrategy resolutionStrategy) {
if (resolutionStrategy == null) {
return;
}
// Applies the resolution strategy with the specified fallback rule
for (Rational key : sortedAspectRatioSizeListMap.keySet()) {
applyResolutionStrategyFallbackRule(sortedAspectRatioSizeListMap.get(key),
resolutionStrategy);
}
}
/**
* Applies the resolution strategy fallback rule to the size list.
*
* @param supportedSizesList the supported sizes list which has been sorted in descending order.
* @param resolutionStrategy the resolution strategy to sort the candidate resolutions.
*/
private static void applyResolutionStrategyFallbackRule(
@NonNull List<Size> supportedSizesList,
@NonNull ResolutionStrategy resolutionStrategy) {
if (supportedSizesList.isEmpty()) {
return;
}
Integer fallbackRule = resolutionStrategy.getFallbackRule();
if (resolutionStrategy.equals(ResolutionStrategy.HIGHEST_AVAILABLE_STRATEGY)) {
// Do nothing for HIGHEST_AVAILABLE_STRATEGY case.
return;
}
Size boundSize = resolutionStrategy.getBoundSize();
switch (fallbackRule) {
case ResolutionStrategy.FALLBACK_RULE_NONE:
sortSupportedSizesByFallbackRuleNone(supportedSizesList, boundSize);
break;
case ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER:
sortSupportedSizesByFallbackRuleClosestHigherThenLower(supportedSizesList,
boundSize, true);
break;
case ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER:
sortSupportedSizesByFallbackRuleClosestHigherThenLower(supportedSizesList,
boundSize, false);
break;
case ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER:
sortSupportedSizesByFallbackRuleClosestLowerThenHigher(supportedSizesList,
boundSize, true);
break;
case ResolutionStrategy.FALLBACK_RULE_CLOSEST_LOWER:
sortSupportedSizesByFallbackRuleClosestLowerThenHigher(supportedSizesList,
boundSize, false);
break;
default:
break;
}
}
/**
* 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
* specified aspect ratio, resolution strategies.
* @param resolutionFilter the specified resolution filter.
* @param targetRotation the use case target rotation info
* @return the result size list applied the specified resolution filter.
* @throws IllegalArgumentException if the specified resolution filter returns any size which
* is not included in the provided supported size list.
*/
@NonNull
private static List<Size> applyResolutionFilter(@NonNull List<Size> sizeList,
@Nullable ResolutionFilter resolutionFilter,
@ImageOutputConfig.RotationValue int targetRotation,
int sensorOrientation,
int lensFacing) {
if (resolutionFilter == null) {
return sizeList;
}
// Invokes ResolutionFilter#filter() to filter/sort and return the result if it is
// specified.
int destRotationDegrees = CameraOrientationUtil.surfaceRotationToDegrees(
targetRotation);
int rotationDegrees =
CameraOrientationUtil.getRelativeImageRotation(destRotationDegrees,
sensorOrientation,
lensFacing == CameraSelector.LENS_FACING_BACK);
List<Size> filteredResultList = resolutionFilter.filter(new ArrayList<>(sizeList),
rotationDegrees);
if (sizeList.containsAll(filteredResultList)) {
return filteredResultList;
} else {
throw new IllegalArgumentException("The returned sizes list of the resolution "
+ "filter must be a subset of the provided sizes list.");
}
}
/**
* Sorts the size list for {@link ResolutionStrategy#FALLBACK_RULE_NONE}.
*
* @param supportedSizesList the supported sizes list which has been sorted in descending order.
* @param boundSize the resolution strategy bound size.
*/
private static void sortSupportedSizesByFallbackRuleNone(
@NonNull List<Size> supportedSizesList, @NonNull Size boundSize) {
boolean containsBoundSize = supportedSizesList.contains(boundSize);
supportedSizesList.clear();
if (containsBoundSize) {
supportedSizesList.add(boundSize);
}
}
/**
* Sorts the size list for {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER}
* or {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_HIGHER}.
*
* @param supportedSizesList the supported sizes list which has been sorted in descending order.
* @param boundSize the resolution strategy bound size.
* @param keepLowerSizes keeps the sizes lower than the bound size in the result list if
* this is {@code true}.
*/
static void sortSupportedSizesByFallbackRuleClosestHigherThenLower(
@NonNull List<Size> supportedSizesList, @NonNull Size boundSize,
boolean keepLowerSizes) {
List<Size> lowerSizes = new ArrayList<>();
for (int i = supportedSizesList.size() - 1; i >= 0; i--) {
Size outputSize = supportedSizesList.get(i);
if (outputSize.getWidth() < boundSize.getWidth()
|| outputSize.getHeight() < boundSize.getHeight()) {
// The supportedSizesList is in descending order. Checking and put the
// bounding-below size at position 0 so that the largest smaller resolution
// will be put in the first position finally.
lowerSizes.add(0, outputSize);
} else {
break;
}
}
// Removes the lower sizes from the list
supportedSizesList.removeAll(lowerSizes);
// Reverses the list so that the smallest larger resolution will be put in the first
// position.
Collections.reverse(supportedSizesList);
if (keepLowerSizes) {
// Appends the lower sizes to the tail
supportedSizesList.addAll(lowerSizes);
}
}
/**
* Sorts the size list for {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_LOWER_THEN_HIGHER}
* or {@link ResolutionStrategy#FALLBACK_RULE_CLOSEST_LOWER}.
*
* @param supportedSizesList the supported sizes list which has been sorted in descending order.
* @param boundSize the resolution strategy bound size.
* @param keepHigherSizes keeps the sizes higher than the bound size in the result list if
* this is {@code true}.
*/
private static void sortSupportedSizesByFallbackRuleClosestLowerThenHigher(
@NonNull List<Size> supportedSizesList, @NonNull Size boundSize,
boolean keepHigherSizes) {
List<Size> higherSizes = new ArrayList<>();
for (int i = 0; i < supportedSizesList.size(); i++) {
Size outputSize = supportedSizesList.get(i);
if (outputSize.getWidth() > boundSize.getWidth()
|| outputSize.getHeight() > boundSize.getHeight()) {
// The supportedSizesList is in descending order. Checking and put the
// bounding-above size at position 0 so that the smallest larger resolution
// will be put in the first position finally.
higherSizes.add(0, outputSize);
} else {
// Breaks the for-loop to keep the equal-to or lower sizes in the list.
break;
}
}
// Removes the higher sizes from the list
supportedSizesList.removeAll(higherSizes);
if (keepHigherSizes) {
// Appends the higher sizes to the tail
supportedSizesList.addAll(higherSizes);
}
}
/**
* Returns the target aspect ratio rational value according to the ResolutionSelector settings.
*/
@Nullable
static Rational getTargetAspectRatioRationalValue(@AspectRatio.Ratio int aspectRatio,
boolean isSensorLandscapeResolution) {
Rational outputRatio = null;
switch (aspectRatio) {
case AspectRatio.RATIO_4_3:
outputRatio = isSensorLandscapeResolution ? ASPECT_RATIO_4_3
: ASPECT_RATIO_3_4;
break;
case AspectRatio.RATIO_16_9:
outputRatio = isSensorLandscapeResolution ? ASPECT_RATIO_16_9
: ASPECT_RATIO_9_16;
break;
case AspectRatio.RATIO_DEFAULT:
break;
default:
Logger.e(TAG, "Undefined target aspect ratio: " + aspectRatio);
}
return outputRatio;
}
/**
* Returns the grouping aspect ratio keys of the input resolution list.
*
* <p>Some sizes might be mod16 case. When grouping, those sizes will be grouped into an
* existing aspect ratio group if the aspect ratio can match by the mod16 rule.
*/
@NonNull
static List<Rational> getResolutionListGroupingAspectRatioKeys(
@NonNull List<Size> resolutionCandidateList) {
List<Rational> aspectRatios = new ArrayList<>();
// Adds the default 4:3 and 16:9 items first to avoid their mod16 sizes to create
// additional items.
aspectRatios.add(ASPECT_RATIO_4_3);
aspectRatios.add(ASPECT_RATIO_16_9);
// Tries to find the aspect ratio which the target size belongs to.
for (Size size : resolutionCandidateList) {
Rational newRatio = new Rational(size.getWidth(), size.getHeight());
boolean aspectRatioFound = aspectRatios.contains(newRatio);
// The checking size might be a mod16 size which can be mapped to an existing aspect
// ratio group.
if (!aspectRatioFound) {
boolean hasMatchingAspectRatio = false;
for (Rational aspectRatio : aspectRatios) {
if (hasMatchingAspectRatio(size, aspectRatio)) {
hasMatchingAspectRatio = true;
break;
}
}
if (!hasMatchingAspectRatio) {
aspectRatios.add(newRatio);
}
}
}
return aspectRatios;
}
/**
* Groups the input sizes into an aspect ratio to size list map.
*/
static Map<Rational, List<Size>> groupSizesByAspectRatio(@NonNull List<Size> sizes) {
Map<Rational, List<Size>> aspectRatioSizeListMap = new HashMap<>();
List<Rational> aspectRatioKeys = getResolutionListGroupingAspectRatioKeys(sizes);
for (Rational aspectRatio : aspectRatioKeys) {
aspectRatioSizeListMap.put(aspectRatio, new ArrayList<>());
}
for (Size outputSize : sizes) {
for (Rational key : aspectRatioSizeListMap.keySet()) {
// Put the size into all groups that is matched in mod16 condition since a size
// may match multiple aspect ratio in mod16 algorithm.
if (hasMatchingAspectRatio(outputSize, key)) {
aspectRatioSizeListMap.get(key).add(outputSize);
}
}
}
return aspectRatioSizeListMap;
}
}