1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.camera.one.v2.common; 18 19import android.graphics.Rect; 20 21import com.android.camera.one.OneCameraAccessException; 22import com.android.camera.one.OneCameraCharacteristics; 23import com.android.camera.util.AspectRatio; 24import com.android.camera.util.Size; 25import com.google.common.base.Objects; 26import com.google.common.base.Preconditions; 27 28import java.util.List; 29 30import javax.annotation.Nonnull; 31import javax.annotation.ParametersAreNonnullByDefault; 32 33/** 34 * Selects the optimal picture size and crop for a particular target size. 35 * <p> 36 * A particular camera2 device will support a finite set of output resolutions. 37 * However, we may wish to take pictures with a size that is not directly 38 * supported. 39 * <p> 40 * For example, we may wish to use a large 4:3 output size to capture 41 * as-large-as-possible 16:9 images. This requires determining the smallest 42 * output size which can contain the target size, and then computing the 43 * appropriate crop region. 44 */ 45@ParametersAreNonnullByDefault 46public final class PictureSizeCalculator { 47 private final OneCameraCharacteristics mCameraCharacteristics; 48 49 public PictureSizeCalculator(OneCameraCharacteristics cameraCharacteristics) { 50 mCameraCharacteristics = cameraCharacteristics; 51 } 52 53 public static final class Configuration { 54 private final Size mSize; 55 private final Rect mPostCrop; 56 57 private Configuration(Size size, Rect postCrop) { 58 mSize = size; 59 mPostCrop = postCrop; 60 } 61 62 /** 63 * @return The crop to be applied to Images returned from the camera 64 * device. 65 */ 66 public Rect getPostCaptureCrop() { 67 return mPostCrop; 68 } 69 70 /** 71 * @return The best natively-supported size to use. 72 */ 73 public Size getNativeOutputSize() { 74 return mSize; 75 } 76 77 @Override 78 public String toString() { 79 return Objects.toStringHelper("PictureSizeCalculator.Configuration") 80 .add("native size", mSize) 81 .add("crop", mPostCrop) 82 .toString(); 83 } 84 85 @Override 86 public boolean equals(Object o) { 87 if (this == o) { 88 return true; 89 } else if (!(o instanceof Configuration)) { 90 return false; 91 } 92 93 Configuration that = (Configuration) o; 94 95 if (!mPostCrop.equals(that.mPostCrop)) { 96 return false; 97 } else if (!mSize.equals(that.mSize)) { 98 return false; 99 } 100 101 return true; 102 } 103 104 @Override 105 public int hashCode() { 106 return Objects.hashCode(mSize, mPostCrop); 107 } 108 } 109 110 @Nonnull 111 private Size getSmallestSupportedSizeContainingTarget(List<Size> supported, Size target) { 112 Preconditions.checkState(!supported.isEmpty()); 113 Size best = null; 114 long bestArea = Long.MAX_VALUE; 115 for (Size candidate : supported) { 116 long pixels = candidate.area(); 117 if (candidate.getWidth() >= target.getWidth() && 118 candidate.getHeight() >= target.getHeight() && 119 pixels < bestArea) { 120 best = candidate; 121 bestArea = pixels; 122 } 123 } 124 125 if (best == null) { 126 // If no supported sizes contain the target size, then select the 127 // largest one. 128 best = getLargestSupportedSize(supported); 129 } 130 131 return best; 132 } 133 134 /** 135 * A picture size Configuration consists of a device-supported size and a 136 * crop-region to apply to images retrieved from the device. The combination 137 * of these should achieve the desired image size specified in 138 * {@link #computeConfiguration}. 139 * 140 * @return The optimal configuration of device-supported picture size and 141 * post-capture crop region to use. 142 * @throws com.android.camera.one.OneCameraAccessException if a 143 * configuration could not be computed. 144 */ 145 public Configuration computeConfiguration(Size targetSize, int imageFormat) 146 throws OneCameraAccessException { 147 List<Size> supportedPictureSizes = mCameraCharacteristics 148 .getSupportedPictureSizes(imageFormat); 149 if (supportedPictureSizes.isEmpty()) { 150 throw new OneCameraAccessException("No picture sizes supported for format: " 151 + imageFormat); 152 } 153 Size size = getSmallestSupportedSizeContainingTarget(supportedPictureSizes, targetSize); 154 Rect cropRegion = getPostCrop(AspectRatio.of(targetSize), size); 155 return new Configuration(size, cropRegion); 156 } 157 158 @Nonnull 159 private Size getLargestSupportedSize(List<Size> supported) { 160 Preconditions.checkState(!supported.isEmpty()); 161 Size largestSize = supported.get(0); 162 long largestArea = largestSize.area(); 163 for (Size candidate : supported) { 164 long area = candidate.area(); 165 if (area > largestArea) { 166 largestSize = candidate; 167 } 168 } 169 return largestSize; 170 } 171 172 private Rect getPostCrop(AspectRatio targetAspect, Size actualSize) { 173 return targetAspect.getLargestCenterCrop(actualSize); 174 } 175} 176