diff options
| author | Igor Murashkin <iam@google.com> | 2014-04-17 14:01:13 -0700 |
|---|---|---|
| committer | Igor Murashkin <iam@google.com> | 2014-05-02 15:55:29 -0700 |
| commit | b3a78b2ca9655396e2d73950221d187b7e5bb3ba (patch) | |
| tree | 5513b1e31176da18dd3388ea26c9dba9149006a9 /core/java/android | |
| parent | 5295745026f669ada0da1484ecc5abb5e4706ff7 (diff) | |
camera2: Add re-usable data types for camera metadata key/values
Adds new types to public API:
* ColorSpaceTransform
* Range<T>
* SizeF (android.util)
* MeteringRectangle
Minor changes to:
* Size (docs and move to android.util)
* Preconditions (@hide)
Adds helper class:
* HashCodeHelpers
Change-Id: Ied6749a19234f3af9da838f365f8d68d90251aaf
Diffstat (limited to 'core/java/android')
| -rw-r--r-- | core/java/android/hardware/camera2/ColorSpaceTransform.java | 239 | ||||
| -rw-r--r-- | core/java/android/hardware/camera2/MeteringRectangle.java | 224 | ||||
| -rw-r--r-- | core/java/android/hardware/camera2/Size.java | 57 | ||||
| -rw-r--r-- | core/java/android/hardware/camera2/impl/HashCodeHelpers.java | 143 | ||||
| -rw-r--r-- | core/java/android/util/Range.java | 144 | ||||
| -rw-r--r-- | core/java/android/util/Size.java | 98 | ||||
| -rw-r--r-- | core/java/android/util/SizeF.java | 108 |
7 files changed, 997 insertions, 16 deletions
diff --git a/core/java/android/hardware/camera2/ColorSpaceTransform.java b/core/java/android/hardware/camera2/ColorSpaceTransform.java new file mode 100644 index 000000000000..9912e4ba5c19 --- /dev/null +++ b/core/java/android/hardware/camera2/ColorSpaceTransform.java @@ -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; +}; diff --git a/core/java/android/hardware/camera2/MeteringRectangle.java b/core/java/android/hardware/camera2/MeteringRectangle.java new file mode 100644 index 000000000000..ff7a745b75a6 --- /dev/null +++ b/core/java/android/hardware/camera2/MeteringRectangle.java @@ -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); + } +} diff --git a/core/java/android/hardware/camera2/Size.java b/core/java/android/hardware/camera2/Size.java index 45aaeaef45a2..9328a00323d2 100644 --- a/core/java/android/hardware/camera2/Size.java +++ b/core/java/android/hardware/camera2/Size.java @@ -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; diff --git a/core/java/android/hardware/camera2/impl/HashCodeHelpers.java b/core/java/android/hardware/camera2/impl/HashCodeHelpers.java new file mode 100644 index 000000000000..2d63827b1ce7 --- /dev/null +++ b/core/java/android/hardware/camera2/impl/HashCodeHelpers.java @@ -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 } ); + } + + +} diff --git a/core/java/android/util/Range.java b/core/java/android/util/Range.java new file mode 100644 index 000000000000..9a4bd4b3f205 --- /dev/null +++ b/core/java/android/util/Range.java @@ -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; +}; diff --git a/core/java/android/util/Size.java b/core/java/android/util/Size.java new file mode 100644 index 000000000000..ba1a35f474d6 --- /dev/null +++ b/core/java/android/util/Size.java @@ -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; +}; diff --git a/core/java/android/util/SizeF.java b/core/java/android/util/SizeF.java new file mode 100644 index 000000000000..0a8b4edf42c2 --- /dev/null +++ b/core/java/android/util/SizeF.java @@ -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; +}; |
