5
votes

I've been working on a project in which a user can interact with a GLSurfaceView to draw shapes on a screen. This all works fine and dandy, but now I'm attempting to do two things: 1) create a thumbnail of their drawing, and 2) save their drawing. It's important to note that the user can draw an image which is larger than the screen.

As far as my research shows, this is best achieved using a Bitmap (which can be 1) rendered to a Canvas for a thumbnail, and 2) be saved to the file system, which achieves both of my goals).

Initially, I tried to read the Renderer from the GLSurfaceView via glReadPixels, but it turns out I couldn't get the off-screen data from this. Instead, I opted to do an off-screen buffer to produce a render of the image, which could be converted to a Bitmap.

I found a lovely post which provided a code for a class called PixelBuffer, which I am now using. (I've made a few tweaks, but all issues I've had occur with or without said tweaking.)

Now, when using the getBitmap() of the code (which I will post below in the event the PixelBuffer forum thread is unreadable), I get a bunch of called unimplemented OpenGL ES API. This struck me as odd at first, so I did some investigating. It turns out that, for some reason, the PixelBuffer class is using OpenGL ES 2.0, while the GLSurfaceView is using OpenGL ES 1.1. The device I'm using (Galaxy Nexus) doesn't support 2.0. (And moreover, I'd like to support the largest range of devices possible.)

So, here is my question: How can I force my PixelBuffer class to use the OpenGL ES 1.1 API? I have already added the following in my Manifest:

<uses-feature android:glEsVersion="0x00010001" />

Additionally, I have attempted to set the version using int[] version = new int[] { 1, 1 };, to no avail.

The PixelBuffer code I'm using: Link. The line in question that calls the Renderer is on line 91. The version code is on line 39-ish onward.

This is the code I use to create the PixelBuffer, called from the GLSurfaceView object:

    setEGLConfigChooser(8, 8, 8, 8, 0, 0);
    mRenderer = new MyRenderer(this);
    setRenderer(mRenderer);
    mPixelBuffer = new CPPixelBuffer(1440, 1280); // TODO Temporary hardcode for Galaxy Nexus wallpaper size
    mPixelBuffer.setRenderer(mRenderer);

If it is not possible to use version 1.1 for an offscreen render, what is the best way to convert my GL surface to a Bitmap using version 1.1 or lower?

EDIT: Through some testing, I discovered that using the below code causes these errors:

// int[] version = new int[2];
int[] version = new int[] { // When using this line, as well as the other marked lines, instead of the line above, the errors are produced.
    1, 1 // Marked
}; // Marked
int[] attribList = new int[] {
    EGL_WIDTH, mWidth,
    EGL_HEIGHT, mHeight,
    EGL_VERSION, 1, // Marked
    EGL_NONE
};

06-11 15:41:52.316: E/libEGL(5618): eglMakeCurrent:674 error 3009 (EGL_BAD_MATCH)
06-11 15:41:52.316: E/libEGL(5618): call to OpenGL ES API with no current context (logged once per thread)

However, neither of these setups alleviate the issue at hand. They are just both present in my PasteBin'd code, so I thought it would be good to point it out.

1
Actually Galaxy Nexus supports OpenGL ES 2.0 just fine. Which function call exactly gives you "unimplemented OpenGL ES API"? If that is PBufferSurface creation, then try to use different attributes for it. Or better just skip it, and use glGreadPixels from framebuffer.Mārtiņš Možeiko
All methods (in the onDrawFrame call) give it to me. Even gl.glLoadIdentity(); my window is literally spammed by them. It's nothing 2.0-specific, which is why this is so odd to me. (However, this doesn't resolve the issue for older 1.1-compatible devices...)Eric
Do you have GL context created for the thread where you are invoking gl... functions?Mārtiņš Možeiko
Hmm... good question. I create the EGL context and get the GL from that (as seen in the Pastebin link). I think that's the only separate context created, though.Eric
Shot in the dark: There's a setEGLContextClientVersion method on GLSurfaceView that internally sets the mEGLContextClientVersion field. Have a look at how this field is interpreted.Stefan Hanke

1 Answers

2
votes

I didnt find for some reason the link to post a comment, so sorry about posting this as an answer. About the GL thread, normally you are in GL thread only in

public void onDrawFrame(GL10 gl)

Which is a method of the Renderer. Everything must be done inside this method. You can just put a boolean, lets say, into the renderer, and set it to true when you want something to be done in the renderer, and that's it. If you do any call outside this method, you will get erorrs.

Also, look at this example: Android OpenGL Screenshot I have used it and it worked for me, but I didn't need to save off-screen area, so I don't know if it will work, maybe not :)

The method must be called inside onDrawFrame