Home | History | Annotate | Download | only in filterfw
      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 //////////////////////////////////////////////////////////////////////////////
     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 
     80     public FrameType getFrameType() {
     81         return mType;
     82     }
     83 
     84     public Object lockData(int mode, int accessFormat) {
     85         return lockBacking(mode, accessFormat).lock(accessFormat);
     86     }
     87 
     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 
     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 
    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 
    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      */
    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 
    149     public int[] getDimensions() {
    150         return mDimensions;
    151     }
    152 
    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 
    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      */
    174     public long getTimestamp() {
    175         return mTimestamp;
    176     }
    177 
    178     /**
    179      * @param timestamp the timestamp to set
    180      */
    181     public void setTimestamp(long timestamp) {
    182         mTimestamp = timestamp;
    183     }
    184 
    185     // Internal Methods ////////////////////////////////////////////////////////////////////////////
    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 
    195     private void syncBacking(Backing backing) {
    196         if (backing != null && backing.isDirty() && mCurrentBacking != null) {
    197             backing.syncTo(mCurrentBacking);
    198         }
    199     }
    200 
    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 
    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 
    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 
    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 
    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 
    292     private void releaseBacking(Backing backing) {
    293         mFrameManager.onBackingAvailable(backing);
    294     }
    295 
    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 
    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 
    339         public abstract void allocate(FrameType frameType);
    340 
    341         public abstract int readAccess();
    342 
    343         public abstract int writeAccess();
    344 
    345         public abstract void syncTo(Backing backing);
    346 
    347         public abstract Object lock(int accessType);
    348 
    349         public abstract int getType();
    350 
    351         public abstract boolean shouldCache();
    352 
    353         public abstract boolean requiresGpu();
    354 
    355         public abstract void destroy();
    356 
    357         public abstract int getSize();
    358 
    359         public void unlock() {
    360             // Default implementation does nothing.
    361         }
    362 
    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 
    368         public void setDimensions(int[] dimensions) {
    369             mDimensions = dimensions;
    370         }
    371 
    372         public void setElementSize(int elemSize) {
    373             mElementSize = elemSize;
    374         }
    375 
    376         public void setElementId(int elemId) {
    377             mElementID = elemId;
    378         }
    379 
    380         public int[] getDimensions() {
    381             return mDimensions;
    382         }
    383 
    384         public int getElementSize() {
    385             return mElementSize;
    386         }
    387 
    388         public int getElementId() {
    389             return mElementID;
    390         }
    391 
    392         public boolean resize(int[] newDimensions) {
    393             return false;
    394         }
    395 
    396         public void markDirty() {
    397             mIsDirty = true;
    398         }
    399 
    400         public boolean isDirty() {
    401             return mIsDirty;
    402         }
    403 
    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
    419         public void allocate(FrameType frameType) {
    420             mObject = null;
    421         }
    422 
    423         @Override
    424         public int readAccess() {
    425             return ACCESS_OBJECT;
    426         }
    427 
    428         @Override
    429         public int writeAccess() {
    430             return ACCESS_OBJECT;
    431         }
    432 
    433         @Override
    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
    451         public Object lock(int accessType) {
    452             return mObject;
    453         }
    454 
    455         @Override
    456         public int getType() {
    457             return BACKING_OBJECT;
    458         }
    459 
    460         @Override
    461         public boolean shouldCache() {
    462             return false;
    463         }
    464 
    465         @Override
    466         public boolean requiresGpu() {
    467             return false;
    468         }
    469 
    470         @Override
    471         public void destroy() {
    472             mObject = null;
    473         }
    474 
    475         @Override
    476         public int getSize() {
    477             return 0;
    478         }
    479 
    480         @Override
    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
    492         public void allocate(FrameType frameType) {
    493             assertImageCompatible(frameType);
    494         }
    495 
    496         @Override
    497         public int readAccess() {
    498             return ACCESS_BITMAP;
    499         }
    500 
    501         @Override
    502         public int writeAccess() {
    503             return ACCESS_BITMAP;
    504         }
    505 
    506         @Override
    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)
    532         private void syncToAllocationBacking(Backing backing) {
    533             Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION);
    534             allocation.copyTo(mBitmap);
    535         }
    536 
    537         @Override
    538         public Object lock(int accessType) {
    539             return mBitmap;
    540         }
    541 
    542         @Override
    543         public int getType() {
    544             return BACKING_BITMAP;
    545         }
    546 
    547         @Override
    548         public boolean shouldCache() {
    549             return false;
    550         }
    551 
    552         @Override
    553         public boolean requiresGpu() {
    554             return false;
    555         }
    556 
    557         @Override
    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
    565         public int getSize() {
    566             return 4 * mDimensions[0] * mDimensions[1];
    567         }
    568 
    569         @Override
    570         public void setData(Object data) {
    571             // We can assume that data will always be a Bitmap instance.
    572             mBitmap = (Bitmap) data;
    573         }
    574 
    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
    586         public void allocate(FrameType frameType) {
    587             assertImageCompatible(frameType);
    588             mTexture = TextureSource.newTexture();
    589         }
    590 
    591         @Override
    592         public int readAccess() {
    593             return ACCESS_TEXTURE;
    594         }
    595 
    596         @Override
    597         public int writeAccess() {
    598             return ACCESS_RENDERTARGET;
    599         }
    600 
    601         @Override
    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)
    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
    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
    647         public int getType() {
    648             return BACKING_TEXTURE;
    649         }
    650 
    651         @Override
    652         public boolean shouldCache() {
    653             return true;
    654         }
    655 
    656         @Override
    657         public boolean requiresGpu() {
    658             return true;
    659         }
    660 
    661         @Override
    662         public void destroy() {
    663             if (mRenderTarget != null) {
    664                 mRenderTarget.release();
    665             }
    666             if (mTexture.isAllocated()) {
    667                 mTexture.release();
    668             }
    669         }
    670 
    671         @Override
    672         public int getSize() {
    673             return 4 * mDimensions[0] * mDimensions[1];
    674         }
    675 
    676         private TextureSource getTexture() {
    677             if (!mTexture.isAllocated()) {
    678                 mTexture.allocate(mDimensions[0], mDimensions[1]);
    679             }
    680             return mTexture;
    681         }
    682 
    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
    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
    708         public int readAccess() {
    709             return ACCESS_BYTES;
    710         }
    711 
    712         @Override
    713         public int writeAccess() {
    714             return ACCESS_BYTES;
    715         }
    716 
    717         @Override
    718         public boolean requiresGpu() {
    719             return false;
    720         }
    721 
    722         @Override
    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)
    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
    764         public Object lock(int accessType) {
    765             return mBuffer.rewind();
    766         }
    767 
    768         @Override
    769         public void unlock() {
    770             mBuffer.rewind();
    771         }
    772 
    773         @Override
    774         public int getType() {
    775             return BACKING_BYTEBUFFER;
    776         }
    777 
    778         @Override
    779         public boolean shouldCache() {
    780             return true;
    781         }
    782 
    783         @Override
    784         public void destroy() {
    785             mBuffer = null;
    786         }
    787 
    788         @Override
    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 
    801         public AllocationBacking(RenderScript renderScript) {
    802             mRenderScript = renderScript;
    803         }
    804 
    805         @Override
    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
    827         public int readAccess() {
    828             return ACCESS_ALLOCATION;
    829         }
    830 
    831         @Override
    832         public int writeAccess() {
    833             return ACCESS_ALLOCATION;
    834         }
    835 
    836         @Override
    837         public boolean requiresGpu() {
    838             return false;
    839         }
    840 
    841         @Override
    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
    875         public Object lock(int accessType) {
    876             return mAllocation;
    877         }
    878 
    879         @Override
    880         public void unlock() {
    881         }
    882 
    883         @Override
    884         public int getType() {
    885             return BACKING_ALLOCATION;
    886         }
    887 
    888         @Override
    889         public boolean shouldCache() {
    890             return true;
    891         }
    892 
    893         @Override
    894         public void destroy() {
    895             if (mAllocation != null) {
    896                 mAllocation.destroy();
    897                 mAllocation = null;
    898             }
    899         }
    900 
    901         @Override
    902         public int getSize() {
    903             int elementCount = 1;
    904             for (int dim : mDimensions) {
    905                 elementCount *= dim;
    906             }
    907             return getElementSize() * elementCount;
    908         }
    909 
    910         public static boolean isSupported() {
    911             return Build.VERSION.SDK_INT >= 11;
    912         }
    913 
    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