1b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael/* 2b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Copyright (C) 2013 The Android Open Source Project 3b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * 4b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Licensed under the Apache License, Version 2.0 (the "License"); 5b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * you may not use this file except in compliance with the License. 6b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * You may obtain a copy of the License at 7b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * 8b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * http://www.apache.org/licenses/LICENSE-2.0 9b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * 10b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Unless required by applicable law or agreed to in writing, software 11b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * distributed under the License is distributed on an "AS IS" BASIS, 12b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * See the License for the specific language governing permissions and 14b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * limitations under the License. 15b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 16b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 17b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelpackage com.android.camera.settings; 18b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 19454d63f617489281971fb151abfe14730209c047Angus Kongimport com.android.camera.util.ApiHelper; 205a344964cb003525727c31d295ca3a592c245606Sol Boucherimport com.android.ex.camera2.portability.Size; 21b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 22b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.math.BigInteger; 23b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.ArrayList; 24b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.Arrays; 25b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.Collections; 26b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.Comparator; 27b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.HashMap; 28b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.LinkedList; 29b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelimport java.util.List; 30b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 31b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael/** 32b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * This class is used to help manage the many different resolutions available on 33b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * the device. <br/> 34b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * It allows you to specify which aspect ratios to offer the user, and then 35b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * chooses which resolutions are the most pertinent to avoid overloading the 36b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * user with so many options. 37b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 38b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphaelpublic class ResolutionUtil { 39b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 40454d63f617489281971fb151abfe14730209c047Angus Kong public static final String NEXUS_5_LARGE_16_BY_9 = "1836x3264"; 41454d63f617489281971fb151abfe14730209c047Angus Kong public static final float NEXUS_5_LARGE_16_BY_9_ASPECT_RATIO = 16f / 9f; 42454d63f617489281971fb151abfe14730209c047Angus Kong public static Size NEXUS_5_LARGE_16_BY_9_SIZE = new Size(1836, 3264); 43454d63f617489281971fb151abfe14730209c047Angus Kong 44b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 45b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * These are the preferred aspect ratios for the settings. We will take HAL 46b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * supported aspect ratios that are within RATIO_TOLERANCE of these values. 47b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * We will also take the maximum supported resolution for full sensor image. 48b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 49b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael private static Float[] sDesiredAspectRatios = { 50b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 16.0f / 9.0f, 4.0f / 3.0f 51b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael }; 52b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 531cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael private static Size[] sDesiredAspectRatioSizes = { 541cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael new Size(16, 9), new Size(4, 3) 551cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael }; 561cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael 57b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael private static final float RATIO_TOLERANCE = .05f; 58b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 59b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 60b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * A resolution bucket holds a list of sizes that are of a given aspect 61b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * ratio. 62b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 63b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael private static class ResolutionBucket { 64b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael public Float aspectRatio; 65b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 66b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * This is a sorted list of sizes, going from largest to smallest. 67b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 68b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael public List<Size> sizes = new LinkedList<Size>(); 69b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 70b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * This is the head of the sizes array. 71b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 72b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael public Size largest; 73b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 74b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * This is the area of the largest size, used for sorting 75b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * ResolutionBuckets. 76b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 77b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael public Integer maxPixels = 0; 78b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 79b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 80b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Use this to add a new resolution to this bucket. It will insert it 81b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * into the sizes array and update appropriate members. 8208b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * 83b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @param size the new size to be added 84b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 85b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael public void add(Size size) { 86b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael sizes.add(size); 87b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael Collections.sort(sizes, new Comparator<Size>() { 88b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael @Override 89b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael public int compare(Size size, Size size2) { 90b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael // sort area greatest to least 91b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael return Integer.compare(size2.width() * size2.height(), 92b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael size.width() * size.height()); 93b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 94b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael }); 95b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael maxPixels = sizes.get(0).width() * sizes.get(0).height(); 96b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 97b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 98b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 99b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 100b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Given a list of camera sizes, this uses some heuristics to decide which 101b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * options to present to a user. It currently returns up to 3 sizes for each 102b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * aspect ratio. The aspect ratios returned include the ones in 103b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * sDesiredAspectRatios, and the largest full sensor ratio. T his guarantees 104b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * that users can use a full-sensor size, as well as any of the preferred 105b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * aspect ratios from above; 10608b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * 107b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @param sizes A super set of all sizes to be displayed 108a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael * @param isBackCamera true if these are sizes for the back camera 109b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @return The list of sizes to display grouped first by aspect ratio 110b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * (sorted by maximum area), and sorted within aspect ratio by area) 111b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 112a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael public static List<Size> getDisplayableSizesFromSupported(List<Size> sizes, boolean isBackCamera) { 113a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael List<ResolutionBucket> buckets = parseAvailableSizes(sizes, isBackCamera); 114b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 115b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael List<Float> sortedDesiredAspectRatios = new ArrayList<Float>(); 116b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael // We want to make sure we support the maximum pixel aspect ratio, even 117b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael // if it doesn't match a desired aspect ratio 118b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael sortedDesiredAspectRatios.add(buckets.get(0).aspectRatio.floatValue()); 119b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 120b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael // Now go through the buckets from largest mp to smallest, adding 121b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael // desired ratios 122b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael for (ResolutionBucket bucket : buckets) { 123b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael Float aspectRatio = bucket.aspectRatio; 124b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael if (Arrays.asList(sDesiredAspectRatios).contains(aspectRatio) 125b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael && !sortedDesiredAspectRatios.contains(aspectRatio)) { 126b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael sortedDesiredAspectRatios.add(aspectRatio); 127b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 128b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 129b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 130b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael List<Size> result = new ArrayList<Size>(sizes.size()); 131b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael for (Float targetRatio : sortedDesiredAspectRatios) { 132b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael for (ResolutionBucket bucket : buckets) { 133b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael Number aspectRatio = bucket.aspectRatio; 134b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael if (Math.abs(aspectRatio.floatValue() - targetRatio) <= RATIO_TOLERANCE) { 135b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael result.addAll(pickUpToThree(bucket.sizes)); 136b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 137b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 138b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 139b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael return result; 140b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 141b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 142b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 143b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Get the area in pixels of a size. 14408b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * 145b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @param size the size to measure 146b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @return the area. 147b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 148b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael private static int area(Size size) { 149b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael if (size == null) { 150b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael return 0; 151b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 152b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael return size.width() * size.height(); 153b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 154b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 155b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 156b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Given a list of sizes of a similar aspect ratio, it tries to pick evenly 157b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * spaced out options. It starts with the largest, then tries to find one at 158b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * 50% of the last chosen size for the subsequent size. 15908b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * 160b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @param sizes A list of Sizes that are all of a similar aspect ratio 161b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @return A list of at least one, and no more than three representative 162b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * sizes from the list. 163b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 164b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael private static List<Size> pickUpToThree(List<Size> sizes) { 165b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael List<Size> result = new ArrayList<Size>(); 166b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael Size largest = sizes.get(0); 167b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael result.add(largest); 168b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael Size lastSize = largest; 169b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael for (Size size : sizes) { 170b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael double targetArea = Math.pow(.5, result.size()) * area(largest); 171b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael if (area(size) < targetArea) { 172b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael // This candidate is smaller than half the mega pixels of the 173b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael // last one. Let's see whether the previous size, or this size 174b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael // is closer to the desired target. 175b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael if (!result.contains(lastSize) 176b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael && (targetArea - area(lastSize) < area(size) - targetArea)) { 177b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael result.add(lastSize); 178b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } else { 179b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael result.add(size); 180b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 181b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 182b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael lastSize = size; 183b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael if (result.size() == 3) { 184b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael break; 185b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 186b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 187b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 188b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael // If we have less than three, we can add the smallest size. 189b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael if (result.size() < 3 && !result.contains(lastSize)) { 190b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael result.add(lastSize); 191b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 192b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael return result; 193b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 194b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 195b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 196b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Take an aspect ratio and squish it into a nearby desired aspect ratio, if 197b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * possible. 19808b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * 199b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @param aspectRatio the aspect ratio to fuzz 200b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @return the closest desiredAspectRatio within RATIO_TOLERANCE, or the 201b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * original ratio 202b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 203b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael private static float fuzzAspectRatio(float aspectRatio) { 204b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael for (float desiredAspectRatio : sDesiredAspectRatios) { 205b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael if ((Math.abs(aspectRatio - desiredAspectRatio)) < RATIO_TOLERANCE) { 206b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael return desiredAspectRatio; 207b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 208b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 209b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael return aspectRatio; 210b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 211b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 212b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 213b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * This takes a bunch of supported sizes and buckets them by aspect ratio. 214b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * The result is a list of buckets sorted by each bucket's largest area. 215b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * They are sorted from largest to smallest. This will bucket aspect ratios 216b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * that are close to the sDesiredAspectRatios in to the same bucket. 21708b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * 218b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @param sizes all supported sizes for a camera 219a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael * @param isBackCamera true if these are sizes for the back camera 220b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @return all of the sizes grouped by their closest aspect ratio 221b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 222a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael private static List<ResolutionBucket> parseAvailableSizes(List<Size> sizes, boolean isBackCamera) { 223b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael HashMap<Float, ResolutionBucket> aspectRatioToBuckets = new HashMap<Float, ResolutionBucket>(); 224b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 225b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael for (Size size : sizes) { 226b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael Float aspectRatio = size.width() / (float) size.height(); 227b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael // If this aspect ratio is close to a desired Aspect Ratio, 228b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael // fuzz it so that they are bucketed together 229b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael aspectRatio = fuzzAspectRatio(aspectRatio); 230b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael ResolutionBucket bucket = aspectRatioToBuckets.get(aspectRatio); 231b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael if (bucket == null) { 232b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael bucket = new ResolutionBucket(); 233b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael bucket.aspectRatio = aspectRatio; 234b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael aspectRatioToBuckets.put(aspectRatio, bucket); 235b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 236b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael bucket.add(size); 237b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 238a4b0118d15d4a3c6b77827feb63c3809c61c0fcfSeth Raphael if (ApiHelper.IS_NEXUS_5 && isBackCamera) { 239454d63f617489281971fb151abfe14730209c047Angus Kong aspectRatioToBuckets.get(16 / 9.0f).add(NEXUS_5_LARGE_16_BY_9_SIZE); 240454d63f617489281971fb151abfe14730209c047Angus Kong } 241b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael List<ResolutionBucket> sortedBuckets = new ArrayList<ResolutionBucket>( 242b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael aspectRatioToBuckets.values()); 243b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael Collections.sort(sortedBuckets, new Comparator<ResolutionBucket>() { 244b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael @Override 245b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael public int compare(ResolutionBucket resolutionBucket, ResolutionBucket resolutionBucket2) { 246b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael return Integer.compare(resolutionBucket2.maxPixels, resolutionBucket.maxPixels); 247b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 248b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael }); 249b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael return sortedBuckets; 250b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 251b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael 252b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael /** 253b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * Given a size, return a string describing the aspect ratio by reducing the 25408b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * 255b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @param size the size to describe 256b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael * @return a string description of the aspect ratio 257b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael */ 258b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael public static String aspectRatioDescription(Size size) { 2591cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael Size aspectRatio = reduce(size); 2601cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael return aspectRatio.width() + "x" + aspectRatio.height(); 2611cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael } 2621cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael 2631cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael /** 2641cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael * Reduce an aspect ratio to its lowest common denominator. The ratio of the 2651cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael * input and output sizes is guaranteed to be the same. 26608b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * 2671cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael * @param aspectRatio the aspect ratio to reduce 2681cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael * @return The reduced aspect ratio which may equal the original. 2691cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael */ 2701cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael public static Size reduce(Size aspectRatio) { 2711cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael BigInteger width = BigInteger.valueOf(aspectRatio.width()); 2721cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael BigInteger height = BigInteger.valueOf(aspectRatio.height()); 273b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael BigInteger gcd = width.gcd(height); 274b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael int numerator = Math.max(width.intValue(), height.intValue()) / gcd.intValue(); 275b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael int denominator = Math.min(width.intValue(), height.intValue()) / gcd.intValue(); 2761cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael return new Size(numerator, denominator); 277b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael } 278d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael 279d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael /** 280d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael * Given a size return the numerator of its aspect ratio 28108b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * 282d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael * @param size the size to measure 283d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael * @return the numerator 284d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael */ 285d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael public static int aspectRatioNumerator(Size size) { 2861cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael Size aspectRatio = reduce(size); 2871cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael return aspectRatio.width(); 2881cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael } 2891cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael 2901cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael /** 2911cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael * Given a size, return the closest aspect ratio that falls close to the 2921cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael * given size. 29308b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * 2941cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael * @param size the size to approximate 2951cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael * @return the closest desired aspect ratio, or the original aspect ratio if 2961cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael * none were close enough 2971cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael */ 2981cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael public static Size getApproximateSize(Size size) { 2991cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael Size aspectRatio = reduce(size); 3001cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael float fuzzy = fuzzAspectRatio(size.width() / (float) size.height()); 3011cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael int index = Arrays.asList(sDesiredAspectRatios).indexOf(fuzzy); 3021cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael if (index != -1) { 3031cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael aspectRatio = new Size(sDesiredAspectRatioSizes[index]); 3041cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael } 3051cced0e893b6a2f37d447a049c1c3c08affd2d41Seth Raphael return aspectRatio; 306d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael } 307d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael 308d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael /** 30908b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * See {@link #getApproximateSize(Size)}. 31008b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * <p> 31108b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * TODO: Move this whole util to {@link android.util.Size} 31208b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling */ 313e3dfd5a433e39d76578b379fe1539864cf924ceeSascha Haeberling public static com.android.camera.util.Size getApproximateSize( 314e3dfd5a433e39d76578b379fe1539864cf924ceeSascha Haeberling com.android.camera.util.Size size) { 31508b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling Size result = getApproximateSize(new Size(size.getWidth(), size.getHeight())); 316e3dfd5a433e39d76578b379fe1539864cf924ceeSascha Haeberling return new com.android.camera.util.Size(result.width(), result.height()); 31708b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling } 31808b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling 31908b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling /** 320d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael * Given a size return the numerator of its aspect ratio 32108b3c94a7d7aff30b5da8e99649346e37b67a5c5Sascha Haeberling * 322d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael * @param size 323d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael * @return the denominator 324d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael */ 325d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael public static int aspectRatioDenominator(Size size) { 326d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael BigInteger width = BigInteger.valueOf(size.width()); 327d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael BigInteger height = BigInteger.valueOf(size.height()); 328d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael BigInteger gcd = width.gcd(height); 329d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael int denominator = Math.min(width.intValue(), height.intValue()) / gcd.intValue(); 330d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael return denominator; 331d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael } 332d074fffd7c85df39893125f29fb3d487864feaf1Seth Raphael 333b19eaa0bb37a1252015eafd3e965a011e6e98727Seth Raphael} 334