am 051dd983: camera2: Add camera-specific data types used for metadata key/value

* commit '051dd9832b0c6c483f2d162154f67a31ef097598':
  camera2: Add camera-specific data types used for metadata key/value
This commit is contained in:
Igor Murashkin
2014-05-02 23:30:13 +00:00
committed by Android Git Automerger
11 changed files with 1921 additions and 3 deletions

View 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 {

View 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;

View File

@@ -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;
};

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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;
};

View 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;
}
}

View 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;
}
}