I'd like to get access to the main (OpenGL) screen in Android to implement some overlay 3D effects.
Is it possible to do so?
If yes, how can I do it?
When amending this context, my application should be a service, right?
You cannot get access to the framebuffer, for obvious security reasons.
What you will probably want to do is research the glReadPixels() function. I ran a test where I had the screen split with a glsurfaceview and image view and wanted to see if I could grab the pixels from the glview and create a Bitmap and then apply that to the ImageView. After some research, I found using glReadPixels() works, but you have to tranform the pixels before using them for an android bitmap. This is the method I ended up using. I'm confident that I found it exactly this way on another forum.
public Bitmap SaveGLPixels(int x, int y, int w, int h, GL10 gl)
{
int b[]=new int[w*h];
int bt[]=new int[w*h];
IntBuffer ib=IntBuffer.wrap(b);
ib.position(0);
gl.glReadPixels(x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);
for(int i=0; i<h; i++)
{//remember, that OpenGL bitmap is incompatible with Android bitmap
//and so, some correction need.
for(int j=0; j<w; j++)
{
int pix=b[i*w+j];
int pb=(pix>>16)&0xff;
int pr=(pix<<16)&0x00ff0000;
int pix1=(pix&0xff00ff00) | pr | pb;
bt[(h-i-1)*w+j]=pix1;
}
}
Bitmap.Config bconfig = Bitmap.Config.RGB_565;
Bitmap sb=Bitmap.createBitmap(bt, w, h, bconfig);
return sb;
}
Related
i can use the follow code to achieve convert OpenGL RGBA int array to android ARGB int array.
int b[]=new int[(int) (w*h)];
int bt[]=new int[(int) (w*h)];
IntBuffer buffer=IntBuffer.wrap(b);
buffer.position(0);
GLES20.glReadPixels(0, 0, w, h,GLES20.GL_RGBA,GLES20.GL_UNSIGNED_BYTE, buffer);
for(int i=0; i<h; i++)
{
//remember, that OpenGL bitmap is incompatible with Android bitmap
//and so, some correction need.
for(int j=0; j<w; j++)
{
int pix=b[i*w+j];
int pb=(pix>>16)&0xff;
int pr=(pix<<16)&0x00ff0000;
int pix1=(pix&0xff00ff00) | pr | pb;
bt[(h-i-1)*w+j]=pix1;
}
}
through i can use convert int array to byte array to achieve my goal,but efficiency is little low,so i want to use the follow code to achieve the same goal:
final ByteBuffer rgbaData = ByteBuffer.allocateDirect(WIDTH * HEIGHT * 4);
GLES20.glReadPixels(0, 0, WIDTH, HEIGHT, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, rgbaData),
I've modified this project, which uses GLSurfaceView and Effects to show a ViewPager with some of the effects applied to an image.
Additionally, I created a overlay bitmap, that is put over every image after the effect has been applied.
Up this point, the app is working fine. But now I have to save the displayed image in a file, when a button is pressed.
So i used this code:
private Bitmap createBitmapFromGLSurface(int x, int y, int w, int h, GL10 gl)
throws OutOfMemoryError {
int bitmapBuffer[] = new int[w * h];
int bitmapSource[] = new int[w * h];
IntBuffer intBuffer = IntBuffer.wrap(bitmapBuffer);
intBuffer.position(0);
try {
gl.glReadPixels(x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, intBuffer);
int offset1, offset2;
for (int i = 0; i < h; i++) {
offset1 = i * w;
offset2 = (h - i - 1) * w;
for (int j = 0; j < w; j++) {
int texturePixel = bitmapBuffer[offset1 + j];
int blue = (texturePixel >> 16) & 0xff;
int red = (texturePixel << 16) & 0x00ff0000;
int pixel = (texturePixel & 0xff00ff00) | red | blue;
bitmapSource[offset2 + j] = pixel;
}
}
} catch (GLException e) {
e.printStackTrace();
return null;
}
return Bitmap.createBitmap(bitmapSource, w, h, Bitmap.Config.ARGB_8888);
}
to obtain a Bitmap. When the button is pressed, I call this method:
protected void onClick() {
read = true;
mEffectView.requestRender();
}
Which forces the rendering, so I generate the bitmap and save it on a file using an AsyncTask.
read is used as a semphore in onDrawFrame(GL10 gl) to generate only the bitmap when I want to save it.
Saving one image works fine. When I save a second one, then I change page, this error comes up:
A/Bitmap: Failed to acquire strong reference to pixels
A/libc: Fatal signal 6 (SIGABRT), code -6 in tid 20475 (GLThread 9540)
Another issue is that the overlay, though is displayed, is not saved in the image.
This is how I apply it:
Generation
EffectFactory effectFactory = mEffectContext.getFactory();
overlayEffect = effectFactory.createEffect(EffectFactory.EFFECT_BITMAPOVERLAY);
overlayEffect.setParameter("bitmap", overlay);
Effect applying
mEffect.apply(mTextures[0], mImageWidth, mImageHeight, mTextures[1]);
overlayEffect.apply(mTextures[1], mImageWidth, mImageHeight, mTextures[2]);
With mEffect is the only effect visible when saving the image.
What I'm doing wrong?
EDIT
I solved the last problem: I find out that you have to release and recreate every Effect object you are using every time is called mEffectView.requestRender().
Apparently, when using
overlayEffect = effectFactory.createEffect(EffectFactory.EFFECT_BITMAPOVERLAY);
overlayEffect.setParameter("bitmap", overlay);
the passed bitmap is recycled!
So I solved the issue passing a copy of it:
overlayEffect = effectFactory.createEffect(EffectFactory.EFFECT_BITMAPOVERLAY);
overlayEffect.setParameter("bitmap", overlay.copy(overlay.getConfig(), false));
Hope this will help somebody else!
I have an application that uses GLSurfaceView to do some 3-D goodness in Android. I'd like the user to be able to take a screenshot. I think this snippet of code should be storing the pixel colors and storing them inside a bitmap. However, does anyone know how to access the gl10 element that the screen is using, so I can feed it into this function?
public static Bitmap savePixelsOnScreen(int x, int y, int width, int height, GL10 gl){
int b[]=new int[w*(y+h)];
int bt[]=new int[w*h];
IntBuffer ib=IntBuffer.wrap(b);
ib.position(0);
//gl.glReadPixels(x, y, w, h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);
gl.glReadPixels(x, 0, w, y+h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);
for(int i=0, k=0; i<h; i++, k++)
{//remember, that OpenGL bitmap is incompatible with Android bitmap
//and so, some correction need.
for(int j=0; j<w; j++)
{
int pix=b[i*w+j];
int pb=(pix>>16)&0xff;
int pr=(pix<<16)&0x00ff0000;
int pix1=(pix&0xff00ff00) | pr | pb;
bt[(h-k-1)*w+j]=pix1;
}
}
Bitmap sb=Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888);
return sb;
return bitmap;
I had tried to do this from within the Activity that calls the GLSurfaceView:
EGL10 egl = (EGL10)EGLContext.getEGL();
GL10 gl = (GL10)egl.eglGetCurrentContext().getGL();
But every element of the int buffer is zero so I think this is not correct.
put the code under draw(GL10 gl) method of your CClayer/CCNode class and also maintain boolean variable to draw it only once. Draw method will be called 60+ times per second.
I'm currently developing an augmented reality application in android. Textured openGL object is drawn over the camera view. I had tried so many methods to take the screen shot of android openGL view (GLSurfaceview). But no success. I want the screenshot with transparent background so that I could draw it over camera image to produce an ARImage. Can any one help me? I know that I have to read the pixels of opengl using glReadPixels() function. I tried it using GL_RGBA mode and created bitmap (used ARGB_8888 and RGB_565). Using ARGB_8888 format I'll get a transparent image with no object and in RGB_565 mode I'll get a image filled with black color. Can any one guide me? Thanks in advance.
Thanks,
Midhun
Hi I found the solution to my question and I'll paste the code here. It may be useful for others who need alpha support.
public static Bitmap SavePixels(int x, int y, int w, int h, GL10 gl)
{
int b[]=new int[w*(y+h)];
int bt[]=new int[w*h];
IntBuffer ib=IntBuffer.wrap(b);
ib.position(0);
gl.glReadPixels(x, 0, w, y+h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);
for(int i=0, k=0; i<h; i++, k++)
{//remember, that OpenGL bitmap is incompatible with Android bitmap
//and so, some correction need.
for(int j=0; j<w; j++)
{
int pix=b[i*w+j];
int pb=(pix>>16)&0xff;
int pr=(pix<<16)&0x00ff0000;
int pix1=(pix&0xff00ff00) | pr | pb;
bt[(h-k-1)*w+j]=pix1;
}
}
Bitmap sb=Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888);
return sb;
}
This so called "solution" is simply making a screen shot. Alpha values that you get from glReadPixels are up to full 255 although you didn't draw anything to those pixels. If it's possible it will be an OpenGL-settings solution.
I've searched a lot about taking screenshot of my OpenGL object on Android and come up with this solution. It worked great but in my case I have camera view and opengl view(with transparent background) on top of camera view. So what I want to do is to get opengl screenshot with transparent background instead of black.
As I said I have tried link above and it worked but I'm stuck with black background. It's little bit complicated to figure out how to get rid of the black background in this particular case.
Hope somebody could help me and asap if it's possible(also I think the solution is easy, I'm just missing something simple).
Thank you.
I used following method and worked like a champ.
public static Bitmap SavePixels(int x, int y, int w, int h, GL10 gl)
{
int b[]=new int[w*(y+h)];
int bt[]=new int[w*h];
IntBuffer ib=IntBuffer.wrap(b);
ib.position(0);
gl.glReadPixels(x, 0, w, y+h, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, ib);
for(int i=0, k=0; i<h; i++, k++)
{//remember, that OpenGL bitmap is incompatible with Android bitmap
//and so, some correction need.
for(int j=0; j<w; j++)
{
int pix=b[i*w+j];
int pb=(pix>>16)&0xff;
int pr=(pix<<16)&0x00ff0000;
int pix1=(pix&0xff00ff00) | pr | pb;
bt[(h-k-1)*w+j]=pix1;
}
}
Bitmap sb=Bitmap.createBitmap(bt, w, h, Bitmap.Config.ARGB_8888);
return sb;
}
You just need to call
YourClass.SavePixels(0,0,width,height,gl);
I hope this will work for you...
Thanks,
Midhun
The solution you mentioned is using a Bitmap.Config.RGB_565 which doesn't support alpha channels.
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Instead you should use Bitmap.Config.ARGB_8888 or Bitmap.Config.ARGB_4444.