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.util; 182f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 192f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport android.graphics.Rect; 202f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 212f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport com.google.common.base.Objects; 222f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 232f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport java.math.BigInteger; 242f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 252f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallimport javax.annotation.ParametersAreNonnullByDefault; 262f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 272f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall/** 282f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * Contains precise (integer) logic for handling aspect ratios as rational 292f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * numbers. 302f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 312f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall@ParametersAreNonnullByDefault 322f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lallpublic final class AspectRatio { 33cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu private static final AspectRatio ASPECT_RATIO_4x3 = AspectRatio.of(4, 3); 34cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu private static final AspectRatio ASPECT_RATIO_16x9 = AspectRatio.of(16, 9); 35cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu 362f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private final int mWidth; 372f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private final int mHeight; 382f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 392f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 402f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @param width The width of the aspect ratio, after simplification. 412f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @param height The height of the aspect ratio, after simplification. 422f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 432f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private AspectRatio(int width, int height) { 442f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall mWidth = width; 452f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall mHeight = height; 462f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 472f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 482f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public static AspectRatio of(int width, int height) { 492f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int gcd = BigInteger.valueOf(width).gcd(BigInteger.valueOf(height)).intValue(); 502f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int simplifiedWidth = width / gcd; 512f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int simplifiedHeight = height / gcd; 522f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return new AspectRatio(simplifiedWidth, simplifiedHeight); 532f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 542f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 552f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public static AspectRatio of(Size size) { 562f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return of(size.width(), size.height()); 572f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 582f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 59cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu public static AspectRatio of4x3() { 60cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu return ASPECT_RATIO_4x3; 61cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu } 62cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu 63cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu public static AspectRatio of16x9() { 64cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu return ASPECT_RATIO_16x9; 65cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu } 66cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu 672f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public int getHeight() { 682f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return mHeight; 692f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 702f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 712f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public int getWidth() { 722f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return mWidth; 732f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 742f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 75cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu public float toFloat() { 76cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu return (float) mWidth / (float) mHeight; 77cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu } 78cc6139467c1c9545de1f098d938409e182c9b7adSenpo Hu 792f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall @Override 802f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public boolean equals(Object o) { 812f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (this == o) 822f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return true; 832f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (!(o instanceof AspectRatio)) 842f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return false; 852f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 862f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall AspectRatio that = (AspectRatio) o; 872f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 882f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (mHeight != that.mHeight) 892f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return false; 902f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (mWidth != that.mWidth) 912f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return false; 922f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 932f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return true; 942f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 952f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 962f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall @Override 972f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public int hashCode() { 982f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return Objects.hashCode(mWidth, mHeight); 992f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1002f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1012f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall @Override 1022f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public String toString() { 1032f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return String.format("AspectRatio[%d:%d]", getWidth(), getHeight()); 1042f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1052f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1062f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 1072f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return The transpose of this aspect ratio. 1082f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 1092f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public AspectRatio transpose() { 1102f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return of(mHeight, mWidth); 1112f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1122f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1132f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 1142f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return The landscape version of this aspect ratio. 1152f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 1162f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public AspectRatio asLandscape() { 1172f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (isLandscape()) { 1182f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return this; 1192f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } else { 1202f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return transpose(); 1212f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1222f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1232f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1242f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 1252f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return The portrait version of this aspect ratio. 1262f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 1272f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public AspectRatio asPortrait() { 1282f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (isPortrait()) { 1292f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return this; 1302f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } else { 1312f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return transpose(); 1322f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1332f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1342f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1352f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 1362f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return The version of this aspect ratio in the same orientation 1372f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * (portrait vs. landscape) of the other. 1382f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 1392f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public AspectRatio withOrientationOf(AspectRatio other) { 1402f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (other.isPortrait()) { 1412f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return asPortrait(); 1422f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } else { 1432f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return asLandscape(); 1442f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1452f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1462f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1472f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 1482f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return True if this aspect ratio is wider than the other. 1492f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 1502f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public boolean isWiderThan(AspectRatio other) { 1512f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall // this.mWidth other.mWidth 1522f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall // ----------- > ------------ 1532f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall // this.mHeight other.mHeight 1542f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return this.mWidth * other.mHeight > other.mWidth * this.mHeight; 1552f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1562f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1572f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 1582f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return True if this aspect ratio is taller than the other. 1592f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 1602f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public boolean isTallerThan(AspectRatio other) { 1612f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall // this.mWidth other.mWidth 1622f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall // ----------- < ------------ 1632f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall // this.mHeight other.mHeight 1642f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return this.mWidth * other.mHeight < other.mWidth * this.mHeight; 1652f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1662f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1672f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 1682f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return The largest centered region of area with this aspect ratio. For 1692f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * non-integer values, the returned rectangle coordinates are the 1702f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * *floor* of the result. 1712f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 1722f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall public Rect getLargestCenterCrop(Size area) { 1732f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall AspectRatio original = of(area); 1742f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1752f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall if (this.isWiderThan(original)) { 1762f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall // Crop off the top and bottom... 1772f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int cropHeight = area.width() * mHeight / mWidth; 1782f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int cropTop = (area.height() - cropHeight) / 2; 1792f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int cropBottom = cropTop + cropHeight; 1802f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int cropLeft = 0; 1812f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int cropRight = area.width(); 1822f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return new Rect(cropLeft, cropTop, cropRight, cropBottom); 1832f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } else { 1842f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall // Crop off the left and right... 1852f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int cropWidth = area.height() * mWidth / mHeight; 1862f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int cropLeft = (area.width() - cropWidth) / 2; 1872f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int cropRight = cropLeft + cropWidth; 1882f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int cropTop = 0; 1892f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall int cropBottom = area.height(); 1902f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return new Rect(cropLeft, cropTop, cropRight, cropBottom); 1912f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1922f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 1932f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 1942f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 1952f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return True if this aspect ratio is in landscape orientation. Square 1962f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * aspect ratios are both portrait *and* landscape. 1972f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 1982f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private boolean isLandscape() { 1992f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return mWidth >= mHeight; 2002f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 2012f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 2022f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall /** 2032f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * @return True if this aspect ratio is in portrait orientation. Square 2042f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall * aspect ratios are both portrait *and* landscape. 2052f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall */ 2062f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall private boolean isPortrait() { 2072f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall return mWidth <= mHeight; 2082f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall } 2092f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall 2102f6fe6f07a6d43d8bb49156a7a73e86896ab7d1ePuneet Lall} 211