Home | History | Annotate | Download | only in common
      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.Objects;
     26 import com.google.common.base.Preconditions;
     27 
     28 import java.util.List;
     29 
     30 import javax.annotation.Nonnull;
     31 import 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
     46 public 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