/*
* Copyright (C) 2012 CyberAgent
* Copyright (C) 2010 jsemler
*
* Original publication without License
* http://www.anddev.org/android-2d-3d-graphics-opengl-tutorials-f2/possible-to-do-opengl-off-screen-rendering-in-android-t13232.html#p41662
*/
package jp.co.cyberagent.android.gpuimage;
import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_DEFAULT_DISPLAY;
import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_HEIGHT;
import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
import static javax.microedition.khronos.egl.EGL10.EGL_NO_CONTEXT;
import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_WIDTH;
import static javax.microedition.khronos.opengles.GL10.GL_RGBA;
import static javax.microedition.khronos.opengles.GL10.GL_UNSIGNED_BYTE;
import java.nio.IntBuffer;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL10;
import android.graphics.Bitmap;
import android.opengl.GLSurfaceView;
import android.util.Log;
public class PixelBuffer {
final static String TAG = "PixelBuffer";
final static boolean LIST_CONFIGS = false;
GLSurfaceView.Renderer mRenderer; // borrow this interface
int mWidth, mHeight;
Bitmap mBitmap;
EGL10 mEGL;
EGLDisplay mEGLDisplay;
EGLConfig[] mEGLConfigs;
EGLConfig mEGLConfig;
EGLContext mEGLContext;
EGLSurface mEGLSurface;
GL10 mGL;
String mThreadOwner;
public PixelBuffer(final int width, final int height) {
mWidth = width;
mHeight = height;
int[] version = new int[2];
int[] attribList = new int[] {
EGL_WIDTH, mWidth,
EGL_HEIGHT, mHeight,
EGL_NONE
};
// No error checking performed, minimum required code to elucidate logic
mEGL = (EGL10) EGLContext.getEGL();
mEGLDisplay = mEGL.eglGetDisplay(EGL_DEFAULT_DISPLAY);
mEGL.eglInitialize(mEGLDisplay, version);
mEGLConfig = chooseConfig(); // Choosing a config is a little more
// complicated
// mEGLContext = mEGL.eglCreateContext(mEGLDisplay, mEGLConfig,
// EGL_NO_CONTEXT, null);
int EGL_CONTEXT_CLIENT_VERSION = 0x3098;
int[] attrib_list = {
EGL_CONTEXT_CLIENT_VERSION, 2,
EGL10.EGL_NONE
};
mEGLContext = mEGL.eglCreateContext(mEGLDisplay, mEGLConfig, EGL_NO_CONTEXT, attrib_list);
mEGLSurface = mEGL.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig, attribList);
mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
mGL = (GL10) mEGLContext.getGL();
// Record thread owner of OpenGL context
mThreadOwner = Thread.currentThread().getName();
}
public void setRenderer(final GLSurfaceView.Renderer renderer) {
mRenderer = renderer;
// Does this thread own the OpenGL context?
if (!Thread.currentThread().getName().equals(mThreadOwner)) {
Log.e(TAG, "setRenderer: This thread does not own the OpenGL context.");
return;
}
// Call the renderer initialization routines
mRenderer.onSurfaceCreated(mGL, mEGLConfig);
mRenderer.onSurfaceChanged(mGL, mWidth, mHeight);
}
public Bitmap getBitmap() {
// Do we have a renderer?
if (mRenderer == null) {
Log.e(TAG, "getBitmap: Renderer was not set.");
return null;
}
// Does this thread own the OpenGL context?
if (!Thread.currentThread().getName().equals(mThreadOwner)) {
Log.e(TAG, "getBitmap: This thread does not own the OpenGL context.");
return null;
}
// Call the renderer draw routine (it seems that some filters do not
// work if this is only called once)
mRenderer.onDrawFrame(mGL);
mRenderer.onDrawFrame(mGL);
convertToBitmap();
return mBitmap;
}
public void destroy() {
mRenderer.onDrawFrame(mGL);
mRenderer.onDrawFrame(mGL);
mEGL.eglMakeCurrent(mEGLDisplay, EGL10.EGL_NO_SURFACE,
EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
mEGL.eglDestroySurface(mEGLDisplay, mEGLSurface);
mEGL.eglDestroyContext(mEGLDisplay, mEGLContext);
mEGL.eglTerminate(mEGLDisplay);
}
private EGLConfig chooseConfig() {
int[] attribList = new int[] {
EGL_DEPTH_SIZE, 0,
EGL_STENCIL_SIZE, 0,
EGL_RED_SIZE, 8,
EGL_GREEN_SIZE, 8,
EGL_BLUE_SIZE, 8,
EGL_ALPHA_SIZE, 8,
EGL10.EGL_RENDERABLE_TYPE, 4,
EGL_NONE
};
// No error checking performed, minimum required code to elucidate logic
// Expand on this logic to be more selective in choosing a configuration
int[] numConfig = new int[1];
mEGL.eglChooseConfig(mEGLDisplay, attribList, null, 0, numConfig);
int configSize = numConfig[0];
mEGLConfigs = new EGLConfig[configSize];
mEGL.eglChooseConfig(mEGLDisplay, attribList, mEGLConfigs, configSize, numConfig);
if (LIST_CONFIGS) {
listConfig();
}
return mEGLConfigs[0]; // Best match is probably the first configuration
}
private void listConfig() {
Log.i(TAG, "Config List {");
for (EGLConfig config : mEGLConfigs) {
int d, s, r, g, b, a;
// Expand on this logic to dump other attributes
d = getConfigAttrib(config, EGL_DEPTH_SIZE);
s = getConfigAttrib(config, EGL_STENCIL_SIZE);
r = getConfigAttrib(config, EGL_RED_SIZE);
g = getConfigAttrib(config, EGL_GREEN_SIZE);
b = getConfigAttrib(config, EGL_BLUE_SIZE);
a = getConfigAttrib(config, EGL_ALPHA_SIZE);
Log.i(TAG, " <d,s,r,g,b,a> = <" + d + "," + s + "," +
r + "," + g + "," + b + "," + a + ">");
}
Log.i(TAG, "}");
}
private int getConfigAttrib(final EGLConfig config, final int attribute) {
int[] value = new int[1];
return mEGL.eglGetConfigAttrib(mEGLDisplay, config,
attribute, value) ? value[0] : 0;
}
private void convertToBitmap() {
IntBuffer ib = IntBuffer.allocate(mWidth * mHeight);
IntBuffer ibt = IntBuffer.allocate(mWidth * mHeight);
mGL.glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, ib);
// Convert upside down mirror-reversed image to right-side up normal
// image.
for (int i = 0; i < mHeight; i++) {
for (int j = 0; j < mWidth; j++) {
ibt.put((mHeight - i - 1) * mWidth + j, ib.get(i * mWidth + j));
}
}
mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
mBitmap.copyPixelsFromBuffer(ibt);
}
}