Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 94814218 authored by Igor Murashkin's avatar Igor Murashkin
Browse files

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

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
parent b3a78b2c
Loading
Loading
Loading
Loading
+50 −0
Original line number Diff line number Diff line
@@ -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 {
+6 −2
Original line number Diff line number Diff line
@@ -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;

+243 −0
Original line number Diff line number Diff line
/*
 * 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;
};
+258 −0
Original line number Diff line number Diff line
/*
 * 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;
}
+199 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading