| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
| * in compliance with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software distributed under the License |
| * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
| * or implied. See the License for the specific language governing permissions and limitations under |
| * the License. |
| */ |
| |
| package androidx.media.filterfw; |
| |
| import android.annotation.TargetApi; |
| import android.graphics.Bitmap; |
| import android.os.Build; |
| import android.renderscript.Allocation; |
| import android.renderscript.Element; |
| import android.renderscript.RenderScript; |
| import android.renderscript.Type; |
| import android.util.Log; |
| |
| import java.nio.ByteBuffer; |
| import java.nio.ByteOrder; |
| import java.util.Arrays; |
| import java.util.Vector; |
| |
| final class BackingStore { |
| |
| /** Access mode None: Frame data will not be accessed at all. */ |
| static final int ACCESS_NONE = 0x00; |
| /** Access mode Bytes: Frame data will be accessed as a ByteBuffer. */ |
| static final int ACCESS_BYTES = 0x01; |
| /** Access mode Texture: Frame data will be accessed as a TextureSource. */ |
| static final int ACCESS_TEXTURE = 0x02; |
| /** Access mode RenderTarget: Frame data will be accessed as a RenderTarget. */ |
| static final int ACCESS_RENDERTARGET = 0x04; |
| /** Access mode Object: Frame data will be accessed as a generic Object. */ |
| static final int ACCESS_OBJECT = 0x08; |
| /** Access mode Bitmap: Frame data will be accessed as a Bitmap. */ |
| static final int ACCESS_BITMAP = 0x10; |
| /** Access mode Allocation: Frame data will be accessed as a RenderScript Allocation. */ |
| static final int ACCESS_ALLOCATION = 0x20; |
| |
| private static final int BACKING_BYTEBUFFER = 1; |
| private static final int BACKING_TEXTURE = 2; |
| private static final int BACKING_OBJECT = 3; |
| private static final int BACKING_BITMAP = 4; |
| private static final int BACKING_ALLOCATION = 5; |
| |
| private final FrameType mType; |
| private int[] mDimensions; |
| private long mTimestamp = Frame.TIMESTAMP_NOT_SET; |
| |
| private final FrameManager mFrameManager; |
| |
| private Vector<Backing> mBackings = new Vector<Backing>(); |
| |
| private boolean mWriteLocked = false; |
| private int mReadLocks = 0; |
| |
| private int mRefCount = 1; |
| |
| /** The most up-to-date data backing */ |
| private Backing mCurrentBacking = null; |
| |
| /** The currently locked backing */ |
| private Backing mLockedBacking = null; |
| |
| // Public Methods ////////////////////////////////////////////////////////////////////////////// |
| public BackingStore(FrameType type, int[] dimensions, FrameManager frameManager) { |
| mType = type; |
| mDimensions = dimensions != null ? Arrays.copyOf(dimensions, dimensions.length) : null; |
| mFrameManager = frameManager; |
| } |
| |
| public FrameType getFrameType() { |
| return mType; |
| } |
| |
| public Object lockData(int mode, int accessFormat) { |
| return lockBacking(mode, accessFormat).lock(accessFormat); |
| } |
| |
| public Backing lockBacking(int mode, int access) { |
| Backing backing = fetchBacking(mode, access); |
| if (backing == null) { |
| throw new RuntimeException("Could not fetch frame data!"); |
| } |
| lock(backing, mode); |
| return backing; |
| } |
| |
| public boolean unlock() { |
| if (mWriteLocked) { |
| mWriteLocked = false; |
| } else if (mReadLocks > 0) { |
| --mReadLocks; |
| } else { |
| return false; |
| } |
| mLockedBacking.unlock(); |
| mLockedBacking = null; |
| return true; |
| } |
| |
| public BackingStore retain() { |
| if (mRefCount >= 10) { |
| Log.w("BackingStore", "High ref-count of " + mRefCount + " on " + this + "!"); |
| } |
| if (mRefCount <= 0) { |
| throw new RuntimeException("RETAINING RELEASED"); |
| } |
| ++mRefCount; |
| return this; |
| } |
| |
| public BackingStore release() { |
| if (mRefCount <= 0) { |
| throw new RuntimeException("DOUBLE-RELEASE"); |
| } |
| --mRefCount; |
| if (mRefCount == 0) { |
| releaseBackings(); |
| return null; |
| } |
| return this; |
| } |
| |
| /** |
| * Resizes the backing store. This invalidates all data in the store. |
| */ |
| public void resize(int[] newDimensions) { |
| Vector<Backing> resized = new Vector<Backing>(); |
| for (Backing backing : mBackings) { |
| if (backing.resize(newDimensions)) { |
| resized.add(backing); |
| } else { |
| releaseBacking(backing); |
| } |
| } |
| mBackings = resized; |
| mDimensions = newDimensions; |
| } |
| |
| public int[] getDimensions() { |
| return mDimensions; |
| } |
| |
| public int getElementCount() { |
| int result = 1; |
| if (mDimensions != null) { |
| for (int dim : mDimensions) { |
| result *= dim; |
| } |
| } |
| return result; |
| } |
| |
| public void importStore(BackingStore store) { |
| // TODO: Better backing selection? |
| if (store.mBackings.size() > 0) { |
| importBacking(store.mBackings.firstElement()); |
| } |
| mTimestamp = store.mTimestamp; |
| } |
| |
| /** |
| * @return the timestamp |
| */ |
| public long getTimestamp() { |
| return mTimestamp; |
| } |
| |
| /** |
| * @param timestamp the timestamp to set |
| */ |
| public void setTimestamp(long timestamp) { |
| mTimestamp = timestamp; |
| } |
| |
| // Internal Methods //////////////////////////////////////////////////////////////////////////// |
| private Backing fetchBacking(int mode, int access) { |
| Backing backing = getBacking(mode, access); |
| if (backing == null) { |
| backing = attachNewBacking(mode, access); |
| } |
| syncBacking(backing); |
| return backing; |
| } |
| |
| private void syncBacking(Backing backing) { |
| if (backing != null && backing.isDirty() && mCurrentBacking != null) { |
| backing.syncTo(mCurrentBacking); |
| } |
| } |
| |
| private Backing getBacking(int mode, int access) { |
| // [Non-iterator looping] |
| for (int i = 0; i < mBackings.size(); ++i) { |
| final Backing backing = mBackings.get(i); |
| |
| int backingAccess = |
| (mode == Frame.MODE_WRITE) ? backing.writeAccess() : backing.readAccess(); |
| if ((backingAccess & access) == access) { |
| return backing; |
| } |
| } |
| return null; |
| } |
| |
| private Backing attachNewBacking(int mode, int access) { |
| Backing backing = createBacking(mode, access); |
| if (mBackings.size() > 0) { |
| backing.markDirty(); |
| } |
| mBackings.add(backing); |
| return backing; |
| } |
| |
| private Backing createBacking(int mode, int access) { |
| // TODO: If the read/write access flags indicate, make/fetch a GraphicBuffer backing. |
| Backing backing = null; |
| int elemSize = mType.getElementSize(); |
| if (shouldFetchCached(access)) { |
| backing = mFrameManager.fetchBacking(mode, access, mDimensions, elemSize); |
| } |
| if (backing == null) { |
| switch (access) { |
| case ACCESS_BYTES: |
| backing = new ByteBufferBacking(); |
| break; |
| case ACCESS_TEXTURE: |
| case ACCESS_RENDERTARGET: |
| backing = new TextureBacking(); |
| break; |
| case ACCESS_OBJECT: |
| backing = new ObjectBacking(); |
| break; |
| case ACCESS_BITMAP: |
| backing = new BitmapBacking(); |
| break; |
| case ACCESS_ALLOCATION: |
| if (!AllocationBacking.isSupported()) { |
| throw new RuntimeException( |
| "Attempted to create an AllocationBacking in context that does " + |
| "not support RenderScript!"); |
| } |
| backing = new AllocationBacking(mFrameManager.getContext().getRenderScript()); |
| break; |
| } |
| if (backing == null) { |
| throw new RuntimeException( |
| "Could not create backing for access type " + access + "!"); |
| } |
| if (backing.requiresGpu() && !mFrameManager.getRunner().isOpenGLSupported()) { |
| throw new RuntimeException( |
| "Cannot create backing that requires GPU in a runner that does not " + |
| "support OpenGL!"); |
| } |
| backing.setDimensions(mDimensions); |
| backing.setElementSize(elemSize); |
| backing.setElementId(mType.getElementId()); |
| backing.allocate(mType); |
| mFrameManager.onBackingCreated(backing); |
| } |
| return backing; |
| } |
| |
| private void importBacking(Backing backing) { |
| // TODO: This actually needs synchronization between the two BackingStore threads for the |
| // general case |
| int access = backing.requiresGpu() ? ACCESS_BYTES : backing.readAccess(); |
| Backing newBacking = createBacking(Frame.MODE_READ, access); |
| newBacking.syncTo(backing); |
| mBackings.add(newBacking); |
| mCurrentBacking = newBacking; |
| } |
| |
| private void releaseBackings() { |
| // [Non-iterator looping] |
| for (int i = 0; i < mBackings.size(); ++i) { |
| releaseBacking(mBackings.get(i)); |
| } |
| mBackings.clear(); |
| mCurrentBacking = null; |
| } |
| |
| private void releaseBacking(Backing backing) { |
| mFrameManager.onBackingAvailable(backing); |
| } |
| |
| private void lock(Backing backingToLock, int mode) { |
| if (mode == Frame.MODE_WRITE) { |
| // Make sure frame is not read-locked |
| if (mReadLocks > 0) { |
| throw new RuntimeException( |
| "Attempting to write-lock the read-locked frame " + this + "!"); |
| } else if (mWriteLocked) { |
| throw new RuntimeException( |
| "Attempting to write-lock the write-locked frame " + this + "!"); |
| } |
| // Mark all other backings dirty |
| // [Non-iterator looping] |
| for (int i = 0; i < mBackings.size(); ++i) { |
| final Backing backing = mBackings.get(i); |
| if (backing != backingToLock) { |
| backing.markDirty(); |
| } |
| } |
| mWriteLocked = true; |
| mCurrentBacking = backingToLock; |
| } else { |
| if (mWriteLocked) { |
| throw new RuntimeException("Attempting to read-lock locked frame " + this + "!"); |
| } |
| ++mReadLocks; |
| } |
| mLockedBacking = backingToLock; |
| } |
| |
| private static boolean shouldFetchCached(int access) { |
| return access != ACCESS_OBJECT; |
| } |
| |
| |
| // Backings //////////////////////////////////////////////////////////////////////////////////// |
| static abstract class Backing { |
| protected int[] mDimensions = null; |
| private int mElementSize; |
| private int mElementID; |
| protected boolean mIsDirty = false; |
| |
| int cachePriority = 0; |
| |
| public abstract void allocate(FrameType frameType); |
| |
| public abstract int readAccess(); |
| |
| public abstract int writeAccess(); |
| |
| public abstract void syncTo(Backing backing); |
| |
| public abstract Object lock(int accessType); |
| |
| public abstract int getType(); |
| |
| public abstract boolean shouldCache(); |
| |
| public abstract boolean requiresGpu(); |
| |
| public abstract void destroy(); |
| |
| public abstract int getSize(); |
| |
| public void unlock() { |
| // Default implementation does nothing. |
| } |
| |
| public void setData(Object data) { |
| throw new RuntimeException("Internal error: Setting data on frame backing " + this |
| + ", which does not support setting data directly!"); |
| } |
| |
| public void setDimensions(int[] dimensions) { |
| mDimensions = dimensions; |
| } |
| |
| public void setElementSize(int elemSize) { |
| mElementSize = elemSize; |
| } |
| |
| public void setElementId(int elemId) { |
| mElementID = elemId; |
| } |
| |
| public int[] getDimensions() { |
| return mDimensions; |
| } |
| |
| public int getElementSize() { |
| return mElementSize; |
| } |
| |
| public int getElementId() { |
| return mElementID; |
| } |
| |
| public boolean resize(int[] newDimensions) { |
| return false; |
| } |
| |
| public void markDirty() { |
| mIsDirty = true; |
| } |
| |
| public boolean isDirty() { |
| return mIsDirty; |
| } |
| |
| protected void assertImageCompatible(FrameType type) { |
| if (type.getElementId() != FrameType.ELEMENT_RGBA8888) { |
| throw new RuntimeException("Cannot allocate texture with non-RGBA data type!"); |
| } else if (mDimensions == null || mDimensions.length != 2) { |
| throw new RuntimeException("Cannot allocate non 2-dimensional texture!"); |
| } |
| } |
| |
| } |
| |
| static class ObjectBacking extends Backing { |
| |
| private Object mObject = null; |
| |
| @Override |
| public void allocate(FrameType frameType) { |
| mObject = null; |
| } |
| |
| @Override |
| public int readAccess() { |
| return ACCESS_OBJECT; |
| } |
| |
| @Override |
| public int writeAccess() { |
| return ACCESS_OBJECT; |
| } |
| |
| @Override |
| public void syncTo(Backing backing) { |
| switch (backing.getType()) { |
| case BACKING_OBJECT: |
| mObject = backing.lock(ACCESS_OBJECT); |
| backing.unlock(); |
| break; |
| case BACKING_BITMAP: |
| mObject = backing.lock(ACCESS_BITMAP); |
| backing.unlock(); |
| break; |
| default: |
| mObject = null; |
| } |
| mIsDirty = false; |
| } |
| |
| @Override |
| public Object lock(int accessType) { |
| return mObject; |
| } |
| |
| @Override |
| public int getType() { |
| return BACKING_OBJECT; |
| } |
| |
| @Override |
| public boolean shouldCache() { |
| return false; |
| } |
| |
| @Override |
| public boolean requiresGpu() { |
| return false; |
| } |
| |
| @Override |
| public void destroy() { |
| mObject = null; |
| } |
| |
| @Override |
| public int getSize() { |
| return 0; |
| } |
| |
| @Override |
| public void setData(Object data) { |
| mObject = data; |
| } |
| |
| } |
| |
| static class BitmapBacking extends Backing { |
| |
| private Bitmap mBitmap = null; |
| |
| @Override |
| public void allocate(FrameType frameType) { |
| assertImageCompatible(frameType); |
| } |
| |
| @Override |
| public int readAccess() { |
| return ACCESS_BITMAP; |
| } |
| |
| @Override |
| public int writeAccess() { |
| return ACCESS_BITMAP; |
| } |
| |
| @Override |
| public void syncTo(Backing backing) { |
| int access = backing.readAccess(); |
| if ((access & ACCESS_BITMAP) != 0) { |
| mBitmap = (Bitmap) backing.lock(ACCESS_BITMAP); |
| } else if ((access & ACCESS_BYTES) != 0) { |
| createBitmap(); |
| ByteBuffer buffer = (ByteBuffer) backing.lock(ACCESS_BYTES); |
| mBitmap.copyPixelsFromBuffer(buffer); |
| buffer.rewind(); |
| } else if ((access & ACCESS_TEXTURE) != 0) { |
| createBitmap(); |
| RenderTarget renderTarget = (RenderTarget) backing.lock(ACCESS_RENDERTARGET); |
| mBitmap.copyPixelsFromBuffer( |
| renderTarget.getPixelData(mDimensions[0], mDimensions[1])); |
| } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) { |
| createBitmap(); |
| syncToAllocationBacking(backing); |
| } else { |
| throw new RuntimeException("Cannot sync bytebuffer backing!"); |
| } |
| backing.unlock(); |
| mIsDirty = false; |
| } |
| |
| @TargetApi(11) |
| private void syncToAllocationBacking(Backing backing) { |
| Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION); |
| allocation.copyTo(mBitmap); |
| } |
| |
| @Override |
| public Object lock(int accessType) { |
| return mBitmap; |
| } |
| |
| @Override |
| public int getType() { |
| return BACKING_BITMAP; |
| } |
| |
| @Override |
| public boolean shouldCache() { |
| return false; |
| } |
| |
| @Override |
| public boolean requiresGpu() { |
| return false; |
| } |
| |
| @Override |
| public void destroy() { |
| // As we share the bitmap with other backings (such as object backings), we must not |
| // recycle it here. |
| mBitmap = null; |
| } |
| |
| @Override |
| public int getSize() { |
| return 4 * mDimensions[0] * mDimensions[1]; |
| } |
| |
| @Override |
| public void setData(Object data) { |
| // We can assume that data will always be a Bitmap instance. |
| mBitmap = (Bitmap) data; |
| } |
| |
| private void createBitmap() { |
| mBitmap = Bitmap.createBitmap(mDimensions[0], mDimensions[1], Bitmap.Config.ARGB_8888); |
| } |
| } |
| |
| static class TextureBacking extends Backing { |
| |
| private RenderTarget mRenderTarget = null; |
| private TextureSource mTexture = null; |
| |
| @Override |
| public void allocate(FrameType frameType) { |
| assertImageCompatible(frameType); |
| mTexture = TextureSource.newTexture(); |
| } |
| |
| @Override |
| public int readAccess() { |
| return ACCESS_TEXTURE; |
| } |
| |
| @Override |
| public int writeAccess() { |
| return ACCESS_RENDERTARGET; |
| } |
| |
| @Override |
| public void syncTo(Backing backing) { |
| int access = backing.readAccess(); |
| if ((access & ACCESS_BYTES) != 0) { |
| ByteBuffer pixels = (ByteBuffer) backing.lock(ACCESS_BYTES); |
| mTexture.allocateWithPixels(pixels, mDimensions[0], mDimensions[1]); |
| } else if ((access & ACCESS_BITMAP) != 0) { |
| Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP); |
| mTexture.allocateWithBitmapPixels(bitmap); |
| } else if ((access & ACCESS_TEXTURE) != 0) { |
| TextureSource texture = (TextureSource) backing.lock(ACCESS_TEXTURE); |
| int w = mDimensions[0]; |
| int h = mDimensions[1]; |
| ImageShader.renderTextureToTarget(texture, getRenderTarget(), w, h); |
| } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) { |
| syncToAllocationBacking(backing); |
| } else { |
| throw new RuntimeException("Cannot sync bytebuffer backing!"); |
| } |
| backing.unlock(); |
| mIsDirty = false; |
| } |
| |
| @TargetApi(11) |
| private void syncToAllocationBacking(Backing backing) { |
| Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION); |
| ByteBuffer pixels = ByteBuffer.allocateDirect(getSize()); |
| allocation.copyTo(pixels.array()); |
| mTexture.allocateWithPixels(pixels, mDimensions[0], mDimensions[1]); |
| } |
| |
| @Override |
| public Object lock(int accessType) { |
| switch (accessType) { |
| case ACCESS_TEXTURE: |
| return getTexture(); |
| |
| case ACCESS_RENDERTARGET: |
| return getRenderTarget(); |
| |
| default: |
| throw new RuntimeException("Illegal access to texture!"); |
| } |
| } |
| |
| @Override |
| public int getType() { |
| return BACKING_TEXTURE; |
| } |
| |
| @Override |
| public boolean shouldCache() { |
| return true; |
| } |
| |
| @Override |
| public boolean requiresGpu() { |
| return true; |
| } |
| |
| @Override |
| public void destroy() { |
| if (mRenderTarget != null) { |
| mRenderTarget.release(); |
| } |
| if (mTexture.isAllocated()) { |
| mTexture.release(); |
| } |
| } |
| |
| @Override |
| public int getSize() { |
| return 4 * mDimensions[0] * mDimensions[1]; |
| } |
| |
| private TextureSource getTexture() { |
| if (!mTexture.isAllocated()) { |
| mTexture.allocate(mDimensions[0], mDimensions[1]); |
| } |
| return mTexture; |
| } |
| |
| private RenderTarget getRenderTarget() { |
| if (mRenderTarget == null) { |
| int w = mDimensions[0]; |
| int h = mDimensions[1]; |
| mRenderTarget = RenderTarget.currentTarget().forTexture(getTexture(), w, h); |
| } |
| return mRenderTarget; |
| } |
| |
| } |
| |
| static class ByteBufferBacking extends Backing { |
| |
| ByteBuffer mBuffer = null; |
| |
| @Override |
| public void allocate(FrameType frameType) { |
| int size = frameType.getElementSize(); |
| for (int dim : mDimensions) { |
| size *= dim; |
| } |
| mBuffer = ByteBuffer.allocateDirect(size); |
| } |
| |
| @Override |
| public int readAccess() { |
| return ACCESS_BYTES; |
| } |
| |
| @Override |
| public int writeAccess() { |
| return ACCESS_BYTES; |
| } |
| |
| @Override |
| public boolean requiresGpu() { |
| return false; |
| } |
| |
| @Override |
| public void syncTo(Backing backing) { |
| int access = backing.readAccess(); |
| if ((access & ACCESS_TEXTURE) != 0) { |
| RenderTarget target = (RenderTarget) backing.lock(ACCESS_RENDERTARGET); |
| GLToolbox.readTarget(target, mBuffer, mDimensions[0], mDimensions[1]); |
| } else if ((access & ACCESS_BITMAP) != 0) { |
| Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP); |
| bitmap.copyPixelsToBuffer(mBuffer); |
| mBuffer.rewind(); |
| } else if ((access & ACCESS_BYTES) != 0) { |
| ByteBuffer otherBuffer = (ByteBuffer) backing.lock(ACCESS_BYTES); |
| mBuffer.put(otherBuffer); |
| otherBuffer.rewind(); |
| } else if ((access & ACCESS_ALLOCATION) != 0 && AllocationBacking.isSupported()) { |
| syncToAllocationBacking(backing); |
| } else { |
| throw new RuntimeException("Cannot sync bytebuffer backing!"); |
| } |
| backing.unlock(); |
| mBuffer.rewind(); |
| mIsDirty = false; |
| } |
| |
| @TargetApi(11) |
| private void syncToAllocationBacking(Backing backing) { |
| Allocation allocation = (Allocation) backing.lock(ACCESS_ALLOCATION); |
| if (getElementId() == FrameType.ELEMENT_RGBA8888) { |
| byte[] bytes = mBuffer.array(); |
| allocation.copyTo(bytes); |
| } else if (getElementId() == FrameType.ELEMENT_FLOAT32) { |
| float[] floats = new float[getSize() / 4]; |
| allocation.copyTo(floats); |
| mBuffer.asFloatBuffer().put(floats); |
| } else { |
| throw new RuntimeException( |
| "Trying to sync to an allocation with an unsupported element id: " |
| + getElementId()); |
| } |
| } |
| |
| @Override |
| public Object lock(int accessType) { |
| return mBuffer.rewind(); |
| } |
| |
| @Override |
| public void unlock() { |
| mBuffer.rewind(); |
| } |
| |
| @Override |
| public int getType() { |
| return BACKING_BYTEBUFFER; |
| } |
| |
| @Override |
| public boolean shouldCache() { |
| return true; |
| } |
| |
| @Override |
| public void destroy() { |
| mBuffer = null; |
| } |
| |
| @Override |
| public int getSize() { |
| return mBuffer.remaining(); |
| } |
| |
| } |
| |
| @TargetApi(11) |
| static class AllocationBacking extends Backing { |
| |
| private final RenderScript mRenderScript; |
| private Allocation mAllocation = null; |
| |
| public AllocationBacking(RenderScript renderScript) { |
| mRenderScript = renderScript; |
| } |
| |
| @Override |
| public void allocate(FrameType frameType) { |
| assertCompatible(frameType); |
| |
| Element element = null; |
| switch (frameType.getElementId()) { |
| case FrameType.ELEMENT_RGBA8888: |
| element = Element.RGBA_8888(mRenderScript); |
| break; |
| case FrameType.ELEMENT_FLOAT32: |
| element = Element.F32(mRenderScript); |
| break; |
| } |
| Type.Builder imageTypeBuilder = new Type.Builder(mRenderScript, element); |
| imageTypeBuilder.setX(mDimensions.length >= 1 ? mDimensions[0] : 1); |
| imageTypeBuilder.setY(mDimensions.length == 2 ? mDimensions[1] : 1); |
| Type imageType = imageTypeBuilder.create(); |
| |
| mAllocation = Allocation.createTyped(mRenderScript, imageType); |
| } |
| |
| @Override |
| public int readAccess() { |
| return ACCESS_ALLOCATION; |
| } |
| |
| @Override |
| public int writeAccess() { |
| return ACCESS_ALLOCATION; |
| } |
| |
| @Override |
| public boolean requiresGpu() { |
| return false; |
| } |
| |
| @Override |
| public void syncTo(Backing backing) { |
| int access = backing.readAccess(); |
| if ((access & ACCESS_TEXTURE) != 0) { |
| RenderTarget target = (RenderTarget) backing.lock(ACCESS_RENDERTARGET); |
| ByteBuffer pixels = ByteBuffer.allocateDirect(getSize()); |
| GLToolbox.readTarget(target, pixels, mDimensions[0], mDimensions[1]); |
| mAllocation.copyFrom(pixels.array()); |
| } else if ((access & ACCESS_BITMAP) != 0) { |
| Bitmap bitmap = (Bitmap) backing.lock(ACCESS_BITMAP); |
| mAllocation.copyFrom(bitmap); |
| } else if ((access & ACCESS_BYTES) != 0) { |
| ByteBuffer buffer = (ByteBuffer) backing.lock(ACCESS_BYTES); |
| if (buffer.order() != ByteOrder.nativeOrder()) { |
| throw new RuntimeException( |
| "Trying to sync to the ByteBufferBacking with non-native byte order!"); |
| } |
| byte[] bytes; |
| if (buffer.hasArray()) { |
| bytes = buffer.array(); |
| } else { |
| bytes = new byte[getSize()]; |
| buffer.get(bytes); |
| buffer.rewind(); |
| } |
| mAllocation.copyFromUnchecked(bytes); |
| } else { |
| throw new RuntimeException("Cannot sync allocation backing!"); |
| } |
| backing.unlock(); |
| mIsDirty = false; |
| } |
| |
| @Override |
| public Object lock(int accessType) { |
| return mAllocation; |
| } |
| |
| @Override |
| public void unlock() { |
| } |
| |
| @Override |
| public int getType() { |
| return BACKING_ALLOCATION; |
| } |
| |
| @Override |
| public boolean shouldCache() { |
| return true; |
| } |
| |
| @Override |
| public void destroy() { |
| if (mAllocation != null) { |
| mAllocation.destroy(); |
| mAllocation = null; |
| } |
| } |
| |
| @Override |
| public int getSize() { |
| int elementCount = 1; |
| for (int dim : mDimensions) { |
| elementCount *= dim; |
| } |
| return getElementSize() * elementCount; |
| } |
| |
| public static boolean isSupported() { |
| return Build.VERSION.SDK_INT >= 11; |
| } |
| |
| private void assertCompatible(FrameType type) { |
| // TODO: consider adding support for other data types. |
| if (type.getElementId() != FrameType.ELEMENT_RGBA8888 |
| && type.getElementId() != FrameType.ELEMENT_FLOAT32) { |
| throw new RuntimeException( |
| "Cannot allocate allocation with a non-RGBA or non-float data type!"); |
| } |
| if (mDimensions == null || mDimensions.length > 2) { |
| throw new RuntimeException( |
| "Cannot create an allocation with more than 2 dimensions!"); |
| } |
| } |
| |
| } |
| |
| } |