Android OpenGL ES implements background plotting and saves it as bitmap

Source: Internet
Author: User

Recently, I came up with an idea on Android: how to use OpenGL ES to draw a 3D image in the background, and then save the drawn image as a bitmap format... After several days of thinking, I tried a variety of methods, but none of them worked. At first I tried the glsurfaceview method, but this would lead to a connection between my activity and the rendered stuff, the result I want is that in any case, my master acivity cannot have any relationship with the image I rendered (that is, the master acitivity cannot display anything I rendered ).

First, OpenGL ES comes from OpenGL (Lite version). Es targets embedded smart devices (embided devices), while OpenGL targets super monsters such as PCs.
This is not difficult to understand why it is "slimming". There is a concept of dual Buffering in OpenGL, that is to say, the front is displayed, and the back is painted, so that the non-blinking realm can be achieved. Therefore, in theory, we should also follow this method to draw images to the background buffer for the purpose. Here we first post an OpenGL method:

glutInitDisplayMode(GLUT_RGB|GLUT_DOUBLE);// draw ...// draw endpixeldata = (GlutByte)malloc(width*height*bytes);glReadPixels(x, y, width, height, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixeldata);

The glut package is used. The above are key functions. If you want to know how to draw bitmap, I will also describe the bitmap method below, it is nothing more than writing the read pixel value pixeldata to the bitmap file, but pay attention to two points here. One is that the pixel arrangement format of bitmap is BGR, so when you try

glReadPixels

Use the gl_bgr_ext parameter when obtaining the original pixel, and bitmap is a struct. It must be written in C and C ++ code, otherwise there is a problem with the generated bitmap file. You can check the specific format. I posted a piece of code that I found from the Internet to implement Bitmap (this is verified to be available, the person who writes this is still relatively reliable, like one), as follows:

Typedef long; typedef unsigned char byte; typedef unsigned int DWORD; typedef unsigned short word; typedef struct {word bftype; DWORD bfsize; Word bfreserved1; Word bfreserved2; DWORD bytes;} bytes; typedef struct {DWORD bisize; DWORD biwidth; DWORD biheight; Word biplanes; Word bibitcount; DWORD bicompression; DWORD bisizeimage; DWORD bixpelspermeter; DWORD biypelspermeter; DWORD Bi Clrused; DWORD biclrimportant;} BMP infoheader_t; void Init (); void display (); static glubyte * pixeldata; void snapshot (byte * pdata, int width, int height, char * filename, DWORD size) {// the first part of the bitmap. The file information is BMP fileheader_t BFH = {0}; BFH. bftype = (Word) 0x4d42; // Bm BFH. bfsize = (DWORD) (size + 54); BFH. bfreserved1 = 0; // reserved BFH. bfreserved2 = 0; // reserved BFH. bfoffbits = 54; // the second part of the bitmap, the data information BMP infoheader _ T BiH = {0}; BiH. bisize = 40; BiH. biwidth = width; BiH. biheight = height; BiH. biplanes = 1; BiH. bibitcount = 24; // 24 Real-color bitmap BiH. bicompression = 0; BiH. bisizeimage = 0; BiH. bixpelspermeter = 0; BiH. biypelspermeter = 0; BiH. biclrused = 0; BiH. biclrimportant = 0; file * fp = fopen (filename, "WB"); If (! FP) return; fwrite (& BFH. bftype, 1, 2, FP); fwrite (& BFH. bfsize, 1, 4, FP); fwrite (& BFH. bfreserved1, 1, 2, FP); fwrite (& BFH. bfreserved2, 1, 2, FP); fwrite (& BFH. bfoffbits, 1, 4, FP); fwrite (& BiH, 1, sizeof (BMP infoheader_t), FP); fwrite (pdata, 1, size, FP); fclose (FP );}

Start with the question:

First, we need to create a very general acitivity, and then add a button above. When we click the button, we start to draw images in the background, then read the pixel of the image and convert it to bitmap for saving.

If idea is ready, start working.

1. Create an acivity and follow the android engineering instructions to create an acivity in eclipse. I am an illiterate app and I know how to do it.

2. Add the button and button listening events in oncreate of the local avivity, and process initialization operations for background painting.

       btn.setOnClickListener(new OnClickListener() {        @Override             public void onClick(View v)              {                  // TODO Auto-generated method stub          //prepare init EGL environment              BackDraw = new BackDraw(); //init backdraw             Log.d("GlActivity:", "render in background");            }          });       

3. below is the concrete construction of the backdraw class. I initialize the EGL environment in the constructor of this class to create conditions for the subsequent painting rendering. It should be noted that we generally want to use OpenGL to render images or draw images through glsurfaceview. the purpose of render is to call the GL function in ondrawframe to draw an image in the background framebuffer, and then display the image to the foreground. This is generally automatic. If you do not change anything, as long as you draw a picture, you will see what you paint on the front desk. So how can I draw images in the background without automatically displaying them to the foreground? I carefully read the implementation of glsurfaceview, which is inherited from the surfaceview class, this class has the reference code we want in surfacecreate, which is not described here. I just want to say that the soul is a function.

eglCreatePbufferSurface

This function creates a off-screen framebuffer in the memory. We can draw an image on it, for details about how to use each function, refer to the function description EGL home on the official website of EGL. Here we will not describe it much. In short, we need to solve how to build a drawing environment before drawing, the question about the painting. When we started to build the environment, EGL needs to establish some attributes, such as the length and width, the byte size of pixels, and the surface type, as shown below:

        private int[] version = new int[2]; EGLConfig[] configs = new EGLConfig[1];int[] num_config = new int[1];        //EglchooseConfig used this config        int[] configSpec ={EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT, EGL10.EGL_RED_SIZE, 8,EGL10.EGL_GREEN_SIZE, 8,EGL10.EGL_BLUE_SIZE, 8,EGL10.EGL_ALPHA_SIZE, 8,EGL10.EGL_NONE  };        //eglCreatePbufferSurface used this config          int attribListPbuffer[] = {EGL10.EGL_WIDTH, 480,EGL10.EGL_HEIGHT, 800,EGL10.EGL_NONE };

It should be noted that

EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT,

If you want to create a pbuffersurface (displayed on the background) type, you need to describe this. If you want to create a windowsurface (displayed on the foreground), you need the egl10.egl _ window_bit type, in addition, we recommend that you do not add values to attribute arrays. Some functions can only accept specific values. If you add them in disorder, the function will fail during execution, for example

attribListPbuffer

It is an attribute passed in by the eglcreatepbuffersurface function when pbuffersuface is created. It only accepts three attributes. I added other attributes at the beginning, and the creation failed.

Another thing to mention is the attriblistpbuffer [] array. You must set the length and width.

EGL10.EGL_WIDTH, 480,EGL10.EGL_HEIGHT, 800,

If it is not set, the default value is 0. if you draw a picture on this surface, you will cry sadly, because no matter how you draw a picture, it will be painted to the destruction of the Earth, at last, there is only air on the surface... If you want to draw a 480*800 image, set the surface length and width accordingly.

After the attributes are configured, the following is the construction of The egl environment. Three things can be summarized:

1. Get a pbuffersurface.

2. Create a context and bind the context to the surface.

3. Use context to create a GL object and use this gl object to draw rendered images.

Here we go...

Private void initegl () {megl = (egl10) eglcontext. getegl (); egldisplay megldisplay = megl. eglgetdisplay (egl10.egl _ default_display); megl. eglinitialize (megldisplay, version); megl. eglchooseconfig (megldisplay, configspec, configs, 1, num_config); eglconfig meglconfig = configs [0]; eglcontext meglcontext = megl. eglcreatecontext (megldisplay, meglconfig, egl10.egl _ no_context, null); If (meglcontext = egl10.egl _ n O_context) {// megl. egldestroysurface (megldisplay, meglsurface); log. D ("error:", "no context") ;}// pay attention to attriblistpbuffer. The Attribute Table eglsurface meglpbsurface = megl. eglcreatepbuffersurface (megldisplay, meglconfig, attriblistpbuffer); If (meglpbsurface = egl10.egl _ no_surface) {// megl. egldestroysurface (megldisplay, meglpbsurface); int EC = megl. eglgeterror (); If (Ec = egl10.egl _ bad_display) {log. D ("error:", "egl_ba D_display ");} If (Ec = egl10.egl _ bad_display) {log. D ("error:", "egl_bad_display");} If (Ec = egl10.egl _ not_initialized) {log. D ("error:", "egl_not_initialized");} If (Ec = egl10.egl _ bad_config) {log. D ("error:", "egl_bad_config");} If (Ec = egl10.egl _ bad_attribute) {log. D ("error:", "egl_bad_attribute");} If (Ec = egl10.egl _ bad_alloc) {log. D ("error:", "egl_bad_alloc");} If (Ec = egl10.egl _ bad_match) {log. d ("Error:", "egl_bad_match") ;}} if (! Megl. eglmakecurrent (megldisplay, meglpbsurface, meglpbsurface, meglcontext) // meglpbsurface indicates that the drawing and reading pictures start from meglpbsurface {log. D ("error:", "bind failed ecode:" + megl. eglgeterror ();} gl10 GL = (gl10) meglcontext. getgl ();}

The above specific initialization process I am referring to the http://blog.sina.com.cn/s/blog_413978670100bxsl.html. Tips: Sometimes in these creation process, there will be failures, we can call

mEgl.eglGetError();

Get the error code of the last EGL function execution. By comparing the error code, you can find the error problem.

eglCreatePbufferSurface

For more information, see.

Well, now we have all of them. Next we will start to draw images on your "paper". I will not say much about the specific painting. It's nothing more than GL. clear ....

OK, now that the picture is finished, it is time to save the image. The pixel values of these images are saved in the Pb framebuffer we just created, that is, the off-screen surface that exists in the memory. the following figure shows how to read the pixels in the current framebuffer in the same way as OpenGL. the glreadpixels function is implemented as follows. Note that its pixel parameter is of the intbuffer type.

IntBuffer PixelBuffer = IntBuffer.allocate(width*height);PixelBuffer.position(0);gl.glReadPixels(0, 0, width, height, GL10.GL_RGBA, GL10.GL_UNSIGNED_BYTE, PixelBuffer); 

For more information about how to use this function parameter, see the function description.

Now, pixels in framebuffer have been read to pixelbuffer. It must be noted that, because I am in the rgba format, one pixel is 4 bytes. If it is RGB, It is 3 bytes, pay attention to the memory allocation.

The original pixel is ready, and the bitmap is finally drawn. Android has a method for creating bitmap, as shown below:

Pixelbuffer. position (0); // reset the read/write position to int pix [] = new int [width * Height]; pixelbuffer. get (pix); // This is to assign the value of data in intbuffer to the bitmap BMP = bitmap in the PIX array. createbitmap (pix, width, height, bitmap. config. argb_8888); // pix is the preceding pixel fileoutputstream Fos = NULL; try {Fos = new fileoutputstream ("/sdcard/screen.png "); // pay attention to the sdcard read and write permissions of the app} catch (filenotfoundexception e) {// todo auto-generated Catch Block E. printstacktrace ();} BMP. compress (compressformat. PNG, 100, FOS); // compress into PNG, 100% display effect try {FOS. flush ();} catch (ioexception e) {// todo auto-generated Catch Block E. printstacktrace ();}}

Okay. It's okay to finish the job.

PS: I have just studied these things and may still be incomplete for reference only. If you have any questions, I hope you can kindly identify them. Thank you.


Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.