1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package androidx.media.filterfw;
16 
17 import android.annotation.TargetApi;
18 import android.graphics.Bitmap;
19 import android.os.Build;
20 import android.renderscript.Allocation;
21 import android.renderscript.Element;
22 import android.renderscript.RenderScript;
23 import android.renderscript.Type;
24 import android.util.Log;
25 
26 import java.nio.ByteBuffer;
27 import java.nio.ByteOrder;
28 import java.util.Arrays;
29 import java.util.Vector;
30 
31 final class BackingStore {
32 
33     /** Access mode None: Frame data will not be accessed at all. */
34     static final int ACCESS_NONE = 0x00;
35     /** Access mode Bytes: Frame data will be accessed as a ByteBuffer. */
36     static final int ACCESS_BYTES = 0x01;
37     /** Access mode Texture: Frame data will be accessed as a TextureSource. */
38     static final int ACCESS_TEXTURE = 0x02;
39     /** Access mode RenderTarget: Frame data will be accessed as a RenderTarget. */
40     static final int ACCESS_RENDERTARGET = 0x04;
41     /** Access mode Object: Frame data will be accessed as a generic Object. */
42     static final int ACCESS_OBJECT = 0x08;
43     /** Access mode Bitmap: Frame data will be accessed as a Bitmap. */
44     static final int ACCESS_BITMAP = 0x10;
45     /** Access mode Allocation: Frame data will be accessed as a RenderScript Allocation. */
46     static final int ACCESS_ALLOCATION = 0x20;
47 
48     private static final int BACKING_BYTEBUFFER = 1;
49     private static final int BACKING_TEXTURE = 2;
50     private static final int BACKING_OBJECT = 3;
51     private static final int BACKING_BITMAP = 4;
52     private static final int BACKING_ALLOCATION = 5;
53 
54     private final FrameType mType;
55     private int[] mDimensions;
56     private long mTimestamp = Frame.TIMESTAMP_NOT_SET;
57 
58     private final FrameManager mFrameManager;
59 
60     private Vector<Backing> mBackings = new Vector<Backing>();
61 
62     private boolean mWriteLocked = false;
63     private int mReadLocks = 0;
64 
65     private int mRefCount = 1;
66 
67     /** The most up-to-date data backing */
68     private Backing mCurrentBacking = null;
69 
70     /** The currently locked backing */
71     private Backing mLockedBacking = null;
72 
73     // Public Methods //////////////////////////////////////////////////////////////////////////////
BackingStore(FrameType type, int[] dimensions, FrameManager frameManager)74     public BackingStore(FrameType type, int[] dimensions, FrameManager frameManager) {
75         mType = type;
76         mDimensions = dimensions != null ? Arrays.copyOf(dimensions, dimensions.length) : null;
77         mFrameManager = frameManager;
78     }
79 
getFrameType()80     public FrameType getFrameType() {
81         return mType;
82     }
83 
lockData(int mode, int accessFormat)84     public Object lockData(int mode, int accessFormat) {
85         return lockBacking(mode, accessFormat).lock(accessFormat);
86     }
87 
lockBacking(int mode, int access)88     public Backing lockBacking(int mode, int access) {
89         Backing backing = fetchBacking(mode, access);
90         if (backing == null) {
91             throw new RuntimeException("Could not fetch frame data!");
92         }
93         lock(backing, mode);
94         return backing;
95     }
96 
unlock()97     public boolean unlock() {
98         if (mWriteLocked) {
99             mWriteLocked = false;
100         } else if (mReadLocks > 0) {
101             --mReadLocks;
102         } else {
103             return false;
104         }
105         mLockedBacking.unlock();
106         mLockedBacking = null;
107         return true;
108     }
109 
retain()110     public BackingStore retain() {
111         if (mRefCount >= 10) {
112             Log.w("BackingStore", "High ref-count of " + mRefCount + " on " + this + "!");
113         }
114         if (mRefCount <= 0) {
115             throw new RuntimeException("RETAINING RELEASED");
116         }
117         ++mRefCount;
118         return this;
119     }
120 
release()121     public BackingStore release() {
122         if (mRefCount <= 0) {
123             throw new RuntimeException("DOUBLE-RELEASE");
124         }
125         --mRefCount;
126         if (mRefCount == 0) {
127             releaseBackings();
128             return null;
129         }
130         return this;
131     }
132 
133     /**
134      * Resizes the backing store. This invalidates all data in the store.
135      */
resize(int[] newDimensions)136     public void resize(int[] newDimensions) {
137         Vector<Backing> resized = new Vector<Backing>();
138         for (Backing backing : mBackings) {
139             if (backing.resize(newDimensions)) {
140                 resized.add(backing);
141             } else {
142                 releaseBacking(backing);
143             }
144         }
145         mBackings = resized;
146         mDimensions = newDimensions;
147     }
148 
getDimensions()149     public int[] getDimensions() {
150         return mDimensions;
151     }
152 
getElementCount()153     public int getElementCount() {
154         int result = 1;
155         if (mDimensions != null) {
156             for (int dim : mDimensions) {
157                 result *= dim;
158             }
159         }
160         return result;
161     }
162 
importStore(BackingStore store)163     public void importStore(BackingStore store) {
164         // TODO: Better backing selection?
165         if (store.mBackings.size() > 0) {
166             importBacking(store.mBackings.firstElement());
167         }
168         mTimestamp = store.mTimestamp;
169     }
170 
171     /**
172      * @return the timestamp
173      */
getTimestamp()174     public long getTimestamp() {
175         return mTimestamp;
176     }
177 
178     /**
179      * @param timestamp the timestamp to set
180      */
setTimestamp(long timestamp)181     public void setTimestamp(long timestamp) {
182         mTimestamp = timestamp;
183     }
184 
185     // Internal Methods ////////////////////////////////////////////////////////////////////////////
fetchBacking(int mode, int access)186     private Backing fetchBacking(int mode, int access) {
187         Backing backing = getBacking(mode, access);
188         if (backing == null) {
189             backing = attachNewBacking(mode, access);
190         }
191         syncBacking(backing);
192         return backing;
193     }
194 
syncBacking(Backing backing)195     private void syncBacking(Backing backing) {
196         if (backing != null && backing.isDirty() && mCurrentBacking != null) {
197             backing.syncTo(mCurrentBacking);
198         }
199     }
200 
getBacking(int mode, int access)201     private Backing getBacking(int mode, int access) {
202         // [Non-iterator looping]
203         for (int i = 0; i < mBackings.size(); ++i) {
204             final Backing backing = mBackings.get(i);
205 
206             int backingAccess =
207                     (mode == Frame.MODE_WRITE) ? backing.writeAccess() : backing.readAccess();
208             if ((backingAccess & access) == access) {
209                 return backing;
210             }
211         }
212         return null;
213     }
214 
attachNewBacking(int mode, int access)215     private Backing attachNewBacking(int mode, int access) {
216         Backing backing = createBacking(mode, access);
217         if (mBackings.size() > 0) {
218             backing.markDirty();
219         }
220         mBackings.add(backing);
221         return backing;
222     }
223 
createBacking(int mode, int access)224     private Backing createBacking(int mode, int access) {
225         // TODO: If the read/write access flags indicate, make/fetch a GraphicBuffer backing.
226         Backing backing = null;
227         int elemSize = mType.getElementSize();
228         if (shouldFetchCached(access)) {
229             backing = mFrameManager.fetchBacking(mode, access, mDimensions, elemSize);
230         }
231         if (backing == null) {
232             switch (access) {
233                 case ACCESS_BYTES:
234                     backing = new ByteBufferBacking();
235                     break;
236                 case ACCESS_TEXTURE:
237                 case ACCESS_RENDERTARGET:
238                     backing = new TextureBacking();
239                     break;
240                 case ACCESS_OBJECT:
241                     backing = new ObjectBacking();
242                     break;
243                 case ACCESS_BITMAP:
244                     backing = new BitmapBacking();
245                     break;
246                 case ACCESS_ALLOCATION:
247                     if (!AllocationBacking.isSupported()) {
248                         throw new RuntimeException(
249                                 "Attempted to create an AllocationBacking in context that does " +
250                                 "not support RenderScript!");
251                     }
252                     backing = new AllocationBacking(mFrameManager.getContext().getRenderScript());
253                     break;
254             }
255             if (backing == null) {
256                 throw new RuntimeException(
257                         "Could not create backing for access type " + access + "!");
258             }
259             if (backing.requiresGpu() && !mFrameManager.getRunner().isOpenGLSupported()) {
260                 throw new RuntimeException(
261                         "Cannot create backing that requires GPU in a runner that does not " +
262                         "support OpenGL!");
263             }
264             backing.setDimensions(mDimensions);
265             backing.setElementSize(elemSize);
266             backing.setElementId(mType.getElementId());
267             backing.allocate(mType);
268             mFrameManager.onBackingCreated(backing);
269         }
270         return backing;
271     }
272 
importBacking(Backing backing)273     private void importBacking(Backing backing) {
274         // TODO: This actually needs synchronization between the two BackingStore threads for the
275         // general case
276         int access = backing.requiresGpu() ? ACCESS_BYTES : backing.readAccess();
277         Backing newBacking = createBacking(Frame.MODE_READ, access);
278         newBacking.syncTo(backing);
279         mBackings.add(newBacking);
280         mCurrentBacking = newBacking;
281     }
282 
releaseBackings()283     private void releaseBackings() {
284         // [Non-iterator looping]
285         for (int i = 0; i < mBackings.size(); ++i) {
286             releaseBacking(mBackings.get(i));
287         }
288         mBackings.clear();
289         mCurrentBacking = null;
290     }
291 
releaseBacking(Backing backing)292     private void releaseBacking(Backing backing) {
293         mFrameManager.onBackingAvailable(backing);
294     }
295 
lock(Backing backingToLock, int mode)296     private void lock(Backing backingToLock, int mode) {
297         if (mode == Frame.MODE_WRITE) {
298             // Make sure frame is not read-locked
299             if (mReadLocks > 0) {
300                 throw new RuntimeException(
301                         "Attempting to write-lock the read-locked frame " + this + "!");
302             } else if (mWriteLocked) {
303                 throw new RuntimeException(
304                         "Attempting to write-lock the write-locked frame " + this + "!");
305             }
306             // Mark all other backings dirty
307             // [Non-iterator looping]
308             for (int i = 0; i < mBackings.size(); ++i) {
309                 final Backing backing = mBackings.get(i);
310                 if (backing != backingToLock) {
311                     backing.markDirty();
312                 }
313             }
314             mWriteLocked = true;
315             mCurrentBacking = backingToLock;
316         } else {
317             if (mWriteLocked) {
318                 throw new RuntimeException("Attempting to read-lock locked frame " + this + "!");
319             }
320             ++mReadLocks;
321         }
322         mLockedBacking = backingToLock;
323     }
324 
shouldFetchCached(int access)325     private static boolean shouldFetchCached(int access) {
326         return access != ACCESS_OBJECT;
327     }
328 
329 
330     // Backings ////////////////////////////////////////////////////////////////////////////////////
331     static abstract class Backing {
332         protected int[] mDimensions = null;
333         private int mElementSize;
334         private int mElementID;
335         protected boolean mIsDirty = false;
336 
337         int cachePriority = 0;
338 
allocate(FrameType frameType)339         public abstract void allocate(FrameType frameType);
340 
readAccess()341         public abstract int readAccess();
342 
writeAccess()343         public abstract int writeAccess();
344 
syncTo(Backing backing)345         public abstract void syncTo(Backing backing);
346 
lock(int accessType)347         public abstract Object lock(int accessType);
348 
getType()349         public abstract int getType();
350 
shouldCache()351         public abstract boolean shouldCache();
352 
requiresGpu()353         public abstract boolean requiresGpu();
354 
destroy()355         public abstract void destroy();
356 
getSize()357         public abstract int getSize();
358 
unlock()359         public void unlock() {
360             // Default implementation does nothing.
361         }
362 
setData(Object data)363         public void setData(Object data) {
364             throw new RuntimeException("Internal error: Setting data on frame backing " + this
365                     + ", which does not support setting data directly!");
366         }
367 
setDimensions(int[] dimensions)368         public void setDimensions(int[] dimensions) {
369             mDimensions = dimensions;
370         }
371 
setElementSize(int elemSize)372         public void setElementSize(int elemSize) {
373             mElementSize = elemSize;
374         }
375 
setElementId(int elemId)376         public void setElementId(int elemId) {
377             mElementID = elemId;
378         }
379 
getDimensions()380         public int[] getDimensions() {
381             return mDimensions;
382         }
383 
getElementSize()384         public int getElementSize() {
385             return mElementSize;
386         }
387 
getElementId()388         public int getElementId() {
389             return mElementID;
390         }
391 
resize(int[] newDimensions)392         public boolean resize(int[] newDimensions) {
393             return false;
394         }
395 
markDirty()396         public void markDirty() {
397             mIsDirty = true;
398         }
399 
isDirty()400         public boolean isDirty() {
401             return mIsDirty;
402         }
403 
assertImageCompatible(FrameType type)404         protected void assertImageCompatible(FrameType type) {
405             if (type.getElementId() != FrameType.ELEMENT_RGBA8888) {
406                 throw new RuntimeException("Cannot allocate texture with non-RGBA data type!");
407             } else if (mDimensions == null || mDimensions.length != 2) {
408                 throw new RuntimeException("Cannot allocate non 2-dimensional texture!");
409             }
410         }
411 
412     }
413 
414     static class ObjectBacking extends Backing {
415 
416         private Object mObject = null;
417 
418         @Override
allocate(FrameType frameType)419         public void allocate(FrameType frameType) {
420             mObject = null;
421         }
422 
423         @Override
readAccess()424         public int readAccess() {
425             return ACCESS_OBJECT;
426         }
427 
428         @Override
writeAccess()429         public int writeAccess() {
430             return ACCESS_OBJECT;
431         }
432 
433         @Override
syncTo(Backing backing)434         public void syncTo(Backing backing) {
435             switch (backing.getType()) {
436                 case BACKING_OBJECT:
437                     mObject = backing.lock(ACCESS_OBJECT);
438                     backing.unlock();
439                     break;
440                 case BACKING_BITMAP:
441                     mObject = backing.lock(ACCESS_BITMAP);
442                     backing.unlock();
443                     break;
444                 default:
445                     mObject = null;
446             }
447             mIsDirty = false;
448         }
449 
450         @Override
lock(int accessType)451         public Object lock(int accessType) {
452             return mObject;
453         }
454 
455         @Override
getType()456         public int getType() {
457             return BACKING_OBJECT;
458         }
459 
460         @Override
shouldCache()461         public boolean shouldCache() {
462             return false;
463         }
464 
465         @Override
requiresGpu()466         public boolean requiresGpu() {
467             return false;
468         }
469 
470         @Override
destroy()471         public void destroy() {
472             mObject = null;
473         }
474 
475         @Override
getSize()476         public int getSize() {
477             return 0;
478         }
479 
480         @Override
setData(Object data)481         public void setData(Object data) {
482             mObject = data;
483         }
484 
485     }
486 
487     static class BitmapBacking extends Backing {
488 
489         private Bitmap mBitmap = null;
490 
491         @Override
allocate(FrameType frameType)492         public void allocate(FrameType frameType) {
493             assertImageCompatible(frameType);
494         }
495 
496         @Override
readAccess()497         public int readAccess() {
498             return ACCESS_BITMAP;
499         }
500 
501         @Override
writeAccess()502         public int writeAccess() {
503             return ACCESS_BITMAP;
504         }
505 
506         @Override
syncTo(Backing backing)507         public void syncTo(Backing backing) {
508             int access = backing.readAccess();
509             if ((access & ACCESS_BITMAP) != 0) {
510                 mBitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
511             } else if ((access & ACCESS_BYTES) != 0) {
512                 createBitmap();
513                 ByteBuffer buffer = (ByteBuffer) backing.lock(ACCESS_BYTES);
514                 mBitmap.copyPixelsFromBuffer(buffer);
515                 buffer.rewind();
516             } else if ((access & ACCESS_TEXTURE) != 0) {
517                 createBitmap();
518                 RenderTarget renderTarget = (RenderTarget) backing.lock(ACCESS_RENDERTARGET);
519                 mBitmap.copyPixelsFromBuffer(
520                         renderTarget.getPixelData(mDimensions[0], mDimensions[1]));
521             } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
522                 createBitmap();
523                 syncToAllocationBacking(backing);
524             } else {
525                 throw new RuntimeException("Cannot sync bytebuffer backing!");
526             }
527             backing.unlock();
528             mIsDirty = false;
529         }
530 
531         @TargetApi(11)
syncToAllocationBacking(Backing backing)532         private void syncToAllocationBacking(Backing backing) {
533             Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
534             allocation.copyTo(mBitmap);
535         }
536 
537         @Override
lock(int accessType)538         public Object lock(int accessType) {
539             return mBitmap;
540         }
541 
542         @Override
getType()543         public int getType() {
544             return BACKING_BITMAP;
545         }
546 
547         @Override
shouldCache()548         public boolean shouldCache() {
549             return false;
550         }
551 
552         @Override
requiresGpu()553         public boolean requiresGpu() {
554             return false;
555         }
556 
557         @Override
destroy()558         public void destroy() {
559             // As we share the bitmap with other backings (such as object backings), we must not
560             // recycle it here.
561             mBitmap = null;
562         }
563 
564         @Override
getSize()565         public int getSize() {
566             return 4 * mDimensions[0] * mDimensions[1];
567         }
568 
569         @Override
setData(Object data)570         public void setData(Object data) {
571             // We can assume that data will always be a Bitmap instance.
572             mBitmap = (Bitmap) data;
573         }
574 
createBitmap()575         private void createBitmap() {
576             mBitmap = Bitmap.createBitmap(mDimensions[0], mDimensions[1], Bitmap.Config.ARGB_8888);
577         }
578     }
579 
580     static class TextureBacking extends Backing {
581 
582         private RenderTarget mRenderTarget = null;
583         private TextureSource mTexture = null;
584 
585         @Override
allocate(FrameType frameType)586         public void allocate(FrameType frameType) {
587             assertImageCompatible(frameType);
588             mTexture = TextureSource.newTexture();
589         }
590 
591         @Override
readAccess()592         public int readAccess() {
593             return ACCESS_TEXTURE;
594         }
595 
596         @Override
writeAccess()597         public int writeAccess() {
598             return ACCESS_RENDERTARGET;
599         }
600 
601         @Override
syncTo(Backing backing)602         public void syncTo(Backing backing) {
603             int access = backing.readAccess();
604             if ((access & ACCESS_BYTES) != 0) {
605                 ByteBuffer pixels = (ByteBuffer) backing.lock(ACCESS_BYTES);
606                 mTexture.allocateWithPixels(pixels, mDimensions[0], mDimensions[1]);
607             } else if ((access & ACCESS_BITMAP) != 0) {
608                 Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
609                 mTexture.allocateWithBitmapPixels(bitmap);
610             } else if ((access & ACCESS_TEXTURE) != 0) {
611                 TextureSource texture = (TextureSource) backing.lock(ACCESS_TEXTURE);
612                 int w = mDimensions[0];
613                 int h = mDimensions[1];
614                 ImageShader.renderTextureToTarget(texture, getRenderTarget(), w, h);
615             } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
616                 syncToAllocationBacking(backing);
617             } else {
618                 throw new RuntimeException("Cannot sync bytebuffer backing!");
619             }
620             backing.unlock();
621             mIsDirty = false;
622         }
623 
624         @TargetApi(11)
syncToAllocationBacking(Backing backing)625         private void syncToAllocationBacking(Backing backing) {
626             Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
627             ByteBuffer pixels = ByteBuffer.allocateDirect(getSize());
628             allocation.copyTo(pixels.array());
629             mTexture.allocateWithPixels(pixels, mDimensions[0], mDimensions[1]);
630         }
631 
632         @Override
lock(int accessType)633         public Object lock(int accessType) {
634             switch (accessType) {
635                 case ACCESS_TEXTURE:
636                     return getTexture();
637 
638                 case ACCESS_RENDERTARGET:
639                     return getRenderTarget();
640 
641                 default:
642                     throw new RuntimeException("Illegal access to texture!");
643             }
644         }
645 
646         @Override
getType()647         public int getType() {
648             return BACKING_TEXTURE;
649         }
650 
651         @Override
shouldCache()652         public boolean shouldCache() {
653             return true;
654         }
655 
656         @Override
requiresGpu()657         public boolean requiresGpu() {
658             return true;
659         }
660 
661         @Override
destroy()662         public void destroy() {
663             if (mRenderTarget != null) {
664                 mRenderTarget.release();
665             }
666             if (mTexture.isAllocated()) {
667                 mTexture.release();
668             }
669         }
670 
671         @Override
getSize()672         public int getSize() {
673             return 4 * mDimensions[0] * mDimensions[1];
674         }
675 
getTexture()676         private TextureSource getTexture() {
677             if (!mTexture.isAllocated()) {
678                 mTexture.allocate(mDimensions[0], mDimensions[1]);
679             }
680             return mTexture;
681         }
682 
getRenderTarget()683         private RenderTarget getRenderTarget() {
684             if (mRenderTarget == null) {
685                 int w = mDimensions[0];
686                 int h = mDimensions[1];
687                 mRenderTarget = RenderTarget.currentTarget().forTexture(getTexture(), w, h);
688             }
689             return mRenderTarget;
690         }
691 
692     }
693 
694     static class ByteBufferBacking extends Backing {
695 
696         ByteBuffer mBuffer = null;
697 
698         @Override
allocate(FrameType frameType)699         public void allocate(FrameType frameType) {
700             int size = frameType.getElementSize();
701             for (int dim : mDimensions) {
702                 size *= dim;
703             }
704             mBuffer = ByteBuffer.allocateDirect(size);
705         }
706 
707         @Override
readAccess()708         public int readAccess() {
709             return ACCESS_BYTES;
710         }
711 
712         @Override
writeAccess()713         public int writeAccess() {
714             return ACCESS_BYTES;
715         }
716 
717         @Override
requiresGpu()718         public boolean requiresGpu() {
719             return false;
720         }
721 
722         @Override
syncTo(Backing backing)723         public void syncTo(Backing backing) {
724             int access = backing.readAccess();
725             if ((access & ACCESS_TEXTURE) != 0) {
726                 RenderTarget target = (RenderTarget) backing.lock(ACCESS_RENDERTARGET);
727                 GLToolbox.readTarget(target, mBuffer, mDimensions[0], mDimensions[1]);
728             } else if ((access & ACCESS_BITMAP) != 0) {
729                 Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
730                 bitmap.copyPixelsToBuffer(mBuffer);
731                 mBuffer.rewind();
732             } else if ((access & ACCESS_BYTES) != 0) {
733                 ByteBuffer otherBuffer = (ByteBuffer) backing.lock(ACCESS_BYTES);
734                 mBuffer.put(otherBuffer);
735                 otherBuffer.rewind();
736             } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) {
737                 syncToAllocationBacking(backing);
738             } else {
739                 throw new RuntimeException("Cannot sync bytebuffer backing!");
740             }
741             backing.unlock();
742             mBuffer.rewind();
743             mIsDirty = false;
744         }
745 
746         @TargetApi(11)
syncToAllocationBacking(Backing backing)747         private void syncToAllocationBacking(Backing backing) {
748             Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
749             if (getElementId() == FrameType.ELEMENT_RGBA8888) {
750                 byte[] bytes = mBuffer.array();
751                 allocation.copyTo(bytes);
752             } else if (getElementId() == FrameType.ELEMENT_FLOAT32) {
753                 float[] floats = new float[getSize() / 4];
754                 allocation.copyTo(floats);
755                 mBuffer.asFloatBuffer().put(floats);
756             } else {
757                 throw new RuntimeException(
758                         "Trying to sync to an allocation with an unsupported element id: "
759                         + getElementId());
760             }
761         }
762 
763         @Override
lock(int accessType)764         public Object lock(int accessType) {
765             return mBuffer.rewind();
766         }
767 
768         @Override
unlock()769         public void unlock() {
770             mBuffer.rewind();
771         }
772 
773         @Override
getType()774         public int getType() {
775             return BACKING_BYTEBUFFER;
776         }
777 
778         @Override
shouldCache()779         public boolean shouldCache() {
780             return true;
781         }
782 
783         @Override
destroy()784         public void destroy() {
785             mBuffer = null;
786         }
787 
788         @Override
getSize()789         public int getSize() {
790             return mBuffer.remaining();
791         }
792 
793     }
794 
795     @TargetApi(11)
796     static class AllocationBacking extends Backing {
797 
798         private final RenderScript mRenderScript;
799         private Allocation mAllocation = null;
800 
AllocationBacking(RenderScript renderScript)801         public AllocationBacking(RenderScript renderScript) {
802             mRenderScript = renderScript;
803         }
804 
805         @Override
allocate(FrameType frameType)806         public void allocate(FrameType frameType) {
807             assertCompatible(frameType);
808 
809             Element element = null;
810             switch (frameType.getElementId()) {
811                 case FrameType.ELEMENT_RGBA8888:
812                     element = Element.RGBA_8888(mRenderScript);
813                     break;
814                 case FrameType.ELEMENT_FLOAT32:
815                     element = Element.F32(mRenderScript);
816                     break;
817             }
818             Type.Builder imageTypeBuilder = new Type.Builder(mRenderScript, element);
819             imageTypeBuilder.setX(mDimensions.length >= 1 ? mDimensions[0] : 1);
820             imageTypeBuilder.setY(mDimensions.length == 2 ? mDimensions[1] : 1);
821             Type imageType = imageTypeBuilder.create();
822 
823             mAllocation = Allocation.createTyped(mRenderScript, imageType);
824         }
825 
826         @Override
readAccess()827         public int readAccess() {
828             return ACCESS_ALLOCATION;
829         }
830 
831         @Override
writeAccess()832         public int writeAccess() {
833             return ACCESS_ALLOCATION;
834         }
835 
836         @Override
requiresGpu()837         public boolean requiresGpu() {
838             return false;
839         }
840 
841         @Override
syncTo(Backing backing)842         public void syncTo(Backing backing) {
843             int access = backing.readAccess();
844             if ((access & ACCESS_TEXTURE) != 0) {
845                 RenderTarget target = (RenderTarget) backing.lock(ACCESS_RENDERTARGET);
846                 ByteBuffer pixels = ByteBuffer.allocateDirect(getSize());
847                 GLToolbox.readTarget(target, pixels, mDimensions[0], mDimensions[1]);
848                 mAllocation.copyFrom(pixels.array());
849             } else if ((access & ACCESS_BITMAP) != 0) {
850                 Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP);
851                 mAllocation.copyFrom(bitmap);
852             } else if ((access & ACCESS_BYTES) != 0) {
853                 ByteBuffer buffer = (ByteBuffer) backing.lock(ACCESS_BYTES);
854                 if (buffer.order() != ByteOrder.nativeOrder()) {
855                     throw new RuntimeException(
856                             "Trying to sync to the ByteBufferBacking with non-native byte order!");
857                 }
858                 byte[] bytes;
859                 if (buffer.hasArray()) {
860                     bytes = buffer.array();
861                 } else {
862                     bytes = new byte[getSize()];
863                     buffer.get(bytes);
864                     buffer.rewind();
865                 }
866                 mAllocation.copyFromUnchecked(bytes);
867             } else {
868                 throw new RuntimeException("Cannot sync allocation backing!");
869             }
870             backing.unlock();
871             mIsDirty = false;
872         }
873 
874         @Override
lock(int accessType)875         public Object lock(int accessType) {
876             return mAllocation;
877         }
878 
879         @Override
unlock()880         public void unlock() {
881         }
882 
883         @Override
getType()884         public int getType() {
885             return BACKING_ALLOCATION;
886         }
887 
888         @Override
shouldCache()889         public boolean shouldCache() {
890             return true;
891         }
892 
893         @Override
destroy()894         public void destroy() {
895             if (mAllocation != null) {
896                 mAllocation.destroy();
897                 mAllocation = null;
898             }
899         }
900 
901         @Override
getSize()902         public int getSize() {
903             int elementCount = 1;
904             for (int dim : mDimensions) {
905                 elementCount *= dim;
906             }
907             return getElementSize() * elementCount;
908         }
909 
isSupported()910         public static boolean isSupported() {
911             return Build.VERSION.SDK_INT >= 11;
912         }
913 
assertCompatible(FrameType type)914         private void assertCompatible(FrameType type) {
915             // TODO: consider adding support for other data types.
916             if (type.getElementId() != FrameType.ELEMENT_RGBA8888
917                     && type.getElementId() != FrameType.ELEMENT_FLOAT32) {
918                 throw new RuntimeException(
919                         "Cannot allocate allocation with a non-RGBA or non-float data type!");
920             }
921             if (mDimensions == null || mDimensions.length > 2) {
922                 throw new RuntimeException(
923                         "Cannot create an allocation with more than 2 dimensions!");
924             }
925         }
926 
927     }
928 
929 }
930