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 */ ReprocessFormatsMap(final int[] entry)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 */ getInputs()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 */ getOutputs(final int format)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 equals(final Object obj)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 hashCode()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