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/hardware/camera2 | |
parent | 5295745026f669ada0da1484ecc5abb5e4706ff7 (diff) | |
download | frameworks_base-b3a78b2ca9655396e2d73950221d187b7e5bb3ba.zip frameworks_base-b3a78b2ca9655396e2d73950221d187b7e5bb3ba.tar.gz frameworks_base-b3a78b2ca9655396e2d73950221d187b7e5bb3ba.tar.bz2 |
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/hardware/camera2')
4 files changed, 647 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 0000000..9912e4b --- /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 0000000..ff7a745 --- /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 45aaeae..9328a003 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 0000000..2d63827 --- /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 } ); + } + + +} |