| /* |
| * 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 android.filterfw.core; |
| |
| import android.filterfw.core.Frame; |
| import android.filterfw.core.FrameFormat; |
| import android.filterfw.core.FrameManager; |
| import android.filterfw.core.GLFrame; |
| import android.filterfw.core.NativeBuffer; |
| import android.graphics.Bitmap; |
| |
| import java.nio.ByteBuffer; |
| |
| /** |
| * @hide |
| */ |
| public class NativeFrame extends Frame { |
| |
| private int nativeFrameId = -1; |
| |
| NativeFrame(FrameFormat format, FrameManager frameManager) { |
| super(format, frameManager); |
| int capacity = format.getSize(); |
| nativeAllocate(capacity); |
| setReusable(capacity != 0); |
| } |
| |
| @Override |
| protected synchronized void releaseNativeAllocation() { |
| nativeDeallocate(); |
| nativeFrameId = -1; |
| } |
| |
| @Override |
| protected synchronized boolean hasNativeAllocation() { |
| return nativeFrameId != -1; |
| } |
| |
| @Override |
| public int getCapacity() { |
| return getNativeCapacity(); |
| } |
| |
| /** |
| * Returns the native frame's Object value. |
| * |
| * If the frame's base-type is not TYPE_OBJECT, this returns a data buffer containing the native |
| * data (this is equivalent to calling getData(). |
| * If the frame is based on an object type, this type is expected to be a subclass of |
| * NativeBuffer. The NativeBuffer returned is only valid for as long as the frame is alive. If |
| * you need to hold on to the returned value, you must retain it. |
| */ |
| @Override |
| public Object getObjectValue() { |
| // If this is not a structured frame, return our data |
| if (getFormat().getBaseType() != FrameFormat.TYPE_OBJECT) { |
| return getData(); |
| } |
| |
| // Get the structure class |
| Class structClass = getFormat().getObjectClass(); |
| if (structClass == null) { |
| throw new RuntimeException("Attempting to get object data from frame that does " + |
| "not specify a structure object class!"); |
| } |
| |
| // Make sure it is a NativeBuffer subclass |
| if (!NativeBuffer.class.isAssignableFrom(structClass)) { |
| throw new RuntimeException("NativeFrame object class must be a subclass of " + |
| "NativeBuffer!"); |
| } |
| |
| // Instantiate a new empty instance of this class |
| NativeBuffer structData = null; |
| try { |
| structData = (NativeBuffer)structClass.newInstance(); |
| } catch (Exception e) { |
| throw new RuntimeException("Could not instantiate new structure instance of type '" + |
| structClass + "'!"); |
| } |
| |
| // Wrap it around our data |
| if (!getNativeBuffer(structData)) { |
| throw new RuntimeException("Could not get the native structured data for frame!"); |
| } |
| |
| // Attach this frame to it |
| structData.attachToFrame(this); |
| |
| return structData; |
| } |
| |
| @Override |
| public void setInts(int[] ints) { |
| assertFrameMutable(); |
| if (ints.length * nativeIntSize() > getFormat().getSize()) { |
| throw new RuntimeException( |
| "NativeFrame cannot hold " + ints.length + " integers. (Can only hold " + |
| (getFormat().getSize() / nativeIntSize()) + " integers)."); |
| } else if (!setNativeInts(ints)) { |
| throw new RuntimeException("Could not set int values for native frame!"); |
| } |
| } |
| |
| @Override |
| public int[] getInts() { |
| return getNativeInts(getFormat().getSize()); |
| } |
| |
| @Override |
| public void setFloats(float[] floats) { |
| assertFrameMutable(); |
| if (floats.length * nativeFloatSize() > getFormat().getSize()) { |
| throw new RuntimeException( |
| "NativeFrame cannot hold " + floats.length + " floats. (Can only hold " + |
| (getFormat().getSize() / nativeFloatSize()) + " floats)."); |
| } else if (!setNativeFloats(floats)) { |
| throw new RuntimeException("Could not set int values for native frame!"); |
| } |
| } |
| |
| @Override |
| public float[] getFloats() { |
| return getNativeFloats(getFormat().getSize()); |
| } |
| |
| // TODO: This function may be a bit confusing: Is the offset the target or source offset? Maybe |
| // we should allow specifying both? (May be difficult for other frame types). |
| @Override |
| public void setData(ByteBuffer buffer, int offset, int length) { |
| assertFrameMutable(); |
| byte[] bytes = buffer.array(); |
| if ((length + offset) > buffer.limit()) { |
| throw new RuntimeException("Offset and length exceed buffer size in native setData: " + |
| (length + offset) + " bytes given, but only " + buffer.limit() + |
| " bytes available!"); |
| } else if (getFormat().getSize() != length) { |
| throw new RuntimeException("Data size in setData does not match native frame size: " + |
| "Frame size is " + getFormat().getSize() + " bytes, but " + |
| length + " bytes given!"); |
| } else if (!setNativeData(bytes, offset, length)) { |
| throw new RuntimeException("Could not set native frame data!"); |
| } |
| } |
| |
| @Override |
| public ByteBuffer getData() { |
| byte[] data = getNativeData(getFormat().getSize()); |
| return data == null ? null : ByteBuffer.wrap(data); |
| } |
| |
| @Override |
| public void setBitmap(Bitmap bitmap) { |
| assertFrameMutable(); |
| if (getFormat().getNumberOfDimensions() != 2) { |
| throw new RuntimeException("Attempting to set Bitmap for non 2-dimensional native frame!"); |
| } else if (getFormat().getWidth() != bitmap.getWidth() || |
| getFormat().getHeight() != bitmap.getHeight()) { |
| throw new RuntimeException("Bitmap dimensions do not match native frame dimensions!"); |
| } else { |
| Bitmap rgbaBitmap = convertBitmapToRGBA(bitmap); |
| int byteCount = rgbaBitmap.getByteCount(); |
| int bps = getFormat().getBytesPerSample(); |
| if (!setNativeBitmap(rgbaBitmap, byteCount, bps)) { |
| throw new RuntimeException("Could not set native frame bitmap data!"); |
| } |
| } |
| } |
| |
| @Override |
| public Bitmap getBitmap() { |
| if (getFormat().getNumberOfDimensions() != 2) { |
| throw new RuntimeException("Attempting to get Bitmap for non 2-dimensional native frame!"); |
| } |
| Bitmap result = Bitmap.createBitmap(getFormat().getWidth(), |
| getFormat().getHeight(), |
| Bitmap.Config.ARGB_8888); |
| int byteCount = result.getByteCount(); |
| int bps = getFormat().getBytesPerSample(); |
| if (!getNativeBitmap(result, byteCount, bps)) { |
| throw new RuntimeException("Could not get bitmap data from native frame!"); |
| } |
| return result; |
| } |
| |
| @Override |
| public void setDataFromFrame(Frame frame) { |
| // Make sure frame fits |
| if (getFormat().getSize() < frame.getFormat().getSize()) { |
| throw new RuntimeException( |
| "Attempting to assign frame of size " + frame.getFormat().getSize() + " to " + |
| "smaller native frame of size " + getFormat().getSize() + "!"); |
| } |
| |
| // Invoke optimized implementations if possible |
| if (frame instanceof NativeFrame) { |
| nativeCopyFromNative((NativeFrame)frame); |
| } else if (frame instanceof GLFrame) { |
| nativeCopyFromGL((GLFrame)frame); |
| } else if (frame instanceof SimpleFrame) { |
| setObjectValue(frame.getObjectValue()); |
| } else { |
| super.setDataFromFrame(frame); |
| } |
| } |
| |
| @Override |
| public String toString() { |
| return "NativeFrame id: " + nativeFrameId + " (" + getFormat() + ") of size " |
| + getCapacity(); |
| } |
| |
| static { |
| System.loadLibrary("filterfw"); |
| } |
| |
| private native boolean nativeAllocate(int capacity); |
| |
| private native boolean nativeDeallocate(); |
| |
| private native int getNativeCapacity(); |
| |
| private static native int nativeIntSize(); |
| |
| private static native int nativeFloatSize(); |
| |
| private native boolean setNativeData(byte[] data, int offset, int length); |
| |
| private native byte[] getNativeData(int byteCount); |
| |
| private native boolean getNativeBuffer(NativeBuffer buffer); |
| |
| private native boolean setNativeInts(int[] ints); |
| |
| private native boolean setNativeFloats(float[] floats); |
| |
| private native int[] getNativeInts(int byteCount); |
| |
| private native float[] getNativeFloats(int byteCount); |
| |
| private native boolean setNativeBitmap(Bitmap bitmap, int size, int bytesPerSample); |
| |
| private native boolean getNativeBitmap(Bitmap bitmap, int size, int bytesPerSample); |
| |
| private native boolean nativeCopyFromNative(NativeFrame frame); |
| |
| private native boolean nativeCopyFromGL(GLFrame frame); |
| } |