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