diff --git a/api/current.txt b/api/current.txt index e5092ff4ffbd0..d160987a08a03 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12237,6 +12237,16 @@ package android.hardware.camera2 { field public static final int SCORE_MIN = 1; // 0x1 } + public final class LensShadingMap { + method public void copyGainFactors(float[], int); + method public int getColumnCount(); + method public float getGainFactor(int, int, int); + method public int getGainFactorCount(); + method public android.hardware.camera2.RggbChannelVector getGainFactorVector(int, int); + method public int getRowCount(); + field public static final float MINIMUM_GAIN_FACTOR = 1.0f; + } + public final class MeteringRectangle { ctor public MeteringRectangle(int, int, int, int, int); ctor public MeteringRectangle(android.graphics.Point, android.util.Size, int); @@ -12258,12 +12268,52 @@ package android.hardware.camera2 { method public int getNumerator(); } + public final class RggbChannelVector { + ctor public RggbChannelVector(float, float, float, float); + method public void copyTo(float[], int); + method public float getBlue(); + method public float getComponent(int); + method public float getGreenEven(); + method public float getGreenOdd(); + method public final float getRed(); + field public static final int BLUE = 3; // 0x3 + field public static final int COUNT = 4; // 0x4 + field public static final int GREEN_EVEN = 1; // 0x1 + field public static final int GREEN_ODD = 2; // 0x2 + field public static final int RED = 0; // 0x0 + } + public final class Size { ctor public Size(int, int); method public final int getHeight(); method public final int getWidth(); } + public final class StreamConfigurationMap { + method public final int[] getOutputFormats(); + method public long getOutputMinFrameDuration(int, android.util.Size); + method public long getOutputMinFrameDuration(java.lang.Class, android.util.Size); + method public android.util.Size[] getOutputSizes(java.lang.Class); + method public android.util.Size[] getOutputSizes(int); + method public long getOutputStallDuration(int, android.util.Size); + method public long getOutputStallDuration(java.lang.Class, android.util.Size); + method public boolean isOutputSupportedFor(int); + method public static boolean isOutputSupportedFor(java.lang.Class); + method public boolean isOutputSupportedFor(android.view.Surface); + } + + public final class TonemapCurve { + method public void copyColorCurve(int, float[], int); + method public android.graphics.PointF getPoint(int, int); + method public int getPointCount(int); + field public static final int CHANNEL_BLUE = 2; // 0x2 + field public static final int CHANNEL_GREEN = 1; // 0x1 + field public static final int CHANNEL_RED = 0; // 0x0 + field public static final float LEVEL_BLACK = 0.0f; + field public static final float LEVEL_WHITE = 1.0f; + field public static final int POINT_SIZE = 2; // 0x2 + } + } package android.hardware.display { diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java index bb290af131c64..9d0e0e1995cd7 100644 --- a/core/java/android/hardware/camera2/CameraDevice.java +++ b/core/java/android/hardware/camera2/CameraDevice.java @@ -192,8 +192,9 @@ public interface CameraDevice extends AutoCloseable { * *

The camera device will query each Surface's size and formats upon this * call, so they must be set to a valid setting at this time (in particular: - * if the format is user-visible, it must be one of android.scaler.availableFormats; - * and the size must be one of android.scaler.available[Processed|Jpeg]Sizes).

+ * if the format is user-visible, it must be one of + * {@link StreamConfigurationMap#getOutputFormats}; and the size must be one of + * {@link StreamConfigurationMap#getOutputSizes(int)}).

* *

When this method is called with valid Surfaces, the device will transition to the {@link * StateListener#onBusy busy state}. Once configuration is complete, the device will transition @@ -239,6 +240,9 @@ public interface CameraDevice extends AutoCloseable { * @see StateListener#onUnconfigured * @see #stopRepeating * @see #flush + * @see StreamConfigurationMap#getOutputFormats() + * @see StreamConfigurationMap#getOutputSizes(int) + * @see StreamConfigurationMap#getOutputSizes(Class) */ public void configureOutputs(List outputs) throws CameraAccessException; diff --git a/core/java/android/hardware/camera2/LensShadingMap.java b/core/java/android/hardware/camera2/LensShadingMap.java new file mode 100644 index 0000000000000..2c224f645ffc1 --- /dev/null +++ b/core/java/android/hardware/camera2/LensShadingMap.java @@ -0,0 +1,243 @@ +/* + * 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 static android.hardware.camera2.RggbChannelVector.*; + +import android.hardware.camera2.impl.HashCodeHelpers; + +import java.util.Arrays; + +/** + * Immutable class for describing a {@code 4 x N x M} lens shading map of floats. + * + * @see CameraCharacteristics#LENS_SHADING_MAP + */ +public final class LensShadingMap { + + /** + * The smallest gain factor in this map. + * + *

All values in this map will be at least this large.

+ */ + public static final float MINIMUM_GAIN_FACTOR = 1.0f; + + /** + * Create a new immutable LensShadingMap instance. + * + *

The elements must be stored in a row-major order (fully packed).

+ * + *

This constructor takes over the array; do not write to the array afterwards.

+ * + * @param elements + * An array of elements whose length is + * {@code RggbChannelVector.COUNT * rows * columns} + * + * @throws IllegalArgumentException + * if the {@code elements} array length is invalid, + * if any of the subelems are not finite or less than {@value #MINIMUM_GAIN_FACTOR}, + * or if rows or columns is not positive + * @throws NullPointerException + * if {@code elements} is {@code null} + * + * @hide + */ + public LensShadingMap(final float[] elements, final int rows, final int columns) { + + mRows = checkArgumentPositive(rows, "rows must be positive"); + mColumns = checkArgumentPositive(rows, "columns must be positive"); + mElements = checkNotNull(elements, "elements must not be null"); + + if (elements.length != getGainFactorCount()) { + throw new IllegalArgumentException("elements must be " + getGainFactorCount() + + " length"); + } + + // Every element must be finite and >= 1.0f + checkArrayElementsInRange(elements, MINIMUM_GAIN_FACTOR, Float.MAX_VALUE, "elements"); + } + + /** + * Get the number of rows in this map. + */ + public int getRowCount() { + return mRows; + } + + /** + * Get the number of columns in this map. + */ + public int getColumnCount() { + return mColumns; + } + + /** + * Get the total number of gain factors in this map. + * + *

A single gain factor contains exactly one color channel. + * Use with {@link #copyGainFactors} to allocate a large-enough array.

+ */ + public int getGainFactorCount() { + return mRows * mColumns * COUNT; + } + + /** + * Get a single color channel gain factor from this lens shading map by its row and column. + * + *

The rows must be within the range [0, {@link #getRowCount}), + * the column must be within the range [0, {@link #getColumnCount}), + * and the color channel must be within the range [0, {@value RggbChannelVector#COUNT}).

+ * + *

The channel order is {@code [R, Geven, Godd, B]}, where + * {@code Geven} is the green channel for the even rows of a Bayer pattern, and + * {@code Godd} is the odd rows. + *

+ * + * @param colorChannel color channel from {@code [R, Geven, Godd, B]} + * @param column within the range [0, {@link #getColumnCount}) + * @param row within the range [0, {@link #getRowCount}) + * + * @return a gain factor >= {@value #MINIMUM_GAIN_FACTOR} + * + * @throws IllegalArgumentException if any of the parameters was out of range + * + * @see #RED + * @see #GREEN_EVEN + * @see #GREEN_ODD + * @see #BLUE + * @see #getRowCount + * @see #getColumnCount + */ + public float getGainFactor(final int colorChannel, final int column, final int row) { + if (colorChannel < 0 || colorChannel > COUNT) { + throw new IllegalArgumentException("colorChannel out of range"); + } else if (column < 0 || column >= mColumns) { + throw new IllegalArgumentException("column out of range"); + } else if (row < 0 || row >= mRows) { + throw new IllegalArgumentException("row out of range"); + } + + return mElements[colorChannel + (row * mColumns + column) * COUNT ]; + } + + /** + * Get a gain factor vector from this lens shading map by its row and column. + * + *

The rows must be within the range [0, {@link #getRowCount}), + * the column must be within the range [0, {@link #getColumnCount}).

+ * + * @param column within the range [0, {@link #getColumnCount}) + * @param row within the range [0, {@link #getRowCount}) + * + * @return an {@link RggbChannelVector} where each gain factor >= {@value #MINIMUM_GAIN_FACTOR} + * + * @throws IllegalArgumentException if any of the parameters was out of range + * + * @see #getRowCount + * @see #getColumnCount + */ + public RggbChannelVector getGainFactorVector(final int column, final int row) { + if (column < 0 || column >= mColumns) { + throw new IllegalArgumentException("column out of range"); + } else if (row < 0 || row >= mRows) { + throw new IllegalArgumentException("row out of range"); + } + + final int offset = (row * mColumns + column) * COUNT; + + final float red = + mElements[RED + offset]; + final float greenEven = + mElements[GREEN_EVEN + offset]; + final float greenOdd = + mElements[GREEN_ODD + offset]; + final float blue = + mElements[BLUE + offset]; + + return new RggbChannelVector(red, greenEven, greenOdd, blue); + } + + /** + * Copy all gain factors in row-major order from this lens shading map into the destination. + * + *

Each gain factor will be >= {@link #MINIMUM_GAIN_FACTOR}.

+ * + * @param destination + * an array big enough to hold at least {@link RggbChannelVector#COUNT} + * elements after the {@code offset} + * @param offset + * a non-negative offset into the array + * @throws NullPointerException + * If {@code destination} was {@code null} + * @throws IllegalArgumentException + * If offset was negative + * @throws ArrayIndexOutOfBoundsException + * If there's not enough room to write the elements at the specified destination and + * offset. + * + * @see CaptureResult#STATISTICS_LENS_SHADING_MAP + */ + public void copyGainFactors(final float[] destination, final int offset) { + checkArgumentNonnegative(offset, "offset must not be negative"); + checkNotNull(destination, "destination must not be null"); + if (destination.length + offset < getGainFactorCount()) { + throw new ArrayIndexOutOfBoundsException("destination too small to fit elements"); + } + + System.arraycopy(mElements, /*srcPos*/0, destination, offset, getGainFactorCount()); + } + + /** + * Check if this LensShadingMap is equal to another LensShadingMap. + * + *

Two lens shading maps are equal if and only if they have the same rows/columns, + * and all of their elements are {@link Object#equals equal}.

+ * + * @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 LensShadingMap) { + final LensShadingMap other = (LensShadingMap) obj; + return mRows == other.mRows + && mColumns == other.mColumns + && Arrays.equals(mElements, other.mElements); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + int elemsHash = HashCodeHelpers.hashCode(mElements); + return HashCodeHelpers.hashCode(mRows, mColumns, elemsHash); + } + + + private final int mRows; + private final int mColumns; + private final float[] mElements; +}; diff --git a/core/java/android/hardware/camera2/ReprocessFormatsMap.java b/core/java/android/hardware/camera2/ReprocessFormatsMap.java new file mode 100644 index 0000000000000..c6c59d433770d --- /dev/null +++ b/core/java/android/hardware/camera2/ReprocessFormatsMap.java @@ -0,0 +1,258 @@ +/* + * 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 to store the input to output formats + * {@link CameraCharacteristics#SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP map} to be used for with + * camera image reprocessing. + * + *

+ * The mapping of image formats that are supported by this camera device for input streams, + * to their corresponding output formats.

+ * + *

+ * Attempting to configure an input stream with output streams not listed as available in this map + * is not valid. + *

+ * + * @see CameraCharacteristics#SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP + * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS + * + * + * @hide + */ +public final class ReprocessFormatsMap { + /** + * Create a new {@link ReprocessFormatsMap} + * + *

This value is encoded as a variable-size array-of-arrays. + * The inner array always contains {@code [format, length, ...]} where ... has length elements. + * An inner array is followed by another inner array if the total metadata entry size hasn't + * yet been exceeded.

+ * + *

Entry must not be {@code null}. Empty array is acceptable.

+ * + *

The entry array ownership is passed to this instance after construction; do not + * write to it afterwards.

+ * + * @param entry Array of ints, not yet deserialized (not-null) + * + * @throws IllegalArgumentException + * if the data was poorly formatted + * (missing output format length or too few output formats) + * @throws NullPointerException + * if entry was null + * + * @hide + */ + public ReprocessFormatsMap(final int[] entry) { + checkNotNull(entry, "entry must not be null"); + + int numInputs = 0; + int left = entry.length; + for (int i = 0; i < entry.length; ) { + final int format = entry[i]; + + left--; + i++; + + if (left < 1) { + throw new IllegalArgumentException( + String.format("Input %x had no output format length listed", format)); + } + + final int length = entry[i]; + left--; + i++; + + if (length > 0) { + if (left < length) { + throw new IllegalArgumentException( + String.format( + "Input %x had too few output formats listed (actual: %d, " + + "expected: %d)", format, left, length)); + } + + i += length; + left -= length; + } + + numInputs++; + } + + mEntry = entry; + mInputCount = numInputs; + } + + /** + * Get a list of all input image formats that can be used to reprocess an input + * stream into an output stream. + * + *

Use this input format to look up the available output formats with {@link #getOutputs}. + *

+ * + * @return an array of inputs (possibly empty, but never {@code null}) + * + * @see ImageFormat + * @see #getOutputs + */ + public int[] getInputs() { + final int[] inputs = new int[mInputCount]; + + int left = mEntry.length; + for (int i = 0, j = 0; i < mEntry.length; j++) { + final int format = mEntry[i]; + + left--; + i++; + + if (left < 1) { + throw new AssertionError( + String.format("Input %x had no output format length listed", format)); + } + // TODO: check format is a valid input format + + final int length = mEntry[i]; + left--; + i++; + + if (length > 0) { + if (left < length) { + throw new AssertionError( + String.format( + "Input %x had too few output formats listed (actual: %d, " + + "expected: %d)", format, left, length)); + } + + i += length; + left -= length; + } + + // TODO: check output format is a valid output format + + inputs[j] = format; + } + + return inputs; + } + + /** + * Get the list of output formats that can be reprocessed into from the input {@code format}. + * + *

The input {@code format} must be one of the formats returned by {@link #getInputs}.

+ * + * @param format an input format + * + * @return list of output image formats + * + * @see ImageFormat + * @see #getInputs + */ + public int[] getOutputs(final int format) { + + int left = mEntry.length; + for (int i = 0; i < mEntry.length; ) { + final int inputFormat = mEntry[i]; + + left--; + i++; + + if (left < 1) { + throw new AssertionError( + String.format("Input %x had no output format length listed", format)); + } + + final int length = mEntry[i]; + left--; + i++; + + if (length > 0) { + if (left < length) { + throw new AssertionError( + String.format( + "Input %x had too few output formats listed (actual: %d, " + + "expected: %d)", format, left, length)); + } + } + + if (inputFormat == format) { + int[] outputs = new int[length]; + + // Copying manually faster than System.arraycopy for small arrays + for (int k = 0; k < length; ++k) { + outputs[k] = mEntry[i + k]; + } + + return outputs; + } + + i += length; + left -= length; + + } + + throw new IllegalArgumentException( + String.format("Input format %x was not one in #getInputs", format)); + } + + /** + * Check if this {@link ReprocessFormatsMap} is equal to another + * {@link ReprocessFormatsMap}. + * + *

These two objects are only equal if and only if each of the respective elements is equal. + *

+ * + * @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 ReprocessFormatsMap) { + final ReprocessFormatsMap other = (ReprocessFormatsMap) obj; + // Do not compare anything besides mEntry, since the rest of the values are derived + return Arrays.equals(mEntry, other.mEntry); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + // Do not hash anything besides mEntry since the rest of the values are derived + return HashCodeHelpers.hashCode(mEntry); + } + + private final int[] mEntry; + /* + * Dependent fields: values are derived from mEntry + */ + private final int mInputCount; +} diff --git a/core/java/android/hardware/camera2/RggbChannelVector.java b/core/java/android/hardware/camera2/RggbChannelVector.java new file mode 100644 index 0000000000000..80167c6b40e5b --- /dev/null +++ b/core/java/android/hardware/camera2/RggbChannelVector.java @@ -0,0 +1,199 @@ +/* + * 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.*; + +/** + * Immutable class to store a 4-element vector of floats indexable by a bayer RAW 2x2 pixel block. + */ +public final class RggbChannelVector { + /** + * The number of color channels in this vector. + */ + public static final int COUNT = 4; + + /** Red color channel in a bayer Raw pattern. */ + public static final int RED = 0; + + /** Green color channel in a bayer Raw pattern used by the even rows. */ + public static final int GREEN_EVEN = 1; + + /** Green color channel in a bayer Raw pattern used by the odd rows. */ + public static final int GREEN_ODD = 2; + + /** Blue color channel in a bayer Raw pattern. */ + public static final int BLUE = 3; + + /** + * Create a new {@link RggbChannelVector} from an RGGB 2x2 pixel. + * + *

All pixel values are considered normalized within {@code [0.0f, 1.0f]} + * (i.e. {@code 1.0f} could be linearized to {@code 255} if converting to a + * non-floating point pixel representation).

+ * + *

All arguments must be finite; NaN and infinity is not allowed.

+ * + * @param red red pixel + * @param greenEven green pixel (even row) + * @param greenOdd green pixel (odd row) + * @param blue blue pixel + * + * @throws IllegalArgumentException if any of the arguments were not finite + */ + public RggbChannelVector(final float red, final float greenEven, final float greenOdd, + final float blue) { + mRed = checkArgumentFinite(red, "red"); + mGreenEven = checkArgumentFinite(greenEven, "greenEven"); + mGreenOdd = checkArgumentFinite(greenOdd, "greenOdd"); + mBlue = checkArgumentFinite(blue, "blue"); + } + + /** + * Get the red component. + * + * @return a floating point value (guaranteed to be finite) + */ + public final float getRed() { + return mRed; + } + + /** + * Get the green (even rows) component. + * + * @return a floating point value (guaranteed to be finite) + */ + public float getGreenEven() { + return mGreenEven; + } + + /** + * Get the green (odd rows) component. + * + * @return a floating point value (guaranteed to be finite) + */ + public float getGreenOdd() { + return mGreenOdd; + } + + /** + * Get the blue component. + * + * @return a floating point value (guaranteed to be finite) + */ + public float getBlue() { + return mBlue; + } + + /** + * Get the component by the color channel index. + * + *

{@code colorChannel} must be one of {@link #RED}, {@link #GREEN_EVEN}, {@link #GREEN_ODD}, + * {@link #BLUE}.

+ * + * @param colorChannel greater or equal to {@code 0} and less than {@link #COUNT} + * @return a floating point value (guaranteed to be finite) + * + * @throws IllegalArgumentException if {@code colorChannel} was out of range + */ + public float getComponent(final int colorChannel) { + if (colorChannel < 0 || colorChannel >= COUNT) { + throw new IllegalArgumentException("Color channel out of range"); + } + + switch (colorChannel) { + case RED: + return mRed; + case GREEN_EVEN: + return mGreenEven; + case GREEN_ODD: + return mGreenOdd; + case BLUE: + return mBlue; + default: + throw new AssertionError("Unhandled case " + colorChannel); + } + } + + /** + * Copy the vector into the destination in the order {@code [R, Geven, Godd, B]}. + * + * @param destination + * an array big enough to hold at least {@value #COUNT} 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 copyTo(final float[] destination, final int offset) { + checkNotNull(destination, "destination must not be null"); + if (destination.length + offset < COUNT) { + throw new ArrayIndexOutOfBoundsException("destination too small to fit elements"); + } + + destination[offset + RED] = mRed; + destination[offset + GREEN_EVEN] = mGreenEven; + destination[offset + GREEN_ODD] = mGreenOdd; + destination[offset + BLUE] = mBlue; + } + + /** + * Check if this {@link RggbChannelVector} is equal to another {@link RggbChannelVector}. + * + *

Two vectors are only equal if and only if each of the respective elements is equal.

+ * + * @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 RggbChannelVector) { + final RggbChannelVector other = (RggbChannelVector) obj; + return mRed == other.mRed && + mGreenEven == other.mGreenEven && + mGreenOdd == other.mGreenOdd && + mBlue == other.mBlue; + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return Float.floatToIntBits(mRed) ^ + Float.floatToIntBits(mGreenEven) ^ + Float.floatToIntBits(mGreenOdd) ^ + Float.floatToIntBits(mBlue); + } + + private final float mRed; + private final float mGreenEven; + private final float mGreenOdd; + private final float mBlue; +} diff --git a/core/java/android/hardware/camera2/StreamConfiguration.java b/core/java/android/hardware/camera2/StreamConfiguration.java new file mode 100644 index 0000000000000..c53dd7ccdd6a4 --- /dev/null +++ b/core/java/android/hardware/camera2/StreamConfiguration.java @@ -0,0 +1,166 @@ +/* + * 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 static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal; + +import android.graphics.ImageFormat; +import android.hardware.camera2.impl.HashCodeHelpers; +import android.util.Size; + +/** + * Immutable class to store the available stream + * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS configurations} to be used + * when configuring streams with {@link CameraDevice#configureOutputs}. + * + * + *

This is the authoritative list for all input/output formats (and sizes respectively + * for that format) that are supported by a camera device.

+ * + * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS + * + * @hide + */ +public final class StreamConfiguration { + + /** + * Create a new {@link StreamConfiguration}. + * + * @param format image format + * @param width image width, in pixels (positive) + * @param height image height, in pixels (positive) + * @param input true if this is an input configuration, false for output configurations + * + * @throws IllegalArgumentException + * if width/height were not positive + * or if the format was not user-defined in ImageFormat/PixelFormat + * (IMPL_DEFINED is ok) + * + * @hide + */ + public StreamConfiguration( + final int format, final int width, final int height, final boolean input) { + mFormat = checkArgumentFormatInternal(format); + mWidth = checkArgumentPositive(width, "width must be positive"); + mHeight = checkArgumentPositive(width, "height must be positive"); + mInput = input; + } + + /** + * Get the image {@code format} in this stream configuration. + * + * @return an integer format + * + * @see ImageFormat + */ + public final int getFormat() { + return mFormat; + } + + + /** + * Return the width of the stream configuration. + * + * @return width > 0 + */ + public int getWidth() { + return mWidth; + } + + /** + * Return the height of the stream configuration. + * + * @return height > 0 + */ + public int getHeight() { + return mHeight; + } + + /** + * Convenience method to return the size of this stream configuration. + * + * @return a Size with positive width and height + */ + public Size getSize() { + return new Size(mWidth, mHeight); + } + + /** + * Determines if this configuration is usable for input streams. + * + *

Input and output stream configurations are not interchangeable; + * input stream configurations must be used when configuring inputs.

+ * + * @return {@code true} if input configuration, {@code false} otherwise + */ + public boolean isInput() { + return mInput; + } + + /** + * Determines if this configuration is usable for output streams. + * + *

Input and output stream configurations are not interchangeable; + * out stream configurations must be used when configuring outputs.

+ * + * @return {@code true} if output configuration, {@code false} otherwise + * + * @see CameraDevice#configureOutputs + */ + public boolean isOutput() { + return !mInput; + } + + /** + * Check if this {@link StreamConfiguration} is equal to another {@link StreamConfiguration}. + * + *

Two vectors are only equal if and only if each of the respective elements is equal.

+ * + * @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 StreamConfiguration) { + final StreamConfiguration other = (StreamConfiguration) obj; + return mFormat == other.mFormat && + mWidth == other.mWidth && + mHeight == other.mHeight && + mInput == other.mInput; + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return HashCodeHelpers.hashCode(mFormat, mWidth, mHeight, mInput ? 1 : 0); + } + + private final int mFormat; + private final int mWidth; + private final int mHeight; + private final boolean mInput; +} diff --git a/core/java/android/hardware/camera2/StreamConfigurationDuration.java b/core/java/android/hardware/camera2/StreamConfigurationDuration.java new file mode 100644 index 0000000000000..189ae6240b273 --- /dev/null +++ b/core/java/android/hardware/camera2/StreamConfigurationDuration.java @@ -0,0 +1,148 @@ +/* + * 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 static android.hardware.camera2.StreamConfigurationMap.checkArgumentFormatInternal; + +import android.graphics.ImageFormat; +import android.hardware.camera2.impl.HashCodeHelpers; +import android.util.Size; + +/** + * Immutable class to store a time duration for any given format/size combination. + * + * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS + * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS + * @see CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS + * + * @hide + */ +public final class StreamConfigurationDuration { + + /** + * Create a new {@link StreamConfigurationDuration}. + * + * @param format image format + * @param width image width, in pixels (positive) + * @param height image height, in pixels (positive) + * @param durationNs duration in nanoseconds (non-negative) + * + * @throws IllegalArgumentException + * if width/height were not positive, or durationNs was negative + * or if the format was not user-defined in ImageFormat/PixelFormat + * (IMPL_DEFINED is OK) + * + * + * @hide + */ + public StreamConfigurationDuration( + final int format, final int width, final int height, final long durationNs) { + mFormat = checkArgumentFormatInternal(format); + mWidth = checkArgumentPositive(width, "width must be positive"); + mHeight = checkArgumentPositive(width, "height must be positive"); + mDurationNs = checkArgumentNonnegative(durationNs, "durationNs must be non-negative"); + } + + /** + * Get the image {@code format} in this stream configuration duration + * + * @return an integer format + * + * @see ImageFormat + */ + public final int getFormat() { + return mFormat; + } + + + /** + * Return the width of the stream configuration duration. + * + * @return width > 0 + */ + public int getWidth() { + return mWidth; + } + + /** + * Return the height of the stream configuration duration + * + * @return height > 0 + */ + public int getHeight() { + return mHeight; + } + + /** + * Convenience method to return the size of this stream configuration duration. + * + * @return a Size with positive width and height + */ + public Size getSize() { + return new Size(mWidth, mHeight); + } + + /** + * Get the time duration (in nanoseconds). + * + * @return long >= 0 + */ + public long getDuration() { + return mDurationNs; + } + + /** + * Check if this {@link StreamConfigurationDuration} is equal to another + * {@link StreamConfigurationDuration}. + * + *

Two vectors are only equal if and only if each of the respective elements is equal.

+ * + * @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 StreamConfigurationDuration) { + final StreamConfigurationDuration other = (StreamConfigurationDuration) obj; + return mFormat == other.mFormat && + mWidth == other.mWidth && + mHeight == other.mHeight && + mDurationNs == other.mDurationNs; + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + return HashCodeHelpers.hashCode(mFormat, mWidth, mHeight, + (int) mDurationNs, (int)(mDurationNs >>> Integer.SIZE)); + } + + private final int mFormat; + private final int mWidth; + private final int mHeight; + private final long mDurationNs; +} diff --git a/core/java/android/hardware/camera2/StreamConfigurationMap.java b/core/java/android/hardware/camera2/StreamConfigurationMap.java new file mode 100644 index 0000000000000..e24fd1b6a0b18 --- /dev/null +++ b/core/java/android/hardware/camera2/StreamConfigurationMap.java @@ -0,0 +1,508 @@ +/* + * 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.graphics.ImageFormat; +import android.graphics.PixelFormat; +import android.hardware.camera2.impl.HashCodeHelpers; +import android.view.Surface; +import android.util.Size; + +import java.util.Arrays; + +import static com.android.internal.util.Preconditions.*; + +/** + * Immutable class to store the available stream + * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS configurations} to be used + * when configuring streams with {@link CameraDevice#configureOutputs}. + * + * + *

This is the authoritative list for all output formats (and sizes respectively + * for that format) that are supported by a camera device.

+ * + *

This also contains the minimum frame durations and stall durations for each format/size + * combination that can be used to calculate effective frame rate when submitting multiple captures. + *

+ * + *

An instance of this object is available from {@link CameraCharacteristics} using + * the {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS} key and the + * {@link CameraCharacteristics#get} method.{@code + * CameraCharacteristics characteristics = ...; + * StreamConfigurationMap configs = characteristics.get( + * CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS); + * } + * + * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS + * @see CameraDevice#configureOutputs + */ +public final class StreamConfigurationMap { + + /** + * Create a new {@link StreamConfigurationMap}. + * + *

The array parameters ownership is passed to this object after creation; do not + * write to them after this constructor is invoked.

+ * + * @param configurations a non-{@code null} array of {@link StreamConfiguration} + * @param durations a non-{@code null} array of {@link StreamConfigurationDuration} + * + * @throws NullPointerException if any of the arguments or subelements were {@code null} + * + * @hide + */ + public StreamConfigurationMap( + StreamConfiguration[] configurations, + StreamConfigurationDuration[] durations) { + // TODO: format check against ImageFormat/PixelFormat ? + + mConfigurations = checkArrayElementsNotNull(configurations, "configurations"); + mDurations = checkArrayElementsNotNull(durations, "durations"); + + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Get the image {@code format} output formats in this stream configuration. + * + *

All image formats returned by this function will be defined in either {@link ImageFormat} + * or in {@link PixelFormat} (and there is no possibility of collision).

+ * + *

Formats listed in this array are guaranteed to return true if queried with + * {@link #isOutputSupportedFor(int).

+ * + * @return an array of integer format + * + * @see ImageFormat + * @see PixelFormat + */ + public final int[] getOutputFormats() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Get the image {@code format} input formats in this stream configuration. + * + *

All image formats returned by this function will be defined in either {@link ImageFormat} + * or in {@link PixelFormat} (and there is no possibility of collision).

+ * + * @return an array of integer format + * + * @see ImageFormat + * @see PixelFormat + * + * @hide + */ + public final int[] getInputFormats() { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Get the supported input sizes for this input format. + * + *

The format must have come from {@link #getInputFormats}; otherwise + * {@code null} is returned.

+ * + * @param format a format from {@link #getInputFormats} + * @return a non-empty array of sizes, or {@code null} if the format was not available. + * + * @hide + */ + public Size[] getInputSizes(final int format) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Determine whether or not output streams can be + * {@link CameraDevice#configureOutputs configured} with a particular user-defined format. + * + *

This method determines that the output {@code format} is supported by the camera device; + * each output {@code surface} target may or may not itself support that {@code format}. + * Refer to the class which provides the surface for additional documentation.

+ * + *

Formats for which this returns {@code true} are guaranteed to exist in the result + * returned by {@link #getOutputSizes}.

+ * + * @param format an image format from either {@link ImageFormat} or {@link PixelFormat} + * @return + * {@code true} iff using a {@code surface} with this {@code format} will be + * supported with {@link CameraDevice#configureOutputs} + * + * @throws IllegalArgumentException + * if the image format was not a defined named constant + * from either {@link ImageFormat} or {@link PixelFormat} + * + * @see ImageFormat + * @see PixelFormat + * @see CameraDevice#configureOutputs + */ + public boolean isOutputSupportedFor(int format) { + checkArgumentFormat(format); + + final int[] formats = getOutputFormats(); + for (int i = 0; i < formats.length; ++i) { + if (format == formats[i]) { + return true; + } + } + + return false; + } + + /** + * Determine whether or not output streams can be configured with a particular class + * as a consumer. + * + *

The following list is generally usable for outputs: + *

    + *
  • {@link android.media.ImageReader} - + * Recommended for image processing or streaming to external resources (such as a file or + * network) + *
  • {@link android.media.MediaRecorder} - + * Recommended for recording video (simple to use) + *
  • {@link android.media.MediaCodec} - + * Recommended for recording video (more complicated to use, with more flexibility) + *
  • {@link android.renderscript.Allocation} - + * Recommended for image processing with {@link android.renderscript RenderScript} + *
  • {@link android.view.SurfaceHolder} - + * Recommended for low-power camera preview with {@link android.view.SurfaceView} + *
  • {@link android.graphics.SurfaceTexture} - + * Recommended for OpenGL-accelerated preview processing or compositing with + * {@link android.view.TextureView} + *
+ *

+ * + *

Generally speaking this means that creating a {@link Surface} from that class may + * provide a producer endpoint that is suitable to be used with + * {@link CameraDevice#configureOutputs}.

+ * + *

Since not all of the above classes support output of all format and size combinations, + * the particular combination should be queried with {@link #isOutputSupportedFor(Surface)}.

+ * + * @param klass a non-{@code null} {@link Class} object reference + * @return {@code true} if this class is supported as an output, {@code false} otherwise + * + * @throws NullPointerException if {@code klass} was {@code null} + * + * @see CameraDevice#configureOutputs + * @see #isOutputSupportedFor(Surface) + */ + public static boolean isOutputSupportedFor(final Class klass) { + checkNotNull(klass, "klass must not be null"); + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Determine whether or not the {@code surface} in its current state is suitable to be + * {@link CameraDevice#configureOutputs configured} as an output. + * + *

Not all surfaces are usable with the {@link CameraDevice}, and not all configurations + * of that {@code surface} are compatible. Some classes that provide the {@code surface} are + * compatible with the {@link CameraDevice} in general + * (see {@link #isOutputSupportedFor(Class)}, but it is the caller's responsibility to put the + * {@code surface} into a state that will be compatible with the {@link CameraDevice}.

+ * + *

Reasons for a {@code surface} being specifically incompatible might be: + *

    + *
  • Using a format that's not listed by {@link #getOutputFormats} + *
  • Using a format/size combination that's not listed by {@link #getOutputSizes} + *
  • The {@code surface} itself is not in a state where it can service a new producer.

    + *
  • + *
+ * + * This is not an exhaustive list; see the particular class's documentation for further + * possible reasons of incompatibility.

+ * + * @param surface a non-{@code null} {@link Surface} object reference + * @return {@code true} if this is supported, {@code false} otherwise + * + * @throws NullPointerException if {@code surface} was {@code null} + * + * @see CameraDevice#configureOutputs + * @see #isOutputSupportedFor(Class) + */ + public boolean isOutputSupportedFor(final Surface surface) { + checkNotNull(surface, "surface must not be null"); + + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Get a list of sizes compatible with {@code klass} to use as an output. + * + *

Since some of the supported classes may support additional formats beyond + * an opaque/implementation-defined (under-the-hood) format; this function only returns + * sizes for the implementation-defined format.

+ * + *

Some classes such as {@link android.media.ImageReader} may only support user-defined + * formats; in particular {@link #isOutputSupportedFor(Class)} will return {@code true} for + * that class and this method will return an empty array (but not {@code null}).

+ * + *

If a well-defined format such as {@code NV21} is required, use + * {@link #getOutputSizes(int)} instead.

+ * + *

The {@code klass} should be a supported output, that querying + * {@code #isOutputSupportedFor(Class)} should return {@code true}.

+ * + * @param klass + * a non-{@code null} {@link Class} object reference + * @return + * an array of supported sizes for implementation-defined formats, + * or {@code null} iff the {@code klass} is not a supported output + * + * @throws NullPointerException if {@code klass} was {@code null} + * + * @see #isOutputSupportedFor(Class) + */ + public Size[] getOutputSizes(final Class klass) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Get a list of sizes compatible with the requested image {@code format}. + * + *

The {@code format} should be a supported format (one of the formats returned by + * {@link #getOutputFormats}).

+ * + * @param format an image format from {@link ImageFormat} or {@link PixelFormat} + * @return + * an array of supported sizes, + * or {@code null} if the {@code format} is not a supported output + * + * @see ImageFormat + * @see PixelFormat + * @see #getOutputFormats + */ + public Size[] getOutputSizes(final int format) { + try { + checkArgumentFormatSupported(format, /*output*/true); + } catch (IllegalArgumentException e) { + return null; + } + + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration} + * for the format/size combination (in nanoseconds). + * + *

{@code format} should be one of the ones returned by {@link #getOutputFormats()}.

+ *

{@code size} should be one of the ones returned by + * {@link #getOutputSizes(int)}.

+ * + * @param format an image format from {@link ImageFormat} or {@link PixelFormat} + * @param size an output-compatible size + * @return a minimum frame duration {@code >=} 0 in nanoseconds + * + * @throws IllegalArgumentException if {@code format} or {@code size} was not supported + * @throws NullPointerException if {@code size} was {@code null} + * + * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @see ImageFormat + * @see PixelFormat + */ + public long getOutputMinFrameDuration(final int format, final Size size) { + checkArgumentFormatSupported(format, /*output*/true); + + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Get the minimum {@link CaptureRequest#SENSOR_FRAME_DURATION frame duration} + * for the class/size combination (in nanoseconds). + * + *

This assumes a the {@code klass} is set up to use an implementation-defined format. + * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.

+ * + *

{@code klass} should be one of the ones which is supported by + * {@link #isOutputSupportedFor(Class)}.

+ * + *

{@code size} should be one of the ones returned by + * {@link #getOutputSizes(int)}.

+ * + * @param klass + * a class which is supported by {@link #isOutputSupportedFor(Class)} and has a + * non-empty array returned by {@link #getOutputSizes(Class)} + * @param size an output-compatible size + * @return a minimum frame duration {@code >=} 0 in nanoseconds + * + * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported + * @throws NullPointerException if {@code size} or {@code klass} was {@code null} + * + * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @see ImageFormat + * @see PixelFormat + */ + public long getOutputMinFrameDuration(final Class klass, final Size size) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Get the {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS stall duration} + * for the format/size combination (in nanoseconds). + * + *

{@code format} should be one of the ones returned by {@link #getOutputFormats()}.

+ *

{@code size} should be one of the ones returned by + * {@link #getOutputSizes(int)}.

+ * + * @param format an image format from {@link ImageFormat} or {@link PixelFormat} + * @param size an output-compatible size + * @return a stall duration {@code >=} 0 in nanoseconds + * + * @throws IllegalArgumentException if {@code format} or {@code size} was not supported + * @throws NullPointerException if {@code size} was {@code null} + * + * @see CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS + * @see ImageFormat + * @see PixelFormat + */ + public long getOutputStallDuration(final int format, final Size size) { + checkArgumentFormatSupported(format, /*output*/true); + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Get the {@link CameraCharacteristics#SCALER_AVAILABLE_STALL_DURATIONS stall duration} + * for the class/size combination (in nanoseconds). + * + *

This assumes a the {@code klass} is set up to use an implementation-defined format. + * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.

+ * + *

{@code klass} should be one of the ones with a non-empty array returned by + * {@link #getOutputSizes(Class)}.

+ * + *

{@code size} should be one of the ones returned by + * {@link #getOutputSizes(Class)}.

+ * + * @param klass + * a class which is supported by {@link #isOutputSupportedFor(Class)} and has a + * non-empty array returned by {@link #getOutputSizes(Class)} + * @param size an output-compatible size + * @return a minimum frame duration {@code >=} 0 in nanoseconds + * + * @throws IllegalArgumentException if {@code klass} or {@code size} was not supported + * @throws NullPointerException if {@code size} or {@code klass} was {@code null} + * + * @see CameraCharacteristics#SCALER_AVAILABLE_MIN_FRAME_DURATIONS + * @see CaptureRequest#SENSOR_FRAME_DURATION + * @see ImageFormat + * @see PixelFormat + */ + public long getOutputStallDuration(final Class klass, final Size size) { + throw new UnsupportedOperationException("Not implemented yet"); + } + + /** + * Check if this {@link StreamConfigurationMap} is equal to another + * {@link StreamConfigurationMap}. + * + *

Two vectors are only equal if and only if each of the respective elements is equal.

+ * + * @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 StreamConfigurationMap) { + final StreamConfigurationMap other = (StreamConfigurationMap) obj; + // TODO: do we care about order? + return Arrays.equals(mConfigurations, other.mConfigurations) && + Arrays.equals(mDurations, other.mDurations); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + // TODO: do we care about order? + return HashCodeHelpers.hashCode(mConfigurations) ^ HashCodeHelpers.hashCode(mDurations); + } + + // Check that the argument is supported by #getOutputFormats or #getInputFormats + private int checkArgumentFormatSupported(int format, boolean output) { + checkArgumentFormat(format); + + int[] formats = output ? getOutputFormats() : getInputFormats(); + for (int i = 0; i < formats.length; ++i) { + if (format == formats[i]) { + return format; + } + } + + throw new IllegalArgumentException(String.format( + "format %x is not supported by this stream configuration map", format)); + } + + /** + * Ensures that the format is either user-defined or implementation defined. + * + *

Any invalid/undefined formats will raise an exception.

+ * + * @param format image format + * @return the format + * + * @throws IllegalArgumentException if the format was invalid + */ + static int checkArgumentFormatInternal(int format) { + if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED) { + return format; + } + + return checkArgumentFormat(format); + } + + /** + * Ensures that the format is user-defined in either ImageFormat or PixelFormat. + * + *

Any invalid/undefined formats will raise an exception, including implementation-defined. + *

+ * + *

Note that {@code @hide} and deprecated formats will not pass this check.

+ * + * @param format image format + * @return the format + * + * @throws IllegalArgumentException if the format was not user-defined + */ + static int checkArgumentFormat(int format) { + if (!ImageFormat.isPublicFormat(format) && !PixelFormat.isPublicFormat(format)) { + throw new IllegalArgumentException(String.format( + "format %x was not defined in either ImageFormat or PixelFormat", format)); + } + + return format; + } + + private static final int HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED = 0x22; + + private final StreamConfiguration[] mConfigurations; + private final StreamConfigurationDuration[] mDurations; + +} diff --git a/core/java/android/hardware/camera2/TonemapCurve.java b/core/java/android/hardware/camera2/TonemapCurve.java new file mode 100644 index 0000000000000..ee20d68d4cf2c --- /dev/null +++ b/core/java/android/hardware/camera2/TonemapCurve.java @@ -0,0 +1,290 @@ +/* + * 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.graphics.PointF; +import android.hardware.camera2.impl.HashCodeHelpers; + +import java.util.Arrays; + +/** + * Immutable class for describing a {@code 2 x M x 3} tonemap curve of floats. + * + *

This defines red, green, and blue curves that the {@link CameraDevice} will + * use as the tonemapping/contrast/gamma curve when {@link CaptureRequest#TONEMAP_MODE} is + * set to {@link CameraMetadata#TONEMAP_MODE_CONTRAST_CURVE}.

+ * + *

The total number of points {@code (Pin, Pout)} for each color channel can be no more than + * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS}.

+ * + *

The coordinate system for each point is within the inclusive range + * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].

+ * + * @see CaptureRequest#TONEMAP_CURVE_BLUE + * @see CaptureRequest#TONEMAP_CURVE_GREEN + * @see CaptureRequest#TONEMAP_CURVE_RED + * @see CameraMetadata#TONEMAP_MODE_CONTRAST_CURVE + * @see CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS + */ +public final class TonemapCurve { + /** + * Lower bound tonemap value corresponding to pure black for a single color channel. + */ + public static final float LEVEL_BLACK = 0.0f; + + /** + * Upper bound tonemap value corresponding to a pure white for a single color channel. + */ + public static final float LEVEL_WHITE = 1.0f; + + /** + * Number of elements in a {@code (Pin, Pout)} point; + */ + public static final int POINT_SIZE = 2; + + /** + * Index of the red color channel curve. + */ + public static final int CHANNEL_RED = 0; + /** + * Index of the green color channel curve. + */ + public static final int CHANNEL_GREEN = 1; + /** + * Index of the blue color channel curve. + */ + public static final int CHANNEL_BLUE = 2; + + /** + * Create a new immutable TonemapCurve instance. + * + *

Values are stored as a contiguous {@code (Pin, Pout}) point.

+ * + *

All parameters may have independent length but should have at most + * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS} * {@value #POINT_SIZE} elements.

+ * + *

All sub-elements must be in the inclusive range of + * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].

+ * + *

This constructor copies the array contents and does not retain ownership of the array.

+ * + * @param elements An array of elements whose length is {@code CHANNEL_COUNT * rows * columns} + * + * @throws IllegalArgumentException + * if the {@code elements} array length is invalid, + * if any of the subelems are not finite + * @throws NullPointerException + * if any of the parameters is {@code null} + * + * @hide + */ + public TonemapCurve(float[] red, float[] green, float[] blue) { + // TODO: maxCurvePoints check? + + checkNotNull(red, "red must not be null"); + checkNotNull(green, "green must not be null"); + checkNotNull(blue, "blue must not be null"); + + checkArgumentArrayLengthDivisibleBy(red, POINT_SIZE, "red"); + checkArgumentArrayLengthDivisibleBy(green, POINT_SIZE, "green"); + checkArgumentArrayLengthDivisibleBy(blue, POINT_SIZE, "blue"); + + checkArrayElementsInRange(red, LEVEL_BLACK, LEVEL_WHITE, "red"); + checkArrayElementsInRange(green, LEVEL_BLACK, LEVEL_WHITE, "green"); + checkArrayElementsInRange(blue, LEVEL_BLACK, LEVEL_WHITE, "blue"); + + mRed = Arrays.copyOf(red, red.length); + mGreen = Arrays.copyOf(green, green.length); + mBlue = Arrays.copyOf(blue, blue.length); + } + + private static void checkArgumentArrayLengthDivisibleBy(float[] array, + int divisible, String arrayName) { + if (array.length % divisible != 0) { + throw new IllegalArgumentException(arrayName + " size must be divisible by " + + divisible); + } + } + + private static int checkArgumentColorChannel(int colorChannel) { + switch (colorChannel) { + case CHANNEL_RED: + case CHANNEL_GREEN: + case CHANNEL_BLUE: + break; + default: + throw new IllegalArgumentException("colorChannel out of range"); + } + + return colorChannel; + } + + /** + * Get the number of points stored in this tonemap curve for the specified color channel. + * + * @param colorChannel one of {@link #CHANNEL_RED}, {@link #CHANNEL_GREEN}, {@link #CHANNEL_BLUE} + * @return number of points stored in this tonemap for that color's curve (>= 0) + * + * @throws IllegalArgumentException if {@code colorChannel} was out of range + */ + public int getPointCount(int colorChannel) { + checkArgumentColorChannel(colorChannel); + + return getCurve(colorChannel).length / POINT_SIZE; + } + + /** + * Get the point for a color channel at a specified index. + * + *

The index must be at least 0 but no greater than {@link #getPointCount(int)} for + * that {@code colorChannel}.

+ * + *

All returned coordinates in the point are between the range of + * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].

+ * + * @param colorChannel {@link #CHANNEL_RED}, {@link #CHANNEL_GREEN}, or {@link #CHANNEL_BLUE} + * @param index at least 0 but no greater than {@code getPointCount(colorChannel)} + * @return the {@code (Pin, Pout)} pair mapping the tone for that index + * + * @throws IllegalArgumentException if {@code colorChannel} or {@code index} was out of range + * + * @see #LEVEL_BLACK + * @see #LEVEL_WHITE + */ + public PointF getPoint(int colorChannel, int index) { + checkArgumentColorChannel(colorChannel); + if (index < 0 || index >= getPointCount(colorChannel)) { + throw new IllegalArgumentException("index out of range"); + } + + final float[] curve = getCurve(colorChannel); + + final float pIn = curve[index * POINT_SIZE + OFFSET_POINT_IN]; + final float pOut = curve[index * POINT_SIZE + OFFSET_POINT_OUT]; + + return new PointF(pIn, pOut); + } + + /** + * Copy the color curve for a single color channel from this tonemap curve into the destination. + * + *

+ * + * Values are stored as packed {@code (Pin, Pout}) points, and there are a total of + * {@link #getPointCount} points for that respective channel.

+ * + *

All returned coordinates are between the range of + * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].

+ * + * @param destination + * an array big enough to hold at least {@link #getPointCount} {@code *} + * {@link #POINT_SIZE} elements after the {@code offset} + * @param offset + * a non-negative offset into the array + * @throws NullPointerException + * If {@code destination} was {@code null} + * @throws IllegalArgumentException + * If offset was negative + * @throws ArrayIndexOutOfBoundsException + * If there's not enough room to write the elements at the specified destination and + * offset. + * + * @see CaptureRequest#TONEMAP_CURVE_BLUE + * @see CaptureRequest#TONEMAP_CURVE_RED + * @see CaptureRequest#TONEMAP_CURVE_GREEN + * @see #LEVEL_BLACK + * @see #LEVEL_WHITE + */ + public void copyColorCurve(int colorChannel, float[] destination, + int offset) { + checkArgumentNonnegative(offset, "offset must not be negative"); + checkNotNull(destination, "destination must not be null"); + + if (destination.length + offset < getPointCount(colorChannel) * POINT_SIZE) { + throw new ArrayIndexOutOfBoundsException("destination too small to fit elements"); + } + + float[] curve = getCurve(colorChannel); + System.arraycopy(curve, /*srcPos*/0, destination, offset, curve.length); + } + + /** + * Check if this TonemapCurve is equal to another TonemapCurve. + * + *

Two matrices are equal if and only if all of their elements are + * {@link Object#equals equal}.

+ * + * @return {@code true} if the objects were equal, {@code false} otherwise + */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (obj instanceof TonemapCurve) { + final TonemapCurve other = (TonemapCurve) obj; + return Arrays.equals(mRed, other.mRed) && + Arrays.equals(mGreen, other.mGreen) && + Arrays.equals(mBlue, other.mBlue); + } + return false; + } + + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + if (mHashCalculated) { + // Avoid re-calculating hash. Data is immutable so this is both legal and faster. + return mHashCode; + } + + mHashCode = HashCodeHelpers.hashCode(mRed, mGreen, mBlue); + mHashCalculated = true; + + return mHashCode; + } + + private float[] getCurve(int colorChannel) { + switch (colorChannel) { + case CHANNEL_RED: + return mRed; + case CHANNEL_GREEN: + return mGreen; + case CHANNEL_BLUE: + return mBlue; + default: + throw new AssertionError("colorChannel out of range"); + } + } + + private final static int OFFSET_POINT_IN = 0; + private final static int OFFSET_POINT_OUT = 1; + + private final float[] mRed; + private final float[] mGreen; + private final float[] mBlue; + + private int mHashCode; + private boolean mHashCalculated = false; +}; diff --git a/graphics/java/android/graphics/ImageFormat.java b/graphics/java/android/graphics/ImageFormat.java index e08ed50b03b80..062acaf8bd134 100644 --- a/graphics/java/android/graphics/ImageFormat.java +++ b/graphics/java/android/graphics/ImageFormat.java @@ -253,4 +253,31 @@ public class ImageFormat { } return -1; } + + /** + * Determine whether or not this is a public-visible {@code format}. + * + *

In particular, {@code @hide} formats will return {@code false}.

+ * + *

Any other formats (including UNKNOWN) will return {@code false}.

+ * + * @param format an integer format + * @return a boolean + * + * @hide + */ + public static boolean isPublicFormat(int format) { + switch (format) { + case RGB_565: + case NV16: + case YUY2: + case YV12: + case NV21: + case YUV_420_888: + case RAW_SENSOR: + return true; + } + + return false; + } } diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java index d96d6d841952c..832b9c344c5ff 100644 --- a/graphics/java/android/graphics/PixelFormat.java +++ b/graphics/java/android/graphics/PixelFormat.java @@ -115,7 +115,7 @@ public class PixelFormat info.bytesPerPixel = 1; break; default: - throw new IllegalArgumentException("unkonwon pixel format " + format); + throw new IllegalArgumentException("unknown pixel format " + format); } } @@ -135,4 +135,29 @@ public class PixelFormat public int bytesPerPixel; public int bitsPerPixel; + + /** + * Determine whether or not this is a public-visible and non-deprecated {@code format}. + * + *

In particular, {@code @hide} formats will return {@code false}.

+ * + *

Any other indirect formats (such as {@code TRANSPARENT} or {@code TRANSLUCENT}) + * will return {@code false}.

+ * + * @param format an integer format + * @return a boolean + * + * @hide + */ + public static boolean isPublicFormat(int format) { + switch (format) { + case RGBA_8888: + case RGBX_8888: + case RGB_888: + case RGB_565: + return true; + } + + return false; + } }