OSDN Git Service

camera2: Add camera-specific data types used for metadata key/value
authorIgor Murashkin <iam@google.com>
Fri, 18 Apr 2014 01:37:06 +0000 (18:37 -0700)
committerIgor Murashkin <iam@google.com>
Fri, 2 May 2014 22:55:30 +0000 (15:55 -0700)
Adds new public API classes:
* InputOutputFormatsMap
* LensShadingMap
* RggbChannelVector
* StreamConfigurationMap
* TonemapCurve

Adds new @hide classes:
* StreamConfiguration
* StreamConfigurationDuration

Minor changes:
* CameraDevice (doc only)
* Preconditions (new @hide function)

Change-Id: I2f3757e2fe9d63e710f51469c650377165fd6631

api/current.txt
core/java/android/hardware/camera2/CameraDevice.java
core/java/android/hardware/camera2/LensShadingMap.java [new file with mode: 0644]
core/java/android/hardware/camera2/ReprocessFormatsMap.java [new file with mode: 0644]
core/java/android/hardware/camera2/RggbChannelVector.java [new file with mode: 0644]
core/java/android/hardware/camera2/StreamConfiguration.java [new file with mode: 0644]
core/java/android/hardware/camera2/StreamConfigurationDuration.java [new file with mode: 0644]
core/java/android/hardware/camera2/StreamConfigurationMap.java [new file with mode: 0644]
core/java/android/hardware/camera2/TonemapCurve.java [new file with mode: 0644]
graphics/java/android/graphics/ImageFormat.java
graphics/java/android/graphics/PixelFormat.java

index be5ad9b..06d1ab0 100644 (file)
@@ -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<T>, android.util.Size);
+    method public android.util.Size[] getOutputSizes(java.lang.Class<T>);
+    method public android.util.Size[] getOutputSizes(int);
+    method public long getOutputStallDuration(int, android.util.Size);
+    method public long getOutputStallDuration(java.lang.Class<T>, android.util.Size);
+    method public boolean isOutputSupportedFor(int);
+    method public static boolean isOutputSupportedFor(java.lang.Class<T>);
+    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 {
index bb290af..9d0e0e1 100644 (file)
@@ -192,8 +192,9 @@ public interface CameraDevice extends AutoCloseable {
      *
      * <p>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).</p>
+     * 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)}).</p>
      *
      * <p>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<Surface> 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 (file)
index 0000000..2c224f6
--- /dev/null
@@ -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.
+     *
+     * <p>All values in this map will be at least this large.</p>
+     */
+    public static final float MINIMUM_GAIN_FACTOR = 1.0f;
+
+    /**
+     * Create a new immutable LensShadingMap instance.
+     *
+     * <p>The elements must be stored in a row-major order (fully packed).</p>
+     *
+     * <p>This constructor takes over the array; do not write to the array afterwards.</p>
+     *
+     * @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.
+     *
+     * <p>A single gain factor contains exactly one color channel.
+     * Use with {@link #copyGainFactors} to allocate a large-enough array.</p>
+     */
+    public int getGainFactorCount() {
+        return mRows * mColumns * COUNT;
+    }
+
+    /**
+     * Get a single color channel gain factor from this lens shading map by its row and column.
+     *
+     * <p>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}).</p>
+     *
+     * <p>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.
+     * </p>
+     *
+     * @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.
+     *
+     * <p>The rows must be within the range [0, {@link #getRowCount}),
+     * the column must be within the range [0, {@link #getColumnCount}).</p>
+     *
+     * @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.
+     *
+     * <p>Each gain factor will be >= {@link #MINIMUM_GAIN_FACTOR}.</p>
+     *
+     * @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.
+     *
+     * <p>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}.</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 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 (file)
index 0000000..c6c59d4
--- /dev/null
@@ -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.
+ *
+ * <p>
+ * The mapping of image formats that are supported by this camera device for input streams,
+ * to their corresponding output formats.</p>
+ *
+ * <p>
+ * Attempting to configure an input stream with output streams not listed as available in this map
+ * is not valid.
+ * </p>
+ *
+ * @see CameraCharacteristics#SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS
+ *
+ * <!-- hide this until we expose input streams through public API -->
+ * @hide
+ */
+public final class ReprocessFormatsMap {
+    /**
+     * Create a new {@link ReprocessFormatsMap}
+     *
+     * <p>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.</p>
+     *
+     * <p>Entry must not be {@code null}. Empty array is acceptable.</p>
+     *
+     * <p>The entry array ownership is passed to this instance after construction; do not
+     * write to it afterwards.</p>
+     *
+     * @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.
+     *
+     * <p>Use this input format to look up the available output formats with {@link #getOutputs}.
+     * </p>
+     *
+     * @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}.
+     *
+     * <p>The input {@code format} must be one of the formats returned by {@link #getInputs}.</p>
+     *
+     * @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}.
+     *
+     * <p>These two objects are only equal if and only if each of the respective elements is 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 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 (file)
index 0000000..80167c6
--- /dev/null
@@ -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.
+     *
+     * <p>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).</p>
+     *
+     * <p>All arguments must be finite; NaN and infinity is not allowed.</p>
+     *
+     * @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.
+     *
+     * <p>{@code colorChannel} must be one of {@link #RED}, {@link #GREEN_EVEN}, {@link #GREEN_ODD},
+     * {@link #BLUE}.</p>
+     *
+     * @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}.
+     *
+     * <p>Two vectors are only equal if and only if each of the respective elements is 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 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 (file)
index 0000000..c53dd7c
--- /dev/null
@@ -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}.
+ * <!-- TODO: link to input stream configuration -->
+ *
+ * <p>This is the authoritative list for all input/output formats (and sizes respectively
+ * for that format) that are supported by a camera device.</p>
+ *
+ * @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.
+     *
+     * <p>Input and output stream configurations are not interchangeable;
+     * input stream configurations must be used when configuring inputs.</p>
+     *
+     * @return {@code true} if input configuration, {@code false} otherwise
+     */
+    public boolean isInput() {
+        return mInput;
+    }
+
+    /**
+     * Determines if this configuration is usable for output streams.
+     *
+     * <p>Input and output stream configurations are not interchangeable;
+     * out stream configurations must be used when configuring outputs.</p>
+     *
+     * @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}.
+     *
+     * <p>Two vectors are only equal if and only if each of the respective elements is 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 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 (file)
index 0000000..189ae62
--- /dev/null
@@ -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}.
+     *
+     * <p>Two vectors are only equal if and only if each of the respective elements is 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 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 (file)
index 0000000..e24fd1b
--- /dev/null
@@ -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}.
+ * <!-- TODO: link to input stream configuration -->
+ *
+ * <p>This is the authoritative list for all <!-- input/ -->output formats (and sizes respectively
+ * for that format) that are supported by a camera device.</p>
+ *
+ * <p>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.
+ * </p>
+ *
+ * <p>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.</p.
+ *
+ * <pre>{@code
+ * CameraCharacteristics characteristics = ...;
+ * StreamConfigurationMap configs = characteristics.get(
+ *         CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
+ * }</pre>
+ *
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS
+ * @see CameraDevice#configureOutputs
+ */
+public final class StreamConfigurationMap {
+
+    /**
+     * Create a new {@link StreamConfigurationMap}.
+     *
+     * <p>The array parameters ownership is passed to this object after creation; do not
+     * write to them after this constructor is invoked.</p>
+     *
+     * @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.
+     *
+     * <p>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).</p>
+     *
+     * <p>Formats listed in this array are guaranteed to return true if queried with
+     * {@link #isOutputSupportedFor(int).</p>
+     *
+     * @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.
+     *
+     * <p>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).</p>
+     *
+     * @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.
+     *
+     * <p>The format must have come from {@link #getInputFormats}; otherwise
+     * {@code null} is returned.</p>
+     *
+     * @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.
+     *
+     * <p>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.</p>
+     *
+     * <p>Formats for which this returns {@code true} are guaranteed to exist in the result
+     * returned by {@link #getOutputSizes}.</p>
+     *
+     * @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.
+     *
+     * <p>The following list is generally usable for outputs:
+     * <ul>
+     * <li>{@link android.media.ImageReader} -
+     * Recommended for image processing or streaming to external resources (such as a file or
+     * network)
+     * <li>{@link android.media.MediaRecorder} -
+     * Recommended for recording video (simple to use)
+     * <li>{@link android.media.MediaCodec} -
+     * Recommended for recording video (more complicated to use, with more flexibility)
+     * <li>{@link android.renderscript.Allocation} -
+     * Recommended for image processing with {@link android.renderscript RenderScript}
+     * <li>{@link android.view.SurfaceHolder} -
+     * Recommended for low-power camera preview with {@link android.view.SurfaceView}
+     * <li>{@link android.graphics.SurfaceTexture} -
+     * Recommended for OpenGL-accelerated preview processing or compositing with
+     * {@link android.view.TextureView}
+     * </ul>
+     * </p>
+     *
+     * <p>Generally speaking this means that creating a {@link Surface} from that class <i>may</i>
+     * provide a producer endpoint that is suitable to be used with
+     * {@link CameraDevice#configureOutputs}.</p>
+     *
+     * <p>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)}.</p>
+     *
+     * @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 <T> boolean isOutputSupportedFor(final Class<T> 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.
+     *
+     * <p>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}.</p>
+     *
+     * <p>Reasons for a {@code surface} being specifically incompatible might be:
+     * <ul>
+     * <li>Using a format that's not listed by {@link #getOutputFormats}
+     * <li>Using a format/size combination that's not listed by {@link #getOutputSizes}
+     * <li>The {@code surface} itself is not in a state where it can service a new producer.</p>
+     * </li>
+     * </ul>
+     *
+     * This is not an exhaustive list; see the particular class's documentation for further
+     * possible reasons of incompatibility.</p>
+     *
+     * @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.
+     *
+     * <p>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.</p>
+     *
+     * <p>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}).</p>
+     *
+     * <p>If a well-defined format such as {@code NV21} is required, use
+     * {@link #getOutputSizes(int)} instead.</p>
+     *
+     * <p>The {@code klass} should be a supported output, that querying
+     * {@code #isOutputSupportedFor(Class)} should return {@code true}.</p>
+     *
+     * @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 <T> Size[] getOutputSizes(final Class<T> klass) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Get a list of sizes compatible with the requested image {@code format}.
+     *
+     * <p>The {@code format} should be a supported format (one of the formats returned by
+     * {@link #getOutputFormats}).</p>
+     *
+     * @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).
+     *
+     * <p>{@code format} should be one of the ones returned by {@link #getOutputFormats()}.</p>
+     * <p>{@code size} should be one of the ones returned by
+     * {@link #getOutputSizes(int)}.</p>
+     *
+     * @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).
+     *
+     * <p>This assumes a the {@code klass} is set up to use an implementation-defined format.
+     * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
+     *
+     * <p>{@code klass} should be one of the ones which is supported by
+     * {@link #isOutputSupportedFor(Class)}.</p>
+     *
+     * <p>{@code size} should be one of the ones returned by
+     * {@link #getOutputSizes(int)}.</p>
+     *
+     * @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 <T> long getOutputMinFrameDuration(final Class<T> 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).
+     *
+     * <p>{@code format} should be one of the ones returned by {@link #getOutputFormats()}.</p>
+     * <p>{@code size} should be one of the ones returned by
+     * {@link #getOutputSizes(int)}.</p>
+     *
+     * @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).
+     *
+     * <p>This assumes a the {@code klass} is set up to use an implementation-defined format.
+     * For user-defined formats, use {@link #getOutputMinFrameDuration(int, Size)}.</p>
+     *
+     * <p>{@code klass} should be one of the ones with a non-empty array returned by
+     * {@link #getOutputSizes(Class)}.</p>
+     *
+     * <p>{@code size} should be one of the ones returned by
+     * {@link #getOutputSizes(Class)}.</p>
+     *
+     * @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 <T> long getOutputStallDuration(final Class<T> klass, final Size size) {
+        throw new UnsupportedOperationException("Not implemented yet");
+    }
+
+    /**
+     * Check if this {@link StreamConfigurationMap} is equal to another
+     * {@link StreamConfigurationMap}.
+     *
+     * <p>Two vectors are only equal if and only if each of the respective elements is 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 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.
+     *
+     * <p>Any invalid/undefined formats will raise an exception.</p>
+     *
+     * @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.
+     *
+     * <p>Any invalid/undefined formats will raise an exception, including implementation-defined.
+     * </p>
+     *
+     * <p>Note that {@code @hide} and deprecated formats will not pass this check.</p>
+     *
+     * @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 (file)
index 0000000..ee20d68
--- /dev/null
@@ -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.
+ *
+ * <p>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}.</p>
+ *
+ * <p>The total number of points {@code (Pin, Pout)} for each color channel can be no more than
+ * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS}.</p>
+ *
+ * <p>The coordinate system for each point is within the inclusive range
+ * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
+ *
+ * @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.
+     *
+     * <p>Values are stored as a contiguous {@code (Pin, Pout}) point.</p>
+     *
+     * <p>All parameters may have independent length but should have at most
+     * {@link CameraCharacteristics#TONEMAP_MAX_CURVE_POINTS} * {@value #POINT_SIZE} elements.</p>
+     *
+     * <p>All sub-elements must be in the inclusive range of
+     * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
+     *
+     * <p>This constructor copies the array contents and does not retain ownership of the array.</p>
+     *
+     * @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.
+     *
+     * <p>The index must be at least 0 but no greater than {@link #getPointCount(int)} for
+     * that {@code colorChannel}.</p>
+     *
+     * <p>All returned coordinates in the point are between the range of
+     * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
+     *
+     * @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.
+     *
+     * <p>
+     * <!--The output is encoded the same as in the constructor -->
+     * Values are stored as packed {@code (Pin, Pout}) points, and there are a total of
+     * {@link #getPointCount} points for that respective channel.</p>
+     *
+     * <p>All returned coordinates are between the range of
+     * [{@value #LEVEL_BLACK}, {@value #LEVEL_WHITE}].</p>
+     *
+     * @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.
+     *
+     * <p>Two matrices 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(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;
+};
index e08ed50..062acaf 100644 (file)
@@ -253,4 +253,31 @@ public class ImageFormat {
         }
         return -1;
     }
+
+    /**
+     * Determine whether or not this is a public-visible {@code format}.
+     *
+     * <p>In particular, {@code @hide} formats will return {@code false}.</p>
+     *
+     * <p>Any other formats (including UNKNOWN) will return {@code false}.</p>
+     *
+     * @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;
+    }
 }
index d96d6d8..832b9c3 100644 (file)
@@ -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}.
+     *
+     * <p>In particular, {@code @hide} formats will return {@code false}.</p>
+     *
+     * <p>Any other indirect formats (such as {@code TRANSPARENT} or {@code TRANSLUCENT})
+     * will return {@code false}.</p>
+     *
+     * @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;
+    }
 }