/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.cameraview; import android.os.Parcel; import android.os.Parcelable; import android.support.annotation.NonNull; import android.support.v4.util.SparseArrayCompat; /** * Immutable class for describing proportional relationship between width and height. */ public class AspectRatio implements Comparable<AspectRatio>, Parcelable { private final static SparseArrayCompat<SparseArrayCompat<AspectRatio>> sCache = new SparseArrayCompat<>(16); private final int mX; private final int mY; /** * Returns an instance of {@link AspectRatio} specified by {@code x} and {@code y} values. * The values {@code x} and {@code} will be reduced by their greatest common divider. * * @param x The width * @param y The height * @return An instance of {@link AspectRatio} */ public static AspectRatio of(int x, int y) { int gcd = gcd(x, y); x /= gcd; y /= gcd; SparseArrayCompat<AspectRatio> arrayX = sCache.get(x); if (arrayX == null) { AspectRatio ratio = new AspectRatio(x, y); arrayX = new SparseArrayCompat<>(); arrayX.put(y, ratio); sCache.put(x, arrayX); return ratio; } else { AspectRatio ratio = arrayX.get(y); if (ratio == null) { ratio = new AspectRatio(x, y); arrayX.put(y, ratio); } return ratio; } } /** * Parse an {@link AspectRatio} from a {@link String} formatted like "4:3". * * @param s The string representation of the aspect ratio * @return The aspect ratio * @throws IllegalArgumentException when the format is incorrect. */ public static AspectRatio parse(String s) { int position = s.indexOf(':'); if (position == -1) { throw new IllegalArgumentException("Malformed aspect ratio: " + s); } try { int x = Integer.parseInt(s.substring(0, position)); int y = Integer.parseInt(s.substring(position + 1)); return AspectRatio.of(x, y); } catch (NumberFormatException e) { throw new IllegalArgumentException("Malformed aspect ratio: " + s, e); } } private AspectRatio(int x, int y) { mX = x; mY = y; } public int getX() { return mX; } public int getY() { return mY; } public boolean matches(Size size) { int gcd = gcd(size.getWidth(), size.getHeight()); int x = size.getWidth() / gcd; int y = size.getHeight() / gcd; return mX == x && mY == y; } @Override public boolean equals(Object o) { if (o == null) { return false; } if (this == o) { return true; } if (o instanceof AspectRatio) { AspectRatio ratio = (AspectRatio) o; return mX == ratio.mX && mY == ratio.mY; } return false; } @Override public String toString() { return mX + ":" + mY; } public float toFloat() { return (float) mX / mY; } @Override public int hashCode() { // assuming most sizes are <2^16, doing a rotate will give us perfect hashing return mY ^ ((mX << (Integer.SIZE / 2)) | (mX >>> (Integer.SIZE / 2))); } @Override public int compareTo(@NonNull AspectRatio another) { if (equals(another)) { return 0; } else if (toFloat() - another.toFloat() > 0) { return 1; } return -1; } /** * @return The inverse of this {@link AspectRatio}. */ public AspectRatio inverse() { //noinspection SuspiciousNameCombination return AspectRatio.of(mY, mX); } private static int gcd(int a, int b) { while (b != 0) { int c = b; b = a % b; a = c; } return a; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mX); dest.writeInt(mY); } public static final Parcelable.Creator<AspectRatio> CREATOR = new Parcelable.Creator<AspectRatio>() { @Override public AspectRatio createFromParcel(Parcel source) { int x = source.readInt(); int y = source.readInt(); return AspectRatio.of(x, y); } @Override public AspectRatio[] newArray(int size) { return new AspectRatio[size]; } }; }