1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 18 package android.filterfw.core; 19 20 import android.filterfw.core.Frame; 21 import android.filterfw.core.FrameFormat; 22 import android.filterfw.core.FrameManager; 23 import android.filterfw.core.NativeFrame; 24 import android.filterfw.core.StopWatchMap; 25 import android.graphics.Bitmap; 26 import android.opengl.GLES20; 27 import android.graphics.Rect; 28 29 import java.nio.ByteBuffer; 30 31 class GLFrameTimer { 32 33 private static StopWatchMap mTimer = null; 34 35 public static StopWatchMap get() { 36 if (mTimer == null) { 37 mTimer = new StopWatchMap(); 38 } 39 return mTimer; 40 } 41 42 } 43 44 /** 45 * @hide 46 */ 47 public class GLFrame extends Frame { 48 49 // GL-related binding types 50 public final static int EXISTING_TEXTURE_BINDING = 100; 51 public final static int EXISTING_FBO_BINDING = 101; 52 public final static int NEW_TEXTURE_BINDING = 102; // TODO: REMOVE THIS 53 public final static int NEW_FBO_BINDING = 103; // TODO: REMOVE THIS 54 public final static int EXTERNAL_TEXTURE = 104; 55 56 private int glFrameId = -1; 57 58 /** 59 * Flag whether we own the texture or not. If we do not, we must be careful when caching or 60 * storing the frame, as the user may delete, and regenerate it. 61 */ 62 private boolean mOwnsTexture = true; 63 64 /** 65 * Keep a reference to the GL environment, so that it does not get deallocated while there 66 * are still frames living in it. 67 */ 68 private GLEnvironment mGLEnvironment; 69 70 GLFrame(FrameFormat format, FrameManager frameManager) { 71 super(format, frameManager); 72 } 73 74 GLFrame(FrameFormat format, FrameManager frameManager, int bindingType, long bindingId) { 75 super(format, frameManager, bindingType, bindingId); 76 } 77 78 void init(GLEnvironment glEnv) { 79 FrameFormat format = getFormat(); 80 mGLEnvironment = glEnv; 81 82 // Check that we have a valid format 83 if (format.getBytesPerSample() != 4) { 84 throw new IllegalArgumentException("GL frames must have 4 bytes per sample!"); 85 } else if (format.getDimensionCount() != 2) { 86 throw new IllegalArgumentException("GL frames must be 2-dimensional!"); 87 } else if (getFormat().getSize() < 0) { 88 throw new IllegalArgumentException("Initializing GL frame with zero size!"); 89 } 90 91 // Create correct frame 92 int bindingType = getBindingType(); 93 boolean reusable = true; 94 if (bindingType == Frame.NO_BINDING) { 95 initNew(false); 96 } else if (bindingType == EXTERNAL_TEXTURE) { 97 initNew(true); 98 reusable = false; 99 } else if (bindingType == EXISTING_TEXTURE_BINDING) { 100 initWithTexture((int)getBindingId()); 101 } else if (bindingType == EXISTING_FBO_BINDING) { 102 initWithFbo((int)getBindingId()); 103 } else if (bindingType == NEW_TEXTURE_BINDING) { 104 initWithTexture((int)getBindingId()); 105 } else if (bindingType == NEW_FBO_BINDING) { 106 initWithFbo((int)getBindingId()); 107 } else { 108 throw new RuntimeException("Attempting to create GL frame with unknown binding type " 109 + bindingType + "!"); 110 } 111 setReusable(reusable); 112 } 113 114 private void initNew(boolean isExternal) { 115 if (isExternal) { 116 if (!nativeAllocateExternal(mGLEnvironment)) { 117 throw new RuntimeException("Could not allocate external GL frame!"); 118 } 119 } else { 120 if (!nativeAllocate(mGLEnvironment, getFormat().getWidth(), getFormat().getHeight())) { 121 throw new RuntimeException("Could not allocate GL frame!"); 122 } 123 } 124 } 125 126 private void initWithTexture(int texId) { 127 int width = getFormat().getWidth(); 128 int height = getFormat().getHeight(); 129 if (!nativeAllocateWithTexture(mGLEnvironment, texId, width, height)) { 130 throw new RuntimeException("Could not allocate texture backed GL frame!"); 131 } 132 mOwnsTexture = false; 133 markReadOnly(); 134 } 135 136 private void initWithFbo(int fboId) { 137 int width = getFormat().getWidth(); 138 int height = getFormat().getHeight(); 139 if (!nativeAllocateWithFbo(mGLEnvironment, fboId, width, height)) { 140 throw new RuntimeException("Could not allocate FBO backed GL frame!"); 141 } 142 } 143 144 void flushGPU(String message) { 145 StopWatchMap timer = GLFrameTimer.get(); 146 if (timer.LOG_MFF_RUNNING_TIMES) { 147 timer.start("glFinish " + message); 148 GLES20.glFinish(); 149 timer.stop("glFinish " + message); 150 } 151 } 152 153 @Override 154 protected synchronized boolean hasNativeAllocation() { 155 return glFrameId != -1; 156 } 157 158 @Override 159 protected synchronized void releaseNativeAllocation() { 160 nativeDeallocate(); 161 glFrameId = -1; 162 } 163 164 public GLEnvironment getGLEnvironment() { 165 return mGLEnvironment; 166 } 167 168 @Override 169 public Object getObjectValue() { 170 assertGLEnvValid(); 171 return ByteBuffer.wrap(getNativeData()); 172 } 173 174 @Override 175 public void setInts(int[] ints) { 176 assertFrameMutable(); 177 assertGLEnvValid(); 178 if (!setNativeInts(ints)) { 179 throw new RuntimeException("Could not set int values for GL frame!"); 180 } 181 } 182 183 @Override 184 public int[] getInts() { 185 assertGLEnvValid(); 186 flushGPU("getInts"); 187 return getNativeInts(); 188 } 189 190 @Override 191 public void setFloats(float[] floats) { 192 assertFrameMutable(); 193 assertGLEnvValid(); 194 if (!setNativeFloats(floats)) { 195 throw new RuntimeException("Could not set int values for GL frame!"); 196 } 197 } 198 199 @Override 200 public float[] getFloats() { 201 assertGLEnvValid(); 202 flushGPU("getFloats"); 203 return getNativeFloats(); 204 } 205 206 @Override 207 public void setData(ByteBuffer buffer, int offset, int length) { 208 assertFrameMutable(); 209 assertGLEnvValid(); 210 byte[] bytes = buffer.array(); 211 if (getFormat().getSize() != bytes.length) { 212 throw new RuntimeException("Data size in setData does not match GL frame size!"); 213 } else if (!setNativeData(bytes, offset, length)) { 214 throw new RuntimeException("Could not set GL frame data!"); 215 } 216 } 217 218 @Override 219 public ByteBuffer getData() { 220 assertGLEnvValid(); 221 flushGPU("getData"); 222 return ByteBuffer.wrap(getNativeData()); 223 } 224 225 @Override 226 public void setBitmap(Bitmap bitmap) { 227 assertFrameMutable(); 228 assertGLEnvValid(); 229 if (getFormat().getWidth() != bitmap.getWidth() || 230 getFormat().getHeight() != bitmap.getHeight()) { 231 throw new RuntimeException("Bitmap dimensions do not match GL frame dimensions!"); 232 } else { 233 Bitmap rgbaBitmap = convertBitmapToRGBA(bitmap); 234 if (!setNativeBitmap(rgbaBitmap, rgbaBitmap.getByteCount())) { 235 throw new RuntimeException("Could not set GL frame bitmap data!"); 236 } 237 } 238 } 239 240 @Override 241 public Bitmap getBitmap() { 242 assertGLEnvValid(); 243 flushGPU("getBitmap"); 244 Bitmap result = Bitmap.createBitmap(getFormat().getWidth(), 245 getFormat().getHeight(), 246 Bitmap.Config.ARGB_8888); 247 if (!getNativeBitmap(result)) { 248 throw new RuntimeException("Could not get bitmap data from GL frame!"); 249 } 250 return result; 251 } 252 253 @Override 254 public void setDataFromFrame(Frame frame) { 255 assertGLEnvValid(); 256 257 // Make sure frame fits 258 if (getFormat().getSize() < frame.getFormat().getSize()) { 259 throw new RuntimeException( 260 "Attempting to assign frame of size " + frame.getFormat().getSize() + " to " + 261 "smaller GL frame of size " + getFormat().getSize() + "!"); 262 } 263 264 // Invoke optimized implementations if possible 265 if (frame instanceof NativeFrame) { 266 nativeCopyFromNative((NativeFrame)frame); 267 } else if (frame instanceof GLFrame) { 268 nativeCopyFromGL((GLFrame)frame); 269 } else if (frame instanceof SimpleFrame) { 270 setObjectValue(frame.getObjectValue()); 271 } else { 272 super.setDataFromFrame(frame); 273 } 274 } 275 276 public void setViewport(int x, int y, int width, int height) { 277 assertFrameMutable(); 278 setNativeViewport(x, y, width, height); 279 } 280 281 public void setViewport(Rect rect) { 282 assertFrameMutable(); 283 setNativeViewport(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top); 284 } 285 286 public void generateMipMap() { 287 assertFrameMutable(); 288 assertGLEnvValid(); 289 if (!generateNativeMipMap()) { 290 throw new RuntimeException("Could not generate mip-map for GL frame!"); 291 } 292 } 293 294 public void setTextureParameter(int param, int value) { 295 assertFrameMutable(); 296 assertGLEnvValid(); 297 if (!setNativeTextureParam(param, value)) { 298 throw new RuntimeException("Could not set texture value " + param + " = " + value + " " + 299 "for GLFrame!"); 300 } 301 } 302 303 public int getTextureId() { 304 return getNativeTextureId(); 305 } 306 307 public int getFboId() { 308 return getNativeFboId(); 309 } 310 311 public void focus() { 312 if (!nativeFocus()) { 313 throw new RuntimeException("Could not focus on GLFrame for drawing!"); 314 } 315 } 316 317 @Override 318 public String toString() { 319 return "GLFrame id: " + glFrameId + " (" + getFormat() + ") with texture ID " 320 + getTextureId() + ", FBO ID " + getFboId(); 321 } 322 323 @Override 324 protected void reset(FrameFormat newFormat) { 325 if (!nativeResetParams()) { 326 throw new RuntimeException("Could not reset GLFrame texture parameters!"); 327 } 328 super.reset(newFormat); 329 } 330 331 @Override 332 protected void onFrameStore() { 333 if (!mOwnsTexture) { 334 // Detach texture from FBO in case user manipulates it. 335 nativeDetachTexFromFbo(); 336 } 337 } 338 339 @Override 340 protected void onFrameFetch() { 341 if (!mOwnsTexture) { 342 // Reattach texture to FBO when using frame again. This may reallocate the texture 343 // in case it has become invalid. 344 nativeReattachTexToFbo(); 345 } 346 } 347 348 private void assertGLEnvValid() { 349 if (!mGLEnvironment.isContextActive()) { 350 if (GLEnvironment.isAnyContextActive()) { 351 throw new RuntimeException("Attempting to access " + this + " with foreign GL " + 352 "context active!"); 353 } else { 354 throw new RuntimeException("Attempting to access " + this + " with no GL context " + 355 " active!"); 356 } 357 } 358 } 359 360 static { 361 System.loadLibrary("filterfw"); 362 } 363 364 private native boolean nativeAllocate(GLEnvironment env, int width, int height); 365 366 private native boolean nativeAllocateWithTexture(GLEnvironment env, 367 int textureId, 368 int width, 369 int height); 370 371 private native boolean nativeAllocateWithFbo(GLEnvironment env, 372 int fboId, 373 int width, 374 int height); 375 376 private native boolean nativeAllocateExternal(GLEnvironment env); 377 378 private native boolean nativeDeallocate(); 379 380 private native boolean setNativeData(byte[] data, int offset, int length); 381 382 private native byte[] getNativeData(); 383 384 private native boolean setNativeInts(int[] ints); 385 386 private native boolean setNativeFloats(float[] floats); 387 388 private native int[] getNativeInts(); 389 390 private native float[] getNativeFloats(); 391 392 private native boolean setNativeBitmap(Bitmap bitmap, int size); 393 394 private native boolean getNativeBitmap(Bitmap bitmap); 395 396 private native boolean setNativeViewport(int x, int y, int width, int height); 397 398 private native int getNativeTextureId(); 399 400 private native int getNativeFboId(); 401 402 private native boolean generateNativeMipMap(); 403 404 private native boolean setNativeTextureParam(int param, int value); 405 406 private native boolean nativeResetParams(); 407 408 private native boolean nativeCopyFromNative(NativeFrame frame); 409 410 private native boolean nativeCopyFromGL(GLFrame frame); 411 412 private native boolean nativeFocus(); 413 414 private native boolean nativeReattachTexToFbo(); 415 416 private native boolean nativeDetachTexFromFbo(); 417 } 418