1 /*
2  * Copyright 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.cts.rs;
18 
19 import static android.hardware.camera2.cts.helpers.Preconditions.*;
20 
21 import android.graphics.ImageFormat;
22 import android.graphics.PixelFormat;
23 import android.util.Size;
24 import android.renderscript.Allocation;
25 import android.renderscript.Element;
26 import android.renderscript.RenderScript;
27 import android.renderscript.Type;
28 import android.util.Log;
29 
30 /**
31  * Abstract the information necessary to create new {@link Allocation allocations} with
32  * their size, element, type, and usage.
33  *
34  * <p>This also includes convenience functions for printing to a string, something RenderScript
35  * lacks at the time of writing.</p>
36  *
37  * <p>Note that when creating a new {@link AllocationInfo} the usage flags <b>always</b> get ORd
38  * to {@link Allocation#USAGE_IO_SCRIPT}.</p>
39  */
40 public class AllocationInfo {
41 
42     private final RenderScript mRS = RenderScriptSingleton.getRS();
43 
44     private final Size mSize;
45     private final Element mElement;
46     private final Type mType;
47     private final int mUsage;
48 
49     private static final String TAG = "AllocationInfo";
50     private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
51 
52     /**
53      * Create a new {@link AllocationInfo} holding the element, size, and usage
54      * from an existing {@link Allocation}.
55      *
56      * @param allocation {@link Allocation}
57      *
58      * @return A new {@link AllocationInfo}
59      *
60      * @throws NullPointerException if allocation was {@code null}.
61      */
newInstance(Allocation allocation)62     public static AllocationInfo newInstance(Allocation allocation) {
63         checkNotNull("allocation", allocation);
64 
65         return new AllocationInfo(allocation.getElement(),
66                 new Size(allocation.getType().getX(), allocation.getType().getY()),
67                 allocation.getUsage());
68     }
69 
70     /**
71      * Create a new {@link AllocationInfo} holding the specified format, {@link Size},
72      * and {@link Allocation#USAGE_SCRIPT usage}.
73      *
74      * <p>The usage is always ORd with {@link Allocation#USAGE_SCRIPT}.</p>
75      *
76      * <p>The closest {@link Element} possible is created from the format.</p>
77      *
78      * @param size {@link Size}
79      * @param format An int format
80      * @param usage Usage flags
81      *
82      * @return A new {@link AllocationInfo} holding the given arguments.
83      *
84      * @throws NullPointerException if size was {@code null}.
85      *
86      * @see ImageFormat
87      * @see PixelFormat
88      */
newInstance(Size size, int format, int usage)89     public static AllocationInfo newInstance(Size size, int format, int usage) {
90         RenderScript rs = RenderScriptSingleton.getRS();
91 
92         Element element;
93         switch (format) {
94             case ImageFormat.YUV_420_888:
95                 element = Element.YUV(rs);
96                 break;
97             case PixelFormat.RGBA_8888:
98                 element = Element.RGBA_8888(rs);
99                 break;
100             // TODO: map more formats here
101             default:
102                 throw new UnsupportedOperationException("Unsupported format " + format);
103         }
104 
105         return new AllocationInfo(element, size, usage);
106     }
107 
108 
109     /**
110      * Create a new {@link AllocationInfo} holding the specified format, {@link Size},
111      * with the default usage.
112      *
113      * <p>The default usage is always {@link Allocation#USAGE_SCRIPT}.</p>
114      *
115      * <p>The closest {@link Element} possible is created from the format.</p>
116      *
117      * @param size {@link Size}
118      * @param format An int format
119      *
120      * @return A new {@link AllocationInfo} holding the given arguments.
121      *
122      * @throws NullPointerException if size was {@code null}.
123      *
124      * @see ImageFormat
125      * @see PixelFormat
126      */
newInstance(Size size, int format)127     public static AllocationInfo newInstance(Size size, int format) {
128         return newInstance(size, format, Allocation.USAGE_SCRIPT);
129     }
130 
131     /**
132      * Create a new {@link AllocationInfo} holding the specified {@link Element}, {@link Size},
133      * with the default usage.
134      *
135      * <p>The default usage is always {@link Allocation#USAGE_SCRIPT}.</p>
136      *
137      * @param element {@link Element}
138      * @param size {@link Size}
139      *
140      * @return A new {@link AllocationInfo} holding the given arguments.
141      *
142      * @throws NullPointerException if size was {@code null}.
143      * @throws NullPointerException if element was {@code null}.
144      */
newInstance(Element element, Size size)145     public static AllocationInfo newInstance(Element element, Size size) {
146         return new AllocationInfo(element, size, Allocation.USAGE_SCRIPT);
147     }
148 
149     /**
150      * Create a new {@link AllocationInfo} holding the specified {@link Element}, {@link Size},
151      * and {@link Allocation#USAGE_SCRIPT usage}.
152      *
153      * <p>The usage is always ORd with {@link Allocation#USAGE_SCRIPT}.</p>
154      *
155      * @param element {@link Element}
156      * @param size {@link Size}
157      * @param usage usage flags
158      *
159      * @return A new {@link AllocationInfo} holding the given arguments.
160      *
161      * @throws NullPointerException if size was {@code null}.
162      * @throws NullPointerException if element was {@code null}.
163      */
newInstance(Element element, Size size, int usage)164     public static AllocationInfo newInstance(Element element, Size size, int usage) {
165         return new AllocationInfo(element, size, usage);
166     }
167 
168     /**
169      * Create a new {@link AllocationInfo} by copying the existing data but appending
170      * the new usage flags to the old usage flags.
171      *
172      * @param usage usage flags
173      *
174      * @return A new {@link AllocationInfo} with new usage flags ORd to the old ones.
175      */
addExtraUsage(int usage)176     public AllocationInfo addExtraUsage(int usage) {
177         return new AllocationInfo(mElement, mSize, mUsage | usage);
178     }
179 
180     /**
181      * Create a new {@link AllocationInfo} by copying the existing data but changing the format,
182      * and appending the new usage flags to the old usage flags.
183      *
184      * @param format Format
185      * @param usage usage flags
186      *
187      * @return A new {@link AllocationInfo} with new format/usage.
188      *
189      * @see ImageFormat
190      * @see PixelFormat
191      */
changeFormatAndUsage(int format, int usage)192     public AllocationInfo changeFormatAndUsage(int format, int usage) {
193         return newInstance(getSize(), format, usage);
194     }
195 
196     /**
197      * Create a new {@link AllocationInfo} by copying the existing data but replacing the old
198      * usage with the new usage flags.
199      *
200      * @param usage usage flags
201      *
202      * @return A new {@link AllocationInfo} with new format/usage.
203      *
204      * @see ImageFormat
205      * @see PixelFormat
206      */
changeElementWithDefaultUsage(Element element)207     public AllocationInfo changeElementWithDefaultUsage(Element element) {
208         return newInstance(element, getSize());
209     }
210 
211     /**
212      * Create a new {@link AllocationInfo} by copying the existing data but changing the format,
213      * and replacing the old usage flags with default usage flags.
214      *
215      * @param format Format
216      *
217      * @return A new {@link AllocationInfo} with new format/usage.
218      *
219      * @see ImageFormat
220      * @see PixelFormat
221      */
changeFormatWithDefaultUsage(int format)222     public AllocationInfo changeFormatWithDefaultUsage(int format) {
223         return newInstance(getSize(), format, Allocation.USAGE_SCRIPT);
224     }
225 
AllocationInfo(Element element, Size size, int usage)226     private AllocationInfo(Element element, Size size, int usage) {
227         checkNotNull("element", element);
228         checkNotNull("size", size);
229 
230         mElement = element;
231         mSize = size;
232         mUsage = usage;
233 
234         Type.Builder typeBuilder = typeBuilder(element, size);
235 
236         if (element.equals(Element.YUV(mRS))) {
237             typeBuilder.setYuvFormat(ImageFormat.YUV_420_888);
238         }
239 
240         mType = typeBuilder.create();
241     }
242 
243     /**
244      * Get the {@link Type type} for this info.
245      *
246      * <p>Note that this is the same type that would get used by the {@link Allocation}
247      * created with {@link #createAllocation()}.
248      *
249      * @return The type (never {@code null}).
250      */
getType()251     public Type getType() {
252         return mType;
253     }
254 
255     /**
256      * Get the usage.
257      *
258      * <p>The bit for {@link Allocation#USAGE_SCRIPT} will always be set to 1.</p>
259      *
260      * @return usage flags
261      */
getUsage()262     public int getUsage() {
263         return mUsage;
264     }
265 
266     /**
267      * Get the size.
268      *
269      * @return The size (never {@code null}).
270      */
getSize()271     public Size getSize() {
272         return mSize;
273     }
274 
275     /**
276      * Get the {@link Element}.
277      *
278      * @return The element (never {@code null}).
279      */
getElement()280     public Element getElement() {
281         return mElement;
282     }
283 
284     /**
285      * Convenience enum to represent commonly-used elements without needing a RenderScript object.
286      */
287     public enum ElementInfo {
288         YUV,
289         RGBA_8888,
290         U8_3,
291         U8_4;
292 
293         private static final String TAG = "ElementInfo";
294 
295         /**
296          * Create an {@link ElementInfo} by converting it from a {@link Element}.
297          *
298          * @param element The element for which you want to get an enum for.
299          *
300          * @return The element info is a corresponding one exists, or {@code null} otherwise.
301          */
fromElement(Element element)302         public static ElementInfo fromElement(Element element) {
303             checkNotNull("element", element);
304 
305             if (element.equals(Element.YUV(RenderScriptSingleton.getRS()))) {
306                 return YUV;
307             } else if (element.equals(Element.RGBA_8888(RenderScriptSingleton.getRS()))) {
308                 return RGBA_8888;
309             } else if (element.equals(Element.U8_3(RenderScriptSingleton.getRS()))) {
310                 return U8_3;
311             } else if (element.equals(Element.U8_4(RenderScriptSingleton.getRS()))) {
312                 return U8_4;
313             }
314             // TODO: add more comparisons here as necessary
315 
316             Log.w(TAG, "Unknown element of data kind " + element.getDataKind());
317             return null;
318         }
319     }
320 
321     /**
322      * Compare the current element against the suggested element (info).
323      *
324      * @param element The other element to compare against.
325      *
326      * @return true if the elements are equal, false otherwise.
327      */
isElementEqualTo(ElementInfo element)328     public boolean isElementEqualTo(ElementInfo element) {
329         checkNotNull("element", element);
330 
331         Element comparison;
332         switch (element) {
333             case YUV:
334                 comparison = Element.YUV(mRS);
335                 break;
336             case RGBA_8888:
337                 comparison = Element.RGBA_8888(mRS);
338                 break;
339             case U8_3:
340                 comparison = Element.U8_3(mRS);
341                 break;
342             case U8_4:
343                 comparison = Element.U8_4(mRS);
344                 break;
345             default:
346             // TODO: add more comparisons here as necessary
347                 comparison = null;
348         }
349 
350         return mElement.equals(comparison);
351     }
352 
353     /**
354      * Human-readable representation of this info.
355      */
356     @Override
toString()357     public String toString() {
358         return String.format("Size: %s, Element: %s, Usage: %x", mSize,
359                 ElementInfo.fromElement(mElement), mUsage);
360     }
361 
362     /**
363      * Compare against another object.
364      *
365      * <p>Comparisons against objects that are not instances of {@link AllocationInfo}
366      * always return {@code false}.</p>
367      *
368      * <p>Two {@link AllocationInfo infos} are considered equal only if their elements,
369      * sizes, and usage flags are also equal.</p>
370      *
371      * @param other Another info object
372      *
373      * @return true if this is equal to other
374      */
375     @Override
equals(Object other)376     public boolean equals(Object other) {
377         if (other instanceof AllocationInfo) {
378             return equals((AllocationInfo)other);
379         } else {
380             return false;
381         }
382     }
383 
384     /**
385      * Compare against another object.
386      *
387      * <p>Two {@link AllocationInfo infos} are considered equal only if their elements,
388      * sizes, and usage flags are also equal.</p>
389      *
390      * @param other Another info object
391      *
392      * @return true if this is equal to other
393      */
equals(AllocationInfo other)394     public boolean equals(AllocationInfo other) {
395         if (other == null) {
396             return false;
397         }
398 
399         // Element, Size equality is already incorporated into Type equality
400         return mType.equals(other.mType) && mUsage == other.mUsage;
401     }
402 
403     /**
404      * Create a new {@link Allocation} using the {@link #getType type} and {@link #getUsage usage}
405      * from this info object.
406      *
407      * <p>The allocation is always created from a {@link AllocationCache cache}. If possible,
408      * return it to the cache once done (although this is not necessary).</p>
409      *
410      * @return a new {@link Allocation}
411      */
createAllocation()412     public Allocation createAllocation() {
413         if (VERBOSE) Log.v(TAG, "createAllocation - for info =" + toString());
414         return RenderScriptSingleton.getCache().getOrCreateTyped(mType, mUsage);
415     }
416 
417     /**
418      * Create a new {@link Allocation} using the {@link #getType type} and {@link #getUsage usage}
419      * from this info object; immediately wrap inside a new {@link BlockingInputAllocation}.
420      *
421      * <p>The allocation is always created from a {@link AllocationCache cache}. If possible,
422      * return it to the cache once done (although this is not necessary).</p>
423      *
424      * @return a new {@link Allocation}
425      *
426      * @throws IllegalArgumentException
427      *            If the usage did not have one of {@code USAGE_IO_INPUT} or {@code USAGE_IO_OUTPUT}
428      */
createBlockingInputAllocation()429     public BlockingInputAllocation createBlockingInputAllocation() {
430         Allocation alloc = createAllocation();
431         return BlockingInputAllocation.wrap(alloc);
432     }
433 
typeBuilder(Element element, Size size)434     private static Type.Builder typeBuilder(Element element, Size size) {
435         Type.Builder builder = (new Type.Builder(RenderScriptSingleton.getRS(), element))
436                 .setX(size.getWidth())
437                 .setY(size.getHeight());
438 
439         return builder;
440     }
441 }
442