Home | History | Annotate | Download | only in params
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.hardware.camera2.params;
     18 
     19 import static com.android.internal.util.Preconditions.*;
     20 
     21 import android.hardware.camera2.CameraCharacteristics;
     22 import android.hardware.camera2.utils.HashCodeHelpers;
     23 
     24 import java.util.Arrays;
     25 
     26 /**
     27  * Immutable class to store the input to output formats
     28  * {@link CameraCharacteristics#SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP map} to be used for with
     29  * camera image reprocessing.
     30  *
     31  * <p>
     32  * The mapping of image formats that are supported by this camera device for input streams,
     33  * to their corresponding output formats.</p>
     34  *
     35  * <p>
     36  * Attempting to configure an input stream with output streams not listed as available in this map
     37  * is not valid.
     38  * </p>
     39  *
     40  * @see CameraCharacteristics#SCALER_AVAILABLE_INPUT_OUTPUT_FORMATS_MAP
     41  * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_CONFIGURATIONS
     42  *
     43  * <!-- hide this until we expose input streams through public API -->
     44  * @hide
     45  */
     46 public final class ReprocessFormatsMap {
     47     /**
     48      * Create a new {@link ReprocessFormatsMap}
     49      *
     50      * <p>This value is encoded as a variable-size array-of-arrays.
     51      * The inner array always contains {@code [format, length, ...]} where ... has length elements.
     52      * An inner array is followed by another inner array if the total metadata entry size hasn't
     53      * yet been exceeded.</p>
     54      *
     55      * <p>Entry must not be {@code null}. Empty array is acceptable.</p>
     56      *
     57      * <p>The entry array ownership is passed to this instance after construction; do not
     58      * write to it afterwards.</p>
     59      *
     60      * @param entry Array of ints, not yet deserialized (not-null)
     61      *
     62      * @throws IllegalArgumentException
     63      *              if the data was poorly formatted
     64      *              (missing output format length or too few output formats)
     65      *              or if any of the input/formats were not valid
     66      * @throws NullPointerException
     67      *              if entry was null
     68      *
     69      * @see StreamConfigurationMap#checkArgumentFormatInternal
     70      *
     71      * @hide
     72      */
     73     public ReprocessFormatsMap(final int[] entry) {
     74         checkNotNull(entry, "entry must not be null");
     75 
     76         int numInputs = 0;
     77         int left = entry.length;
     78         for (int i = 0; i < entry.length; ) {
     79             int inputFormat = StreamConfigurationMap.checkArgumentFormatInternal(entry[i]);
     80 
     81             left--;
     82             i++;
     83 
     84             if (left < 1) {
     85                 throw new IllegalArgumentException(
     86                         String.format("Input %x had no output format length listed", inputFormat));
     87             }
     88 
     89             final int length = entry[i];
     90             left--;
     91             i++;
     92 
     93             for (int j = 0; j < length; ++j) {
     94                 int outputFormat = entry[i + j];
     95                 StreamConfigurationMap.checkArgumentFormatInternal(outputFormat);
     96             }
     97 
     98             if (length > 0) {
     99                 if (left < length) {
    100                     throw new IllegalArgumentException(
    101                             String.format(
    102                                     "Input %x had too few output formats listed (actual: %d, " +
    103                                     "expected: %d)", inputFormat, left, length));
    104                 }
    105 
    106                 i += length;
    107                 left -= length;
    108             }
    109 
    110             numInputs++;
    111         }
    112 
    113         mEntry = entry;
    114         mInputCount = numInputs;
    115     }
    116 
    117     /**
    118      * Get a list of all input image formats that can be used to reprocess an input
    119      * stream into an output stream.
    120      *
    121      * <p>Use this input format to look up the available output formats with {@link #getOutputs}.
    122      * </p>
    123      *
    124      * @return an array of inputs (possibly empty, but never {@code null})
    125      *
    126      * @see ImageFormat
    127      * @see #getOutputs
    128      */
    129     public int[] getInputs() {
    130         final int[] inputs = new int[mInputCount];
    131 
    132         int left = mEntry.length;
    133         for (int i = 0, j = 0; i < mEntry.length; j++) {
    134             final int format = mEntry[i];
    135 
    136             left--;
    137             i++;
    138 
    139             if (left < 1) {
    140                 throw new AssertionError(
    141                         String.format("Input %x had no output format length listed", format));
    142             }
    143 
    144             final int length = mEntry[i];
    145             left--;
    146             i++;
    147 
    148             if (length > 0) {
    149                 if (left < length) {
    150                     throw new AssertionError(
    151                             String.format(
    152                                     "Input %x had too few output formats listed (actual: %d, " +
    153                                     "expected: %d)", format, left, length));
    154                 }
    155 
    156                 i += length;
    157                 left -= length;
    158             }
    159 
    160             inputs[j] = format;
    161         }
    162 
    163         return StreamConfigurationMap.imageFormatToPublic(inputs);
    164     }
    165 
    166     /**
    167      * Get the list of output formats that can be reprocessed into from the input {@code format}.
    168      *
    169      * <p>The input {@code format} must be one of the formats returned by {@link #getInputs}.</p>
    170      *
    171      * @param format an input format
    172      *
    173      * @return list of output image formats
    174      *
    175      * @see ImageFormat
    176      * @see #getInputs
    177      */
    178     public int[] getOutputs(final int format) {
    179 
    180         int left = mEntry.length;
    181         for (int i = 0; i < mEntry.length; ) {
    182             final int inputFormat = mEntry[i];
    183 
    184             left--;
    185             i++;
    186 
    187             if (left < 1) {
    188                 throw new AssertionError(
    189                         String.format("Input %x had no output format length listed", format));
    190             }
    191 
    192             final int length = mEntry[i];
    193             left--;
    194             i++;
    195 
    196             if (length > 0) {
    197                 if (left < length) {
    198                     throw new AssertionError(
    199                             String.format(
    200                                     "Input %x had too few output formats listed (actual: %d, " +
    201                                     "expected: %d)", format, left, length));
    202                 }
    203             }
    204 
    205             if (inputFormat == format) {
    206                 int[] outputs = new int[length];
    207 
    208                 // Copying manually faster than System.arraycopy for small arrays
    209                 for (int k = 0; k < length; ++k) {
    210                     outputs[k] = mEntry[i + k];
    211                 }
    212 
    213                 return StreamConfigurationMap.imageFormatToPublic(outputs);
    214             }
    215 
    216             i += length;
    217             left -= length;
    218 
    219         }
    220 
    221         throw new IllegalArgumentException(
    222                 String.format("Input format %x was not one in #getInputs", format));
    223     }
    224 
    225     /**
    226      * Check if this {@link ReprocessFormatsMap} is equal to another
    227      * {@link ReprocessFormatsMap}.
    228      *
    229      * <p>These two objects are only equal if and only if each of the respective elements is equal.
    230      * </p>
    231      *
    232      * @return {@code true} if the objects were equal, {@code false} otherwise
    233      */
    234     @Override
    235     public boolean equals(final Object obj) {
    236         if (obj == null) {
    237             return false;
    238         }
    239         if (this == obj) {
    240             return true;
    241         }
    242         if (obj instanceof ReprocessFormatsMap) {
    243             final ReprocessFormatsMap other = (ReprocessFormatsMap) obj;
    244             // Do not compare anything besides mEntry, since the rest of the values are derived
    245             return Arrays.equals(mEntry, other.mEntry);
    246         }
    247         return false;
    248     }
    249 
    250     /**
    251      * {@inheritDoc}
    252      */
    253     @Override
    254     public int hashCode() {
    255         // Do not hash anything besides mEntry since the rest of the values are derived
    256         return HashCodeHelpers.hashCode(mEntry);
    257     }
    258 
    259     private final int[] mEntry;
    260     /*
    261      * Dependent fields: values are derived from mEntry
    262      */
    263     private final int mInputCount;
    264 }
    265