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
17package android.hardware.camera2.cts.rs;
18
19import static android.hardware.camera2.cts.helpers.Preconditions.*;
20
21import android.graphics.ImageFormat;
22import android.graphics.PixelFormat;
23import android.util.Size;
24import android.renderscript.Allocation;
25import android.renderscript.Element;
26import android.renderscript.RenderScript;
27import android.renderscript.Type;
28import 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 */
40public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
222    public AllocationInfo changeFormatWithDefaultUsage(int format) {
223        return newInstance(getSize(), format, Allocation.USAGE_SCRIPT);
224    }
225
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     */
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     */
262    public int getUsage() {
263        return mUsage;
264    }
265
266    /**
267     * Get the size.
268     *
269     * @return The size (never {@code null}).
270     */
271    public Size getSize() {
272        return mSize;
273    }
274
275    /**
276     * Get the {@link Element}.
277     *
278     * @return The element (never {@code null}).
279     */
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         */
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     */
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
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
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     */
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     */
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     */
429    public BlockingInputAllocation createBlockingInputAllocation() {
430        Allocation alloc = createAllocation();
431        return BlockingInputAllocation.wrap(alloc);
432    }
433
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