am 9c31c106: camera2: Add re-usable data types for camera metadata key/values

* commit '9c31c106db115c8fba67f83a1b416839b99317ee':
  camera2: Add re-usable data types for camera metadata key/values
This commit is contained in:
Igor Murashkin
2014-05-02 23:30:09 +00:00
committed by Android Git Automerger
9 changed files with 1210 additions and 20 deletions

View File

@@ -0,0 +1,239 @@
/*
* Copyright (C) 2014 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 android.hardware.camera2;
import static com.android.internal.util.Preconditions.*;
import android.hardware.camera2.impl.HashCodeHelpers;
import java.util.Arrays;
/**
* Immutable class for describing a 3x3 matrix of {@link Rational} values in row-major order.
*
* <p>This matrix maps a transform from one color space to another. For the particular color space
* source and target, see the appropriate camera metadata documentation for the key that provides
* this value.</p>
*
* @see CameraMetadata
*/
public final class ColorSpaceTransform {
/** The number of rows in this matrix. */
private static final int ROWS = 3;
/** The number of columns in this matrix. */
private static final int COLUMNS = 3;
/** The number of total Rational elements in this matrix. */
private static final int COUNT = ROWS * COLUMNS;
/** Number of int elements in a rational. */
private static final int RATIONAL_SIZE = 2;
/** Numerator offset inside a rational (pair). */
private static final int OFFSET_NUMERATOR = 0;
/** Denominator offset inside a rational (pair). */
private static final int OFFSET_DENOMINATOR = 1;
/** Number of int elements in this matrix. */
private static final int COUNT_INT = ROWS * COLUMNS * RATIONAL_SIZE;
/**
* Create a new immutable {@link ColorSpaceTransform} instance from a {@link Rational} array.
*
* <p>The elements must be stored in a row-major order.</p>
*
* @param elements An array of {@code 9} elements
*
* @throws IllegalArgumentException
* if the count of {@code elements} is not {@code 9}
* @throws NullPointerException
* if {@code elements} or any sub-element is {@code null}
*/
public ColorSpaceTransform(Rational[] elements) {
checkNotNull(elements, "elements must not be null");
if (elements.length != COUNT) {
throw new IllegalArgumentException("elements must be " + COUNT + " length");
}
mElements = new int[COUNT_INT];
for (int i = 0; i < elements.length; ++i) {
checkNotNull(elements, "element[" + i + "] must not be null");
mElements[i * RATIONAL_SIZE + OFFSET_NUMERATOR] = elements[i].getNumerator();
mElements[i * RATIONAL_SIZE + OFFSET_DENOMINATOR] = elements[i].getDenominator();
}
}
/**
* Create a new immutable {@link ColorSpaceTransform} instance from an {@code int} array.
*
* <p>The elements must be stored in a row-major order. Each rational is stored
* contiguously as a {@code (numerator, denominator)} pair.</p>
*
* <p>In particular:<pre>{@code
* int[] elements = new int[
* N11, D11, N12, D12, N13, D13,
* N21, D21, N22, D22, N23, D23,
* N31, D31, N32, D32, N33, D33
* ];
*
* new ColorSpaceTransform(elements)}</pre>
*
* where {@code Nij} and {@code Dij} is the numerator and denominator for row {@code i} and
* column {@code j}.</p>
*
* @param elements An array of {@code 18} elements
*
* @throws IllegalArgumentException
* if the count of {@code elements} is not {@code 18}
* @throws NullPointerException
* if {@code elements} is {@code null}
*/
public ColorSpaceTransform(int[] elements) {
checkNotNull(elements, "elements must not be null");
if (elements.length != COUNT_INT) {
throw new IllegalArgumentException("elements must be " + COUNT_INT + " length");
}
for (int i = 0; i < elements.length; ++i) {
checkNotNull(elements, "element " + i + " must not be null");
}
mElements = Arrays.copyOf(elements, elements.length);
}
/**
* Get an element of this matrix by its row and column.
*
* <p>The rows must be within the range [0, 3),
* and the column must be within the range [0, 3).</p>
*
* @return element (non-{@code null})
*
* @throws IllegalArgumentException if column or row was out of range
*/
public Rational getElement(int column, int row) {
if (column < 0 || column >= COLUMNS) {
throw new IllegalArgumentException("column out of range");
} else if (row < 0 || row >= ROWS) {
throw new IllegalArgumentException("row out of range");
}
int numerator = mElements[row * ROWS * RATIONAL_SIZE + column + OFFSET_NUMERATOR];
int denominator = mElements[row * ROWS * RATIONAL_SIZE + column + OFFSET_DENOMINATOR];
return new Rational(numerator, denominator);
}
/**
* Copy the {@link Rational} elements in row-major order from this matrix into the destination.
*
* @param destination
* an array big enough to hold at least {@code 9} elements after the
* {@code offset}
* @param offset
* a non-negative offset into the array
* @throws NullPointerException
* If {@code destination} was {@code null}
* @throws ArrayIndexOutOfBoundsException
* If there's not enough room to write the elements at the specified destination and
* offset.
*/
public void copyElements(Rational[] destination, int offset) {
checkArgumentNonnegative(offset, "offset must not be negative");
checkNotNull(destination, "destination must not be null");
if (destination.length + offset < COUNT) {
throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
}
for (int i = 0, j = 0; i < COUNT; ++i, j += RATIONAL_SIZE) {
int numerator = mElements[j + OFFSET_NUMERATOR];
int denominator = mElements[j + OFFSET_DENOMINATOR];
destination[i + offset] = new Rational(numerator, denominator);
}
}
/**
* Copy the {@link Rational} elements in row-major order from this matrix into the destination.
*
* <p>Each element is stored as a contiguous rational packed as a
* {@code (numerator, denominator)} pair of ints, identical to the
* {@link ColorSpaceTransform#ColorSpaceTransform(int[]) constructor}.</p>
*
* @param destination
* an array big enough to hold at least {@code 18} elements after the
* {@code offset}
* @param offset
* a non-negative offset into the array
* @throws NullPointerException
* If {@code destination} was {@code null}
* @throws ArrayIndexOutOfBoundsException
* If there's not enough room to write the elements at the specified destination and
* offset.
*
* @see ColorSpaceTransform#ColorSpaceTransform(int[])
*/
public void copyElements(int[] destination, int offset) {
checkArgumentNonnegative(offset, "offset must not be negative");
checkNotNull(destination, "destination must not be null");
if (destination.length + offset < COUNT_INT) {
throw new ArrayIndexOutOfBoundsException("destination too small to fit elements");
}
// Manual copy faster than System#arraycopy for very small loops
for (int i = 0; i < COUNT_INT; ++i) {
destination[i + offset] = mElements[i];
}
}
/**
* Check if this {@link ColorSpaceTransform} is equal to another {@link ColorSpaceTransform}.
*
* <p>Two color space transforms are equal if and only if all of their elements are
* {@link Object#equals equal}.</p>
*
* @return {@code true} if the objects were equal, {@code false} otherwise
*/
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (obj instanceof ColorSpaceTransform) {
final ColorSpaceTransform other = (ColorSpaceTransform) obj;
return Arrays.equals(mElements, other.mElements);
}
return false;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return HashCodeHelpers.hashCode(mElements);
}
private final int[] mElements;
};

View File

@@ -0,0 +1,224 @@
/*
* Copyright (C) 2014 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 android.hardware.camera2;
import android.util.Size;
import static com.android.internal.util.Preconditions.*;
import android.graphics.Point;
import android.graphics.Rect;
import android.hardware.camera2.impl.HashCodeHelpers;
/**
* An immutable class to represent a rectangle {@code (x,y, width, height)} with an
* additional weight component.
*
* </p>The rectangle is defined to be inclusive of the specified coordinates.</p>
*
* <p>When used with a {@link CaptureRequest}, the coordinate system is based on the active pixel
* array, with {@code (0,0)} being the top-left pixel in the
* {@link CameraCharacteristics#SENSOR_INFO_ACTIVE_ARRAY_SIZE active pixel array}, and
* {@code (android.sensor.info.activeArraySize.width - 1,
* android.sensor.info.activeArraySize.height - 1)}
* being the bottom-right pixel in the active pixel array.
* </p>
*
* <p>The metering weight is nonnegative.</p>
*/
public final class MeteringRectangle {
private final int mX;
private final int mY;
private final int mWidth;
private final int mHeight;
private final int mWeight;
/**
* Create a new metering rectangle.
*
* @param x coordinate >= 0
* @param y coordinate >= 0
* @param width width >= 0
* @param height height >= 0
* @param meteringWeight weight >= 0
*
* @throws IllegalArgumentException if any of the parameters were non-negative
*/
public MeteringRectangle(int x, int y, int width, int height, int meteringWeight) {
mX = checkArgumentNonnegative(x, "x must be nonnegative");
mY = checkArgumentNonnegative(y, "y must be nonnegative");
mWidth = checkArgumentNonnegative(width, "width must be nonnegative");
mHeight = checkArgumentNonnegative(height, "height must be nonnegative");
mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
}
/**
* Create a new metering rectangle.
*
* @param xy a non-{@code null} {@link Point} with both x,y >= 0
* @param dimensions a non-{@code null} {@link android.util.Size Size} with width, height >= 0
* @param meteringWeight weight >= 0
*
* @throws IllegalArgumentException if any of the parameters were non-negative
* @throws NullPointerException if any of the arguments were null
*/
public MeteringRectangle(Point xy, Size dimensions, int meteringWeight) {
checkNotNull(xy, "xy must not be null");
checkNotNull(dimensions, "dimensions must not be null");
mX = checkArgumentNonnegative(xy.x, "x must be nonnegative");
mY = checkArgumentNonnegative(xy.y, "y must be nonnegative");
mWidth = checkArgumentNonnegative(dimensions.getWidth(), "width must be nonnegative");
mHeight = checkArgumentNonnegative(dimensions.getHeight(), "height must be nonnegative");
mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
}
/**
* Create a new metering rectangle.
*
* @param rect a non-{@code null} rectangle with all x,y,w,h dimensions >= 0
* @param meteringWeight weight >= 0
*
* @throws IllegalArgumentException if any of the parameters were non-negative
* @throws NullPointerException if any of the arguments were null
*/
public MeteringRectangle(Rect rect, int meteringWeight) {
checkNotNull(rect, "rect must not be null");
mX = checkArgumentNonnegative(rect.left, "rect.left must be nonnegative");
mY = checkArgumentNonnegative(rect.top, "rect.top must be nonnegative");
mWidth = checkArgumentNonnegative(rect.width(), "rect.width must be nonnegative");
mHeight = checkArgumentNonnegative(rect.height(), "rect.height must be nonnegative");
mWeight = checkArgumentNonnegative(meteringWeight, "meteringWeight must be nonnegative");
}
/**
* Return the X coordinate of the left side of the rectangle.
*
* @return x coordinate >= 0
*/
public int getX() {
return mX;
}
/**
* Return the Y coordinate of the upper side of the rectangle.
*
* @return y coordinate >= 0
*/
public int getY() {
return mY;
}
/**
* Return the width of the rectangle.
*
* @return width >= 0
*/
public int getWidth() {
return mWidth;
}
/**
* Return the height of the rectangle.
*
* @return height >= 0
*/
public int getHeight() {
return mHeight;
}
/**
* Return the metering weight of the rectangle.
*
* @return weight >= 0
*/
public int getMeteringWeight() {
return mWeight;
}
/**
* Convenience method to create the upper-left (X,Y) coordinate as a {@link Point}.
*
* @return {@code (x,y)} point with both x,y >= 0
*/
public Point getUpperLeftPoint() {
return new Point(mX, mY);
}
/**
* Convenience method to create the size from this metering rectangle.
*
* <p>This strips away the X,Y,weight from the rectangle.</p>
*
* @return a Size with non-negative width and height
*/
public Size getSize() {
return new Size(mWidth, mHeight);
}
/**
* Convenience method to create a {@link Rect} from this metering rectangle.
*
* <p>This strips away the weight from the rectangle.</p>
*
* @return a {@link Rect} with non-negative x1, y1, x2, y2
*/
public Rect getRect() {
return new Rect(mX, mY, mX + mWidth, mY + mHeight);
}
/**
* {@inheritDoc}
*/
@Override
public boolean equals(final Object other) {
if (other instanceof MeteringRectangle) {
return equals(other);
}
return false;
}
/**
* Compare two metering rectangles to see if they are equal.
*
* Two weighted rectangles are only considered equal if each of their components
* (x, y, width, height, weight) is respectively equal.
*
* @param other Another MeteringRectangle
*
* @return {@code true} if the metering rectangles are equal, {@code false} otherwise
*/
public boolean equals(final MeteringRectangle other) {
if (other == null) {
return false;
}
return (mX == other.mX
&& mY == other.mY
&& mWidth == other.mWidth
&& mHeight == other.mHeight
&& mWidth == other.mWidth);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return HashCodeHelpers.hashCode(mX, mY, mWidth, mHeight, mWeight);
}
}

View File

@@ -16,32 +16,55 @@
package android.hardware.camera2;
// TODO: Delete this class, since it was moved to android.util as public API
/**
* A simple immutable class for describing the dimensions of camera image
* buffers.
* Immutable class for describing width and height dimensions in pixels.
*
* @hide
*/
public final class Size {
/**
* Create a new immutable Size instance
* Create a new immutable Size instance.
*
* @param width The width to store in the Size instance
* @param height The height to store in the Size instance
* @param width The width of the size, in pixels
* @param height The height of the size, in pixels
*/
public Size(int width, int height) {
public Size(final int width, final int height) {
mWidth = width;
mHeight = height;
}
/**
* Get the width of the size (in pixels).
* @return width
*/
public final int getWidth() {
return mWidth;
}
/**
* Get the height of the size (in pixels).
* @return height
*/
public final int getHeight() {
return mHeight;
}
/**
* Check if this size is equal to another size.
* <p>
* Two sizes are equal if and only if both their widths and heights are
* equal.
* </p>
* <p>
* A size object is never equal to any other type of object.
* </p>
*
* @return {@code true} if the objects were equal, {@code false} otherwise
*/
@Override
public boolean equals(Object obj) {
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
@@ -49,27 +72,29 @@ public final class Size {
return true;
}
if (obj instanceof Size) {
Size other = (Size) obj;
final Size other = (Size) obj;
return mWidth == other.mWidth && mHeight == other.mHeight;
}
return false;
}
/**
* Return the size represented as a string with the format {@code "WxH"}
*
* @return string representation of the size
*/
@Override
public String toString() {
return mWidth + "x" + mHeight;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
final long INT_MASK = 0xffffffffL;
long asLong = INT_MASK & mWidth;
asLong <<= 32;
asLong |= (INT_MASK & mHeight);
return ((Long)asLong).hashCode();
// assuming most sizes are <2^16, doing a rotate will give us perfect hashing
return mHeight ^ ((mWidth << (Integer.SIZE / 2)) | (mWidth >>> (Integer.SIZE / 2)));
}
private final int mWidth;

View File

@@ -0,0 +1,143 @@
/*
* Copyright (C) 2014 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 android.hardware.camera2.impl;
/**
* Provide hashing functions using the Modified Bernstein hash
*/
public final class HashCodeHelpers {
/**
* Hash every element uniformly using the Modified Bernstein hash.
*
* <p>Useful to implement a {@link Object#hashCode} for uniformly distributed data.</p>
*
* @param array a non-{@code null} array of integers
*
* @return the numeric hash code
*/
public static int hashCode(int[] array) {
if (array == null) {
return 0;
}
/*
* Note that we use 31 here instead of 33 since it's preferred in Effective Java
* and used elsewhere in the runtime (e.g. Arrays#hashCode)
*
* That being said 33 and 31 are nearly identical in terms of their usefulness
* according to http://svn.apache.org/repos/asf/apr/apr/trunk/tables/apr_hash.c
*/
int h = 1;
for (int x : array) {
// Strength reduction; in case the compiler has illusions about divisions being faster
h = ((h << 5) - h) ^ x; // (h * 31) XOR x
}
return h;
}
/**
* Hash every element uniformly using the Modified Bernstein hash.
*
* <p>Useful to implement a {@link Object#hashCode} for uniformly distributed data.</p>
*
* @param array a non-{@code null} array of floats
*
* @return the numeric hash code
*/
public static int hashCode(float[] array) {
if (array == null) {
return 0;
}
int h = 1;
for (float f : array) {
int x = Float.floatToIntBits(f);
h = ((h << 5) - h) ^ x; // (h * 31) XOR x
}
return h;
}
/**
* Hash every element uniformly using the Modified Bernstein hash.
*
* <p>Useful to implement a {@link Object#hashCode} for uniformly distributed data.</p>
*
* @param array a non-{@code null} array of objects
*
* @return the numeric hash code
*/
public static <T> int hashCode(T[] array) {
if (array == null) {
return 0;
}
int h = 1;
for (T o : array) {
int x = (o == null) ? 0 : o.hashCode();
h = ((h << 5) - h) ^ x; // (h * 31) XOR x
}
return h;
}
public static <T> int hashCode(T a) {
return (a == null) ? 0 : a.hashCode();
}
public static <T> int hashCode(T a, T b) {
int h = hashCode(a);
int x = (b == null) ? 0 : b.hashCode();
h = ((h << 5) - h) ^ x; // (h * 31) XOR x
return h;
}
public static <T> int hashCode(T a, T b, T c) {
int h = hashCode(a, b);
int x = (a == null) ? 0 : a.hashCode();
h = ((h << 5) - h) ^ x; // (h * 31) XOR x
return h;
}
public static int hashCode(int x) {
return hashCode(new int[] { x } );
}
public static int hashCode(int x, int y) {
return hashCode(new int[] { x, y } );
}
public static int hashCode(int x, int y, int z) {
return hashCode(new int[] { x, y, z } );
}
public static int hashCode(int x, int y, int z, int w) {
return hashCode(new int[] { x, y, z, w } );
}
public static int hashCode(int x, int y, int z, int w, int t) {
return hashCode(new int[] { x, y, z, w, t } );
}
}

View File

@@ -0,0 +1,144 @@
/*
* Copyright (C) 2014 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 android.util;
import static com.android.internal.util.Preconditions.*;
import android.hardware.camera2.impl.HashCodeHelpers;
/**
* Immutable class for describing the range of two numeric values.
* <p>
* A range (or "interval") defines the inclusive boundaries around a contiguous span of
* values of some {@link Comparable} type; for example,
* "integers from 1 to 100 inclusive."
* </p>
* <p>
* All ranges are bounded, and the left side of the range is always {@code >=}
* the right side of the range.
* </p>
*
* <p>Although the implementation itself is immutable, there is no restriction that objects
* stored must also be immutable. If mutable objects are stored here, then the range
* effectively becomes mutable. </p>
*/
public final class Range<T extends Comparable<? super T>> {
/**
* Create a new immutable range.
*
* <p>
* The endpoints are {@code [lower, upper]}; that
* is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal}
* to {@code upper}.
* </p>
*
* @param lower The lower endpoint (inclusive)
* @param upper The upper endpoint (inclusive)
*
* @throws NullPointerException if {@code lower} or {@code upper} is {@code null}
*/
public Range(final T lower, final T upper) {
mLower = checkNotNull(lower, "lower must not be null");
mUpper = checkNotNull(upper, "upper must not be null");
if (lower.compareTo(upper) > 0) {
throw new IllegalArgumentException("lower must be less than or equal to upper");
}
}
/**
* Create a new immutable range, with the argument types inferred.
*
* <p>
* The endpoints are {@code [lower, upper]}; that
* is the range is bounded. {@code lower} must be {@link Comparable#compareTo lesser or equal}
* to {@code upper}.
* </p>
*
* @param lower The lower endpoint (inclusive)
* @param upper The upper endpoint (inclusive)
*
* @throws NullPointerException if {@code lower} or {@code upper} is {@code null}
*/
public static <T extends Comparable<? super T>> Range<T> create(final T lower, final T upper) {
return new Range<T>(lower, upper);
}
/**
* Get the lower endpoint.
*
* @return a non-{@code null} {@code T} reference
*/
public T getLower() {
return mLower;
}
/**
* Get the upper endpoint.
*
* @return a non-{@code null} {@code T} reference
*/
public T getUpper() {
return mUpper;
}
/**
* Compare two ranges for equality.
*
* <p>A range is considered equal if and only if both the lower and upper endpoints
* are also equal.</p>
*
* @return {@code true} if the ranges are equal, {@code false} otherwise
*/
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (obj instanceof Range) {
@SuppressWarnings("rawtypes")
final
Range other = (Range) obj;
return mLower.equals(other.mLower) && mUpper.equals(other.mUpper);
}
return false;
}
/**
* Return the range as a string representation {@code "[lower, upper]"}.
*
* @return string representation of the range
*/
@Override
public String toString() {
return String.format("[%s, %s]", mLower, mUpper);
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return HashCodeHelpers.hashCode(mLower, mUpper);
}
private final T mLower;
private final T mUpper;
};

View File

@@ -0,0 +1,98 @@
/*
* Copyright (C) 2013 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 android.util;
/**
* Immutable class for describing width and height dimensions in pixels.
*/
public final class Size {
/**
* Create a new immutable Size instance.
*
* @param width The width of the size, in pixels
* @param height The height of the size, in pixels
*/
public Size(int width, int height) {
mWidth = width;
mHeight = height;
}
/**
* Get the width of the size (in pixels).
* @return width
*/
public int getWidth() {
return mWidth;
}
/**
* Get the height of the size (in pixels).
* @return height
*/
public int getHeight() {
return mHeight;
}
/**
* Check if this size is equal to another size.
* <p>
* Two sizes are equal if and only if both their widths and heights are
* equal.
* </p>
* <p>
* A size object is never equal to any other type of object.
* </p>
*
* @return {@code true} if the objects were equal, {@code false} otherwise
*/
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (obj instanceof Size) {
Size other = (Size) obj;
return mWidth == other.mWidth && mHeight == other.mHeight;
}
return false;
}
/**
* Return the size represented as a string with the format {@code "WxH"}
*
* @return string representation of the size
*/
@Override
public String toString() {
return mWidth + "x" + mHeight;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
// assuming most sizes are <2^16, doing a rotate will give us perfect hashing
return mHeight ^ ((mWidth << (Integer.SIZE / 2)) | (mWidth >>> (Integer.SIZE / 2)));
}
private final int mWidth;
private final int mHeight;
};

View File

@@ -0,0 +1,108 @@
/*
* Copyright (C) 2014 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 android.util;
import static com.android.internal.util.Preconditions.*;
/**
* Immutable class for describing width and height dimensions in some arbitrary
* unit.
* <p>
* Width and height are finite values stored as a floating point representation.
* </p>
*/
public final class SizeF {
/**
* Create a new immutable SizeF instance.
*
* <p>Both the {@code width} and the {@code height} must be a finite number.
* In particular, {@code NaN} and positive/negative infinity are illegal values.</p>
*
* @param width The width of the size
* @param height The height of the size
*
* @throws IllegalArgumentException
* if either {@code width} or {@code height} was not finite.
*/
public SizeF(final float width, final float height) {
mWidth = checkArgumentFinite(width, "width");
mHeight = checkArgumentFinite(height, "height");
}
/**
* Get the width of the size (as an arbitrary unit).
* @return width
*/
public float getWidth() {
return mWidth;
}
/**
* Get the height of the size (as an arbitrary unit).
* @return height
*/
public float getHeight() {
return mHeight;
}
/**
* Check if this size is equal to another size.
*
* <p>Two sizes are equal if and only if both their widths and heights are the same.</p>
*
* <p>For this purpose, the width/height float values are considered to be the same if and only
* if the method {@link Float#floatToIntBits(float)} returns the identical {@code int} value
* when applied to each.</p>
*
* @return {@code true} if the objects were equal, {@code false} otherwise
*/
@Override
public boolean equals(final Object obj) {
if (obj == null) {
return false;
}
if (this == obj) {
return true;
}
if (obj instanceof SizeF) {
final SizeF other = (SizeF) obj;
return mWidth == other.mWidth && mHeight == other.mHeight;
}
return false;
}
/**
* Return the size represented as a string with the format {@code "WxH"}
*
* @return string representation of the size
*/
@Override
public String toString() {
return mWidth + "x" + mHeight;
}
/**
* {@inheritDoc}
*/
@Override
public int hashCode() {
return Float.floatToIntBits(mWidth) ^ Float.floatToIntBits(mHeight);
}
private final float mWidth;
private final float mHeight;
};

View File

@@ -30,7 +30,7 @@ public class Preconditions {
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
*/
public static <T> T checkNotNull(T reference) {
public static <T> T checkNotNull(final T reference) {
if (reference == null) {
throw new NullPointerException();
}
@@ -47,7 +47,7 @@ public class Preconditions {
* @return the non-null reference that was validated
* @throws NullPointerException if {@code reference} is null
*/
public static <T> T checkNotNull(T reference, Object errorMessage) {
public static <T> T checkNotNull(final T reference, final Object errorMessage) {
if (reference == null) {
throw new NullPointerException(String.valueOf(errorMessage));
}
@@ -61,7 +61,7 @@ public class Preconditions {
* @param expression a boolean expression
* @throws IllegalStateException if {@code expression} is false
*/
public static void checkState(boolean expression) {
public static void checkState(final boolean expression) {
if (!expression) {
throw new IllegalStateException();
}
@@ -71,11 +71,178 @@ public class Preconditions {
* Check the requested flags, throwing if any requested flags are outside
* the allowed set.
*/
public static void checkFlagsArgument(int requestedFlags, int allowedFlags) {
public static void checkFlagsArgument(final int requestedFlags, final int allowedFlags) {
if ((requestedFlags & allowedFlags) != requestedFlags) {
throw new IllegalArgumentException("Requested flags 0x"
+ Integer.toHexString(requestedFlags) + ", but only 0x"
+ Integer.toHexString(allowedFlags) + " are allowed");
}
}
/**
* Ensures that that the argument numeric value is non-negative.
*
* @param value a numeric int value
* @param errorMessage the exception message to use if the check fails
* @return the validated numeric value
* @throws IllegalArgumentException if {@code value} was negative
*/
public static int checkArgumentNonnegative(final int value, final String errorMessage) {
if (value < 0) {
throw new IllegalArgumentException(errorMessage);
}
return value;
}
/**
* Ensures that that the argument numeric value is non-negative.
*
* @param value a numeric long value
* @param errorMessage the exception message to use if the check fails
* @return the validated numeric value
* @throws IllegalArgumentException if {@code value} was negative
*/
public static long checkArgumentNonnegative(final long value, final String errorMessage) {
if (value < 0) {
throw new IllegalArgumentException(errorMessage);
}
return value;
}
/**
* Ensures that that the argument numeric value is positive.
*
* @param value a numeric int value
* @param errorMessage the exception message to use if the check fails
* @return the validated numeric value
* @throws IllegalArgumentException if {@code value} was not positive
*/
public static int checkArgumentPositive(final int value, final String errorMessage) {
if (value <= 0) {
throw new IllegalArgumentException(errorMessage);
}
return value;
}
/**
* Ensures that the argument floating point value is a finite number.
*
* <p>A finite number is defined to be both representable (that is, not NaN) and
* not infinite (that is neither positive or negative infinity).</p>
*
* @param value a floating point value
* @param valueName the name of the argument to use if the check fails
*
* @return the validated floating point value
*
* @throws IllegalArgumentException if {@code value} was not finite
*/
public static float checkArgumentFinite(final float value, final String valueName) {
if (Float.isNaN(value)) {
throw new IllegalArgumentException(valueName + " must not be NaN");
} else if (Float.isInfinite(value)) {
throw new IllegalArgumentException(valueName + " must not be infinite");
}
return value;
}
/**
* Ensures that the argument floating point value is within the inclusive range.
*
* <p>While this can be used to range check against +/- infinity, note that all NaN numbers
* will always be out of range.</p>
*
* @param value a floating point value
* @param lower the lower endpoint of the inclusive range
* @param upper the upper endpoint of the inclusive range
* @param valueName the name of the argument to use if the check fails
*
* @return the validated floating point value
*
* @throws IllegalArgumentException if {@code value} was not within the range
*/
public static float checkArgumentInRange(float value, float lower, float upper,
String valueName) {
if (Float.isNaN(value)) {
throw new IllegalArgumentException(valueName + " must not be NaN");
} else if (value < lower) {
throw new IllegalArgumentException(
String.format(
"%s is out of range of [%f, %f] (too low)", valueName, lower, upper));
} else if (value > upper) {
throw new IllegalArgumentException(
String.format(
"%s is out of range of [%f, %f] (too high)", valueName, lower, upper));
}
return value;
}
/**
* Ensures that the array is not {@code null}, and none if its elements are {@code null}.
*
* @param value an array of boxed objects
* @param valueName the name of the argument to use if the check fails
*
* @return the validated array
*
* @throws NullPointerException if the {@code value} or any of its elements were {@code null}
*/
public static <T> T[] checkArrayElementsNotNull(final T[] value, final String valueName) {
if (value == null) {
throw new NullPointerException(valueName + " must not be null");
}
for (int i = 0; i < value.length; ++i) {
if (value[i] == null) {
throw new NullPointerException(
String.format("%s[%d] must not be null", valueName, i));
}
}
return value;
}
/**
* Ensures that all elements in the argument floating point array are within the inclusive range
*
* <p>While this can be used to range check against +/- infinity, note that all NaN numbers
* will always be out of range.</p>
*
* @param value a floating point array of values
* @param lower the lower endpoint of the inclusive range
* @param upper the upper endpoint of the inclusive range
* @param valueName the name of the argument to use if the check fails
*
* @return the validated floating point value
*
* @throws IllegalArgumentException if any of the elements in {@code value} were out of range
* @throws NullPointerException if the {@code value} was {@code null}
*/
public static float[] checkArrayElementsInRange(float[] value, float lower, float upper,
String valueName) {
checkNotNull(value, valueName + " must not be null");
for (int i = 0; i < value.length; ++i) {
float v = value[i];
if (Float.isNaN(v)) {
throw new IllegalArgumentException(valueName + "[" + i + "] must not be NaN");
} else if (v < lower) {
throw new IllegalArgumentException(
String.format("%s[%d] is out of range of [%f, %f] (too low)",
valueName, i, lower, upper));
} else if (v > upper) {
throw new IllegalArgumentException(
String.format("%s[%d] is out of range of [%f, %f] (too high)",
valueName, i, lower, upper));
}
}
return value;
}
}