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