/* * 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.NativeFrame; import android.filterfw.core.StopWatchMap; import android.graphics.Bitmap; import android.opengl.GLES20; import android.graphics.Rect; import java.nio.ByteBuffer; class GLFrameTimer { private static StopWatchMap mTimer = null; public static StopWatchMap get() { if (mTimer == null) { mTimer = new StopWatchMap(); } return mTimer; } } /** * @hide */ public class GLFrame extends Frame { // GL-related binding types public final static int EXISTING_TEXTURE_BINDING = 100; public final static int EXISTING_FBO_BINDING = 101; public final static int NEW_TEXTURE_BINDING = 102; // TODO: REMOVE THIS public final static int NEW_FBO_BINDING = 103; // TODO: REMOVE THIS public final static int EXTERNAL_TEXTURE = 104; private int glFrameId = -1; /** * Flag whether we own the texture or not. If we do not, we must be careful when caching or * storing the frame, as the user may delete, and regenerate it. */ private boolean mOwnsTexture = true; /** * Keep a reference to the GL environment, so that it does not get deallocated while there * are still frames living in it. */ private GLEnvironment mGLEnvironment; GLFrame(FrameFormat format, FrameManager frameManager) { super(format, frameManager); } GLFrame(FrameFormat format, FrameManager frameManager, int bindingType, long bindingId) { super(format, frameManager, bindingType, bindingId); } void init(GLEnvironment glEnv) { FrameFormat format = getFormat(); mGLEnvironment = glEnv; // Check that we have a valid format if (format.getBytesPerSample() != 4) { throw new IllegalArgumentException("GL frames must have 4 bytes per sample!"); } else if (format.getDimensionCount() != 2) { throw new IllegalArgumentException("GL frames must be 2-dimensional!"); } else if (getFormat().getSize() < 0) { throw new IllegalArgumentException("Initializing GL frame with zero size!"); } // Create correct frame int bindingType = getBindingType(); boolean reusable = true; if (bindingType == Frame.NO_BINDING) { initNew(false); } else if (bindingType == EXTERNAL_TEXTURE) { initNew(true); reusable = false; } else if (bindingType == EXISTING_TEXTURE_BINDING) { initWithTexture((int)getBindingId()); } else if (bindingType == EXISTING_FBO_BINDING) { initWithFbo((int)getBindingId()); } else if (bindingType == NEW_TEXTURE_BINDING) { initWithTexture((int)getBindingId()); } else if (bindingType == NEW_FBO_BINDING) { initWithFbo((int)getBindingId()); } else { throw new RuntimeException("Attempting to create GL frame with unknown binding type " + bindingType + "!"); } setReusable(reusable); } private void initNew(boolean isExternal) { if (isExternal) { if (!nativeAllocateExternal(mGLEnvironment)) { throw new RuntimeException("Could not allocate external GL frame!"); } } else { if (!nativeAllocate(mGLEnvironment, getFormat().getWidth(), getFormat().getHeight())) { throw new RuntimeException("Could not allocate GL frame!"); } } } private void initWithTexture(int texId) { int width = getFormat().getWidth(); int height = getFormat().getHeight(); if (!nativeAllocateWithTexture(mGLEnvironment, texId, width, height)) { throw new RuntimeException("Could not allocate texture backed GL frame!"); } mOwnsTexture = false; markReadOnly(); } private void initWithFbo(int fboId) { int width = getFormat().getWidth(); int height = getFormat().getHeight(); if (!nativeAllocateWithFbo(mGLEnvironment, fboId, width, height)) { throw new RuntimeException("Could not allocate FBO backed GL frame!"); } } void flushGPU(String message) { StopWatchMap timer = GLFrameTimer.get(); if (timer.LOG_MFF_RUNNING_TIMES) { timer.start("glFinish " + message); GLES20.glFinish(); timer.stop("glFinish " + message); } } @Override protected synchronized boolean hasNativeAllocation() { return glFrameId != -1; } @Override protected synchronized void releaseNativeAllocation() { nativeDeallocate(); glFrameId = -1; } public GLEnvironment getGLEnvironment() { return mGLEnvironment; } @Override public Object getObjectValue() { assertGLEnvValid(); return ByteBuffer.wrap(getNativeData()); } @Override public void setInts(int[] ints) { assertFrameMutable(); assertGLEnvValid(); if (!setNativeInts(ints)) { throw new RuntimeException("Could not set int values for GL frame!"); } } @Override public int[] getInts() { assertGLEnvValid(); flushGPU("getInts"); return getNativeInts(); } @Override public void setFloats(float[] floats) { assertFrameMutable(); assertGLEnvValid(); if (!setNativeFloats(floats)) { throw new RuntimeException("Could not set int values for GL frame!"); } } @Override public float[] getFloats() { assertGLEnvValid(); flushGPU("getFloats"); return getNativeFloats(); } @Override public void setData(ByteBuffer buffer, int offset, int length) { assertFrameMutable(); assertGLEnvValid(); byte[] bytes = buffer.array(); if (getFormat().getSize() != bytes.length) { throw new RuntimeException("Data size in setData does not match GL frame size!"); } else if (!setNativeData(bytes, offset, length)) { throw new RuntimeException("Could not set GL frame data!"); } } @Override public ByteBuffer getData() { assertGLEnvValid(); flushGPU("getData"); return ByteBuffer.wrap(getNativeData()); } @Override public void setBitmap(Bitmap bitmap) { assertFrameMutable(); assertGLEnvValid(); if (getFormat().getWidth() != bitmap.getWidth() || getFormat().getHeight() != bitmap.getHeight()) { throw new RuntimeException("Bitmap dimensions do not match GL frame dimensions!"); } else { Bitmap rgbaBitmap = convertBitmapToRGBA(bitmap); if (!setNativeBitmap(rgbaBitmap, rgbaBitmap.getByteCount())) { throw new RuntimeException("Could not set GL frame bitmap data!"); } } } @Override public Bitmap getBitmap() { assertGLEnvValid(); flushGPU("getBitmap"); Bitmap result = Bitmap.createBitmap(getFormat().getWidth(), getFormat().getHeight(), Bitmap.Config.ARGB_8888); if (!getNativeBitmap(result)) { throw new RuntimeException("Could not get bitmap data from GL frame!"); } return result; } @Override public void setDataFromFrame(Frame frame) { assertGLEnvValid(); // Make sure frame fits if (getFormat().getSize() < frame.getFormat().getSize()) { throw new RuntimeException( "Attempting to assign frame of size " + frame.getFormat().getSize() + " to " + "smaller GL 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); } } public void setViewport(int x, int y, int width, int height) { assertFrameMutable(); setNativeViewport(x, y, width, height); } public void setViewport(Rect rect) { assertFrameMutable(); setNativeViewport(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); } public void generateMipMap() { assertFrameMutable(); assertGLEnvValid(); if (!generateNativeMipMap()) { throw new RuntimeException("Could not generate mip-map for GL frame!"); } } public void setTextureParameter(int param, int value) { assertFrameMutable(); assertGLEnvValid(); if (!setNativeTextureParam(param, value)) { throw new RuntimeException("Could not set texture value " + param + " = " + value + " " + "for GLFrame!"); } } public int getTextureId() { return getNativeTextureId(); } public int getFboId() { return getNativeFboId(); } public void focus() { if (!nativeFocus()) { throw new RuntimeException("Could not focus on GLFrame for drawing!"); } } @Override public String toString() { return "GLFrame id: " + glFrameId + " (" + getFormat() + ") with texture ID " + getTextureId() + ", FBO ID " + getFboId(); } @Override protected void reset(FrameFormat newFormat) { if (!nativeResetParams()) { throw new RuntimeException("Could not reset GLFrame texture parameters!"); } super.reset(newFormat); } @Override protected void onFrameStore() { if (!mOwnsTexture) { // Detach texture from FBO in case user manipulates it. nativeDetachTexFromFbo(); } } @Override protected void onFrameFetch() { if (!mOwnsTexture) { // Reattach texture to FBO when using frame again. This may reallocate the texture // in case it has become invalid. nativeReattachTexToFbo(); } } private void assertGLEnvValid() { if (!mGLEnvironment.isContextActive()) { if (GLEnvironment.isAnyContextActive()) { throw new RuntimeException("Attempting to access " + this + " with foreign GL " + "context active!"); } else { throw new RuntimeException("Attempting to access " + this + " with no GL context " + " active!"); } } } static { System.loadLibrary("filterfw"); } private native boolean nativeAllocate(GLEnvironment env, int width, int height); private native boolean nativeAllocateWithTexture(GLEnvironment env, int textureId, int width, int height); private native boolean nativeAllocateWithFbo(GLEnvironment env, int fboId, int width, int height); private native boolean nativeAllocateExternal(GLEnvironment env); private native boolean nativeDeallocate(); private native boolean setNativeData(byte[] data, int offset, int length); private native byte[] getNativeData(); private native boolean setNativeInts(int[] ints); private native boolean setNativeFloats(float[] floats); private native int[] getNativeInts(); private native float[] getNativeFloats(); private native boolean setNativeBitmap(Bitmap bitmap, int size); private native boolean getNativeBitmap(Bitmap bitmap); private native boolean setNativeViewport(int x, int y, int width, int height); private native int getNativeTextureId(); private native int getNativeFboId(); private native boolean generateNativeMipMap(); private native boolean setNativeTextureParam(int param, int value); private native boolean nativeResetParams(); private native boolean nativeCopyFromNative(NativeFrame frame); private native boolean nativeCopyFromGL(GLFrame frame); private native boolean nativeFocus(); private native boolean nativeReattachTexToFbo(); private native boolean nativeDetachTexFromFbo(); }