12f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall/* 22f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * Copyright (C) 2015 The Android Open Source Project 32f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * 42f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * Licensed under the Apache License, Version 2.0 (the "License"); 52f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * you may not use this file except in compliance with the License. 62f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * You may obtain a copy of the License at 72f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * 82f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * http://www.apache.org/licenses/LICENSE-2.0 92f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * 102f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * Unless required by applicable law or agreed to in writing, software 112f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * distributed under the License is distributed on an "AS IS" BASIS, 122f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * See the License for the specific language governing permissions and 142f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * limitations under the License. 152f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 162f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 172f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallpackage com.android.camera.one.v2.common; 182f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 192f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport android.graphics.Rect; 202f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 213ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberlingimport com.android.camera.one.OneCameraAccessException; 222f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport com.android.camera.one.OneCameraCharacteristics; 232f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport com.android.camera.util.AspectRatio; 242f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport com.android.camera.util.Size; 252f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport com.google.common.base.Objects; 262f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport com.google.common.base.Preconditions; 272f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 282f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport java.util.List; 292f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 302f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport javax.annotation.Nonnull; 312f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport javax.annotation.ParametersAreNonnullByDefault; 322f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 332f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall/** 342f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * Selects the optimal picture size and crop for a particular target size. 352f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * <p> 362f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * A particular camera2 device will support a finite set of output resolutions. 372f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * However, we may wish to take pictures with a size that is not directly 382f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * supported. 392f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * <p> 402f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * For example, we may wish to use a large 4:3 output size to capture 412f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * as-large-as-possible 16:9 images. This requires determining the smallest 422f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * output size which can contain the target size, and then computing the 432f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * appropriate crop region. 442f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 452f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall@ParametersAreNonnullByDefault 462f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallpublic final class PictureSizeCalculator { 472f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private final OneCameraCharacteristics mCameraCharacteristics; 482f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 492f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public PictureSizeCalculator(OneCameraCharacteristics cameraCharacteristics) { 502f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall mCameraCharacteristics = cameraCharacteristics; 512f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 522f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 532f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public static final class Configuration { 542f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private final Size mSize; 552f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private final Rect mPostCrop; 562f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 572f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private Configuration(Size size, Rect postCrop) { 582f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall mSize = size; 592f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall mPostCrop = postCrop; 602f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 612f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 622f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 632f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return The crop to be applied to Images returned from the camera 642f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * device. 652f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 662f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public Rect getPostCaptureCrop() { 672f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return mPostCrop; 682f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 692f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 702f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 712f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return The best natively-supported size to use. 722f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 732f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public Size getNativeOutputSize() { 742f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return mSize; 752f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 762f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 772f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall @Override 782f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public String toString() { 792f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return Objects.toStringHelper("PictureSizeCalculator.Configuration") 802f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall .add("native size", mSize) 812f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall .add("crop", mPostCrop) 822f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall .toString(); 832f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 842f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 852f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall @Override 862f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public boolean equals(Object o) { 872f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (this == o) { 882f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return true; 892f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } else if (!(o instanceof Configuration)) { 902f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return false; 912f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 922f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 932f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall Configuration that = (Configuration) o; 942f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 952f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (!mPostCrop.equals(that.mPostCrop)) { 962f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return false; 972f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } else if (!mSize.equals(that.mSize)) { 982f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return false; 992f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1002f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1012f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return true; 1022f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1032f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1042f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall @Override 1052f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public int hashCode() { 1062f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return Objects.hashCode(mSize, mPostCrop); 1072f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1082f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1092f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1102f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall @Nonnull 1112f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private Size getSmallestSupportedSizeContainingTarget(List<Size> supported, Size target) { 1122f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall Preconditions.checkState(!supported.isEmpty()); 1132f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall Size best = null; 1142f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall long bestArea = Long.MAX_VALUE; 1152f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall for (Size candidate : supported) { 1162f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall long pixels = candidate.area(); 1172f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (candidate.getWidth() >= target.getWidth() && 1182f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall candidate.getHeight() >= target.getHeight() && 1192f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall pixels < bestArea) { 1202f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall best = candidate; 1212f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall bestArea = pixels; 1222f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1232f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1242f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1252f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (best == null) { 1262f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall // If no supported sizes contain the target size, then select the 1272f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall // largest one. 1282f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall best = getLargestSupportedSize(supported); 1292f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1302f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1312f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return best; 1322f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1332f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1342f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 1352f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * A picture size Configuration consists of a device-supported size and a 1362f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * crop-region to apply to images retrieved from the device. The combination 1372f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * of these should achieve the desired image size specified in 1382f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * {@link #computeConfiguration}. 1393ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberling * 1402f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return The optimal configuration of device-supported picture size and 1412f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * post-capture crop region to use. 1423ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberling * @throws com.android.camera.one.OneCameraAccessException if a 1433ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberling * configuration could not be computed. 1442f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 1453ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberling public Configuration computeConfiguration(Size targetSize, int imageFormat) 1463ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberling throws OneCameraAccessException { 1473ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberling List<Size> supportedPictureSizes = mCameraCharacteristics 1483ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberling .getSupportedPictureSizes(imageFormat); 1493ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberling if (supportedPictureSizes.isEmpty()) { 1503ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberling throw new OneCameraAccessException("No picture sizes supported for format: " 1513ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberling + imageFormat); 1523ad9c9035f8be50f6b2ef8cac9943045f7cf8a74Sascha Haeberling } 1532f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall Size size = getSmallestSupportedSizeContainingTarget(supportedPictureSizes, targetSize); 1542f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall Rect cropRegion = getPostCrop(AspectRatio.of(targetSize), size); 1552f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return new Configuration(size, cropRegion); 1562f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1572f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1582f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall @Nonnull 1592f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private Size getLargestSupportedSize(List<Size> supported) { 1602f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall Preconditions.checkState(!supported.isEmpty()); 1612f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall Size largestSize = supported.get(0); 1622f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall long largestArea = largestSize.area(); 1632f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall for (Size candidate : supported) { 1642f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall long area = candidate.area(); 1652f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (area > largestArea) { 1662f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall largestSize = candidate; 1672f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1682f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1692f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return largestSize; 1702f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1712f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1722f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private Rect getPostCrop(AspectRatio targetAspect, Size actualSize) { 1732f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return targetAspect.getLargestCenterCrop(actualSize); 1742f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1752f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall} 176