Possible to do OpenGL off-screen rendering in Android?

Tutorials concerning the OpenGL® ES cross-platform API for full-function 2D and 3D graphics on the Google-Android platform.

Possible to do OpenGL off-screen rendering in Android?

Postby jsemler » Tue Apr 20, 2010 1:50 am

I need to render into an off screen buffer so that I can extract the generated image to load into my app widget. App widget does not support the use of GLSurfaceView.

Anyone know how to do this under Android?
jsemler
Junior Developer
Junior Developer
 
Posts: 24
Joined: Sun Apr 18, 2010 3:53 pm

Top

Postby MichaelEGR » Tue Apr 20, 2010 3:28 am

A good first question is what are you trying to render/draw?

Does it require OpenGL or can you achieve what you are aiming for with the Android 2D API?

If you can achieve it with the 2D API it'll be much easier and a lot more reliable across the ecosystem of devices.
Founder & Principal Architect; EGR Software LLC
http://www.typhonrt.org
http://www.egrsoftware.com
User avatar
MichaelEGR
Senior Developer
Senior Developer
 
Posts: 147
Joined: Thu Jan 21, 2010 5:30 am
Location: San Francisco, CA

Postby jsemler » Tue Apr 20, 2010 3:42 am

Render the surface of the Moon with its attendant phase. I'm starting to lean toward simple 2D rendering. It would simply show the phase without surface details. If the user wants detailed rendering, he can simply tap on the widget to start the full application that renders the Moon in all its glory (at least that's how I think it will work).
jsemler
Junior Developer
Junior Developer
 
Posts: 24
Joined: Sun Apr 18, 2010 3:53 pm

Postby MichaelEGR » Tue Apr 20, 2010 5:55 am

Yeah, I'd say the 2D API should do just fine for this purpose. You can do lots of stylized off screen drawing with the 2D API quite easily. Unless you need an interactive 3D model that can be rotated / viewed from different perspectives or accurate texture mapping of features on the moons surface then GL would be an option.

You can take a picture of the full moon and simply cut out or mask with an on the fly generated or perhaps even a stock pre-generated mask image to create the current phase.

Creating and rendering to an offscreen bitmap is really easy with the Android 2D API and very reliable. I've posted sample code of doing offscreen rendering in previous posts of mine and you can find some more details in one of those perhaps.

Good luck!
Founder & Principal Architect; EGR Software LLC
http://www.typhonrt.org
http://www.egrsoftware.com
User avatar
MichaelEGR
Senior Developer
Senior Developer
 
Posts: 147
Joined: Thu Jan 21, 2010 5:30 am
Location: San Francisco, CA

Postby jsemler » Wed Apr 21, 2010 1:21 am

Your sample 2D code fragment for rendering into a bitmap was a big help.

My MoonClock widget currently uses the 2 tone representation. Tapping the MoonClock widget will also start my main MoonClock activity. Preferences, such as viewing orientation, are shared between MoonClock activity and MoonClock widget.

Here are the screen captures that captures the relationship between the two.

http://jsemler.net/blogData/MoonClockWidget.jpg
http://jsemler.net/blogData/MoonClockActivity.jpg

Thanks!

:D
jsemler
Junior Developer
Junior Developer
 
Posts: 24
Joined: Sun Apr 18, 2010 3:53 pm

Postby MichaelEGR » Wed Apr 21, 2010 6:11 am

Awesome..

Yeah. You can pull off an effect like this image using the 2D API and Porter-Duff blending (multiply):
http://jsemler.net/blogData/MoonClockActivity.jpg

You can generate a radial white to black gradient image, maybe blur it slightly (probably not necessary, but there is a filter for that), then use PorterDuff.Mode blending (see Paint.setXfermode() method) to draw the full moon with the radial blur image and you'll end up with a variable eclipsed moon. By doing it dynamically you can have infinite variations of the phase states of the moon and also save a lot of space from say storing a unique image per phase state. Combine that with the offscreen Bitmap rendering and it should look nice!

Here is a wiki article with a nice sample image (You'd want to do something like the middle row of that image):
http://en.wikipedia.org/wiki/Texture_splatting

Cheers...
Founder & Principal Architect; EGR Software LLC
http://www.typhonrt.org
http://www.egrsoftware.com
User avatar
MichaelEGR
Senior Developer
Senior Developer
 
Posts: 147
Joined: Thu Jan 21, 2010 5:30 am
Location: San Francisco, CA

Top

Postby jsemler » Mon Apr 26, 2010 3:27 am

I wanted to capture the effects of lunar librations so I went with 3D off-screen rendering.

Here's the result. The Moon Clock Widget sets the Widget's ImageView to the bitmap representation of the 3D rendered Moon (Currently updates once a minute).

http://jsemler.net/blogData/MoonClockWidget3D.jpg

3D off-screen rendering is done by rendering into EGL pixel buffers (a.k.a., pbuffers) instead of EGL window. My knowledge of how to work with pbuffers is incorporated into a class called "PixelBuffer". It uses the same GLSurfaceView.Renderer implementation so that Renderer instances that works with GLSurfaceView instances will also work with PixelBuffer instances.

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. package net.jsemler.utility.graphics3d.pixelbuffer;
  2.  
  3. import static javax.microedition.khronos.egl.EGL10.*;
  4. import static javax.microedition.khronos.opengles.GL10.*;
  5.  
  6. import java.nio.IntBuffer;
  7.  
  8. import javax.microedition.khronos.egl.EGL10;
  9. import javax.microedition.khronos.egl.EGLConfig;
  10. import javax.microedition.khronos.egl.EGLContext;
  11. import javax.microedition.khronos.egl.EGLDisplay;
  12. import javax.microedition.khronos.egl.EGLSurface;
  13. import javax.microedition.khronos.opengles.GL10;
  14.  
  15. import android.graphics.Bitmap;
  16. import android.opengl.GLSurfaceView;
  17. import android.util.Log;
  18.  
  19. public class PixelBuffer {
  20.     final static String TAG = "PixelBuffer";
  21.     final static boolean LIST_CONFIGS = false;
  22.  
  23.     GLSurfaceView.Renderer mRenderer; // borrow this interface
  24.     int mWidth, mHeight;
  25.     Bitmap mBitmap;
  26.        
  27.     EGL10 mEGL;
  28.     EGLDisplay mEGLDisplay;
  29.     EGLConfig[] mEGLConfigs;
  30.     EGLConfig mEGLConfig;
  31.     EGLContext mEGLContext;
  32.     EGLSurface mEGLSurface;
  33.     GL10 mGL;
  34.    
  35.     String mThreadOwner;
  36.    
  37.     public PixelBuffer(int width, int height) {
  38.         mWidth = width;
  39.         mHeight = height;
  40.                
  41.         int[] version = new int[2];
  42.         int[] attribList = new int[] {
  43.             EGL_WIDTH, mWidth,
  44.             EGL_HEIGHT, mHeight,
  45.             EGL_NONE
  46.         };
  47.                
  48.         // No error checking performed, minimum required code to elucidate logic
  49.         mEGL = (EGL10) EGLContext.getEGL();
  50.         mEGLDisplay = mEGL.eglGetDisplay(EGL_DEFAULT_DISPLAY);
  51.         mEGL.eglInitialize(mEGLDisplay, version);
  52.         mEGLConfig = chooseConfig(); // Choosing a config is a little more complicated
  53.         mEGLContext = mEGL.eglCreateContext(mEGLDisplay, mEGLConfig, EGL_NO_CONTEXT, null);
  54.         mEGLSurface = mEGL.eglCreatePbufferSurface(mEGLDisplay, mEGLConfig,  attribList);
  55.         mEGL.eglMakeCurrent(mEGLDisplay, mEGLSurface, mEGLSurface, mEGLContext);
  56.         mGL = (GL10) mEGLContext.getGL();
  57.        
  58.         // Record thread owner of OpenGL context
  59.         mThreadOwner = Thread.currentThread().getName();
  60.     }
  61.    
  62.     public void setRenderer(GLSurfaceView.Renderer renderer) {
  63.         mRenderer = renderer;
  64.        
  65.         // Does this thread own the OpenGL context?
  66.         if (!Thread.currentThread().getName().equals(mThreadOwner)) {
  67.             Log.e(TAG, "setRenderer: This thread does not own the OpenGL context.");
  68.             return;
  69.         }
  70.        
  71.         // Call the renderer initialization routines
  72.         mRenderer.onSurfaceCreated(mGL, mEGLConfig);
  73.         mRenderer.onSurfaceChanged(mGL, mWidth, mHeight);
  74.     }
  75.        
  76.     public Bitmap getBitmap() {
  77.         // Do we have a renderer?
  78.         if (mRenderer == null) {
  79.             Log.e(TAG, "getBitmap: Renderer was not set.");
  80.             return null;
  81.         }
  82.        
  83.         // Does this thread own the OpenGL context?
  84.         if (!Thread.currentThread().getName().equals(mThreadOwner)) {
  85.             Log.e(TAG, "getBitmap: This thread does not own the OpenGL context.");
  86.             return null;
  87.         }
  88.                
  89.         // Call the renderer draw routine
  90.         mRenderer.onDrawFrame(mGL);
  91.         convertToBitmap();
  92.         return mBitmap;
  93.     }
  94.  
  95.     private EGLConfig chooseConfig() {
  96.         int[] attribList = new int[] {                  
  97.             EGL_DEPTH_SIZE, 0,
  98.             EGL_STENCIL_SIZE, 0,
  99.             EGL_RED_SIZE, 8,
  100.             EGL_GREEN_SIZE, 8,
  101.             EGL_BLUE_SIZE, 8,
  102.             EGL_ALPHA_SIZE, 8,
  103.             EGL_NONE
  104.         };
  105.  
  106.         // No error checking performed, minimum required code to elucidate logic
  107.         // Expand on this logic to be more selective in choosing a configuration
  108.         int[] numConfig = new int[1];
  109.         mEGL.eglChooseConfig(mEGLDisplay, attribList, null, 0, numConfig);
  110.         int configSize = numConfig[0];
  111.         mEGLConfigs = new EGLConfig[configSize];
  112.         mEGL.eglChooseConfig(mEGLDisplay, attribList, mEGLConfigs, configSize, numConfig);
  113.  
  114.         if (LIST_CONFIGS) {
  115.             listConfig();
  116.         }
  117.  
  118.         return mEGLConfigs[0];  // Best match is probably the first configuration
  119.     }
  120.    
  121.     private void listConfig() {
  122.         Log.i(TAG, "Config List {");
  123.  
  124.         for (EGLConfig config : mEGLConfigs) {
  125.             int d, s, r, g, b, a;
  126.                    
  127.             // Expand on this logic to dump other attributes        
  128.             d = getConfigAttrib(config, EGL_DEPTH_SIZE);
  129.             s = getConfigAttrib(config, EGL_STENCIL_SIZE);
  130.             r = getConfigAttrib(config, EGL_RED_SIZE);
  131.             g = getConfigAttrib(config, EGL_GREEN_SIZE);
  132.             b = getConfigAttrib(config, EGL_BLUE_SIZE);
  133.             a = getConfigAttrib(config, EGL_ALPHA_SIZE);                
  134.             Log.i(TAG, "    <d,s,r,g,b,a> = <" + d + "," + s + "," +
  135.                 r + "," + g + "," + b + "," + a + ">");
  136.         }
  137.  
  138.         Log.i(TAG, "}");
  139.     }
  140.        
  141.     private int getConfigAttrib(EGLConfig config, int attribute) {
  142.         int[] value = new int[1];
  143.         return mEGL.eglGetConfigAttrib(mEGLDisplay, config,
  144.                         attribute, value)? value[0] : 0;
  145.     }
  146.        
  147.     private void convertToBitmap() {
  148.         IntBuffer ib = IntBuffer.allocate(mWidth*mHeight);
  149.         IntBuffer ibt = IntBuffer.allocate(mWidth*mHeight);
  150.         mGL.glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, ib);
  151.  
  152.         // Convert upside down mirror-reversed image to right-side up normal image.
  153.         for (int i = 0; i < mHeight; i++) {    
  154.             for (int j = 0; j < mWidth; j++) {
  155.                 ibt.put((mHeight-i-1)*mWidth + j, ib.get(i*mWidth + j));
  156.             }
  157.         }                  
  158.  
  159.         mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
  160.         mBitmap.copyPixelsFromBuffer(ibt);
  161.     }
  162. }
  163.  
Parsed in 0.052 seconds, using GeSHi 1.0.8.4
jsemler
Junior Developer
Junior Developer
 
Posts: 24
Joined: Sun Apr 18, 2010 3:53 pm

Re: Possible to do OpenGL off-screen rendering in Android?

Postby TigerK » Tue Feb 08, 2011 7:27 am

To jsemler, did you finally solve your problem. I meet the same issue now and can not find a way out. Can you guide me? thanks
TigerK
Once Poster
Once Poster
 
Posts: 1
Joined: Tue Feb 08, 2011 7:24 am

Re: Possible to do OpenGL off-screen rendering in Android?

Postby jsemler » Tue Feb 08, 2011 2:10 pm

TigerK wrote:To jsemler, did you finally solve your problem. I meet the same issue now and can not find a way out. Can you guide me? thanks


Using the class "PixelBuffer" (source included with my post above), I was able to solve my problem. Feel free to use it.

Set size of desired bitmap using the constructor, set the renderer using the setter method "setRenderer" (rendering algorithm same as one that works for the display), and the call the getter method "getBitMap" to fire off the rendering algorithm one time and then to retrieve the rendered in-memory bitmap (I haven't work with this in a while so I'm working from memory and the source code submitted above).

Since serveral version of Android have come out since then, there may be other better ways to do this but it gets the job done for me. Haven't developed under Android in a few months.
jsemler
Junior Developer
Junior Developer
 
Posts: 24
Joined: Sun Apr 18, 2010 3:53 pm

Re: Possible to do OpenGL off-screen rendering in Android?

Postby sstteevvee » Thu Apr 19, 2012 1:47 pm

Rather than that for loop after glReadPixels to flip the image vertically, you can do it much quicker with something like this (untested, but I think it's right!):

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1. mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
  2. mBitmap.copyPixelsFromBuffer(ib); // create bitmap with upside down image
  3.  
  4. Matrix matrix = new Matrix();
  5. matrix.preScale(1.0f, -1.0f); // scaling: x = x, y = -y, i.e. vertically flip
  6.  
  7. mBitmap = Bitmap.createBitmap(mBitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); // new bitmap, using the matrix to flip it
Parsed in 0.031 seconds, using GeSHi 1.0.8.4
sstteevvee
Freshman
Freshman
 
Posts: 2
Joined: Thu Apr 19, 2012 1:17 pm

Re: Possible to do OpenGL off-screen rendering in Android?

Postby ppatidar » Sat Dec 29, 2012 8:03 pm

I used the PixelBuffer but it fails at glReadPixel call with segmentation fault. Same logic for generating a bitmap from GLSurfaceView works. Please help.
ppatidar
Once Poster
Once Poster
 
Posts: 1
Joined: Sat Dec 29, 2012 7:54 pm

Re: Possible to do OpenGL off-screen rendering in Android?

Postby Paour » Tue Jan 22, 2013 2:54 pm

Thank you very much for this code, it seems to be working great!

Steve's code to perform the vertical flip is fine for small buffers, but since it causes the memory usage to go from 3x buffer size (gl memory, buffer, bitmap) to 4x, I used a similar but faster version of the original implementation.

Also, to cut down on big allocations, my implementation allows passing the buffer and bitmap from the caller, for those cases when repeated calls are required.

Syntax: [ Download ] [ Hide ]
Using java Syntax Highlighting
  1.         public Bitmap getBitmap(Bitmap inBitmap, IntBuffer buffer) {
  2.                 // Do we have a renderer?
  3.                 if (mRenderer == null) {
  4.                         Log.e(TAG, "getBitmap: Renderer was not set.");
  5.                         return null;
  6.                 }
  7.  
  8.                 // Does this thread own the OpenGL context?
  9.                 if (!Thread.currentThread().getName().equals(mThreadOwner)) {
  10.                         Log.e(TAG, "getBitmap: This thread does not own the OpenGL context.");
  11.                         return null;
  12.                 }
  13.  
  14.                 // Call the renderer draw routine
  15.                 mRenderer.onDrawFrame(gl);
  16.  
  17.                 // copy pixels from OpenGL
  18.                 if (buffer == null || buffer.capacity() != width * height) {
  19.                         buffer = IntBuffer.allocate(width * height);
  20.                 }
  21.  
  22.                 gl.glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
  23.  
  24.                 // vertical mirror image
  25.                 int[] swapArray = new int[width];
  26.                 int[] array = buffer.array();
  27.                 for (int i = 0; i < height / 2; i++) {
  28.                         System.arraycopy(array, i * width, swapArray, 0, width);
  29.                         System.arraycopy(array, (height - i - 1) * width, array, i * width, width);
  30.                         System.arraycopy(swapArray, 0, array, (height - i - 1) * width, width);
  31.                 }
  32.  
  33.                 // copy to Bitmap
  34.                 if (inBitmap == null || !inBitmap.isMutable() || inBitmap.getWidth() != width || inBitmap.getHeight() != height) {
  35.                         inBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  36.                 }
  37.  
  38.                 inBitmap.copyPixelsFromBuffer(buffer);
  39.  
  40.                 return inBitmap;
  41.         }
  42.  
Parsed in 0.038 seconds, using GeSHi 1.0.8.4
Paour
Freshman
Freshman
 
Posts: 2
Joined: Tue Jan 22, 2013 2:47 pm

Re: Possible to do OpenGL off-screen rendering in Android?

Postby Paour » Thu Jan 24, 2013 1:54 pm

Of course an even better way is to set up the GL viewport such that the offscreen buffer is oriented correctly (with GLU.gluOrtho2D).
Paour
Freshman
Freshman
 
Posts: 2
Joined: Tue Jan 22, 2013 2:47 pm

Top

Return to Android 2D/3D Graphics - OpenGL Tutorials

Who is online

Users browsing this forum: No registered users and 2 guests