1 /*
2  * Copyright (C) 2011 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 
18 package android.filterfw.core;
19 
20 import android.filterfw.core.KeyValueMap;
21 import android.filterfw.core.MutableFrameFormat;
22 
23 import java.util.Arrays;
24 import java.util.Map.Entry;
25 
26 /**
27  * @hide
28  */
29 public class FrameFormat {
30 
31     public static final int TYPE_UNSPECIFIED = 0;
32     public static final int TYPE_BIT         = 1;
33     public static final int TYPE_BYTE        = 2;
34     public static final int TYPE_INT16       = 3;
35     public static final int TYPE_INT32       = 4;
36     public static final int TYPE_FLOAT       = 5;
37     public static final int TYPE_DOUBLE      = 6;
38     public static final int TYPE_POINTER     = 7;
39     public static final int TYPE_OBJECT      = 8;
40 
41     public static final int TARGET_UNSPECIFIED  = 0;
42     public static final int TARGET_SIMPLE       = 1;
43     public static final int TARGET_NATIVE       = 2;
44     public static final int TARGET_GPU          = 3;
45     public static final int TARGET_VERTEXBUFFER = 4;
46     public static final int TARGET_RS           = 5;
47 
48     public static final int SIZE_UNSPECIFIED = 0;
49 
50     // TODO: When convenience formats are used, consider changing this to 0 and have the convenience
51     // intializers use a proper BPS.
52     public static final int BYTES_PER_SAMPLE_UNSPECIFIED = 1;
53 
54     protected static final int SIZE_UNKNOWN = -1;
55 
56     protected int mBaseType = TYPE_UNSPECIFIED;
57     protected int mBytesPerSample = 1;
58     protected int mSize = SIZE_UNKNOWN;
59     protected int mTarget = TARGET_UNSPECIFIED;
60     protected int[] mDimensions;
61     protected KeyValueMap mMetaData;
62     protected Class mObjectClass;
63 
FrameFormat()64     protected FrameFormat() {
65     }
66 
FrameFormat(int baseType, int target)67     public FrameFormat(int baseType, int target) {
68         mBaseType = baseType;
69         mTarget = target;
70         initDefaults();
71     }
72 
unspecified()73     public static FrameFormat unspecified() {
74         return new FrameFormat(TYPE_UNSPECIFIED, TARGET_UNSPECIFIED);
75     }
76 
getBaseType()77     public int getBaseType() {
78         return mBaseType;
79     }
80 
isBinaryDataType()81     public boolean isBinaryDataType() {
82         return mBaseType >= TYPE_BIT && mBaseType <= TYPE_DOUBLE;
83     }
84 
getBytesPerSample()85     public int getBytesPerSample() {
86         return mBytesPerSample;
87     }
88 
getValuesPerSample()89     public int getValuesPerSample() {
90         return mBytesPerSample / bytesPerSampleOf(mBaseType);
91     }
92 
getTarget()93     public int getTarget() {
94         return mTarget;
95     }
96 
getDimensions()97     public int[] getDimensions() {
98         return mDimensions;
99     }
100 
getDimension(int i)101     public int getDimension(int i) {
102         return mDimensions[i];
103     }
104 
getDimensionCount()105     public int getDimensionCount() {
106         return mDimensions == null ? 0 : mDimensions.length;
107     }
108 
hasMetaKey(String key)109     public boolean hasMetaKey(String key) {
110         return mMetaData != null ? mMetaData.containsKey(key) : false;
111     }
112 
hasMetaKey(String key, Class expectedClass)113     public boolean hasMetaKey(String key, Class expectedClass) {
114         if (mMetaData != null && mMetaData.containsKey(key)) {
115             if (!expectedClass.isAssignableFrom(mMetaData.get(key).getClass())) {
116                 throw new RuntimeException(
117                     "FrameFormat meta-key '" + key + "' is of type " +
118                     mMetaData.get(key).getClass() + " but expected to be of type " +
119                     expectedClass + "!");
120             }
121             return true;
122         }
123         return false;
124     }
125 
getMetaValue(String key)126     public Object getMetaValue(String key) {
127         return mMetaData != null ? mMetaData.get(key) : null;
128     }
129 
getNumberOfDimensions()130     public int getNumberOfDimensions() {
131         return mDimensions != null ? mDimensions.length : 0;
132     }
133 
getLength()134     public int getLength() {
135         return (mDimensions != null && mDimensions.length >= 1) ? mDimensions[0] : -1;
136     }
137 
getWidth()138     public int getWidth() {
139         return getLength();
140     }
141 
getHeight()142     public int getHeight() {
143         return (mDimensions != null && mDimensions.length >= 2) ? mDimensions[1] : -1;
144     }
145 
getDepth()146     public int getDepth() {
147         return (mDimensions != null && mDimensions.length >= 3) ? mDimensions[2] : -1;
148     }
149 
getSize()150     public int getSize() {
151         if (mSize == SIZE_UNKNOWN) mSize = calcSize(mDimensions);
152         return mSize;
153     }
154 
getObjectClass()155     public Class getObjectClass() {
156         return mObjectClass;
157     }
158 
mutableCopy()159     public MutableFrameFormat mutableCopy() {
160         MutableFrameFormat result = new MutableFrameFormat();
161         result.setBaseType(getBaseType());
162         result.setTarget(getTarget());
163         result.setBytesPerSample(getBytesPerSample());
164         result.setDimensions(getDimensions());
165         result.setObjectClass(getObjectClass());
166         result.mMetaData = mMetaData == null ? null : (KeyValueMap)mMetaData.clone();
167         return result;
168     }
169 
170     @Override
equals(Object object)171     public boolean equals(Object object) {
172         if (this == object) {
173             return true;
174         }
175 
176         if (!(object instanceof FrameFormat)) {
177             return false;
178         }
179 
180         FrameFormat format = (FrameFormat)object;
181         return format.mBaseType == mBaseType &&
182                 format.mTarget == mTarget &&
183                 format.mBytesPerSample == mBytesPerSample &&
184                 Arrays.equals(format.mDimensions, mDimensions) &&
185                 format.mMetaData.equals(mMetaData);
186     }
187 
188     @Override
hashCode()189     public int hashCode() {
190         return 4211 ^ mBaseType ^ mBytesPerSample ^ getSize();
191     }
192 
isCompatibleWith(FrameFormat specification)193     public boolean isCompatibleWith(FrameFormat specification) {
194         // Check base type
195         if (specification.getBaseType() != TYPE_UNSPECIFIED
196             && getBaseType() != specification.getBaseType()) {
197             return false;
198         }
199 
200         // Check target
201         if (specification.getTarget() != TARGET_UNSPECIFIED
202             && getTarget() != specification.getTarget()) {
203             return false;
204         }
205 
206         // Check bytes per sample
207         if (specification.getBytesPerSample() != BYTES_PER_SAMPLE_UNSPECIFIED
208             && getBytesPerSample() != specification.getBytesPerSample()) {
209             return false;
210         }
211 
212         // Check number of dimensions
213         if (specification.getDimensionCount() > 0
214             && getDimensionCount() != specification.getDimensionCount()) {
215             return false;
216         }
217 
218         // Check dimensions
219         for (int i = 0; i < specification.getDimensionCount(); ++i) {
220             int specDim = specification.getDimension(i);
221             if (specDim != SIZE_UNSPECIFIED && getDimension(i) != specDim) {
222                 return false;
223             }
224         }
225 
226         // Check class
227         if (specification.getObjectClass() != null) {
228             if (getObjectClass() == null
229                 || !specification.getObjectClass().isAssignableFrom(getObjectClass())) {
230                 return false;
231             }
232         }
233 
234         // Check meta-data
235         if (specification.mMetaData != null) {
236             for (String specKey : specification.mMetaData.keySet()) {
237                 if (mMetaData == null
238                 || !mMetaData.containsKey(specKey)
239                 || !mMetaData.get(specKey).equals(specification.mMetaData.get(specKey))) {
240                     return false;
241                 }
242             }
243         }
244 
245         // Passed all the tests
246         return true;
247     }
248 
mayBeCompatibleWith(FrameFormat specification)249     public boolean mayBeCompatibleWith(FrameFormat specification) {
250         // Check base type
251         if (specification.getBaseType() != TYPE_UNSPECIFIED
252             && getBaseType() != TYPE_UNSPECIFIED
253             && getBaseType() != specification.getBaseType()) {
254             return false;
255         }
256 
257         // Check target
258         if (specification.getTarget() != TARGET_UNSPECIFIED
259             && getTarget() != TARGET_UNSPECIFIED
260             && getTarget() != specification.getTarget()) {
261             return false;
262         }
263 
264         // Check bytes per sample
265         if (specification.getBytesPerSample() != BYTES_PER_SAMPLE_UNSPECIFIED
266             && getBytesPerSample() != BYTES_PER_SAMPLE_UNSPECIFIED
267             && getBytesPerSample() != specification.getBytesPerSample()) {
268             return false;
269         }
270 
271         // Check number of dimensions
272         if (specification.getDimensionCount() > 0
273             && getDimensionCount() > 0
274             && getDimensionCount() != specification.getDimensionCount()) {
275             return false;
276         }
277 
278         // Check dimensions
279         for (int i = 0; i < specification.getDimensionCount(); ++i) {
280             int specDim = specification.getDimension(i);
281             if (specDim != SIZE_UNSPECIFIED
282                 && getDimension(i) != SIZE_UNSPECIFIED
283                 && getDimension(i) != specDim) {
284                 return false;
285             }
286         }
287 
288         // Check class
289         if (specification.getObjectClass() != null && getObjectClass() != null) {
290             if (!specification.getObjectClass().isAssignableFrom(getObjectClass())) {
291                 return false;
292             }
293         }
294 
295         // Check meta-data
296         if (specification.mMetaData != null && mMetaData != null) {
297             for (String specKey : specification.mMetaData.keySet()) {
298                 if (mMetaData.containsKey(specKey)
299                     && !mMetaData.get(specKey).equals(specification.mMetaData.get(specKey))) {
300                     return false;
301                 }
302             }
303         }
304 
305         // Passed all the tests
306         return true;
307     }
308 
bytesPerSampleOf(int baseType)309     public static int bytesPerSampleOf(int baseType) {
310         // Defaults based on base-type
311         switch (baseType) {
312             case TYPE_BIT:
313             case TYPE_BYTE:
314                 return 1;
315             case TYPE_INT16:
316                 return 2;
317             case TYPE_INT32:
318             case TYPE_FLOAT:
319             case TYPE_POINTER:
320                 return 4;
321             case TYPE_DOUBLE:
322                 return 8;
323             default:
324                 return 1;
325         }
326     }
327 
dimensionsToString(int[] dimensions)328     public static String dimensionsToString(int[] dimensions) {
329         StringBuffer buffer = new StringBuffer();
330         if (dimensions != null) {
331             int n = dimensions.length;
332             for (int i = 0; i < n; ++i) {
333                 if (dimensions[i] == SIZE_UNSPECIFIED) {
334                     buffer.append("[]");
335                 } else {
336                     buffer.append("[" + String.valueOf(dimensions[i]) + "]");
337                 }
338             }
339         }
340         return buffer.toString();
341     }
342 
baseTypeToString(int baseType)343     public static String baseTypeToString(int baseType) {
344         switch (baseType) {
345             case TYPE_UNSPECIFIED: return "unspecified";
346             case TYPE_BIT:         return "bit";
347             case TYPE_BYTE:        return "byte";
348             case TYPE_INT16:       return "int";
349             case TYPE_INT32:       return "int";
350             case TYPE_FLOAT:       return "float";
351             case TYPE_DOUBLE:      return "double";
352             case TYPE_POINTER:     return "pointer";
353             case TYPE_OBJECT:      return "object";
354             default:               return "unknown";
355         }
356     }
357 
targetToString(int target)358     public static String targetToString(int target) {
359         switch (target) {
360             case TARGET_UNSPECIFIED:  return "unspecified";
361             case TARGET_SIMPLE:       return "simple";
362             case TARGET_NATIVE:       return "native";
363             case TARGET_GPU:          return "gpu";
364             case TARGET_VERTEXBUFFER: return "vbo";
365             case TARGET_RS:           return "renderscript";
366             default:                  return "unknown";
367         }
368     }
369 
metaDataToString(KeyValueMap metaData)370     public static String metaDataToString(KeyValueMap metaData) {
371         if (metaData == null) {
372             return "";
373         } else {
374             StringBuffer buffer = new StringBuffer();
375             buffer.append("{ ");
376             for (Entry<String, Object> entry : metaData.entrySet()) {
377                 buffer.append(entry.getKey() + ": " + entry.getValue() + " ");
378             }
379             buffer.append("}");
380             return buffer.toString();
381         }
382     }
383 
readTargetString(String targetString)384     public static int readTargetString(String targetString) {
385         if (targetString.equalsIgnoreCase("CPU") || targetString.equalsIgnoreCase("NATIVE")) {
386             return FrameFormat.TARGET_NATIVE;
387         } else if (targetString.equalsIgnoreCase("GPU")) {
388             return FrameFormat.TARGET_GPU;
389         } else if (targetString.equalsIgnoreCase("SIMPLE")) {
390             return FrameFormat.TARGET_SIMPLE;
391         } else if (targetString.equalsIgnoreCase("VERTEXBUFFER")) {
392             return FrameFormat.TARGET_VERTEXBUFFER;
393         } else if (targetString.equalsIgnoreCase("UNSPECIFIED")) {
394             return FrameFormat.TARGET_UNSPECIFIED;
395         } else {
396             throw new RuntimeException("Unknown target type '" + targetString + "'!");
397         }
398     }
399 
400     // TODO: FromString
401 
toString()402     public String toString() {
403         int valuesPerSample = getValuesPerSample();
404         String sampleCountString = valuesPerSample == 1 ? "" : String.valueOf(valuesPerSample);
405         String targetString = mTarget == TARGET_UNSPECIFIED ? "" : (targetToString(mTarget) + " ");
406         String classString = mObjectClass == null
407             ? ""
408             : (" class(" + mObjectClass.getSimpleName() + ") ");
409 
410         return targetString
411             + baseTypeToString(mBaseType)
412             + sampleCountString
413             + dimensionsToString(mDimensions)
414             + classString
415             + metaDataToString(mMetaData);
416     }
417 
initDefaults()418     private void initDefaults() {
419         mBytesPerSample = bytesPerSampleOf(mBaseType);
420     }
421 
422     // Core internal methods ///////////////////////////////////////////////////////////////////////
calcSize(int[] dimensions)423     int calcSize(int[] dimensions) {
424         if (dimensions != null && dimensions.length > 0) {
425             int size = getBytesPerSample();
426             for (int dim : dimensions) {
427                 size *= dim;
428             }
429             return size;
430         }
431         return 0;
432     }
433 
isReplaceableBy(FrameFormat format)434     boolean isReplaceableBy(FrameFormat format) {
435         return mTarget == format.mTarget
436             && getSize() == format.getSize()
437             && Arrays.equals(format.mDimensions, mDimensions);
438     }
439 }
440