Home | History | Annotate | Download | only in util
      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.util;
     18 
     19 import android.graphics.Rect;
     20 
     21 import com.google.common.base.Objects;
     22 
     23 import java.math.BigInteger;
     24 
     25 import javax.annotation.ParametersAreNonnullByDefault;
     26 
     27 /**
     28  * Contains precise (integer) logic for handling aspect ratios as rational
     29  * numbers.
     30  */
     31 @ParametersAreNonnullByDefault
     32 public final class AspectRatio {
     33     private static final AspectRatio ASPECT_RATIO_4x3 = AspectRatio.of(4, 3);
     34     private static final AspectRatio ASPECT_RATIO_16x9 = AspectRatio.of(16, 9);
     35 
     36     private final int mWidth;
     37     private final int mHeight;
     38 
     39     /**
     40      * @param width The width of the aspect ratio, after simplification.
     41      * @param height The height of the aspect ratio, after simplification.
     42      */
     43     private AspectRatio(int width, int height) {
     44         mWidth = width;
     45         mHeight = height;
     46     }
     47 
     48     public static AspectRatio of(int width, int height) {
     49         int gcd = BigInteger.valueOf(width).gcd(BigInteger.valueOf(height)).intValue();
     50         int simplifiedWidth = width / gcd;
     51         int simplifiedHeight = height / gcd;
     52         return new AspectRatio(simplifiedWidth, simplifiedHeight);
     53     }
     54 
     55     public static AspectRatio of(Size size) {
     56         return of(size.width(), size.height());
     57     }
     58 
     59     public static AspectRatio of4x3() {
     60         return ASPECT_RATIO_4x3;
     61     }
     62 
     63     public static AspectRatio of16x9() {
     64         return ASPECT_RATIO_16x9;
     65     }
     66 
     67     public int getHeight() {
     68         return mHeight;
     69     }
     70 
     71     public int getWidth() {
     72         return mWidth;
     73     }
     74 
     75     public float toFloat() {
     76         return (float) mWidth / (float) mHeight;
     77     }
     78 
     79     @Override
     80     public boolean equals(Object o) {
     81         if (this == o)
     82             return true;
     83         if (!(o instanceof AspectRatio))
     84             return false;
     85 
     86         AspectRatio that = (AspectRatio) o;
     87 
     88         if (mHeight != that.mHeight)
     89             return false;
     90         if (mWidth != that.mWidth)
     91             return false;
     92 
     93         return true;
     94     }
     95 
     96     @Override
     97     public int hashCode() {
     98         return Objects.hashCode(mWidth, mHeight);
     99     }
    100 
    101     @Override
    102     public String toString() {
    103         return String.format("AspectRatio[%d:%d]", getWidth(), getHeight());
    104     }
    105 
    106     /**
    107      * @return The transpose of this aspect ratio.
    108      */
    109     public AspectRatio transpose() {
    110         return of(mHeight, mWidth);
    111     }
    112 
    113     /**
    114      * @return The landscape version of this aspect ratio.
    115      */
    116     public AspectRatio asLandscape() {
    117         if (isLandscape()) {
    118             return this;
    119         } else {
    120             return transpose();
    121         }
    122     }
    123 
    124     /**
    125      * @return The portrait version of this aspect ratio.
    126      */
    127     public AspectRatio asPortrait() {
    128         if (isPortrait()) {
    129             return this;
    130         } else {
    131             return transpose();
    132         }
    133     }
    134 
    135     /**
    136      * @return The version of this aspect ratio in the same orientation
    137      *         (portrait vs. landscape) of the other.
    138      */
    139     public AspectRatio withOrientationOf(AspectRatio other) {
    140         if (other.isPortrait()) {
    141             return asPortrait();
    142         } else {
    143             return asLandscape();
    144         }
    145     }
    146 
    147     /**
    148      * @return True if this aspect ratio is wider than the other.
    149      */
    150     public boolean isWiderThan(AspectRatio other) {
    151         // this.mWidth other.mWidth
    152         // ----------- > ------------
    153         // this.mHeight other.mHeight
    154         return this.mWidth * other.mHeight > other.mWidth * this.mHeight;
    155     }
    156 
    157     /**
    158      * @return True if this aspect ratio is taller than the other.
    159      */
    160     public boolean isTallerThan(AspectRatio other) {
    161         // this.mWidth other.mWidth
    162         // ----------- < ------------
    163         // this.mHeight other.mHeight
    164         return this.mWidth * other.mHeight < other.mWidth * this.mHeight;
    165     }
    166 
    167     /**
    168      * @return The largest centered region of area with this aspect ratio. For
    169      *         non-integer values, the returned rectangle coordinates are the
    170      *         *floor* of the result.
    171      */
    172     public Rect getLargestCenterCrop(Size area) {
    173         AspectRatio original = of(area);
    174 
    175         if (this.isWiderThan(original)) {
    176             // Crop off the top and bottom...
    177             int cropHeight = area.width() * mHeight / mWidth;
    178             int cropTop = (area.height() - cropHeight) / 2;
    179             int cropBottom = cropTop + cropHeight;
    180             int cropLeft = 0;
    181             int cropRight = area.width();
    182             return new Rect(cropLeft, cropTop, cropRight, cropBottom);
    183         } else {
    184             // Crop off the left and right...
    185             int cropWidth = area.height() * mWidth / mHeight;
    186             int cropLeft = (area.width() - cropWidth) / 2;
    187             int cropRight = cropLeft + cropWidth;
    188             int cropTop = 0;
    189             int cropBottom = area.height();
    190             return new Rect(cropLeft, cropTop, cropRight, cropBottom);
    191         }
    192     }
    193 
    194     /**
    195      * @return True if this aspect ratio is in landscape orientation. Square
    196      *         aspect ratios are both portrait *and* landscape.
    197      */
    198     private boolean isLandscape() {
    199         return mWidth >= mHeight;
    200     }
    201 
    202     /**
    203      * @return True if this aspect ratio is in portrait orientation. Square
    204      *         aspect ratios are both portrait *and* landscape.
    205      */
    206     private boolean isPortrait() {
    207         return mWidth <= mHeight;
    208     }
    209 
    210 }
    211